'. t('The project module makes special use of the taxonomy (category) system. A special vocabulary, %vocabulary_name, has been created automatically.', array('%vocabulary_name' => $vocabulary->name)) .'
'; $text .= ''. t('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".') .'
'; $text .= ''. t('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".') .'
'; if ($vocab_link) { $text .= ''. t('Use the vocabulary administration page to view and add terms.', array('@taxonomy-admin' => url('admin/content/taxonomy/'. $vid))) .'
'; } return $text; } /** * Implementation of hook_block(). */ function project_block($op = 'list', $delta = 0, $edit = array()) { if ($op == 'list') { $blocks[0]['info'] = t('Project navigation'); if (module_exists('search')) { $blocks[1]['info'] = t('Project search'); } return $blocks; } else if ($op == 'view') { switch ($delta) { case 0: $block = array( 'subject' => t('Project navigation'), 'content' => drupal_get_form('project_quick_navigate_form'), ); break; case 1: if (user_access('search content')) { $block = array( 'subject' => t('Search projects'), 'content' => drupal_get_form('project_search_block_form', 'project_project'), ); } break; } return $block; } elseif ($op == 'configure' && $delta == 1) { $form = array(); $form['help_text'] = array( '#type' => 'textfield', '#title' => t('Help text'), '#description' => t('Enter optional help text to display in the block.'), '#default_value' => variable_get('project_search_block_help_text', ''), ); return $form; } elseif ($op == 'save' && $delta == 1) { variable_set('project_search_block_help_text', $edit['help_text']); } } function project_search_block_form($node_type) { $form = search_box('project_search_block_form_'. $node_type); $form['node_type'] = array( '#type' => 'value', '#value' => $node_type, ); $form['#base'] = 'project_search_block_form'; $help_text = variable_get('project_search_block_help_text', ''); if (!empty($help_text)) { $element = 'project_search_block_form_'. $node_type .'_keys'; $form[$element]['#description'] = check_plain($help_text); } return $form; } function project_search_block_form_submit($form_id, $form_values) { return 'search/node/type:'. $form_values['node_type'] .' '. trim($form_values[$form_id .'_'. $form_values['node_type'] .'_keys']); } 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; } /** * Implementation of hook_db_rewrite_sql(). * * Both project (and, if enabled, project_issue) provide some permissions that * restrict access to viewing projects (and issues). For these permissions to * be globally honored by the system, db_rewrite_sql() has to check what * permissions the current user has and restrict query results accordingly. * * If a user has 'access projects' and 'access project issues', they have full * access, so there's nothing to re-write. If they have 'access own projects' * and/or 'access own project issues', the resulting query should JOIN on * {node} and ensure that the node type is not project_project or * project_issue, or that the owner of the node is the current user. If they * do not have any access to either, then we can just restrict the query based * on the {node}.type column. * * @see project_perm() * @see project_issue_perm() * @see project_find_alias() */ function project_db_rewrite_sql($query, $primary_table, $primary_field) { if ($primary_field == 'nid') { $return = array(); $access_projects = user_access('access projects'); $admin_projects = user_access('administer projects'); if (module_exists('project_issue')) { $access_issues = user_access('access project issues'); $access_own_issues = user_access('access own project issues'); } else { $access_issues = TRUE; $access_own_issues = TRUE; } if ($admin_projects || ($access_projects && $access_issues)) { // User has full access, nothing to re-write. return; } else { // We have to make sure {node} is in the query and know the alias. if ($primary_table == 'n' || $primary_table == 'node') { // Great, it's the primary table and we already know the alias. $alias = $primary_table; } else { // Look for {node} in the query. if (!($alias = project_find_alias($query, 'node'))) { // Not already in the query, JOIN it. $return['join'] = "INNER JOIN {node} pn ON pn.nid = ". $primary_table .'.nid'; $alias = 'pn'; } } } // At this point, we know we have to restrict something, and we know what // the {node} table's alias is in the query. $where = array(); global $user; $uid = $user->uid; // Some node types will be restriced by our query, but we want to allow // every other node type. We keep track of the types we're handling, and // at the end we'll add a clause to ignore/allow all other types. $restricted_types = array(); if (!$access_projects) { $restricted_types[] = "'project_project'"; if (user_access('access own projects')) { $where[] = "($alias.type = 'project_project' AND $alias.uid = $uid)"; } } if (module_exists('project_issue') && !$access_issues) { $restricted_types[] = "'project_issue'"; if ($access_own_issues) { $where[] = "($alias.type = 'project_issue' AND $alias.uid = $uid)"; } } // If the type is not one of the restricted project* ones, allow access. $where[] = "($alias.type NOT IN (". implode(', ', $restricted_types) ."))"; // Now that we have all our WHERE clauses, we just OR them all together. $return['where'] = implode(' OR ', $where); return $return; } } /** * Find the table alias for a table in a query. * * @param $query * The query to examine for the alias. * @param * $table The table to examine for the alias. * @return * The alias if it exists, or the name of the table if it's present but not * aliased, or FALSE if the table is not present. * * @see project_db_rewrite_sql() */ function project_find_alias($query, $table) { // See if {$table} is already in the query. $match = array(); // This regexp handles many cases: // - {$table} can be either in FROM or in a JOIN clause // - Query might end immediately after {$table} // - Might not have a table alias for {$table} $pattern = "@.*(FROM|JOIN)\s+\{$table\}\s*(\S+)?@"; if (preg_match($pattern, $query, $match)) { $keywords = '@(ON|INNER|LEFT|RIGHT|WHERE|ORDER|GROUP|HAVING|LIMIT)@'; if (!isset($match[2]) || preg_match($keywords, $match[2])) { // No alias found, just use $table. $alias = $table; } else { // Alias found. $alias = $match[2]; } } else { // Table not in query. $alias = FALSE; } return $alias; } /** * 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'), '#description' => t('Each type of project on your site will have its own project browsing page at %link. Each browsing page can be configured for different browsing options (by name, by date, etc). The settings below determine which browsing methods are available for each project type.', array('%link' => url('project', NULL, NULL, TRUE) . '/[type]')), ); $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' => array_filter(variable_get('project_sort_method_used_'. $term->tid, array_keys($sort_methods))), '#options' => $sort_methods, ); } } $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.') ); return system_settings_form($form); } /** * 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('Project types'), '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, $array = NULL) { if ($op == 'delete' && $type == 'vocabulary' && $array['vid'] == _project_get_vid()) { variable_del('project_vocabulary'); } elseif ($type == 'term' && $array['vid'] == _project_get_vid()) { menu_rebuild(); } } function project_menu($may_cache) { $items = array(); global $user; if ($may_cache) { // User pages: $access_all = user_access('access projects'); $access_own = user_access('access own projects'); $access_admin = user_access('administer projects'); $access = $access_all || $access_own || $access_admin; $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()) { $default_sort = variable_get('project_sort_method', 'category'); $sort_methods = module_invoke_all('project_sort_methods', 'methods'); $terms = taxonomy_get_tree(_project_get_vid()); 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_keys(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, // It's a default task if a) sort method matches default, or b) default sort isn't // in the list of sort methods, and it's the first tab. 'type' => (($sort_method == $default_sort || (($j == 0) && !in_array($default_sort, $term_methods))) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK), 'weight' => ($sort_method == $default_sort) ? -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); // Administration pages $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' => $access_admin, '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' => $access_admin, 'type' => MENU_NORMAL_ITEM, ); } 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, ); } } } return $items; } function project_check_admin_access($project, $cvs_access = NULL) { 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 $cvs_access is not defined, check to make sure the user has cvs access // and that the user's cvs account is approved. if (project_use_cvs($project_obj) && !isset($cvs_access)) { if (db_num_rows(db_query("SELECT * FROM {cvs_accounts} WHERE uid = %d AND status = %d", $user->uid, CVS_APPROVED))) { $cvs_access = TRUE; } else { $cvs_access = FALSE; } } if (user_access('maintain projects')) { if ($user->uid == $project_obj->uid) { return TRUE; } if (project_use_cvs($project_obj) && $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( '#prefix' => '' . filter_xss($type->description) . '
'; } $module = $sort_methods[$sort_method]; // Bad sort method passed, return page not found. if (!isset($module)) { return drupal_not_found(); } 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(); // Make sure the API compatability term contains one or more terms // (not including the term with key of -1, which is always // automatically added to the array of terms. If not then browsing // by version is not possible. project_release_compatibility_list() // only returns active tids if browsing by version is enabled. if (count($active_tids) > 1) { $version_params = array(); if ($version != -1) { $version_where = 'tr.tid = %d'; $version_params[] = $version; } else { $placeholders = array(); foreach ($active_tids as $tid => $api_term) { $placeholders[] = '%d'; $version_params[] = $tid; } $version_where = 'tr.tid IN ('. implode(',', $placeholders) .')'; } // 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 $version_where AND (n.status = 1) GROUP BY tp.tid", $version_params); $tids = array(); while ($item = db_fetch_object($result)) { $tids[$item->tid] = $item->count; } } foreach ($tree as $cterm) { if (!module_exists('project_release') || (!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; } } } // Look for a specific category term. if ($arg = arg(3)) { // Bad term, return page not found. if (!($term = taxonomy_get_term($arg))) { return drupal_not_found(); } } } // The sorting method is not supported by the module, so any term passed is invalid. // Return page not found if one is found. elseif (arg(3)) { return drupal_not_found(); } // Set the default elements that will be used to construct the SQL statement. $sql_elements = project_empty_query(); $sql_elements['fields']['pieces'] = array( 'DISTINCT(n.nid)', 'n.title', 'n.sticky', 'n.type', 'nr.teaser', 'nr.format', ); $sql_elements['from']['pieces'] = array( '{node} n ' ); $sql_elements['joins']['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' ); $sql_elements['wheres']['pieces'] = array( 'n.status = 1', 'r.tid = %d' ); $sql_elements['parameters']['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 (empty($sql_settings)) { $sql_settings = array(); } $active_tids = array(); if (module_exists('project_release')) { $project_release_sql = array( 'joins' => array('INNER JOIN {project_release_projects} prp ON n.nid = prp.nid'), 'wheres' => array('prp.releases = 1'), ); $sql_settings = array_merge_recursive($sql_settings, $project_release_sql); $active_tids = project_release_compatibility_list(); } // Make sure the API compatability term contains one or more terms // (not including the term with key of -1, which is always // automatically added to the array of terms. If not then browsing // by version is not possible. project_release_compatibility_list() // only returns active tids if browsing by version is enabled. if (count($active_tids) > 1) { $release_settings = array( 'fields' => array('prn.file_path', 'prn.version', 'MAX(prn.file_date) AS changed', 'COUNT(*) AS release_count'), 'group_bys' => array('n.nid'), ); $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', 'INNER JOIN {node} rn ON rn.nid = prn.nid', ); $release_settings['wheres'][] = 'rn.status = 1'; if ($version != -1) { $release_settings['joins'][] = 'INNER JOIN {project_release_supported_versions} prsv ON prsv.nid = p.nid AND prsv.tid = tr.tid'; $release_settings['wheres'][] = 'tr.tid = %d'; $release_settings['wheres'][] = 'prsv.supported = 1'; $release_settings['parameters'][] = $version; } else { $placeholders = array(); foreach ($active_tids as $tid => $compatibility_term) { $placeholders[] = '%d'; $release_settings['parameters'][] = $tid; } $where = 'tr.tid IN ('. implode(',', $placeholders) .')'; $release_settings['wheres'][] = $where; // We need to grab the uid 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'; } $sql_settings = array_merge_recursive($sql_settings, $release_settings); } else { $release_settings = array( 'fields' => array('n.changed AS changed'), ); $sql_settings = array_merge_recursive($sql_settings, $release_settings); } // Merge in $sql_elements if (!empty($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)"); // ORDER BY can screw up COUNT(DISTINCT), and we don't care // about the order for the count query. unset($sql_elements['order_bys']); $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 { // Any terms passed without taxonomy module enabled are bogus -- return page not found. if (isset($termname)) { return drupal_not_found(); } // 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")); } $projects = ''; $class = 'even'; while ($project = db_fetch_object($result)) { $project->body = check_markup($project->teaser, $project->format, FALSE); if (module_exists('taxonomy')) { $project->taxonomy = taxonomy_node_get_terms($project->nid); if (isset($type)) { // Hide the top-level project type term from the links, but add it to // the $project object for theme_project_summary() to use if it wants. unset($project->taxonomy[$type->tid]); $project->term = $termname; } $project->terms = taxonomy_link('taxonomy terms', $project); } // Make sure we have the latest release if (isset($project->release_count) && $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 {node} n ON p.nid = n.nid INNER JOIN {term_node} t ON p.nid = t.nid LEFT JOIN {project_release_supported_versions} prsv ON p.pid = prsv.nid AND prsv.tid = t.tid AND prsv.major = p.version_major WHERE p.pid = %d AND t.tid = %d AND prsv.supported = 1 AND n.status = 1 ORDER BY prsv.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'] = theme('project_release_download_link', $project->file_path, t('Download'), 'array'); } } else { $project->download_table = theme('project_release_table_overview', $project, 'recommended', 'all', 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 .= "' . t('Last changed: !interval ago', array('!interval' => format_interval(time() - $project->changed, 2))) . '
'; } $output .= $project->body; $output .= theme('links', $project->links); if (!empty($project->download_table)) { $output .= $project->download_table; } if (!empty($project->terms)) { $output .= theme('links', $project->terms); } $output .= '' . filter_xss($term->description) . '
'; } return $output; } /** * Returns a string of valid project names formatted to be suitable * for use with JS autocomplete fields. The user enters a * comma-separated list of project names. We only autocomplete the * last one. This code is stolen heavily from taxonomy_autocomplete(). */ function project_autocomplete($string) { // This regexp allows the following types of user input: // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; preg_match_all($regexp, $string, $matches); $array = $matches[1]; // Fetch last project $last_string = trim(array_pop($array)); if ($last_string != '') { $result = db_query_range("SELECT n.title FROM {node} n INNER JOIN {project_projects} p ON n.nid = p.nid WHERE LOWER(n.title) LIKE LOWER('%%%s%%')", $last_string, 0, 10); $prefix = count($array) ? implode(', ', $array) .', ' : ''; $matches = array(); while ($project = db_fetch_object($result)) { $t = $project->title; $matches[$prefix . $t] = check_plain($project->title); } print drupal_to_js($matches); exit(); } } /** * Returns whether or not the project module should use * taxonomy-specific functionality. */ function project_use_taxonomy() { return module_exists('taxonomy') && taxonomy_get_tree(_project_get_vid()); } /** * Returns whether or not the project uses * version control or not */ function project_use_cvs($project) { if (module_exists('cvs')) { $project = is_numeric($project) ? node_load($project) : $project; return isset($project->cvs_repository) && ($project->cvs_repository != 0); } } /** * Determines if the current site supports caching of project-related pages. * * The pages can be cached if: * 1. Anonymous users have the 'access projects' permission. * 2. No node access modules are installed. * * @return TRUE if the output can be cached, FALSE otherwise. */ function project_can_cache() { $grants = module_implements('node_grants'); if (!empty($grants)) { return FALSE; } $allowed_roles = user_roles(FALSE, 'access projects'); if (!isset($allowed_roles[DRUPAL_ANONYMOUS_RID])) { return FALSE; } return TRUE; } /** * Display an RSS feed icon for use on project nodes. */ function theme_project_feed_icon($url, $title) { if ($icon = theme('image', 'misc/feed.png', $title, $title)) { return ''. $icon .''; } }