'. 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') { // Note: We can get by with using BLOCK_CACHE_PER_ROLE below because // block caching is disabled when node access control modules are in use. $blocks[0] = array( 'info' => t('Project navigation (drop-down select)'), 'cache' => BLOCK_CACHE_PER_ROLE, ); if (module_exists('search')) { $blocks[1] = array( 'info' => t('Project search'), 'cache' => BLOCK_CACHE_PER_ROLE, ); } $blocks[2] = array( 'info' => t('Project navigation (text field)'), 'cache' => BLOCK_CACHE_PER_ROLE, ); 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'), ); } case 2: $block = array( 'subject' => t('Project navigation'), 'content' => drupal_get_form('project_quick_navigate_title_form'), ); 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($form_state, $node_type) { $form = search_box($form_state, 'project_search_block_form'); $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'; $form[$element]['#description'] = check_plain($help_text); } $form['#submit'][] = 'project_search_block_form_submit'; return $form; } function project_search_block_form_submit($form, &$form_state) { $form_state['redirect'] = 'search/node/type:'. $form_state['values']['node_type'] .' '. trim($form_state['values'][$form_state['values']['form_id']]); } 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', 'browse project listings', ); 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. * * @TODO: This function is probably one we can delete, since there are no settings * to be set anymore. */ function project_settings_form() { $form = array(); 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(). * * @TODO: Modify this function to use the new project settings where the * user selects the views to use. */ function project_term_path($term) { // Make sure we have the entire term object. $term = taxonomy_get_term($term->tid); // 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/' . drupal_strtolower($termname); } break; } } return 'project/' . drupal_strtolower($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(); } } /** * Determine if the currently logged in user could have access to any project_project nodes. */ function project_project_access_any() { return user_access('access projects') || user_access('access own projects') || user_access('administer projects'); } function project_menu() { $items = array(); // @TODO: The project type and browsing views are currently set to require // 'access projects' permission. However, we really want them to require at // least one of 'access projects', 'access own projects', or 'administer // projects'. At this point I'm not sure what the best way to do this is, // but we need to figure this out to keep the same functionality. $items['project/autocomplete'] = array( 'title' => 'Autocomplete project', 'page callback' => 'project_autocomplete', 'access callback' => 'project_project_access_any', 'type' => MENU_CALLBACK, ); // Developers $items['project/developers'] = array( 'title' => 'Developers', 'page callback' => 'project_developers', 'access callback' => 'project_project_access_any', 'type' => MENU_CALLBACK, ); // CVS messages: $items['project/cvs'] = array( 'title' => 'CVS', 'page callback' => 'project_cvs', 'access callback' => 'project_project_access_any', 'type' => MENU_CALLBACK, ); // Administration pages $items['admin/project'] = array( 'title' => 'Project administration', 'description' => 'Administrative interface for project management and related modules.', 'page callback' => 'system_admin_menu_block_page', 'access arguments' => array('administer projects'), 'file' => 'system.admin.inc', 'file path' => drupal_get_path('module', 'system'), 'type' => MENU_NORMAL_ITEM, ); $items['node/%project_edit_project/edit/project'] = array( 'title' => 'Project', 'page callback' => 'node_page', 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('update', 1), 'weight' => -5, 'type' => MENU_DEFAULT_LOCAL_TASK, ); return $items; } /** * Menu loader callback to load a project node. */ function project_node_load($nid) { if (!is_numeric($nid)) { return FALSE; } $node = node_load($nid); if (!isset($node->type) || $node->type != 'project_project') { return FALSE; } return $node; } /** * Menu loader callback to load a project node if the edit tab needs subtabs. * * Load a project_project node if the given nid is a project_project node and * if the user has permission to edit the project and if either the * project_issue or project_release module exists. */ function project_edit_project_load($nid) { if (!module_exists('project_issue') && !module_exists('project_release')) { return FALSE; } return project_node_load($nid); } 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) || (isset($project_obj->type) && $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_result(db_query("SELECT COUNT(*) 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_result(db_query("SELECT COUNT(*) 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, &$form_state, $form_id) { switch ($form_id) { case '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 (!empty($form['taxonomy']) && (!element_children($form['taxonomy']))) { unset($form['taxonomy']); } // In D6, FAPI changed in such a way that completely unsetting // $form['taxonomy'] causes warnings and errors when the project node is // previewed. In order to prevent these problems, we need to add a // submit handler to the preview button, and this handler needs to put // the taxonomy terms back into $node->taxonomy like they would be if // project wasn't replacing the usual taxonomy selection form elements // with its own elements. if (isset($form['buttons']['preview'])) { $form['buttons']['preview']['#submit'][] = 'project_project_form_preview_submit'; } // 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' => '' . 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 .= '