'Package browser', 'description' => 'Configure package browser', 'page callback' => 'system_admin_menu_block_page', 'access arguments' => array('access apk browser'), //system_admin_menu_block_page needs admin inc 'file' => 'system.admin.inc', 'file path' => drupal_get_path('module', 'system'), ); $items['admin/config/apk_browser/settings'] = array( 'title' => 'Settings', 'description' => 'Configure global APK browser settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('apk_browser_settings'), 'access arguments' => array('access apk browser'), ); $items['admin/config/apk_browser/repositories'] = array( 'title' => 'Repositories', 'description' => 'Configure APK repositories', 'page callback' => 'apk_browser_repositories_list', 'access arguments' => array('access apk browser'), ); $items['admin/config/apk_browser/repositories/add'] = array( 'title' => 'Add repository', 'description' => 'Add repositoriy', 'page callback' => 'drupal_get_form', 'page arguments' => array('apk_browser_repository_edit_form'), 'access arguments' => array('access apk browser'), ); $items['admin/config/apk_browser/repositories/%/edit'] = array( 'title' => 'Edit repository', 'description' => 'Edit repositoriy', 'page callback' => 'drupal_get_form', 'page arguments' => array('apk_browser_repository_edit_form', 4), 'access arguments' => array('access apk browser'), 'access callback' => TRUE, ); $items['admin/config/apk_browser/repositories/%/delete'] = array( 'title' => 'Delete repository', 'description' => 'Delete repositoriy', 'page callback' => 'drupal_get_form', 'page arguments' => array('apk_browser_repository_delete_form', 4), 'access arguments' => array('access apk browser'), 'access callback' => TRUE, ); $items['admin/config/apk_browser/import'] = array( 'title' => 'Import packages', 'description' => 'Import APK packages', 'page callback' => 'apk_browser_importer', 'access arguments' => array('access apk browser'), ); return $items; } function apk_browser_permission() { return array( 'access apk browser' => array( 'title' => t('Access APK browser'), 'description' => t('Access permissions for APK browser'), ), ); } function apk_browser_settings($form) { $form['settings'] = array( '#type' => 'fieldset', '#title' => t('APK browser settings'), ); $form['settings']['apk_import_delete_all_apk_limit'] = array( '#type' => 'textfield', '#size' => '4', '#title' => t('Node delete batch limit'), '#description' => t('While deleting all packages, we need to limit the batch process. If it times out please lower this value.'), '#default_value' => variable_get('apk_import_delete_all_apk_limit', '20') ); $form['settings']['apk_import_commit_url'] = array( '#type' => 'textfield', '#title' => t('Commit URL'), '#description' => t('URL to construct a link to repository browser. Use %commit% to be replaced with the actual commit'), '#default_value' => variable_get('apk_import_commit_url', '') ); $form['settings']['apk_import_status'] = array( '#type' => 'checkbox', '#title' => t('Enable apk import'), '#description' => t('Enable or disable package importing and cleanup'), '#default_value' => variable_get('apk_import_status', '0') ); $feeds = array('0' => t('Disabled')); if (module_exists('aggregator')) { $feeds += db_query("SELECT fid,title FROM {aggregator_feed}")->fetchAllKeyed(); $disabled = FALSE; $description = t('When enabled, commit title will be searched in this aggregator feed.'); } else { $disabled = TRUE; $description = t('You need to have aggregator enabled and configured to enable this.'); } $form['settings']['apk_import_aggregator'] = array( '#type' => 'select', '#title' => t('RSS commit lookup'), '#description' => $description, '#default_value' => variable_get('apk_import_aggregator', '0'), '#options' => $feeds, '#disabled' => $disabled, ); return system_settings_form($form); } function apk_browser_cron() { if (variable_get('apk_import_status', '0')) { apk_browser_importer(); //cleanup old packages ones in 24h if ((time() - variable_get('apk_browser_cleanup', '0')) > '86400') { apk_browser_cleanup(); } //set maintainer if users signs up after the package has been imported if ((time() - variable_get('apk_browser_assign_maintainer', '0')) > '86400') { apk_browser_assign_maintainer(); } } } function apk_browser_repository_delete_form($form, &$form_state, $rid) { $repos = variable_get('apk_repositories', array()); if (array_key_exists($rid, $repos)) { $form['repo_id'] = array('#type' => 'hidden', '#value' => $rid); return confirm_form($form, t('Are you sure you want to delete repository with ID @ID?', array('@ID' => $rid)), 'admin/config/apk_browser/repositories', t('This action cannot be undone.'), t('Delete repository'), t('Cancel')); } else { drupal_set_message(t('Please select an existing repository ID'), 'error'); drupal_goto('admin/config/apk_browser/repositories'); } } function apk_browser_repository_delete_form_submit($form, &$form_state) { $repos = variable_get('apk_repositories', array()); unset($repos[$form_state['values']['repo_id']]); variable_set('apk_repositories', array_values($repos)); drupal_set_message(t('Repository deleted')); $form_state['redirect'] = 'admin/config/apk_browser/repositories'; } function apk_browser_repository_edit_form($form, &$form_state, $rid = array()) { $repos = variable_get('apk_repositories', array()); if (is_string($rid)) { $form['repo_id'] = array('#type' => 'hidden', '#value' => $rid); if (!array_key_exists($rid, $repos)) { drupal_set_message(t('Please select an existing repository ID'), 'error'); drupal_goto('admin/config/apk_browser/repositories'); } } $repo = taxonomy_vocabulary_machine_name_load('apk_repo'); $repo_tree = taxonomy_get_tree($repo->vid); $arch = taxonomy_vocabulary_machine_name_load('apk_arch'); $arch_tree = taxonomy_get_tree($arch->vid); if (empty($arch_tree) || empty($repo_tree)) { $form['warning'] = array( '#type' => 'item', '#markup' => '
Please add terms to you your repositories and architectures
' . l('Manage Taxonomy', 'admin/structure/taxonomy') ); } else { $form['settings'] = array( '#type' => 'fieldset', '#title' => t('Repository settings'), ); $form['settings']['apk_repo_url'] = array( '#type' => 'textfield', '#title' => t('Repository URL'), '#size' => '100', '#default_value' => is_string($rid) ? $repos[$rid]['url'] : '', '#description' => t('Link to the APKINDEX.tar.gz file inside the repository'), '#required' => TRUE, ); foreach ($repo_tree as $key => $term) { $repo_options[$term->tid] = $term->name; } //generate a form item to select terms $form['settings']['repo'] = array( '#type' => 'select', '#options' => $repo_options, '#title' => 'Select repository', '#default_value' => is_string($rid) ? $repos[$rid]['repo'] : '', '#description' => t('Please make sure you select the correct repository. Preventing to do so will break package listing') ); foreach ($arch_tree as $key => $term) { $arch_options[$term->tid] = $term->name; } $form['settings']['arch'] = array( '#type' => 'select', '#options' => $arch_options, '#default_value' => is_string($rid) ? $repos[$rid]['arch'] : '', '#title' => 'Select architecture', '#description' => t('Please make sure you select the correct architecture. Preventing to dox so will break package listing') ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save repository') ); } return $form; } function apk_browser_repository_edit_form_validate($form, &$form_state) { if (!valid_url($form_state['values']['apk_repo_url'], TRUE)) { form_set_error('apk_repo_url', t('Please specify a valid URL!')); } //foreach (variable_get('apk_repositories', array()) as $repo) { // if ($form_state['values']['apk_repo_url'] == $repo['url']) { // form_set_error('apk_repo_url', t('You are trying to add a double url')); // } // } } function apk_browser_repository_edit_form_submit($form, &$form_state) { $repos = array( 'url' => $form_state['values']['apk_repo_url'], 'arch' => $form_state['values']['arch'], 'repo' => $form_state['values']['repo'] ); $repo_db = variable_get('apk_repositories', array()); isset($form_state['values']['repo_id']) ? ( $repo_db[$form_state['values']['repo_id']] = $repos) : ($repo_db[] = $repos); variable_set('apk_repositories', $repo_db); drupal_set_message(t('Repositories are saved')); $form_state['redirect'] = 'admin/config/apk_browser/repositories'; //drupal_set_message(t('Submitting values: @values', array('@values' => var_export($form_state, TRUE)))); } function apk_browser_repositories_list() { $terms = ''; $header = array('ID', 'URL', 'Repo', 'Arch', array('data' => 'Manage', 'colspan' => '2')); $add = l('', 'admin/config/apk_browser/repositories/add', array('html' => TRUE)); foreach (variable_get('apk_repositories', array()) as $key => $repo) { $edit = l('', 'admin/config/apk_browser/repositories/' . $key . '/edit', array('html' => TRUE)); $delete = l('', 'admin/config/apk_browser/repositories/' . $key . '/delete', array('html' => TRUE)); $terms = '' . t('None') . '
'; //prepare depends fields $depends = ''; if (!empty($vars['apk_depends'])) { foreach ($vars['apk_depends'] as $dep) { $value = trim($dep['value'], '!'); $value = preg_split("/[<>=]/", $value); $depends .= '' . l($dep['value'], 'apk/' . $vars['taxonomy_apk_repo']['0']['taxonomy_term']->name . '/' . $vars['taxonomy_apk_arch']['0']['taxonomy_term']->name . '/' . $value[0], array()) . '
'; } } else { $depends = $empty; } //prepare install if fields $installif = ''; if (!empty($vars['apk_install_if'])) { foreach ($vars['apk_install_if'] as $iif) { $value = trim($iif['value'], '!'); $value = preg_split("/[<>=]/", $value); $installif .= '' . l($iif['value'], 'apk/' . $vars['taxonomy_apk_repo']['0']['taxonomy_term']->name . '/' . $vars['taxonomy_apk_arch']['0']['taxonomy_term']->name . '/' . $value[0], array()) . '
'; } } //prepare origin link if (!empty($vars['apk_origin'])) { $value = trim($vars['apk_origin'][0]['value'], '!'); $value = preg_split("/[<>=]/", $value); $origin = l($vars['apk_origin'][0]['safe_value'], 'apk/' . $vars['taxonomy_apk_repo']['0']['taxonomy_term']->name . '/' . $vars['taxonomy_apk_arch']['0']['taxonomy_term']->name . '/' . $vars['apk_origin'][0]['value'] ); } else { $origin = $empty; } //prepare all fields $fields[t('Package')] = $vars['title']; $fields[t('Version')] = $vars['apk_version'][0]['safe_value']; $fields[t('Architecture')] = $vars['taxonomy_apk_arch'][0]['taxonomy_term']->name; $fields[t('Repository')] = $vars['taxonomy_apk_repo'][0]['taxonomy_term']->name; $fields[t('Project')] = l('URL', $vars['apk_url'][0]['safe_value']); $fields[t('Description')] = $vars['apk_description'][0]['safe_value']; $fields[t('License')] = $vars['apk_license'][0]['safe_value']; $fields[t('Maintainer')] = empty($vars['apk_maintainer']) ? $empty : $vars['apk_maintainer'][0]['safe_value']; $fields[t('Build date')] = empty($vars['apk_build_time']) ? $empty : date('r', $vars['apk_build_time'][0]['value']); $fields[t('Commit')] = empty($vars['apk_commit']) ? $empty : apk_browser_commit_url($vars['apk_commit']['0']['value']); //only show origin if its different then itself if (!empty($vars['apk_origin']) && ($vars['apk_origin'][0]['value'] != $vars['title'])) { $fields[t('Origin')] = $origin; } //to prevent misunderstanding only display when needed. $fields[t('Depends')] = $depends; if (!empty($installif)) { $fields[t('Install if')] = $installif; } //setup array for theme function foreach ($fields as $header => $cell) { $table['rows'][]['data'] = array(array('data' => $header, 'header' => 'header'), $cell); } //render the table return theme('table', $table); } //create template variable with package table function apk_browser_preprocess_node(&$vars) { if ($vars['node']->type == 'apk') { $vars['apk_table'] = apk_browser_package_table($vars); } } function apk_browser_origin_list() { //Get a loaded object from a router item. $node = menu_get_object(); //Some older packages are missing origin field. if (isset($node->apk_origin)) { $rows = db_query(" SELECT node.title FROM {node} LEFT JOIN {field_data_apk_origin} as origin ON (node.nid = origin.entity_id) LEFT JOIN {field_data_taxonomy_apk_arch} as arch ON (node.nid = arch.entity_id) LEFT JOIN {field_data_taxonomy_apk_repo} as repo ON (node.nid = repo.entity_id) WHERE origin.apk_origin_value = :origin AND arch.taxonomy_apk_arch_tid = :arch AND repo.taxonomy_apk_repo_tid = :repo AND node.status = '1'", array( ':origin' => $node->apk_origin['und']['0']['value'], ':arch' => $node->taxonomy_apk_arch['und']['0']['tid'], ':repo' => $node->taxonomy_apk_repo['und']['0']['tid'] ) )->fetchCol(); if ($rows) { foreach ($rows as $key => $row) { $link = l($row, 'apk/' . $node->taxonomy_apk_repo['und']['0']['taxonomy_term']->name . '/' . $node->taxonomy_apk_arch['und']['0']['taxonomy_term']->name . '/' . $row ); $list['items'][]['data'] = $link; } return theme('item_list', $list); } } return FALSE; } function apk_browser_reverse_depends() { $node = menu_get_object(); $rows = db_query(" SELECT node.title FROM {node} LEFT JOIN {field_data_apk_depends} as deps ON (node.nid = deps.entity_id) LEFT JOIN {field_data_taxonomy_apk_arch} as arch ON (node.nid = arch.entity_id) LEFT JOIN {field_data_taxonomy_apk_repo} as repo ON (node.nid = repo.entity_id) WHERE deps.apk_depends_value = :deps AND arch.taxonomy_apk_arch_tid = :arch AND repo.taxonomy_apk_repo_tid = :repo AND node.status = '1' ", array( ':deps' => $node->title, ':arch' => $node->taxonomy_apk_arch['und']['0']['tid'], ':repo' => $node->taxonomy_apk_repo['und']['0']['tid'] ) )->fetchCol(); if ($rows) { foreach ($rows as $key => $row) { $link = l($row, 'apk/' . $node->taxonomy_apk_repo['und']['0']['taxonomy_term']->name . '/' . $node->taxonomy_apk_arch['und']['0']['taxonomy_term']->name . '/' . $row ); $list['items'][]['data'] = $link; } return theme('item_list', $list); } else { return FALSE; } } function apk_browser_block_info() { $blocks['apk_browser_origin'] = array( 'info' => t('Packages with same origin'), 'visibility' => BLOCK_VISIBILITY_LISTED, // This block should only display on pages starting with apk. 'pages' => 'apk/*' ); $blocks['apk_browser_reverse_depends'] = array( 'info' => t('Package reverse dependicies'), 'visibility' => BLOCK_VISIBILITY_LISTED, 'pages' => 'apk/*' ); return $blocks; } function apk_browser_block_view($delta = '') { $js = 'jQuery(document).ready(function() { jQuery(".block-apk-browser .block-content").hide(); jQuery(".block-apk-browser .block-title").click(function() { jQuery(this).next(".block-apk-browser .block-content").slideToggle(500); return FALSE; }); });'; //The $delta parameter tells us which block is being requested. switch ($delta) { case 'apk_browser_origin': $origin = apk_browser_origin_list(); if ($origin) { $block['subject'] = t('Same origin'); $block['content'] = $origin; } break; case 'apk_browser_reverse_depends': $revdep = apk_browser_reverse_depends(); if ($revdep) { $block['subject'] = t('Reverse dependecies'); $block['content'] = $revdep; } } // If one of the functions above returns true, we add collapse script if (($origin) || ($revdep)) { drupal_add_js($js, 'inline'); } return ($block['content']) ? $block : FALSE; } // If possible convert the maintainer field to a valid email address // and return the drupal uid function apk_browser_maintainer_to_user($maintainer) { $email = mailparse_rfc822_parse_addresses($maintainer); if (filter_var($email[0]['address'], FILTER_VALIDATE_EMAIL)) { return db_query("SELECT uid FROM {users} WHERE mail = :mail AND status = '1'", array(':mail' => $email[0]['address']) )->fetchField(); } return FALSE; } function apk_browser_assign_maintainer() { $updated = array(); $rows = db_query("SELECT entity_id, apk_maintainer_value FROM {field_data_apk_maintainer}")->fetchAllKeyed(0, 1); foreach ($rows as $nid => $maintainer) { $uid = apk_browser_maintainer_to_user($maintainer); if ($uid) { $updated[] = db_update('node') ->fields(array('uid' => $uid)) ->condition('nid', $nid) ->condition('uid', $uid, '!=') ->execute(); } } variable_set('apk_browser_assign_maintainer', time()); watchdog('apk', 'Updated @qty package maintainers', array('@qty' => count(array_filter($updated))), WATCHDOG_INFO, NULL); }