[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/project/ -> project.module (source)

   1  <?php
   2  // $Id: project.module,v 1.356 2010/01/30 02:43:23 dww Exp $
   3  
   4  /**
   5   * Implementation of hook_init().
   6   */
   7  function project_init() {
   8    $path = drupal_get_path('module', 'project');
   9    drupal_add_css($path .'/project.css');
  10    if (file_exists("$path/project.inc")) {
  11      require_once "$path/project.inc";
  12    }
  13  }
  14  
  15  function project_help($path, $arg) {
  16    switch ($path) {
  17      case 'admin/project/project-settings':
  18        if (project_use_taxonomy()) {
  19          return _project_taxonomy_help();
  20        }
  21        break;
  22      case 'admin/content/taxonomy/%':
  23        $vid = _project_get_vid();
  24        if ($arg[3] == $vid) {
  25          return _project_taxonomy_help($vid, FALSE);
  26        }
  27        break;
  28    }
  29  }
  30  
  31  /**
  32   * Prints out a help message about how to configure the project vocabulary.
  33   *
  34   * @param $vid
  35   *   Vocabulary ID of the project taxonomy.
  36   * @param $vocab_link
  37   *   Boolean that controls if a link to the vocabulary admin page is added.
  38   */
  39  function _project_taxonomy_help($vid = 0, $vocab_link = TRUE) {
  40    if (!$vid) {
  41      $vid = _project_get_vid();
  42    }
  43    $vocabulary = taxonomy_vocabulary_load($vid);
  44    $text = '<p>'. 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)) .'</p>';
  45    $text .= '<p>'. 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".') .'</p>';
  46    $text .= '<p>'. 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".') .'</p>';
  47    if ($vocab_link) {
  48      $text .= '<p>'. t('Use the <a href="@taxonomy-admin">vocabulary administration page</a> to view and add terms.', array('@taxonomy-admin' => url('admin/content/taxonomy/'. $vid))) .'</p>';
  49    }
  50    return $text;
  51  }
  52  
  53  /**
  54   * Implementation of hook_block().
  55   */
  56  function project_block($op = 'list', $delta = 0, $edit = array()) {
  57    if ($op == 'list') {
  58      // Note:  We can get by with using BLOCK_CACHE_PER_ROLE below because
  59      // block caching is disabled when node access control modules are in use.
  60      $blocks['project_navigation_select'] = array(
  61        'info' => t('Project navigation (drop-down select)'),
  62        'cache' => BLOCK_CACHE_PER_ROLE,
  63      );
  64      $blocks['project_navigation_text'] = array(
  65        'info' => t('Project navigation (text field)'),
  66        'cache' => BLOCK_CACHE_PER_ROLE,
  67      );
  68      if (module_exists('search')) {
  69        $blocks['project_search'] = array(
  70          'info' => t('Project search'),
  71          'cache' => BLOCK_CACHE_PER_ROLE,
  72        );
  73      }
  74  
  75      module_load_include('inc', 'project', 'project');
  76      foreach (project_get_project_link_info() as $key => $links) {
  77        if (isset($links['type']) && $links['type'] == 'block') {
  78          $blocks[$key] = array(
  79            'info' => t('Project: @section', array('@section' => $links['name'])),
  80            'cache' => BLOCK_CACHE_PER_ROLE | BLOCK_CACHE_PER_PAGE,
  81          );
  82        }
  83      }
  84  
  85      return $blocks;
  86    }
  87    else if ($op == 'view') {
  88      switch ($delta) {
  89        case 'project_navigation_select':
  90          $block = array(
  91            'subject' => t('Project navigation'),
  92            'content' => drupal_get_form('project_quick_navigate_form'),
  93          );
  94          break;
  95  
  96        case 'project_navigation_text':
  97          $block = array(
  98            'subject' => t('Project navigation'),
  99            'content' => drupal_get_form('project_quick_navigate_title_form'),
 100          );
 101          break;
 102  
 103        case 'project_search':
 104          if (user_access('search content')) {
 105            $block = array(
 106              'subject' => t('Search projects'), 
 107              'content' => drupal_get_form('project_search_block_form', 'project_project'),
 108            );
 109          }
 110          break;
 111  
 112      }
 113      if (empty($block) && ($node = project_get_project_from_menu()) && node_access('view', $node)) {
 114        module_load_include('inc', 'project', 'project');
 115        foreach (project_get_project_link_info($node) as $key => $links) {
 116          if ($delta == $key && isset($links['type']) && $links['type'] == 'block' && !empty($links['links'])) {
 117            $block = array(
 118              'subject' => $links['name'],
 119              'content' => theme('item_list', $links['links']),
 120            );
 121          }
 122        }
 123      }
 124      return $block;
 125    }
 126    elseif ($op == 'configure' && $delta == 1) {
 127      $form = array();
 128      $form['help_text'] = array(
 129        '#type' => 'textfield',
 130        '#title' => t('Help text'),
 131        '#description' => t('Enter optional help text to display in the block.'),
 132        '#default_value' => variable_get('project_search_block_help_text', ''),
 133      );
 134      return $form;
 135    }
 136    elseif ($op == 'save' && $delta == 1) {
 137      variable_set('project_search_block_help_text', $edit['help_text']);
 138    }
 139  }
 140  
 141  function project_search_block_form($form_state, $node_type) {
 142    $form = search_box($form_state, 'project_search_block_form');
 143    $form['node_type'] = array(
 144      '#type' => 'value',
 145      '#value' => $node_type,
 146    );
 147    $form['#base'] = 'project_search_block_form';
 148    $help_text = variable_get('project_search_block_help_text', '');
 149    if (!empty($help_text)) {
 150       $element = 'project_search_block_form';
 151       $form[$element]['#description'] = check_plain($help_text);
 152    }
 153    $form['#submit'][] = 'project_search_block_form_submit';
 154    return $form;
 155  }
 156  
 157  function project_search_block_form_submit($form, &$form_state) {
 158    $form_state['redirect'] = 'search/node/type:'. $form_state['values']['node_type'] .' '. trim($form_state['values'][$form_state['values']['form_id']]);
 159  }
 160  
 161  function project_node_info() {
 162    return array(
 163      'project_project' => array(
 164        'name' => t('Project'),
 165        'module' => 'project_project',
 166        '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.'),
 167      ),
 168    );
 169  }
 170  
 171  function project_perm() {
 172    $perms = array(
 173      'administer projects',
 174      'maintain projects',
 175      'access projects',
 176      'access own projects',
 177      'delete any projects',
 178      'delete own projects',
 179      'browse project listings',
 180    );
 181    return $perms;
 182  }
 183  
 184  /**
 185   * Implementation of hook_db_rewrite_sql().
 186   *
 187   * Both project (and, if enabled, project_issue) provide some permissions that
 188   * restrict access to viewing projects (and issues). For these permissions to
 189   * be globally honored by the system, db_rewrite_sql() has to check what
 190   * permissions the current user has and restrict query results accordingly.
 191   *
 192   * If a user has 'access projects' and 'access project issues', they have full
 193   * access, so there's nothing to re-write. If they have 'access own projects'
 194   * and/or 'access own project issues', the resulting query should JOIN on
 195   * {node} and ensure that the node type is not project_project or
 196   * project_issue, or that the owner of the node is the current user. If they
 197   * do not have any access to either, then we can just restrict the query based
 198   * on the {node}.type column.
 199   *
 200   * @see project_perm()
 201   * @see project_issue_perm()
 202   * @see project_find_alias()
 203   */
 204  function project_db_rewrite_sql($query, $primary_table, $primary_field) {
 205    if ($primary_field == 'nid') {
 206      $return = array();
 207      $access_projects = user_access('access projects');
 208      $admin_projects = user_access('administer projects');
 209      if (module_exists('project_issue')) {
 210        $access_issues = user_access('access project issues');
 211        $access_own_issues = user_access('access own project issues');
 212      }
 213      else {
 214        $access_issues = TRUE;
 215        $access_own_issues = TRUE;
 216      }
 217      if ($admin_projects || ($access_projects && $access_issues)) {
 218        // User has full access, nothing to re-write.
 219        return;
 220      }
 221      else {
 222        // We have to make sure {node} is in the query and know the alias.
 223        if ($primary_table == 'n' || $primary_table == 'node') {
 224          // Great, it's the primary table and we already know the alias.
 225          $alias = $primary_table;
 226        }
 227        else {
 228          // Look for {node} in the query.
 229          if (!($alias = project_find_alias($query, 'node'))) {
 230            // Not already in the query, JOIN it.
 231            $return['join'] = "INNER JOIN {node} pn ON pn.nid = ". $primary_table .'.nid';
 232            $alias = 'pn';
 233          }
 234        }
 235      }
 236      // At this point, we know we have to restrict something, and we know what
 237      // the {node} table's alias is in the query.
 238      $where = array();
 239  
 240      global $user;
 241      $uid = $user->uid;
 242  
 243      // Some node types will be restriced by our query, but we want to allow
 244      // every other node type. We keep track of the types we're handling, and
 245      // at the end we'll add a clause to ignore/allow all other types.
 246      $restricted_types = array();
 247  
 248      if (!$access_projects) {
 249        $restricted_types[] = "'project_project'";
 250        if (user_access('access own projects')) {
 251          $where[] = "($alias.type = 'project_project' AND $alias.uid = $uid)";
 252        }
 253      }
 254  
 255      if (module_exists('project_issue') && !$access_issues) {
 256        $restricted_types[] = "'project_issue'";
 257        if ($access_own_issues) {
 258          $where[] = "($alias.type = 'project_issue' AND $alias.uid = $uid)";
 259        }
 260      }
 261  
 262      // If the type is not one of the restricted project* ones, allow access.
 263      $where[] = "($alias.type NOT IN (". implode(', ', $restricted_types) ."))";
 264  
 265      // Now that we have all our WHERE clauses, we just OR them all together.
 266      $return['where'] = implode(' OR ', $where);
 267      return $return;
 268    }
 269  }
 270  
 271  /**
 272   * Find the table alias for a table in a query.
 273   *
 274   * @param $query
 275   *   The query to examine for the alias.
 276   * @param
 277   *   $table The table to examine for the alias.
 278   * @return
 279   *   The alias if it exists, or the name of the table if it's present but not
 280   *   aliased, or FALSE if the table is not present.
 281   *
 282   * @see project_db_rewrite_sql()
 283   */
 284  function project_find_alias($query, $table) {
 285    // See if {$table} is already in the query.
 286    $match = array();
 287    // This regexp handles many cases:
 288    // - {$table} can be either in FROM or in a JOIN clause
 289    // - Query might end immediately after {$table}
 290    // - Might not have a table alias for {$table}
 291    $pattern = "@.*(FROM|JOIN)\s+\{$table\}\s*(\S+)?@";
 292    if (preg_match($pattern, $query, $match)) {
 293      $keywords = '@(ON|INNER|LEFT|RIGHT|WHERE|ORDER|GROUP|HAVING|LIMIT)@';
 294      if (!isset($match[2]) || preg_match($keywords, $match[2])) {
 295        // No alias found, just use $table.
 296        $alias = $table;
 297      }
 298      else {
 299        // Alias found.
 300        $alias = $match[2];
 301      }
 302    }
 303    else {
 304      // Table not in query.
 305      $alias = FALSE;
 306    }
 307  
 308    return $alias;
 309  }
 310  
 311  /**
 312   * Callback for the main settings page.
 313   *
 314   * @TODO:  This function is probably one we can delete, since there are no settings
 315   * to be set anymore.
 316   */
 317  function project_settings_form() {
 318    $form = array();
 319  
 320    if (module_exists('path')) {
 321      $form['project_enable_alias'] = array(
 322        '#type' => 'checkbox',
 323        '#title' => t('Enable auto-generated project aliases'),
 324        '#default_value' => variable_get('project_enable_alias', TRUE),
 325        '#description' => t('If checked, project module will automatically generate path aliases (eg. /project/&lt;shortname&gt;/) for each project. Uncheck if you want to use another module, such as pathauto, to generate aliases.')
 326      );
 327    }
 328    else {
 329      $form['project_enable_alias'] = array(
 330        '#type' => 'markup',
 331        '#value' => t('If you enable the Path module from Drupal core, you can control if the Project module will automatically generate path aliases (eg. /project/&lt;shortname&gt;/) for each project.')
 332      );
 333      variable_set('project_enable_alias', FALSE);
 334    }
 335  
 336    return system_settings_form($form);
 337  }
 338  
 339  /**
 340   * Returns the vocabulary id for projects.
 341   */
 342  function _project_get_vid() {
 343    $vid = variable_get('project_vocabulary', '');
 344    if (empty($vid)) {
 345      // Check to see if a project module vocabulary exists.
 346      $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module='%s'", 'project'));
 347      if (!$vid) {
 348        $edit = array(
 349          'name' => t('Project types'),
 350          'multiple' => 1,
 351          'hierarchy' => 1,
 352          'relations' => 0,
 353          'module' => 'project',
 354          'nodes' => array('project_project' => 1),
 355        );
 356        // If there is already a vocabulary assigned to 'project_project' nodes, use it.
 357        $vocabularies = taxonomy_get_vocabularies('project_project');
 358        if (count($vocabularies)) {
 359          $vocabulary = reset($vocabularies);
 360          $edit['vid'] = $vocabulary->vid;
 361        }
 362        taxonomy_save_vocabulary($edit);
 363        $vid = $edit['vid'];
 364      }
 365      variable_set('project_vocabulary', $vid);
 366    }
 367  
 368    return $vid;
 369  }
 370  
 371  /**
 372   * Implementation of hook_term_path().
 373   *
 374   * @TODO:  Modify this function to use the new project settings where the
 375   * user selects the views to use.
 376   */
 377  function project_term_path($term) {
 378    // Make sure we have the entire term object.
 379    $term = taxonomy_get_term($term->tid);
 380  
 381    // The path must include the first-level term name for this term.
 382    $tree = taxonomy_get_tree(_project_get_vid());
 383    $parents = taxonomy_get_parents_all($term->tid);
 384    foreach($parents as $parent) {
 385      $ancestors[] = $parent->tid;
 386    }
 387    foreach ($tree as $t) {
 388      if (in_array($t->tid, $ancestors) && $t->depth == 0) {
 389        $termname = $t->name;
 390        if ($term->name == $termname) {
 391          return 'project/' . drupal_strtolower($termname);
 392        }
 393        break;
 394      }
 395    }
 396    // TODO: when the project browsing views work, if there's a direct URL to
 397    // link to a project category view, we should add that here.
 398    // Also, note that in the project_solr case, we'd like to link to,
 399    // e.g. "project/modules?filter=tid:12" but it's apparently impossible to
 400    // return url() for this since things get double encoded or something.
 401    // So, for now, we punt and always link the subterms to the taxonomy page.
 402    return 'taxonomy/term/'. $term->tid;
 403  }
 404  
 405  /**
 406   * Implementation of hook_taxonomy().
 407   */
 408  function project_taxonomy($op, $type, $array = NULL) {
 409    if ($op == 'delete' && $type == 'vocabulary' && $array['vid'] == _project_get_vid())  {
 410      variable_del('project_vocabulary');
 411    }
 412  }
 413  
 414  /**
 415   * Determine if the currently logged in user could have access to any project_project nodes.
 416   */
 417  function project_project_access_any() {
 418    return user_access('access projects') || user_access('access own projects') || user_access('administer projects');
 419  }
 420  
 421  /**
 422   * Implement hook_menu().
 423   */
 424  function project_menu() {
 425    $items = array();
 426  
 427    // @TODO: The project type and browsing views are currently set to require
 428    // 'access projects' permission.  However, we really want them to require at
 429    // least one of 'access projects', 'access own projects', or 'administer
 430    // projects'.  At this point I'm not sure what the best way to do this is,
 431    // but we need to figure this out to keep the same functionality.
 432  
 433    $items['project/autocomplete/project'] = array(
 434      'title' => 'Autocomplete project',
 435      'page callback' => 'project_autocomplete_project',
 436      'access callback' => 'project_project_access_any',
 437      'type' => MENU_CALLBACK,
 438    );
 439  
 440    $items['project/autocomplete/project-user/%'] = array(
 441      'title' => 'Autocomplete project',
 442      'page callback' => 'project_autocomplete_project_user',
 443      'page arguments' => array(3, 4),
 444      'access callback' => 'project_project_access_any',
 445      'type' => MENU_CALLBACK,
 446    );
 447  
 448    // CVS messages:
 449    $items['project/cvs'] = array(
 450      'title' => 'CVS',
 451      'page callback' => 'project_cvs',
 452      'access callback' => 'project_project_access_any',
 453      'type' => MENU_CALLBACK,
 454    );
 455  
 456    // Administration pages
 457    $items['admin/project'] = array(
 458      'title' => 'Project administration',
 459      'description' => 'Administrative interface for project management and related modules.',
 460      'page callback' => 'system_admin_menu_block_page',
 461      'access arguments' => array('administer projects'),
 462      'file' => 'system.admin.inc',
 463      'file path' => drupal_get_path('module', 'system'),
 464      'type' => MENU_NORMAL_ITEM,
 465    );
 466    $items['admin/project/project-settings'] = array(
 467      'title' => 'Project settings',
 468      'description' => 'Configure settings for the Project module.',
 469      'page callback' => 'drupal_get_form',
 470      'page arguments' => array('project_settings_form'),
 471      'access arguments' => array('administer projects'),
 472      'type' => MENU_NORMAL_ITEM,
 473    );
 474  
 475    $items['node/%project_edit_project/edit/project'] = array(
 476      'title' => 'Project',
 477      'page callback' => 'node_page',
 478      'page arguments' => array(1),
 479      'access callback' => 'node_access',
 480      'access arguments' => array('update', 1), 
 481      'weight' => -5, 'type' => MENU_DEFAULT_LOCAL_TASK,
 482    );
 483    return $items;
 484  }
 485  
 486  /**
 487   * Menu loader callback to load a project node.
 488   *
 489   * @param $arg
 490   *   The menu argument to attempt to load a project from.  Can be either a
 491   *   numeric node ID (nid), or a string project short name (uri).
 492   *
 493   * @return
 494   *   The loaded node object if the argument was a valid project, FALSE if not.
 495   */
 496  function project_node_load($arg) {
 497    if (is_numeric($arg)) {
 498      $nid = $arg;
 499    }
 500    else {
 501      $nid = db_result(db_query(db_rewrite_sql("SELECT p.nid FROM {project_projects} p WHERE p.uri = '%s'", 'p'), $arg));
 502    }
 503    $node = node_load($nid);
 504    if (!isset($node->type) || $node->type != 'project_project') {
 505      return FALSE;
 506    }
 507    return $node;
 508  }
 509  
 510  /**
 511   * Menu loader callback to load a project node if the edit tab needs subtabs.
 512   *
 513   * Load a project_project node if the given nid is a project_project node and
 514   * if the user has permission to edit the project and if either the
 515   * project_issue or project_release module exists.
 516   */
 517  function project_edit_project_load($nid) {
 518    if (!module_exists('project_issue') && !module_exists('project_release')) {
 519      return FALSE;
 520    }
 521    return project_node_load($nid);
 522  }
 523  
 524  function project_check_admin_access($project, $cvs_access = NULL) {
 525    global $user;
 526    if (empty($user->uid)) {
 527      return FALSE;
 528    }
 529  
 530    $project_obj = is_numeric($project) ? node_load($project) : $project;
 531    if (!isset($project_obj) || (isset($project_obj->type) && $project_obj->type != 'project_project')) {
 532      return FALSE;
 533    }
 534  
 535    if (user_access('administer projects')) {
 536       return TRUE;
 537    }
 538  
 539    // If $cvs_access is not defined, check to make sure the user has cvs access
 540    // and that the user's cvs account is approved.
 541    if (project_use_cvs($project_obj) && !isset($cvs_access)) {
 542      if (db_result(db_query("SELECT COUNT(*) FROM {cvs_accounts} WHERE uid = %d AND status = %d", $user->uid, CVS_APPROVED))) {
 543        $cvs_access = TRUE;
 544      }
 545      else {
 546        $cvs_access = FALSE;
 547      }
 548    }
 549  
 550    if (user_access('maintain projects')) {
 551      if ($user->uid == $project_obj->uid) {
 552         return TRUE;
 553      }
 554      if (project_use_cvs($project_obj) && $cvs_access) {
 555        if (db_result(db_query("SELECT COUNT(*) FROM {cvs_project_maintainers} WHERE uid = %d AND nid = %d", $user->uid, $project_obj->nid))) {
 556          return TRUE;
 557        }
 558      }
 559    }
 560    return FALSE;
 561  }
 562  
 563  /**
 564   * Implementation of form_alter. This removes the work of
 565   * taxonomy.module's form_alter() so we can do our own taxonomy
 566   * selection.
 567   */
 568  function project_form_alter(&$form, &$form_state, $form_id) {
 569    switch ($form_id) {
 570      case 'project_project_node_form':
 571        $vid = _project_get_vid();
 572        if (isset($form['taxonomy'][$vid])) {
 573          unset($form['taxonomy'][$vid]);
 574        }
 575        // If there are no children elements, we should unset the entire
 576        // thing so we don't end up with an empty fieldset.
 577        if (!empty($form['taxonomy']) && (!element_children($form['taxonomy']))) {
 578          unset($form['taxonomy']);
 579        }
 580    
 581        // In D6, FAPI changed in such a way that completely unsetting
 582        // $form['taxonomy'] causes warnings and errors when the project node is
 583        // previewed. In order to prevent these problems, we need to add a
 584        // submit handler to the preview button, and this handler needs to put
 585        // the taxonomy terms back into $node->taxonomy like they would be if
 586        // project wasn't replacing the usual taxonomy selection form elements
 587        // with its own elements.
 588        if (isset($form['buttons']['preview'])) {
 589          $form['buttons']['preview']['#submit'][] = 'project_project_form_preview_submit';
 590        }
 591    
 592        // If the form has an element for specifying a URL alias, we want
 593        // to alter it, since we're just going to automatically override
 594        // the specified value.
 595        if (variable_get('project_enable_alias', TRUE) && isset($form['path'])) {
 596          $url_alias = $form['path']['path']['#default_value'];
 597          if (empty($url_alias)) {
 598            unset($form['path']);
 599          }
 600          else {
 601            unset($form['path']['path']);
 602            $form['path']['value'] = array(
 603              '#prefix' => '<div>',
 604              '#value' => t('Automatically generated path alias: %url', array('%url' => $url_alias)),
 605              '#suffix' => '</div>',
 606            );
 607          }
 608        }
 609        break;
 610    
 611      case 'taxonomy_form_vocabulary':
 612        // Add a validate handler to the vocabulary form if the
 613        // Project type vocabulary term is being edited so that its
 614        // possible to detect if this vocabulary was set to be
 615        // a free tagging vocabulary, and if so then throw an error.
 616        $project_type_vid = _project_get_vid();
 617        if (isset($project_type_vid) && !empty($form['vid']['#value']) && $form['vid']['#value'] == $project_type_vid) {
 618          $form['#validate'][] = 'project_project_type_vocabulary_validate';
 619          
 620        }
 621        break;
 622    }
 623  }
 624  
 625  /**
 626   * Implement hook_access().
 627   */
 628  function project_project_access($op, $node, $account) {
 629    switch ($op) {
 630      case 'view':
 631        // Since this function is shared for project_release nodes, we have to
 632        // be careful what node we pass to project_check_admin_access().
 633        if ($node->type == 'project_release') {
 634          $node = node_load($node->project_release['pid']);
 635        }
 636        if (project_check_admin_access($node)) {
 637          return TRUE;
 638        }
 639        if (!user_access('access projects')) {
 640           return FALSE;
 641        }
 642        break;
 643      case 'create':
 644        if ($account->uid && user_access('maintain projects')) {
 645          // Since this CVS access checking is non-standard, we need to
 646          // special-case uid 1 to always allow everything.
 647          if ($account->uid != 1 && module_exists('cvs') && variable_get('cvs_restrict_project_creation', 1)) {
 648            return db_result(db_query("SELECT uid FROM {cvs_accounts} WHERE uid = %d AND status = %d", $account->uid, CVS_APPROVED)) ? TRUE : FALSE;
 649          }
 650          else {
 651            return TRUE;
 652          }
 653        }
 654        break;
 655      case 'update':
 656        if (project_check_admin_access($node)) {
 657          return TRUE;
 658        }
 659        break;
 660      case 'delete':
 661        if (user_access('delete any projects', $account) || (user_access('delete own projects', $account) && ($account->uid == $node->uid))) {
 662          return TRUE;
 663        }
 664        break;
 665    }
 666  }
 667  
 668  /**
 669   * Implement hook_load().
 670   */
 671  function project_project_load($node) {
 672    $additions = db_fetch_array(db_query('SELECT * FROM {project_projects} WHERE nid = %d', $node->nid));
 673    $project = new stdClass;
 674    $project->project = $additions;
 675    return $project;
 676  }
 677  
 678  /**
 679   * hook_nodeapi() implementation. This just decides what type of node
 680   * is being passed, and calls the appropriate type-specific hook.
 681   *
 682   * @see project_project_nodeapi().
 683   */
 684  function project_nodeapi(&$node, $op, $arg) {
 685    switch ($node->type) {
 686      case 'project_project':
 687        module_load_include('inc', 'project', 'project');
 688        project_project_nodeapi($node, $op, $arg);
 689        break;
 690    }
 691  }
 692  
 693  /**
 694   * Build a SQL statment from a structured array.
 695   *
 696   * @param $sql_elements
 697   *   An array with the following keys:
 698   *     'fields', 'from', 'joins', 'wheres', 'group_bys', 'order_bys',
 699   *     'parameters'
 700   *   Each value is an array with the following keys:
 701   *     'prefix', 'glue', 'pieces'
 702   * @return
 703   *   SQL string.
 704   *
 705   * @TODO This should move to project_usage.module
 706   */
 707  function project_build_query($sql_elements) {
 708    $sql = '';
 709    foreach ($sql_elements as $key => $sql_element) {
 710      if ($key != 'parameters' && count($sql_element['pieces'])) {
 711        $sql .= $sql_element['prefix'] . implode($sql_element['glue'], $sql_element['pieces']);
 712      }
 713    }
 714    return db_rewrite_sql($sql);
 715  }
 716  
 717  /**
 718   * Construct an empty query for project_build_query().
 719   *
 720   * Set the default elements that will be used to construct the SQL statement.
 721   *
 722   * @TODO This should move to project_usage.module
 723   */
 724  function project_empty_query() {
 725    return array(
 726      'fields' => array(
 727        'prefix' => 'SELECT ',
 728        'glue' => ', ',
 729        'pieces' => array(),
 730      ),
 731      'from' => array(
 732        'prefix' => ' FROM ',
 733        'glue' => NULL,
 734        'pieces' => array(''),
 735      ),
 736      'joins' => array(
 737        'prefix' => '',
 738        'glue' => ' ',
 739        'pieces' => array(),
 740      ),
 741      'wheres' => array(
 742        'prefix' => ' WHERE ',
 743        'glue' => ' AND ',
 744        'pieces' => array(),
 745      ),
 746      'group_bys' => array(
 747        'prefix' => ' GROUP BY ',
 748        'glue' => ', ',
 749        'pieces' => array(),
 750      ),
 751      'order_bys' => array(
 752        'prefix' => ' ORDER BY ',
 753        'glue' => ', ',
 754        'pieces' => array(),
 755      ),
 756      'parameters' => array(
 757        'prefix' => NULL,
 758        'glue' => NULL,
 759        'pieces' => array(),
 760      )
 761    );
 762  }
 763  
 764  /**
 765   * Helper function for grouping nodes by date.
 766   */
 767  function _project_date($timestamp) {
 768    static $last;
 769    $date = format_date($timestamp, 'custom', 'F j, Y');
 770    if ($date != $last) {
 771      $last = $date;
 772      return $date;
 773    }
 774  }
 775  
 776  /**
 777   * Returns an array of all projects on the system for use with select
 778   * form elements. The keys are the project nid, and the values are
 779   * the project names. If the project taxonomy is in use, the array
 780   * will be sorted into the project categories with appropriate headers
 781   * for each term.
 782   *
 783   * @param $project_urls
 784   *   Reference to be filled with an array of project uri => id mappings. This
 785   *   array is used by the project_issue search form code.
 786   * @param $issues
 787   *   If TRUE, only projects with issue tracking  enabled are returned.
 788   *   For this option to do much good, the project_issue.module should be
 789   *   enabled.
 790   * @param $key_prefix
 791   *   Prefix to prepend to all keys in the returned array.
 792   */
 793  function project_projects_select_options(&$project_urls, $issues = TRUE, $key_prefix = NULL) {
 794    $projects = array();
 795    $ISSUE_JOIN = '';
 796    $ISSUE_WHERE = '';
 797    $args_use_taxonomy = array(1, 0);    // n.status, h.parent
 798    $args_no_taxonomy = array(1);        // n.status
 799    if ($issues && module_exists('project_issue')) {
 800      $ISSUE_JOIN ='INNER JOIN {project_issue_projects} pip ON n.nid = pip.nid';
 801      $ISSUE_WHERE = 'AND pip.issues = %d';
 802      $args_use_taxonomy[] = 1;
 803      $args_no_taxonomy[] = 1;
 804    }
 805    if (project_use_taxonomy()) {
 806      $vid = _project_get_vid();
 807      $args_use_taxonomy[] = $vid;
 808      $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, d.name, p.uri FROM {project_projects} p INNER JOIN {node} n ON n.nid = p.nid $ISSUE_JOIN LEFT JOIN {term_node} t ON t.vid = n.vid INNER JOIN {term_data} d ON t.tid = d.tid INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE n.status = %d AND h.parent = %d $ISSUE_WHERE AND d.vid = %d GROUP BY n.nid, n.title, d.name, p.uri, d.weight ORDER BY d.weight, n.title"), $args_use_taxonomy);
 809      while ($project = db_fetch_object($result)) {
 810        if (isset($project->name)) {
 811          if (!isset($projects[$project->name])) {
 812            $projects[$project->name] = array();
 813          }
 814          $projects[$project->name][$key_prefix . $project->nid] = $project->title;
 815        }
 816        else {
 817          $projects[$key_prefix . $project->nid] = $project->title;
 818        }
 819        if (is_array($project_urls)) {
 820          $project_urls[$project->uri] = $project->nid;
 821        }
 822      }
 823    }
 824    else {
 825     $result = db_query(db_rewrite_sql("SELECT n.nid, p.uri, n.title FROM {project_projects} p INNER JOIN {node} n ON n.nid = p.nid $ISSUE_JOIN WHERE n.status = %d $ISSUE_WHERE ORDER BY n.title"), $args_no_taxonomy);
 826      while ($project = db_fetch_object($result)) {
 827        $projects[$key_prefix . $project->nid] = $project->title;
 828        if (is_array($project_urls)) {
 829          $project_urls[$project->uri] = $project->nid;
 830        }
 831      }
 832    }
 833    return $projects;
 834  }
 835  
 836  function project_quick_navigate_form() {
 837    $uris = NULL;
 838    $projects = array(t('- Select a project -')) + project_projects_select_options($uris, FALSE);
 839    $form = array();
 840    $form['project_goto'] = array(
 841      '#type' => 'select',
 842      '#default_value' => '0',
 843      '#options' => $projects,
 844      '#attributes' => array('title' => t('Select a project to view')),
 845    );
 846    $form['project'] = array(
 847      '#type' => 'submit',
 848      '#value' => t('View project'),
 849      '#submit' => array('project_quick_navigate_project_submit'),
 850    );
 851    return $form;
 852  }
 853  
 854  function project_quick_navigate_form_validate($form, $form_state) {
 855    if (empty($form_state['values']['project_goto'])) {
 856      form_set_error('project_goto', t('You must select a project to navigate to.'));
 857    }
 858  }
 859  
 860  function project_quick_navigate_project_submit($form, &$form_state) {
 861    $form_state['redirect'] = 'node/'. $form_state['values']['project_goto'];
 862  }
 863   
 864  function project_quick_navigate_title_form() {
 865    $form = array();
 866    $form['project_title'] = array(
 867      '#title' => t('Project name'),
 868      '#type' => 'textfield',
 869      '#size' => 20,
 870      '#autocomplete_path' => 'project/autocomplete/project',
 871    );
 872    $form['project'] = array(
 873      '#type' => 'submit',
 874      '#value' => t('View project'),
 875      '#validate' => array('project_quick_navigate_title_project_validate'),
 876      '#submit' => array('project_quick_navigate_title_project_submit'),
 877    );
 878    return $form;
 879  }
 880  
 881  function project_quick_navigate_title_project_validate($form, &$form_state) {
 882    if (empty($form_state['values']['project_title'])) {
 883      form_set_error('project_title', t('You must enter a project to navigate to.'));
 884    }
 885    else {
 886      $nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s' AND type = '%s'", $form_state['values']['project_title'], 'project_project'));
 887      if (empty($nid)) {
 888        form_set_error('project_title', t('The name you entered (%title) is not a valid project.', array('%title' => $form_state['values']['project_title'])));
 889      }
 890      else {
 891        $form_state['nid'] = $nid;
 892      }
 893    }
 894  }
 895  
 896  function project_quick_navigate_title_project_submit($form, &$form_state) {
 897    $form_state['redirect'] = 'node/'. $form_state['nid'];
 898  }
 899   
 900  
 901  /**
 902   * Implementation of hook_views_api().
 903   */
 904  function project_views_api() {
 905    return array(
 906      'api' => 2,
 907      'path' => drupal_get_path('module', 'project') .'/views',
 908    );
 909  }
 910  
 911  // Themables
 912  
 913  /**
 914   * Implementation of hook_theme().
 915   */
 916  function project_theme() {
 917    return array(
 918      'project_term_list' => array(
 919        'arguments' => array(
 920          'terms' => NULL,
 921          'path' => NULL,
 922        ),
 923      ),
 924      'project_summary' => array(
 925        'arguments' => array(
 926          'project' => NULL,
 927        ),
 928      ),
 929      'project_feed_icon' => array(
 930        'arguments' => array(
 931          'url' => NULL,
 932          'title' => NULL,
 933        ),
 934      ),
 935      'project_project_node_form_taxonomy' => array(
 936        'file' => 'project.inc',
 937        'arguments' => array(
 938          'form' => NULL,
 939        ),
 940      ),
 941      'project_views_project_sort_method_options_form' => array(
 942        'file' => 'theme.inc',
 943        'path' => drupal_get_path('module', 'project') .'/views/theme',
 944        'arguments' => array(
 945          'form' => NULL,
 946        ),
 947      ),
 948      'project_type' => array(
 949        'arguments' => array(
 950          'term' => NULL,
 951        ),
 952      ),
 953      'project_type_description' => array(
 954        'arguments' => array(
 955          'term' => NULL,
 956        ),
 957      ),
 958    );
 959  }
 960  
 961  function theme_project_term_list($terms, $path) {
 962    $depth = 0;
 963    $output = "\n<ul class=\"project-terms\">\n";
 964    foreach ($terms as $term) {
 965      if (!empty($term->description)){
 966        $description = strip_tags($term->description);
 967      }
 968      else {
 969        $description = '';
 970      }
 971      $link = l($term->name .' ('. $term->count .')', "$path/$term->tid", array('attributes' => array('title' => $description)));
 972      $output .= '<li class="leaf">' . $link . "</li>\n";
 973    }
 974    $output .= "\n</ul>\n";
 975    return $output;
 976  }
 977  
 978  /**
 979   * Theme a compact project view/summary.
 980   *
 981   * $project has the following fields:
 982   * - title: Title
 983   * - nid: Node id
 984   * - body: Filtered description
 985   * - term: String with the selected term name
 986   * - version: String with the version
 987   * - links: Array of links
 988   */
 989  function theme_project_summary($project) {
 990    $output = '<div class="' . $project->class . '">';
 991    $output .= '<h2>'. l($project->title, "node/$project->nid") .'</h2>';
 992    if (!empty($project->changed)) {
 993      $output .= '<p><small>' . t('Last changed: !interval ago', array('!interval' => format_interval(time() - $project->changed, 2))) . '</small></p>';
 994    }
 995    $output .= $project->body;
 996    if (!empty($project->download_table)) {
 997      $output .= $project->download_table;
 998    }
 999    if (!empty($project->links)) {
1000      $output .= theme('links', $project->links);
1001    }
1002    if (!empty($project->terms)) {
1003      $output .= theme('links', $project->terms);
1004    }
1005    $output .= '</div>';
1006    return $output;
1007  }
1008  
1009  /**
1010   * Display an RSS feed icon for use on project nodes.
1011   */
1012  function theme_project_feed_icon($url, $title) {
1013    if ($icon = theme('image', 'misc/feed.png', $title, $title)) {
1014      return '<a href="'. check_url($url) .'" class="project-feed-icon">'. $icon .'</a>';
1015    }
1016  }
1017  
1018  /**
1019   * Return a string of valid project names for use with JS autocomplete fields.
1020   */
1021  function project_autocomplete_project($string = '') {
1022    $matches = array();
1023  
1024    // The user enters a comma-separated list of projects. We only autocomplete
1025    // the last one.
1026    $array = drupal_explode_tags($string);
1027    $last_string = trim(array_pop($array));
1028  
1029    if ($last_string != '') {
1030      $result = db_query_range(db_rewrite_sql("SELECT n.title FROM {node} n WHERE n.status = %d AND n.type = '%s' AND LOWER(n.title) LIKE LOWER('%%%s%%')"), 1, 'project_project', $last_string, 0, 10);
1031  
1032      $prefix = count($array) ? implode(', ', $array) .', ' : '';
1033      while ($project = db_fetch_object($result)) {
1034        $title = $project->title;
1035        // Commas and quotes in terms are special cases, so encode 'em.
1036        if (strpos($title, ',') !== FALSE || strpos($title, '"') !== FALSE) {
1037          $title = '"'. str_replace('"', '""', $project->title) .'"';
1038        }
1039        $matches[$prefix . $title] = check_plain($project->title);
1040      }
1041    }
1042  
1043    drupal_json($matches);
1044  }
1045  
1046  
1047  /**
1048   * Return a string of valid project names owned by a given user.
1049   */
1050  function project_autocomplete_project_user($uid, $string = '') {
1051    $matches = array();
1052  
1053    // The user enters a comma-separated list of projects. We only autocomplete
1054    // the last one.
1055    $array = drupal_explode_tags($string);
1056    $last_string = trim(array_pop($array));
1057  
1058    if ($last_string != '') {
1059      $result = db_query_range(db_rewrite_sql("SELECT n.title FROM {node} n WHERE n.status = %d AND n.type = '%s' AND n.uid = %d AND LOWER(n.title) LIKE LOWER('%%%s%%')"), 1, 'project_project', $uid, $last_string, 0, 10);
1060  
1061      $prefix = count($array) ? implode(', ', $array) .', ' : '';
1062      while ($project = db_fetch_object($result)) {
1063        $title = $project->title;
1064        // Commas and quotes in terms are special cases, so encode 'em.
1065        if (strpos($title, ',') !== FALSE || strpos($title, '"') !== FALSE) {
1066          $title = '"'. str_replace('"', '""', $project->title) .'"';
1067        }
1068        $matches[$prefix . $title] = check_plain($project->title);
1069      }
1070    }
1071  
1072    drupal_json($matches);
1073  }
1074  
1075  /**
1076   * Returns whether or not the project module should use
1077   * taxonomy-specific functionality.
1078   */
1079  function project_use_taxonomy() {
1080    return module_exists('taxonomy') && taxonomy_get_tree(_project_get_vid());
1081  }
1082  
1083  /**
1084   * Returns whether or not the project uses version control or not.
1085   */
1086  function project_use_cvs($project) {
1087    if (module_exists('cvs')) {
1088      $project = is_numeric($project) ? node_load($project) : $project;
1089      return !empty($project->cvs['repository']);
1090    }
1091  }
1092  
1093  /**
1094   * Determines if the current site supports caching of project-related pages.
1095   *
1096   * The pages can be cached if:
1097   *   1. Anonymous users have the 'access projects' permission.
1098   *   2. No node access modules are installed.
1099   *
1100   * @return TRUE if the output can be cached, FALSE otherwise.
1101   */
1102  function project_can_cache() {
1103    $grants = module_implements('node_grants');
1104    if (!empty($grants)) {
1105      return FALSE;
1106    }
1107    $allowed_roles = user_roles(FALSE, 'access projects');
1108    if (!isset($allowed_roles[DRUPAL_ANONYMOUS_RID])) {
1109      return FALSE;
1110    }
1111  
1112    return TRUE;
1113  }
1114  
1115  /**
1116   * Intelligently merge project type terms selected by the
1117   * user back into $node->taxonomy during a node preview.
1118   */
1119  function project_project_form_preview_submit($form, &$form_state) {
1120    $project_type_vid = _project_get_vid();
1121    if (isset($project_type_vid) && isset($form_state['values']['project_type'])) {
1122      $project_type_tid = $form_state['values']['project_type'];
1123      $project_type_tid_field = 'tid_' . $project_type_tid;
1124      $project_type_terms = array();
1125      $project_type_terms[$project_type_tid] = $project_type_tid;
1126      if (isset($form_state['values']->$project_type_tid_field)) {
1127        foreach ($form_state['values'][$project_type_tid_field] as $tid) {
1128          $project_type_terms[$tid] = $tid;
1129        }
1130      }
1131      $form_state['node']['taxonomy'][$project_type_vid] = $project_type_terms;
1132    }
1133  }
1134  
1135  /**
1136   * Present a validation error if the user tries to make the
1137   * Project types vocabulary a free tagging vocabulary.
1138   */
1139  function project_project_type_vocabulary_validate($form, &$form_state) {
1140    if (!empty($form_state['values']['tags'])) {
1141      form_set_error('tags', t('The %name vocabulary does not support tags.', array('%name' => $form_state['values']['name'])));
1142    }
1143  }
1144  
1145  /**
1146   * Theme a project type item.
1147   */
1148  function theme_project_type($term) {
1149    $link = l($term->name, check_url('project/'. drupal_strtolower($term->name)));
1150    $link = str_replace('%20', '+', $link);
1151    $output = "<div>$link</div>\n";
1152    if ($term->description) {
1153      $output .= '<p>' . filter_xss($term->description) . '</p>';
1154    }
1155    return $output;
1156  }
1157  
1158  /**
1159   * Theme a project type description.
1160   */
1161  function theme_project_type_description($term) {
1162    $output = '';
1163    if ($term->description) {
1164      $output .= '<p>' . filter_xss($term->description) . '</p>';
1165    }
1166    return $output;
1167  }
1168  
1169  
1170  /**
1171   * Find a project node ID (nid) based on project short name (uri).
1172   *
1173   * @param $uri
1174   *   The project shortname (uri) to search for.
1175   *
1176   * @return
1177   *   The project node ID (nid) of the given project, or NULL if not found.
1178   */
1179  function project_get_nid_from_uri($uri) {
1180    static $nids = array();
1181    if (!isset($nids[$uri])) {
1182      $nids[$uri] = db_result(db_query_range("SELECT nid FROM {project_projects} WHERE uri = '%s'", $uri, 0, 1));
1183    }
1184    return $nids[$uri];
1185  }
1186  
1187  /**
1188   * Find a project shortname (uri) based on project node ID (nid).
1189   *
1190   * @param $uri
1191   *   The project node ID (nid) to search for.
1192   *
1193   * @return
1194   *   The project shortname (uri) of the given project, or NULL if not found.
1195   */
1196  function project_get_uri_from_nid($nid) {
1197    static $uris = array();
1198    if (!isset($uris[$nid])) {
1199      $uris[$nid] = db_result(db_query_range("SELECT uri FROM {project_projects} WHERE nid = %d", $nid, 0, 1));
1200    }
1201    return $uris[$nid];
1202  }
1203  
1204  /**
1205   * Get the project node context from the currently active menu, if any.
1206   *
1207   * @return
1208   *   A fully loaded project $node object if the currently active menu has a
1209   *   project node context, or NULL if the menu isn't pointing to a project.
1210   */
1211  function project_get_project_from_menu() {
1212    if (($node = menu_get_object('project_node')) ||
1213        (($node = menu_get_object()) && $node->type == 'project_project')) {
1214      return $node;
1215    }
1216  }
1217  
1218  /**
1219   * Implement hook_token_list() (from token.module)
1220   */
1221  function project_token_list($type) {
1222    if ($type == 'node') {
1223      $tokens['node'] = array(
1224        'project_shortname' => t("A project's short name"),
1225        'project_homepage' => t("Link to a project's home page"),
1226        'project_changelog' => t("Link to a project's changelog"),
1227        'project_cvs' => t("Link to a project's cvs tree"),
1228        'project_demo' => t('Demonstration link for a project'),
1229        'project_documentation' => t("Link to a project's documentation"),
1230        'project_screenshots' => t("Link to a project's screenshots"),
1231        'project_license' => t("Link to a project's license"),
1232      );
1233      return $tokens;
1234    }
1235  }
1236   
1237  /**
1238   * Implement hook_token_values() (from token.module)
1239   */
1240  function project_token_values($type = 'all', $object = NULL) {
1241    if ($type == 'node') {
1242      if ($object->type == 'project_project') {
1243        $values['project_shortname'] = check_plain($object->project['uri']);
1244        $values['project_homepage'] = check_url($object->project['homepage']);
1245        $values['project_changelog'] = check_url($object->project['changelog']);
1246        $values['project_cvs'] = check_url($object->project['cvs']);
1247        $values['project_demo'] = check_url($object->project['demo']);
1248        $values['project_documentation'] = check_url($object->project['documentation']);
1249        $values['project_screenshots'] = check_url($object->project['screenshots']);
1250        $values['project_license'] = check_url($object->project['license']);
1251      }
1252      else {
1253        $values['project_shortname'] = '';
1254        $values['project_homepage'] = '';
1255        $values['project_changelog'] = '';
1256        $values['project_cvs'] = '';
1257        $values['project_demo'] = '';
1258        $values['project_documentation'] = '';
1259        $values['project_screenshots'] = '';
1260        $values['project_license'] = '';
1261      }
1262      return $values;
1263    }
1264  }
1265  


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7