You are here

pm.module in Drupal PM (Project Management) 7.3

Same filename and directory in other branches
  1. 8 pm.module
  2. 7 pm.module
  3. 7.2 pm.module
  4. 4.x pm.module

Main module file for the Project Management module.

File

pm.module
View source
<?php

/**
 * @file
 * Main module file for the Project Management module.
 */
define('PM_ICON_SET_DEFAULT_BEHAVIOUR', 'default');
define('PM_ICON_SET_FONTAWESOME', 'fontawesome');
define('PM_ICON_SET_STORM', 'storm');
define('PM_ICON_SET_NO_ICON', 'none');
define('PM_CONTEXTUAL_LINK_PSEUDO_FIELD', 'pm_contextual_link');
define('PM_FONTAWESOME_DISABLED', 0x0);
define('PM_FONTAWESOME_LIBRARY_ENABLED', 0x1);
define('PM_FONTAWESOME_MODULE_ENABLED', 0x2);
define('PM_FONTAWESOME_ENABLED', PM_FONTAWESOME_LIBRARY_ENABLED | PM_FONTAWESOME_MODULE_ENABLED);
module_load_include('inc', 'pm', 'includes/pm.permission');

/**
 * Implements hook_help().
 */
function pm_help($path, $arg) {
  $output = '';
  switch ($path) {
    case "admin/help#pm":
      $output = '<p>' . t("Provides a complete project management environment") . '</p>';
      break;
    case 'admin/help#pm-permission':
      return '<p>' . t('PM Permission allows you to grant access to content based on interactions with Drupal PM.') . '</p>';
  }
  return $output;
}

/**
 * Implements hook_permission().
 */
function pm_permission() {
  $permissions = array(
    'administer pm permission' => array(
      'title' => t('Administer Project Management Permission'),
      'restrict access' => TRUE,
    ),
  );

  // Generate override node permissions for all applicable node types.
  foreach (pm_permission_get_enabled_types() as $bundle_name) {
    $permissions += pm_permission_permission_per_bundle($bundle_name);
  }
  $permissions += array(
    'Project Management: access dashboard' => array(
      'title' => t('Access Dashboard'),
      'description' => t('Allows the user to access the Project Management dashboard. The items included on the dashboard are set through permissions on the individual modules that provide those links.'),
    ),
    'Project Management: access administration pages' => array(
      'title' => t('Administer Project Management'),
      'description' => t('Allow the user to perform administrative tasks on Project Management modules, such as altering configuration.'),
    ),
  );
  return $permissions;
}

/**
 * Implements hook_menu().
 */
function pm_menu() {
  $items = array();
  $items['pm'] = array(
    'title' => 'Project Management',
    'description' => 'Access various components of <em>Project Management</em>',
    'page callback' => 'pm_page_front',
    'access arguments' => array(
      'Project Management: access dashboard',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'includes/pm.pages.inc',
  );

  // Configuration group.
  $items['admin/config/pm'] = array(
    'title' => 'Project Management',
    'description' => 'Project Management settings',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array(
      'Project Management: access administration pages',
    ),
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
  );

  // Project Management settings page (tabs defined below).
  $items['admin/config/pm/pm'] = array(
    'title' => 'General Settings',
    'description' => 'Configure <em>Project Management</em> behaviour across components.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'pm_admin_settings',
    ),
    'access arguments' => array(
      'Project Management: access administration pages',
    ),
    'weight' => -100,
    'file' => 'pm.settings.inc',
    'file path' => drupal_get_path('module', 'pm') . '/includes',
  );

  // Basic settings tab (default).
  $items['admin/config/pm/pm/basic'] = array(
    'title' => 'Basic Settings',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );

  // Permissions tab.
  $items['admin/config/pm/pm/permissions'] = array(
    'title' => 'Permissions',
    'description' => 'Configure access controls based on PM fields.',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'pm_permission_admin',
    ),
    'access arguments' => array(
      'administer pm permission',
    ),
    'file' => 'pm.permission.inc',
    'file path' => drupal_get_path('module', 'pm') . '/includes',
  );
  $items['admin/config/pm/pm/menu'] = array(
    'title' => 'Menu',
    'description' => 'Manage menu exposed in block and on dashboard.',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'pm_menu_list',
    ),
    'access arguments' => array(
      'administer pm permission',
    ),
    'file' => 'pm.menu.inc',
    'file path' => drupal_get_path('module', 'pm') . '/includes',
  );
  $items['admin/config/pm/pm/menu/%'] = array(
    'title' => 'Menu Settings',
    'title callback' => 'pm_menu_admin_title_callback',
    'title arguments' => array(
      5,
    ),
    'description' => 'Manage Dashboard Menu.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'pm_menu_admin',
      5,
    ),
    'access arguments' => array(
      'administer pm permission',
    ),
    'file' => 'pm.menu.inc',
    'file path' => drupal_get_path('module', 'pm') . '/includes',
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function pm_theme() {
  return array(
    'pm_dashboard' => array(
      'file' => 'pm.theme.inc',
      'variables' => array(
        'links' => array(),
      ),
    ),
    'pm_dashboard_block' => array(
      'file' => 'pm.theme.inc',
      'variables' => array(
        'links' => array(),
      ),
    ),
    'pm_dashboard_link' => array(
      'file' => 'pm.theme.inc',
      'variables' => array(
        'link_blocks' => array(),
      ),
      'template' => 'templates/pm.menu.link',
    ),
    'pm_icon_none' => array(
      'file' => 'pm.theme.inc',
      'variables' => array(
        'title' => '',
      ),
      'template' => 'templates/pm.icon.none',
    ),
    'pm_icon' => array(
      'file' => 'pm.theme.inc',
      'variables' => array(
        'title' => '',
        'icon' => '',
      ),
      'template' => 'templates/pm.icon',
    ),
    'pm_list_default' => array(
      'file' => 'pm.theme.inc',
      'template' => 'pm-list-default',
      'variables' => array(
        'value' => NULL,
        'key' => NULL,
        'field_name' => NULL,
      ),
    ),
    'pm_parent_breadcrumb' => array(
      'file' => 'pm.theme.inc',
      'variables' => array(
        'data' => array(),
      ),
    ),
    'pm_menu_entry_table' => array(
      'file' => 'pm.theme.inc',
      'render element' => 'element',
    ),
  );
}

/**
 * Implements hook_field_extra_fields().
 */
function pm_field_extra_fields() {
  $default = array(
    'node:pmorganization' => 'node:pmorganization',
    'node:pmproject' => 'node:pmproject',
    'node:pmissue' => 'node:pmissue',
    'node:pmtask' => 'node:pmtask',
    'node:pmticket' => 'node:pmticket',
    'node:pmtimetracking' => 'node:pmtimetracking',
    'node:pmteam' => 'node:pmteam',
    'node:pmnote' => 'node:pmnote',
    'node:pmexpense' => 'node:pmexpense',
    'user:user' => 'user:user',
  );
  $enabled_content_types = variable_get('pm_contextual_links_enabled_content_types', $default);
  foreach ($enabled_content_types as $info) {
    if ($info) {
      $info = explode(':', $info);
      $entity_type = $info[0];
      $bundle = $info[1];
      $extra[$entity_type][$bundle]['display'][PM_CONTEXTUAL_LINK_PSEUDO_FIELD] = array(
        'label' => t('Drupal PM contextual links.'),
        'description' => t('Add a contextual link exposing various Drupal PM related tasks on Drupal Entities.'),
        'weight' => 50,
      );
    }
  }
  return $extra;
}

/**
 * Implements hook_entity_view().
 */
function pm_entity_view($entity, $type, $view_mode, $langcode) {
  list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
  $extrafields = field_extra_fields_get_display($type, $bundle, $view_mode);
  if (isset($extrafields[PM_CONTEXTUAL_LINK_PSEUDO_FIELD]) && isset($extrafields[PM_CONTEXTUAL_LINK_PSEUDO_FIELD]['visible']) && $extrafields[PM_CONTEXTUAL_LINK_PSEUDO_FIELD]['visible']) {
    $links = module_invoke_all('pm_contextual_links', $entity, $type, $view_mode, $langcode);
    ksort($links);
    if (!empty($links)) {
      $entity->content[PM_CONTEXTUAL_LINK_PSEUDO_FIELD] = array(
        '#theme' => 'links__ctools_dropbutton',
        '#links' => $links,
      );
    }
    else {
      $entity->content[PM_CONTEXTUAL_LINK_PSEUDO_FIELD] = '';
    }
  }
}

/**
 * Return links array for the pm dashboard.
 *
 * @param bool $check_active
 *   When FALSE, returns all links whether active or not (for admin settings).
 * @param string $type
 *   Dashboard type.
 *
 * @return array
 *   Dashboard links.
 */
function pm_dashboard_get_links($check_active = TRUE, $type = 'page') {
  module_load_include('inc', 'pm', 'includes/pm.menu');
  $links = pm_menu_get_links($type, $check_active);
  return $links;
}

/**
 * Orders dashboard links by weight. Helper for pm_dashboard_get_links().
 *
 * @param array $a
 *   Dashboard link array.
 * @param array $b
 *   Dashboard link array.
 *
 * @return int
 *   Relative weight of $a and $b.
 *
 * @see uasort()
 */
function _pm_dashboard_sort_links(array $a, array $b) {
  if (intval($a['weight']) == intval($b['weight'])) {
    return 0;
  }
  elseif (intval($a['weight']) < intval($b['weight'])) {
    return -1;
  }
  else {
    return 1;
  }
}

/**
 * Implements hook_pm_dashboard_links().
 */
function pm_pm_dashboard_links($type) {
  $links = array();
  if ($type == 'page' || $type == 'block') {
    $links[] = array(
      'title' => t('Configuration'),
      'icon' => 'pmconfiguration',
      'path' => 'admin/config/pm',
      'params' => array(),
      'node_type' => '',
      'add_type' => '',
      'map' => array(),
      'weight' => 15,
    );
  }
  return $links;
}

/**
 * Implements hook_block_info().
 */
function pm_block_info() {

  // Register Project Management menu block.
  $blocks['pm_menu'] = array(
    'info' => t('Project Management Menu'),
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function pm_block_view($delta = '') {

  // Define Project Management menu block.
  $block = array();
  switch ($delta) {
    case 'pm_menu':
      $block = array(
        'subject' => 'Project Management Menu',
        'content' => array(
          'quick_links' => array(
            '#theme' => 'pm_dashboard_block',
            '#links' => pm_dashboard_get_links(TRUE, 'block'),
            '#attached' => array(
              'css' => array(
                drupal_get_path('module', 'pm') . '/pm-dashboard.css',
              ),
            ),
          ),
        ),
      );
      break;
  }
  return $block;
}

/**
 * Set body class to use pm default icons if required.
 */
function pm_preprocess_html(&$vars) {
  $pm_icon = variable_get('pm_icon', PM_ICON_SET_DEFAULT_BEHAVIOUR);
  switch ($pm_icon) {
    case PM_ICON_SET_DEFAULT_BEHAVIOUR:

      // Use fontawesome icons if available, defer to legacy otherwise.
      $fontawesome_status = _pm_get_fontawesome_status();
      $vars['classes_array'][] = $fontawesome_status == PM_FONTAWESOME_ENABLED ? "pm-use-fa-icons" : "pm-use-storm-icons";
      break;
    case PM_ICON_SET_FONTAWESOME:
      $vars['classes_array'][] = "pm-use-fa-icons";
      break;
    case PM_ICON_SET_STORM:
      $vars['classes_array'][] = "pm-use-storm-icons";
      break;
  }
}

/**
 * Helper function to return nid of particular bundle in hierarchy.
 *
 * @param object $node
 *   The node whose parent's should be hunted for.
 * @param string $type
 *   The parent node type that is being hunted for.
 * @param int $iteration
 *   Number of maximum iterations to try for.
 *
 * @return int
 *   Node id of the required parent.
 */
function pm_get_parent($node, $type = 'pmorganization', $iteration = 10) {
  try {
    $wrapper = entity_metadata_wrapper('node', $node);
    while ($iteration) {
      $iteration--;
      $bundle = $wrapper
        ->getBundle();
      if ($bundle == $type) {
        return $wrapper
          ->getIdentifier();
      }
      if ($wrapper
        ->__isset($bundle . '_parent')) {
        $parent_field = $bundle . '_parent';
      }
      elseif ($wrapper
        ->__isset('field_' . $bundle . '_parent')) {
        $parent_field = 'field_' . $bundle . '_parent';
      }
      else {
        return FALSE;
      }
      $wrapper = $wrapper->{$parent_field};
    }
  } catch (EntityMetadataWrapperException $exc) {
    watchdog('pm', 'See ' . __FUNCTION__ . '() <pre>' . $exc
      ->getTraceAsString() . '</pre>', NULL, WATCHDOG_ERROR);
  }
}

/**
 * Implements hook_views_api().
 */
function pm_views_api() {
  return array(
    'api' => 3.0,
    'path' => drupal_get_path('module', 'pm') . '/includes/views',
  );
}

/**
 * Implements hook_field_formatter_info().
 */
function pm_field_formatter_info() {
  return array(
    'pm_list_default' => array(
      'label' => t('Project Management Icons'),
      'field types' => array(
        'list_integer',
        'list_float',
        'list_text',
        'list_boolean',
      ),
    ),
    'pm_parent_breadcrumb' => array(
      'label' => t('Project Management Parent Breadcrumb'),
      'field types' => array(
        'entityreference',
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_view().
 */
function pm_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  switch ($display['type']) {
    case 'pm_list_default':
      $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
      foreach ($items as $delta => $item) {
        $value = '';
        if (isset($allowed_values[$item['value']])) {
          $value = field_filter_xss($allowed_values[$item['value']]);
        }
        else {

          // If no match was found in allowed values, fall back to the key.
          $value = field_filter_xss($item['value']);
        }
        $key = $item['value'];
        $output = theme('pm_list_default', array(
          'value' => $value,
          'key' => $key,
          'field_name' => $instance['field_name'],
        ));
        $element[$delta] = array(
          '#markup' => $output,
        );
      }
      break;
    case 'pm_parent_breadcrumb':
      foreach ($items as $delta => $item) {
        $element[$delta] = array(
          '#data' => pm_get_parents($entity_type, $entity, $field, $delta),
          '#theme' => 'pm_parent_breadcrumb',
        );
      }
      break;
  }
  return $element;
}

/**
 * Recursively gather all parents for a given entity.
 */
function pm_get_parents($entity_type, $entity, $field, $delta) {
  $iteration = 10;
  $field_name = $field['field_name'];
  $parents = array();
  try {
    $e = entity_metadata_wrapper('node', $entity);
    if ($field['cardinality'] == '1') {
      if (isset($e->{$field_name})) {
        $wrapper = $e->{$field_name};
      }
    }
    else {
      if (isset($e->{$field_name})) {
        $wrapper = $e->{$field_name}[$delta];
      }
    }
    while ($iteration and $wrapper) {
      $iteration--;
      $bundle = $wrapper
        ->getBundle();
      if ($wrapper
        ->getIdentifier()) {
        $parents[$wrapper
          ->getIdentifier()] = pm_get_parent_label('node', $wrapper);
      }
      if ($wrapper
        ->__isset($bundle . '_parent')) {
        $parent_field = $bundle . '_parent';
      }
      elseif ($wrapper
        ->__isset('field_' . $bundle . '_parent')) {
        $parent_field = 'field_' . $bundle . '_parent';
      }
      else {
        break;
      }
      if ($bundle == 'pmorganization') {
        break;
      }
      $wrapper = $wrapper->{$parent_field};
    }
  } catch (EntityMetadataWrapperException $exc) {
    watchdog('pm', 'See ' . __FUNCTION__ . '() <pre>' . $exc
      ->getTraceAsString() . '</pre>', NULL, WATCHDOG_ERROR);
  }
  return array_reverse($parents);
}

/**
 * Get the label for a given entity.
 */
function pm_get_parent_label($entity_type, $wrapper) {
  $label = $wrapper
    ->label();
  $id = $wrapper
    ->getIdentifier();
  $path = $entity_type . '/' . $id;
  return drupal_valid_path($path) ? l($label, $path) : $label;
}

/**
 * Implements hook_node_access_records().
 */
function pm_node_access_records($node) {
  if (empty($node->status)) {

    // Lets Drupal take care of permission to unpublished nodes.
    return array();
  }
  $bundle_name = is_string($node) ? $node : $node->type;
  $grants = array();
  if (!variable_get('node_permissions_' . $bundle_name, 1)) {
    $default_grants = _pm_permission_grants_default($node, $bundle_name);
    $grants = array_merge($grants, $default_grants);
  }
  if (module_exists('pmorganization')) {
    if ($bundle_name == 'pmorganization' or _pm_permission_get_field_name($bundle_name, 'parent')) {
      $belonged_grants = _pm_permission_grants_belonged($node, $bundle_name, 'Organization');
      if ($belonged_grants) {
        $grants = array_merge($grants, $belonged_grants);
      }
    }
  }
  if (module_exists('pmproject')) {
    if (_pm_permission_get_field_name($bundle_name, 'pm')) {
      $assigned_grants_pm = _pm_permission_grants_assigned($node, $bundle_name, 'Project Manager');
      if ($assigned_grants_pm) {
        $grants = array_merge($grants, $assigned_grants_pm);
      }
    }
  }
  if (_pm_permission_get_field_name($bundle_name, 'assigned')) {
    $assigned_grants_assigned = _pm_permission_grants_assigned($node, $bundle_name, 'Assigned');
    if ($assigned_grants_assigned) {
      $grants = array_merge($grants, $assigned_grants_assigned);
    }
  }
  return $grants;
}

/**
 * Implements hook_node_grants().
 */
function pm_node_grants($account, $op) {
  $grants = array();
  foreach (pm_permission_get_enabled_types() as $type) {
    $grant_per_type = pm_permission_grants_list($account, $op, $type);
    if ($grant_per_type) {
      $grants += $grant_per_type;
    }
  }
  return $grants;
}

/**
 * Implements hook_node_access().
 */
function pm_node_access($node, $op, $account) {
  $type = is_string($node) ? $node : $node->type;
  if (variable_get("pm_permission_node_{$type}_enabled", FALSE) && $op == 'create') {
    if (user_access("PM permission {$type}: create", $account)) {
      return NODE_ACCESS_ALLOW;
    }
  }

  // Returning nothing from this function would have the same effect.
  return NODE_ACCESS_IGNORE;
}

/**
 * Implements hook_node_update().
 */
function pm_node_update($node) {
  $enabled = pm_permission_get_enabled_types();
  $type = $node->type;
  if (in_array($type, $enabled)) {
    if (pm_permission_check_if_child_permission_needs_update($node)) {
      pm_permission_hunt_and_update_all_child_permission($node);
    }
  }
}

/**
 * Helper function to set breadcrumb based on which links a user has access to.
 */
function pm_set_breadcrumb(array $links, $pm_root = TRUE) {
  $breadcrumb = array();

  // Unless set otherwise, all breadcrumbs start with a link to pm.
  if ($pm_root && drupal_valid_path('pm')) {
    $breadcrumb = array(
      l(t('Project Management'), 'pm'),
    );
  }

  // Now process any other links provided.
  foreach ($links as $link) {
    $text = $link['text'];
    $path = $link['path'];
    $options = isset($link['options']) ? $link['options'] : array();
    if (drupal_valid_path($path)) {
      $breadcrumb[] = l($text, $path, $options);
    }
  }

  // Only set the breadcrumb if one or more links are present.
  if (!empty($breadcrumb)) {
    drupal_set_breadcrumb($breadcrumb);
  }
}

/**
 * Helper function to check if fontawesome is available or not.
 */
function _pm_get_fontawesome_status() {
  $status =& drupal_static(__FUNCTION__);
  if (!isset($status)) {
    $status = PM_FONTAWESOME_DISABLED;
    $status |= module_exists('fontawesome') ? PM_FONTAWESOME_MODULE_ENABLED : 0x0;
    if (module_exists('libraries')) {
      $library_info = libraries_detect('fontawesome');
      if (isset($library_info['installed']) and $library_info['installed'] == TRUE) {
        $status |= PM_FONTAWESOME_LIBRARY_ENABLED;
      }
    }
  }
  return $status;
}

Functions

Namesort descending Description
pm_block_info Implements hook_block_info().
pm_block_view Implements hook_block_view().
pm_dashboard_get_links Return links array for the pm dashboard.
pm_entity_view Implements hook_entity_view().
pm_field_extra_fields Implements hook_field_extra_fields().
pm_field_formatter_info Implements hook_field_formatter_info().
pm_field_formatter_view Implements hook_field_formatter_view().
pm_get_parent Helper function to return nid of particular bundle in hierarchy.
pm_get_parents Recursively gather all parents for a given entity.
pm_get_parent_label Get the label for a given entity.
pm_help Implements hook_help().
pm_menu Implements hook_menu().
pm_node_access Implements hook_node_access().
pm_node_access_records Implements hook_node_access_records().
pm_node_grants Implements hook_node_grants().
pm_node_update Implements hook_node_update().
pm_permission Implements hook_permission().
pm_pm_dashboard_links Implements hook_pm_dashboard_links().
pm_preprocess_html Set body class to use pm default icons if required.
pm_set_breadcrumb Helper function to set breadcrumb based on which links a user has access to.
pm_theme Implements hook_theme().
pm_views_api Implements hook_views_api().
_pm_dashboard_sort_links Orders dashboard links by weight. Helper for pm_dashboard_get_links().
_pm_get_fontawesome_status Helper function to check if fontawesome is available or not.

Constants