$term) { if ($term->depth == 0) { $types[] = $term->name; } elseif (($term->depth == 1) && ($index == 1) && (count($categories)< 3)) { $categories[] = $term->name; } } if (count($categories)) { $help = t('
Properly categorizing your project will help ensure that users can find it easily. Please follow these steps:
The project module makes special use of the taxonomy (category) system. A special vocabulary, "Projects", has been created automatically.
To take full advantage of project categorization, add at least two levels of terms to this vocabulary. The first level will be the basic project types, e.g., "modules", "themes", "translations".
Subterms of each of these types will be the categories that users can select to classify the projects. For example, "modules" might have sub-terms including "mail" and "XML".
Use the vocabulary admin page to view and add terms.
', array('!taxonomy-admin' => url('admin/content/taxonomy/' . $vid))); } break; } } /** * Implementation of hook_block(). */ function project_block($op = 'list', $delta = 0) { if ($op == 'list') { $blocks[0]['info'] = t('Project navigation'); return $blocks; } else if ($op == 'view') { $block['subject'] = t('Project navigation'); $block['content'] = drupal_get_form('project_quick_navigate_form'); return $block; } } function project_node_info() { return array( 'project_project' => array( 'name' => t('project'), 'module' => 'project_project', 'description' => t('A project is something a group is working on. It can optionally have issue tracking, integration with revision control systems, releases, and so on.' ), ), ); } function project_perm() { $perms = array( 'administer projects', 'maintain projects', 'access projects', 'access own projects', ); return $perms; } /** * Callback for the main settings page. */ function project_settings_form() { $sort_methods = drupal_map_assoc(array_keys(module_invoke_all('project_sort_methods', 'methods'))); if (project_use_taxonomy()) { // For now, date-based browsing doesn't work once you disable // taxonomy (only because the code involved is rather complicated // and needs to be majorly refactored and cleaned up). $form['project_sort_method'] = array( '#type' => 'radios', '#title' => t('Default sort option'), '#default_value' => variable_get('project_sort_method', 'category'), '#options' => $sort_methods, '#description' => t('Default sorting option to use on the overview page'), ); $form['sort_methods'] = array( '#type' => 'fieldset', '#title' => t('Enabled sorting options'), ); $tree = taxonomy_get_tree(_project_get_vid(), 0 , -1, 1); foreach ($tree as $term) { $form['sort_methods']['project_sort_method_used_' . $term->tid] = array( '#type' => 'checkboxes', '#title' => $term->name, '#default_value' => variable_get('project_sort_method_used_' . $term->tid, array_keys($sort_methods)), '#options' => $sort_methods, '#description' => t('Sorting options to enable for %term', array('%term' => $term->name)) ); } } $form['project_browse_nodes'] = array( '#type' => 'select', '#title' => t('Number of projects to list in paged browsing'), '#default_value' => variable_get('project_browse_nodes', 30), '#options' => drupal_map_assoc(array(5, 10, 15, 20, 25, 30, 35, 40, 45, 50)), '#description' => t('The default maximum number of projects to list when browsing lists, e.g., by category.') ); if (module_exists('forum') && project_use_taxonomy()) { $form['project_support_forum'] = taxonomy_form(_forum_get_vid(), variable_get('project_support_forum', ''), t('Select the support forum for projects'), 'project_support_forum'); } // Custom submit handler $form['#submit']['project_settings_form_extra_submit'] = array(); // system_settings_form() doesn't add the default if #submit already exists. $form['#submit']['system_settings_form_submit'] = array(); return system_settings_form($form); } function project_settings_form_extra_submit($form_id, $form_values) { // If the project_sort_method setting has changed, the menu will // need to be rebuilt. menu_rebuild(); } function project_link($type, $node = 0, $main = 0) { $links = array(); switch ($type) { case 'page': if (user_access('access projects')) { $links['project_view_all'] = array( 'title' => t('Projects'), 'href' => 'project', 'attributes' => array('title' => t('View all projects.')), ); } break; } return $links; } /** * Returns the vocabulary id for projects. */ function _project_get_vid() { $vid = variable_get('project_vocabulary', ''); if (empty($vid)) { // Check to see if a project module vocabulary exists. $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module='%s'", 'project')); if (!$vid) { $edit = array('name' => t('Projects'), 'multiple' => 1, 'hierarchy' => 1, 'relations' => 0, 'module' => 'project', 'nodes' => array('project_project' => 1)); // If there is already a vocabulary assigned to 'project_project' nodes, use it. $vocabularies = taxonomy_get_vocabularies('project_project'); if (count($vocabularies)) { $vocabulary = reset($vocabularies); $edit['vid'] = $vocabulary->vid; } taxonomy_save_vocabulary($edit); $vid = $edit['vid']; } variable_set('project_vocabulary', $vid); } return $vid; } /** * Implementation of hook_term_path(). */ function project_term_path($term) { // The path must include the first-level term name for this term. $tree = taxonomy_get_tree(_project_get_vid()); $parents = taxonomy_get_parents_all($term->tid); foreach($parents as $parent) { $ancestors[] = $parent->tid; } foreach ($tree as $t) { if (in_array($t->tid, $ancestors) && $t->depth == 0) { $termname = $t->name; if ($term->name == $termname) { return "project/$termname"; } break; } } return "project/$termname/category/$term->tid"; } /** * Implementation of hook_taxonomy(). */ function project_taxonomy($op, $type, $object = NULL) { if ($op == 'delete' && $type == 'vocabulary' && $object->vid == _project_get_vid()) { variable_del('project_vocabulary'); } elseif ($type == 'term' && $object->vid == _project_get_vid()) { menu_rebuild(); } } function project_menu($may_cache) { $items = array(); global $user; if ($may_cache) { // User pages: $access = user_access('access projects'); $items[] = array('path' => 'project', 'title' => t('Projects'), 'callback' => 'project_page_overview', 'access' => $access, 'type' => MENU_NORMAL_ITEM); $items[] = array('path' => 'project/autocomplete', 'title' => t('Autocomplete project'), 'callback' => 'project_autocomplete', 'access' => $access, 'type' => MENU_CALLBACK); // Project browsing pages if (project_use_taxonomy()) { $sort_methods = module_invoke_all('project_sort_methods', 'methods'); $terms = taxonomy_get_tree(_project_get_vid()); $releases = variable_get('project_release_browse_versions', 0); foreach ($terms as $i => $term) { // Only use the first-level terms. if ($term->depth == 0) { $items[] = array('path' => 'project/'. $term->name, 'title' => $term->name, 'access' => $access, 'type' => MENU_NORMAL_ITEM, 'weight' => $term->weight, 'callback arguments' => array($term->name)); $j = 0; $term_methods = array_filter(variable_get('project_sort_method_used_' . $term->tid, $sort_methods)); foreach ($term_methods as $sort_method) { $items[] = array('path' => 'project/' . $term->name . '/'. $sort_method, 'title' => t('Browse by !sort_method', array('!sort_method' => $sort_method)), 'access' => $access, 'type' => (($sort_method == variable_get('project_sort_method', 'category') || (($j == 0) && !in_array(variable_get('project_sort_method', 'category'), $term_methods))) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK), 'weight' => ($sort_method == variable_get('project_sort_method', 'category')) ? -10 : $j, 'callback arguments' => array($term->name, $sort_method)); $j++; } } } } // Developers $items[] = array('path' => 'project/developers', 'title' => t('Developers'), 'callback' => 'project_developers', 'access' => $access, 'type' => MENU_CALLBACK); // CVS messages: $items[] = array('path' => 'project/cvs', 'title' => t('CVS'), 'callback' => 'project_cvs', 'access' => $access, 'type' => MENU_CALLBACK); } else { drupal_add_css(drupal_get_path('module', 'project') .'/project.css'); if (arg(0) == 'node' && is_numeric(arg(1))) { $node = node_load(arg(1)); if ($node->type == 'project_project' && node_access('update', $node) && (module_exists('project_issue') || module_exists('project_release'))) { $items[] = array( 'path' => 'node/'. arg(1) .'/edit/project', 'title' => t('Project'), 'callback' => 'node_page', 'weight' => -5, 'type' => MENU_DEFAULT_LOCAL_TASK, ); } } $items[] = array( 'path' => 'admin/project', 'title' => t('Project administration'), 'description' => t('Administrative interface for project management and related modules.'), 'callback' => 'system_admin_menu_block_page', 'access' => user_access('administer projects'), 'type' => MENU_NORMAL_ITEM, ); $items[] = array( 'path' => 'admin/project/project-settings', 'title' => t('Project settings'), 'description' => t('Configure the behavior and appearance of the project browsing pages and other settings for the Project module.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('project_settings_form'), 'access' => user_access('administer projects'), 'type' => MENU_NORMAL_ITEM, ); } return $items; } function project_check_admin_access($project, $cvs_access = TRUE) { global $user; if (empty($user->uid)) { return FALSE; } $project_obj = is_numeric($project) ? node_load($project) : $project; if (!isset($project_obj) || $project_obj->type != 'project_project') { return FALSE; } if (user_access('administer projects')) { return TRUE; } if (user_access('maintain projects')) { if ($user->uid == $project_obj->uid) { return TRUE; } if (module_exists('cvs') && $cvs_access) { if (db_num_rows(db_query("SELECT * FROM {cvs_project_maintainers} WHERE uid = %d AND nid = %d", $user->uid, $project_obj->nid))) { return TRUE; } } } return FALSE; } /** * Implementation of form_alter. This removes the work of * taxonomy.module's form_alter() so we can do our own taxonomy * selection. */ function project_form_alter($form_id, &$form) { if ($form_id == 'project_project_node_form') { $vid = _project_get_vid(); if (isset($form['taxonomy'][$vid])) { unset($form['taxonomy'][$vid]); } // If there are no children elements, we should unset the entire // thing so we don't end up with an empty fieldset. if (!element_children($form['taxonomy'])) { unset($form['taxonomy']); } // If the form has an element for specifying a URL alias, we want // to alter it, since we're just going to automatically override // the specified value. if (isset($form['path'])) { $url_alias = $form['path']['path']['#default_value']; if (empty($url_alias)) { unset($form['path']); } else { unset($form['path']['path']); $form['path']['value'] = array('#value' => t('Automatically generated path alias: %url', array('%url' => $url_alias))); } } } } /** * hook_nodeapi() implementation. This just decides what type of node * is being passed, and calls the appropriate type-specific hook. * * @see project_project_nodeapi(). */ function project_nodeapi(&$node, $op, $arg) { switch ($node->type) { case 'project_project': project_project_nodeapi($node, $op, $arg); break; } } function project_page() { global $user; switch (arg(1)) { default: project_page_overview(); break; } } function project_page_overview($termname = NULL, $sort_method = NULL) { global $form_values; project_project_set_breadcrumb(); $sort_methods = module_invoke_all('project_sort_methods', 'methods'); if (module_exists('project_release') && variable_get('project_release_browse_versions', 0)) { $version_form = drupal_get_form('project_release_version_filter_form', $version); $output .= $version_form; // Read in requested version, if any. $version = isset($_SESSION['project_version']) ? $_SESSION['project_version'] : variable_get('project_release_overview', -1); } // If browsing by taxonomy, only fetch projects for this term. if (project_use_taxonomy()) { $vid = _project_get_vid(); if ($termname) { $type = db_fetch_object(db_query(db_rewrite_sql("SELECT t.tid, t.* FROM {term_data} t WHERE t.vid = %d AND t.name = '%s'", 't', 'tid'), $vid, $termname)); if (!$sort_method) { // For the default, we use the overall default if enabled for this term, and if not we use the first enabled method. $term_methods = array_filter(variable_get('project_sort_method_used_' . $type->tid, array_keys($sort_methods))); $sort_method = in_array(variable_get('project_sort_method', 'category'), $term_methods) ? variable_get('project_sort_method', 'category') : array_shift($term_methods); } if ($type->description) { $output .= '' . filter_xss($type->description) . '
'; } $module = $sort_methods[$sort_method]; if (module_invoke($module, 'project_sort_methods', 'set term', $sort_method)) { $tree = taxonomy_get_tree(_project_get_vid(), $type->tid); $terms = array(); if ($tree) { $tids = array(); if (variable_get('project_release_browse_versions', 0) && $version != -1) { // Find all terms associated with the requested version. $result = db_query("SELECT tp.tid, COUNT(DISTINCT(n.nid)) AS count FROM {term_node} tp INNER JOIN {project_release_nodes} p ON tp.nid = p.pid INNER JOIN {node} n ON n.nid = p.pid INNER JOIN {term_node} tr ON tr.nid = p.nid WHERE tr.tid = %d AND (p.file_path <> '') AND (n.status = 1) GROUP BY tp.tid", $version); $tids = array(); while ($item = db_fetch_object($result)) { $tids[$item->tid] = $item->count; } } foreach ($tree as $cterm) { if (!variable_get('project_release_browse_versions', 0) || ($version == -1) || array_key_exists($cterm->tid, $tids)) { if ($tids[$cterm->tid]) { $cterm->count = $tids[$cterm->tid]; } else { // We don't use taxonomy_term_count_nodes() because it includes child terms' counts. $result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND t.tid = %d GROUP BY t.tid'), $cterm->tid); $term = db_fetch_object($result); $cterm->count = $term->c; } $terms[] = $cterm; } } } if (arg(3) && is_numeric(arg(3))) { $term = taxonomy_get_term(arg(3)); } } // Set the default elements that will be used to construct the SQL statement. $sql_elements = array( 'fields' => array( 'prefix' => 'SELECT ', 'glue' => ', ', 'pieces' => array( 'DISTINCT(n.nid)', 'n.title', 'nr.teaser', 'nr.format', ) ), 'from' => array( 'prefix' => ' FROM ', 'glue' => NULL, 'pieces' => array( '{node} n ' ) ), 'joins' => array( 'prefix' => '', 'glue' => ' ', 'pieces' => array( 'INNER JOIN {node_revisions} nr ON n.vid = nr.vid', 'INNER JOIN {project_projects} p ON n.nid = p.nid', 'LEFT JOIN {term_node} r ON n.nid = r.nid' ) ), 'wheres' => array( 'prefix' => ' WHERE ', 'glue' => ' AND ', 'pieces' => array( 'n.status = 1', 'r.tid = %d' ) ), 'group_bys' => array( 'prefix' => ' GROUP BY ', 'glue' => ', ', 'pieces' => array( ) ), 'order_bys' => array( 'prefix' => ' ORDER BY ', 'glue' => ', ', 'pieces' => array( ) ), 'parameters' => array( 'prefix' => NULL, 'glue' => NULL, 'pieces' => array( isset($term) ? $term->tid : $type->tid ) ) ); // If the site has enabled issue tracking via the project_issue // module, we want to add 1 more field and JOIN to our query so // we can provide the link for "Bugs and feature requests"... if (module_exists('project_issue')) { $sql_elements['fields']['pieces'][] = 'pip.issues'; $sql_elements['joins']['pieces'][] = 'INNER JOIN {project_issue_projects} pip ON n.nid = pip.nid'; } // Only the 'pieces' are returned; the prefix and glue elements remain unchanged. $sql_settings = module_invoke($module, 'project_sort_methods', 'sql_settings', $sort_method); if (variable_get('project_release_browse_versions', 0)) { $release_settings = array( 'fields' => array('prn.file_path', 'prn.version', 'MAX(prn.file_date) AS changed', 'COUNT(*) AS release_count'), 'wheres' => array("prn.file_path <> ''"), 'group_bys' => array('n.nid'), ); if ($version != -1) { $release_settings['joins'] = array( 'INNER JOIN {project_release_nodes} prn ON n.nid = prn.pid', 'INNER JOIN {term_node} tr ON tr.nid = prn.nid' ); $release_settings['wheres'][] = 'tr.tid = %d'; $release_settings['parameters'][] = $version; } else { $release_settings['joins'] = array( 'INNER JOIN {project_release_nodes} prn ON n.nid = prn.pid', 'INNER JOIN {project_release_projects} prp ON n.nid = prp.nid', ); $release_settings['fields'][] = 'prp.snapshot_table'; // We need to grab the uid and type for the project node, so // that node_access() will work properly without a full // node_load() when we're generating the download table in // project_release_table(). $release_settings['fields'][] = 'n.uid'; $release_settings['fields'][] = 'n.type'; } $sql_settings = array_merge_recursive($sql_settings, $release_settings); } // Merge in $sql_elements if (is_array($sql_settings)) { foreach(array_keys($sql_settings) as $key) { $sql_elements[$key]['pieces'] = array_merge($sql_elements[$key]['pieces'], $sql_settings[$key]); $sql_elements[$key]['pieces'] = array_unique($sql_elements[$key]['pieces']); } } $parameters = $sql_elements['parameters']['pieces']; $sql = project_build_query($sql_elements); $pager = module_invoke($module, 'project_sort_methods', 'pager', $sort_method); if ($pager) { $first_field = array_shift($sql_elements['fields']['pieces']); if (count($sql_elements['group_bys']['pieces'])) { $first_field = 'DISTINCT(' . $sql_elements['group_bys']['pieces'][0] . ')'; $sql_elements['group_bys']['pieces'] = array(); } $sql_elements['fields']['pieces'] = array("COUNT($first_field)"); $count_query = project_build_query($sql_elements); $result = pager_query($sql, variable_get('project_browse_nodes', 30), 0, $count_query, $parameters); } else { $result = db_query($sql, $parameters); } // If we've just switched versions, we may have a term requested but no matching projects. // In that case, we returned an uncollapsed fieldset. if (module_invoke($module, 'project_sort_methods', 'set term', $sort_method)) { $output .= theme('fieldset', array('#title' => t('Categories'), '#collapsible' => TRUE, '#collapsed' => (arg(3) && db_num_rows($result)) ? TRUE : FALSE, '#children' => theme('project_term_list', $terms, "project/$termname/$sort_method"))); if (!arg(3) || !db_num_rows($result)) { return $output; } drupal_set_title(t('@project_type: %category', array('@project_type' => $type->name, '%category' => $term->name))); } else { drupal_set_title(check_plain($type->name)); } } // If taxonomy is enabled but no termname is selected, show a list of terms to choose from. else { $tree = taxonomy_get_tree($vid, 0, -1, 1); $items = array(); foreach ($tree as $term) { $items[] = theme('project_type', $term); } drupal_set_title(t('Project types')); return theme('item_list', $items); } } // If taxonomy is not enabled, fetch all projects else { // If the site has enabled issue tracking via the project_issue // module, we want to add 1 more field and JOIN to our query so // we can provide the link for "Bugs and feature requests"... if (module_exists('project_issue')) { $ISSUES = ', pip.issues'; $ISSUE_JOIN ='INNER JOIN {project_issue_projects} pip ON n.nid = pip.nid'; } $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, nr.teaser, nr.format$ISSUES FROM {node} n INNER JOIN {node_revisions} nr ON n.vid = nr.vid $ISSUE_JOIN WHERE n.status = 1 AND n.type = 'project_project' ORDER BY n.title ASC")); } $class = 'even'; if ($version != -1) { $joins = 'INNER JOIN {term_node} t ON p.nid = t.nid LEFT JOIN {project_release_default_versions} prdv ON p.pid = prdv.nid AND prdv.tid = t.tid AND prdv.major = p.version_major'; $wheres = 'AND t.tid = %d'; $order_bys = 'ORDER BY prdv.nid DESC, p.rebuild ASC, p.version_major ASC, p.version_minor DESC, p.version_patch DESC, p.file_date DESC'; } else { $joins = ''; $wheres = ''; $order_bys = 'ORDER BY p.file_date DESC'; } while ($project = db_fetch_object($result)) { $project->body = check_markup($project->teaser, $project->format, FALSE); if (project_use_taxonomy()) { $project->term = $termname; $project->terms = array(); foreach (taxonomy_node_get_terms($project->nid) as $term) { if ($term->name != $termname) { $project->terms[$term->name] = array( 'title' => $term->name, 'href' => project_term_path($term), ); } } } // Make sure we have the latest release if ($project->release_count > 1 && $version != -1) { $latest = db_fetch_object(db_query_range("SELECT file_path, version, file_date FROM {project_release_nodes} p INNER JOIN {term_node} t ON p.nid = t.nid LEFT JOIN {project_release_default_versions} prdv ON p.pid = prdv.nid AND prdv.tid = t.tid AND prdv.major = p.version_major WHERE p.pid = %d AND t.tid = %d ORDER BY prdv.nid DESC, p.rebuild ASC, p.version_major ASC, p.version_minor DESC, p.version_patch DESC, p.file_date DESC", $project->nid, $version, 0, 1)); $project->file_path = $latest->file_path; $project->version = $latest->version; } $project->links = array(); if ($version != -1) { if ($project->file_path) { $project->links['project_download'] = project_release_download_link($project->file_path, t('Download'), TRUE); } } else { $release_type = $project->snapshot_table ? 'all' : 'official'; $project->download_table = project_release_table($project, 'defaults', $release_type, t('Version'), FALSE); } $project->links['project_more_info'] = array( 'title' => t('Find out more'), 'href' => "node/$project->nid", ); if ($project->issues) { $project->links['project_issues'] = array( 'title' => t('Bugs and feature requests'), 'href' => "project/issues/$project->nid", ); } if (module_invoke($module, 'project_sort_methods', 'group by date', $sort_method) && $date = _project_date($project->changed)) { $projects .= "