drupal-civicrm/sites/all/modules/civicrm/drupal/civicrm.module

1303 lines
37 KiB
Plaintext
Raw Permalink Normal View History

2018-01-14 15:10:16 +02:00
<?php
/**
* @file
* CiviCRM file for integrating with Drupal.
*
* Project: CiviCRM: Constituent Relationship Management for NP's
* File: civicrm.module
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Drupal module file.
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2017
*/
require_once 'civicrm_user.inc';
define('CIVICRM_UF_HEAD', TRUE);
/**
* Adds CiviCRM CSS and JS resources into the header.
*/
function civicrm_html_head() {
if (!civicrm_initialize()) {
return;
}
if (arg(0) == 'civicrm') {
// Add our standard css & js
CRM_Core_Resources::singleton()->addCoreResources();
}
else {
CRM_Core_Resources::singleton()->addCoreStyles();
}
}
/**
* Implements hook_permissions().
*/
function civicrm_permission() {
// make sure the system is initialized
if (!civicrm_initialize()) {
return;
}
CRM_Core_Config::singleton();
$permissions = CRM_Core_Permission::basicPermissions(FALSE, TRUE);
$perms_array = array();
foreach ($permissions as $perm => $attr) {
$title = array_shift($attr);
$description = array_shift($attr);
//order matters here, but we deal with that later
$perms_array[$perm] = array('title' => $title);
if ($description) {
$perms_array[$perm]['description'] = $description;
}
}
return $perms_array;
}
/**
* Implements hook_block_info().
*/
function civicrm_block_info() {
if (!civicrm_initialize()) {
return;
}
$block = CRM_Core_Block::getInfo();
return $block;
}
/**
* Implements hook_block_view().
*/
function civicrm_block_view($delta = '0') {
if (!civicrm_initialize()) {
return array();
}
$block = CRM_Core_Block::getContent($delta);
return $block;
}
/**
* Implements hook_menu().
*/
function civicrm_menu() {
if (!civicrm_initialize()) {
return;
}
return array(
'civicrm' => array(
'title' => 'CiviCRM',
'access callback' => TRUE,
'page callback' => 'civicrm_invoke',
'type' => 4,
'weight' => 0,
),
// administration section for civicrm integration modules.
'admin/config/civicrm' => array(
'title' => 'CiviCRM',
'description' => 'Configure CiviCRM integration modules.',
'position' => 'left',
'weight' => -10,
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array('access administration pages'),
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
),
);
}
/**
* Implements hook_page_build().
*
* CRM-11823 - If Civi bootstrapped, then merge its HTML header with the CMS's header.
* This hook is intended for page modification, here we are using it to add a header.
*/
function civicrm_page_build($page) {
global $civicrm_root;
if (empty($civicrm_root)) {
return;
}
if ($region = CRM_Core_Region::instance('html-header', FALSE)) {
CRM_Utils_System::addHTMLHead($region->render(''));
}
}
/**
* Initialize CiviCRM.
*
* Call this function from other modules too if they use the CiviCRM API.
*/
function civicrm_initialize() {
// Check for php version and ensure its greater than minPhpVersion
$minPhpVersion = '5.3.4';
if (version_compare(PHP_VERSION, $minPhpVersion) < 0) {
echo "CiviCRM requires PHP Version $minPhpVersion or greater. You are running PHP Version " . PHP_VERSION . "<p>";
exit();
}
_civicrm_registerClassLoader();
$initialized = &drupal_static('civicrm_initialize', FALSE);
$failure = &drupal_static('civicrm_initialize_failure', FALSE);
if ($failure) {
return FALSE;
}
if (!$initialized) {
if (function_exists('conf_path')) {
$settingsFile = conf_path() . '/civicrm.settings.php';
}
else {
$settingsFile = conf_init() . '/civicrm.settings.php';
}
if (!defined('CIVICRM_SETTINGS_PATH')) {
define('CIVICRM_SETTINGS_PATH', $settingsFile);
}
// get ready for problems
$docLinkInstall = "http://wiki.civicrm.org/confluence/display/CRMDOC/Drupal+Installation+Guide";
$docLinkTrouble = "http://wiki.civicrm.org/confluence/display/CRMDOC/Installation+and+Configuration+Trouble-shooting";
$forumLink = "http://forum.civicrm.org/index.php/board,6.0.html";
$errorMsgAdd = t("Please review the <a href='!1'>Drupal Installation Guide</a> and the <a href='!2'>Trouble-shooting page</a> for assistance. If you still need help installing, you can often find solutions to your issue by searching for the error message in the <a href='!3'>installation support section of the community forum</a>.</strong></p>",
array(
'!1' => $docLinkInstall,
'!2' => $docLinkTrouble,
'!3' => $forumLink,
)
);
if (!include_once($settingsFile)) {
$failure = TRUE;
drupal_set_message("<strong><p class='error'>" .
t("Oops! - The CiviCRM settings file (civicrm.settings.php) was not found in the expected location ") .
"(" . $settingsFile . "). </p><p class='error'>" .
$errorMsgAdd . '</p></strong>'
);
return FALSE;
}
// this does pretty much all of the civicrm initialization
if (!include_once('CRM/Core/Config.php')) {
$failure = TRUE;
drupal_set_message("<strong><p class='error'>" .
t("Oops! - The path for including CiviCRM code files is not set properly. Most likely there is an error in the <em>civicrm_root</em> setting in your CiviCRM settings file (!1).",
array('!1' => $settingsFile)
) .
"</p><p class='error'> &raquo; " .
t("civicrm_root is currently set to: <em>!1</em>.", array(
'!1' => $civicrm_root)) .
"</p><p class='error'>" . $errorMsgAdd . "</p></strong>"
);
return FALSE;
}
$initialized = TRUE;
// initialize the system by creating a config object
$config = CRM_Core_Config::singleton();
// Add module-specific header elements
$header = civicrm_html_head();
if (!empty($header)) {
drupal_add_html_head($header);
}
CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
}
return TRUE;
}
/**
* Get CiviCRM query parameters from the url.
*
* This is useful for re-adding them to generated urls as drupal tends to drop
* them and we need them for language switching and generating urls for metadata.
*/
function _civicrm_get_url_parameters() {
$excludes = array('q', 'IDS_request_uri', 'IDS_user_agent');
return drupal_get_query_parameters(NULL, $excludes);
}
/**
* Get CiviCRM query parameters from the url as a string for url output.
*
* Drupal tends to strip CiviCRM parameters from urls and we sometimes want to put them back.
* For example drupal will output civicrm/contribution/transact as the metadata url whereas
* we want civicrm/contribution/transact?reset=1&id=2
*
* @return string
* String of url parameters e.g '?reset=1&id=2'.
*/
function _civicrm_get_url_parameters_as_url_string() {
$string = drupal_http_build_query(_civicrm_get_url_parameters());
if (!empty($string)) {
$string = '?' . $string;
}
return $string;
}
/**
* Alter metatags before being cached.
*
* This hook is invoked prior to the meta tags for a given page are cached.
*
* @param array $output
* Metatags to be displayed.
* @param string $instance
* Context.
*/
function civicrm_metatag_metatags_view_alter(&$output, $instance) {
if (arg(0) != 'civicrm') {
return;
}
$linkUrls = array('og:url', 'canonical', 'shortlink');
foreach ($linkUrls as $url) {
if (isset($output[$url]['#attached']['drupal_add_html_head'][0][0]['#value'])) {
$output[$url]['#attached']['drupal_add_html_head'][0][0]['#value'] .= _civicrm_get_url_parameters_as_url_string();
}
}
}
/**
* Make the language switcher work with civicrm.
*
* We override theme_links__locale_block() so that civicrm language switcher
* links hold the relevant civicrm parameters.
*
* @param array $variables
*
* @return array
*/
function civicrm_links__locale_block($variables) {
if (arg(0) == 'civicrm') {
foreach ($variables['links'] as $lang => $attr) {
$variables['links'][$lang]['query'] = _civicrm_get_url_parameters();
}
}
return theme('links', $variables);
}
/**
* Find & register class loader and store location in Drupal variable.
*
* Per CRM-13737 this allows for drupal code to be outside the core directory
* which makes it easier for sites managing their own installation methods that
* may need to cover different drupal versions
*/
function _civicrm_registerClassLoader() {
$home = dirname(__FILE__);
$path = variable_get('civicrm_class_loader', NULL);
if (empty($path) || !file_exists($home . $path)) {
$candidates = array(
'/../CRM/Core/ClassLoader.php',
'/../civicrm-core/CRM/Core/ClassLoader.php',
'/../core/CRM/Core/ClassLoader.php',
);
foreach ($candidates as $candidate) {
if (file_exists($home . $candidate)) {
$path = $candidate;
variable_set('civicrm_class_loader', $candidate);
break;
}
}
}
require_once $home . $path;
CRM_Core_ClassLoader::singleton()->register();
}
/**
* Function to get the contact type.
*
* @param string $default contact type
*
* @return string $ctype
* Contact type
*/
function civicrm_get_ctype($default = NULL) {
// here we are creating a new contact
// get the contact type from the POST variables if any
if (isset($_REQUEST['ctype'])) {
$ctype = $_REQUEST['ctype'];
}
elseif (isset($_REQUEST['edit']) &&
isset($_REQUEST['edit']['ctype'])
) {
$ctype = $_REQUEST['edit']['ctype'];
}
else {
$ctype = $default;
}
if ($ctype != 'Individual' &&
$ctype != 'Organization' &&
$ctype != 'Household'
) {
$ctype = $default;
}
return $ctype;
}
/**
* This is the main function that is called on any civicrm page.
*/
function civicrm_invoke() {
// check if this is a redirect and maybe a user login?
// this changed between D6 and D7, seems hackish but not sure
// what we can / should do
// CRM-9853
if (isset($_POST['form_build_id']) &&
isset($_POST['form_id']) &&
($_POST['form_id'] == 'user_login_block' || $_POST['form_id'] == 'user_login') &&
isset($_GET['destination'])
) {
// process the user login form and let it do the redirect?
return drupal_get_form('user_login');
}
// make sure the system is initialized
if (!civicrm_initialize()) {
return drupal_not_found();
}
civicrm_cache_disable();
$args = explode('/', $_GET['q']);
// synchronize the drupal uid with the contacts db
global $user;
/**
* Bypass synchronize if running upgrade to avoid any serious
* non-recoverable error which might hinder the upgrade process.
*
* @FIXME
*/
if (!isset($args[1]) || $args[1] != 'upgrade') {
CRM_Core_BAO_UFMatch::synchronize($user, FALSE, 'Drupal', civicrm_get_ctype('Individual'));
}
// Fix the path for the url alias module.
$urlAlias = FALSE;
foreach ($args as $index => $arg) {
if (strpos($arg, '=') !== FALSE) {
$keepArg = NULL;
// first check if there is a ?
if (strpos($arg, '?') !== FALSE) {
$items = CRM_Utils_System::explode('?', $arg, 2);
$keepArg = $items[0];
$item = $items[1];
}
else {
$item = $arg;
}
// next split it on &
$elements = explode('&', $item);
foreach ($elements as $element) {
// finally split on =
list($key, $value) = CRM_Utils_System::explode('=', $element, 2);
if ($value) {
$_REQUEST[$key] = $value;
}
}
if ($keepArg) {
$args[$index] = $keepArg;
}
else {
unset($args[$index]);
}
$urlAlias = TRUE;
}
}
if ($urlAlias) {
$_GET['q'] = implode('/', $args);
}
$printedContent = NULL;
ob_start();
$pageContent = CRM_Core_Invoke::invoke($args);
$printedContent = ob_get_clean();
if (empty($pageContent) AND
!empty($printedContent)
) {
$pageContent = $printedContent;
}
return $pageContent;
}
/**
* Determine if the user is on a CiviCRM generated page.
*
* i.e. does the form have some civicrm unique token?
*/
function civicrm_on_user_page() {
return isset($_POST['_qf_default']);
}
function _civicrm_categories_access($profile_id) {
if (!civicrm_initialize()) {
return FALSE;
}
$allUFGroups = CRM_Core_BAO_UFGroup::getModuleUFGroup('User Account', 0, TRUE);
if (is_array(CRM_Utils_Array::value($profile_id, $allUFGroups))) {
return TRUE;
}
}
/*
* Translating profile menu title dynamicaly to overide caching
*/
function civicrm_menu_alter(&$items) {
$categories = civicrm_user_categories();
foreach ($categories as $cat) {
$path = 'user/%user_category/edit/' . $cat['name'];
$items[$path]['title callback'] = 'civicrm_profile_title_callback';
$items[$path]['title arguments'] = array((string) $cat['id'], $cat['title']);
}
}
function civicrm_profile_title_callback($profile_id, $fallback) {
if (!civicrm_initialize() || empty($profile_id)) {
return $fallback;
}
return CRM_Core_BAO_UFGroup::getTitle($profile_id);
}
/**
* Function needing explanation.
*
* @param $edit
* @param $user
* @param $category
* @param $reset
* @param bool $doNotProcess
*
* @return array
*/
function civicrm_register_data($edit, &$user, $category, $reset, $doNotProcess = FALSE) {
// lets suppress key generation for all registration forms
civicrm_key_disable();
$ctype = civicrm_get_ctype('Individual');
if ($user->uid) {
// Happens on $type == 'insert'
// $reset == false always
// $doNotProcess == false always
CRM_Core_BAO_UFMatch::synchronize($user, TRUE, 'Drupal', $ctype);
$userID = CRM_Core_BAO_UFMatch::getContactId($user->uid);
// CRM-7858
if (isset($edit['mail'])) {
CRM_Core_BAO_UFMatch::updateContactEmail($userID,
trim($edit['mail'])
);
}
$html = CRM_Core_BAO_UFGroup::getEditHTML($userID, '',
2,
TRUE,
$reset, NULL,
$doNotProcess, $ctype
);
}
else {
// Happens on $type == 'register'
$html = CRM_Core_BAO_UFGroup::getEditHTML(NULL, '',
1,
TRUE,
$reset, NULL,
$doNotProcess, $ctype
);
}
$output = array();
if ($html) {
$html = civicrm_add_jquery($html);
$index = empty($category) ? 'civicrm-profile-register' : $category;
$output[$index] = array(
'#title' => $category,
'#type' => 'item',
'#markup' => $html,
'#weight' => 1,
);
}
return $output;
}
function civicrm_form_data($edit, &$user, $category, $reset, $doNotProcess = FALSE) {
// lets suppress key generation for all CMS forms
civicrm_key_disable();
$output = array();
$userID = CRM_Core_BAO_UFMatch::getContactId($user->uid);
if (!$userID) {
$ctype = civicrm_get_ctype('Individual');
CRM_Core_BAO_UFMatch::synchronize($user, FALSE, 'Drupal', $ctype);
$userID = CRM_Core_BAO_UFMatch::getContactId($user->uid);
}
// at this point we better have a valid userID
if (!$userID) {
// we get into this scenario if we do not like the email address supplied by the user
return;
}
// check for permission
// CRM-7509
$session = CRM_Core_Session::singleton();
$sessionUserID = $session->get('userID');
$session->replaceUserContext(url(current_path(), array('absolute' => TRUE)));
if ($sessionUserID != $userID) {
// do not allow edit for anon users in joomla frontend, CRM-4668, unless u have checksum CRM-5228
$config = CRM_Core_Config::singleton();
if ($config->userFrameworkFrontend) {
CRM_Contact_BAO_Contact_Permission::validateOnlyChecksum($userID, $this);
}
else {
CRM_Contact_BAO_Contact_Permission::validateChecksumContact($userID, $this);
}
}
$ctype = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $userID, 'contact_type');
try {
$profileID = _civicrm_get_user_profile_id($category, $userID, $ctype);
}
catch (Exception $e) {
CRM_Core_Error::fatal($e->getMessage());
}
$html = CRM_Core_BAO_UFGroup::getEditHTML($userID, $category, NULL, FALSE, $reset, $profileID, $doNotProcess, $ctype);
if ($html) {
$title = CRM_Core_DAO::getFieldValue("CRM_Core_DAO_UFGroup", $profileID, 'title', 'id');
$html = civicrm_add_jquery($html);
$index = empty($category) ? 'civicrm-profile-my-account' : $category;
$output[$index][] = array(
'#title' => $title,
'#value' => $html,
'#weight' => 1,
);
$output[$index][] = array(
'#title' => $title,
'#type' => 'item',
'#markup' => $html,
'#weight' => 1,
);
}
return $output;
}
/**
* Get appropriate profile ID for edit screen.
*
* @param $name
*
* @return int
* Profile ID.
*
* @throws \CRM_Core_Exception
*/
function _civicrm_get_user_profile_id($name) {
$profiles = civicrm_api3('uf_group', 'get', array(
'name' => $name,
'is_active' => 1,
));
if (!$profiles['count']) {
$profiles = civicrm_api3('uf_group', 'get', array(
'title' => $name,
'is_active' => 1,
'options' => array('limit' => 1,),
));
}
if (!$profiles['count']) {
// @todo I suspect the profile listing is not possible in both scenarios but need to understand what it
// means before removing from one warning.
throw new CRM_Core_Exception(ts('The requested Profile (%1) is disabled OR it is not configured to be used for \'Profile\' listings in its Settings OR there is no Profile with that ID. Please contact the site administrator if you need assistance.',
array(1 => $name)
));
}
// CRM-15952 we expected 'name' to be a unique field but the DB doesn't enforce that.
// Extra check if it is not unique here.
if ($profiles['count'] > 1) {
foreach ($profiles['values'] as $profile) {
try {
$profiles['id'] = civicrm_api3('uf_join', 'getvalue', array(
'uf_group_id' => $profile['id'],
'module' => 'User Account',
'return' => 'uf_group_id',
));
continue;
}
catch (Exception $e){
}
}
}
$ufGroupIDs = CRM_Core_Permission::ufGroupClause(CRM_Core_Permission::EDIT, NULL, TRUE);
$profileID = $profiles['id'];
if (!in_array($profileID, $ufGroupIDs)) {
throw new CRM_Core_Exception(ts('The requested Profile (id = %1) is not configured to be used for \'Profile\' listings in its Settings OR there is no Profile with that ID OR you do not have permission to access this profile. Please contact the site administrator if you need assistance.',
array(1 => $profileID)
));
}
return $profileID;
}
function civicrm_user_form_validate($form, &$form_state) {
// lets suppress key generation for all validation also
civicrm_key_disable();
$validated = &drupal_static(__FUNCTION__, FALSE);
if ($validated) {
return;
}
$validated = TRUE;
// check for either user/register or admin/people/create
$register =
((arg(0) == 'user' && arg(1) == 'register') ||
(arg(0) == 'admin' && arg(1) == 'people' && arg(2) == 'create')
) ? TRUE : FALSE;
$userID = NULL;
if (!empty($form['#user'])) {
$userID = CRM_Core_BAO_UFMatch::getContactId($form['#user']->uid);
}
$errors = CRM_Core_BAO_UFGroup::isValid($userID, $form['#user_category'], $register);
if ($errors && is_array($errors)) {
foreach ($errors as $name => $error) {
form_set_error($name, $error);
}
return FALSE;
}
return TRUE;
}
/**
* Disable the drupal cache for all civicrm pages which should not be cached.
*/
function civicrm_cache_disable() {
if (function_exists('drupal_page_is_cacheable')) {
// This is a Drupal 7 function only - using 'easy option' of checking function.
drupal_page_is_cacheable(FALSE);
}
}
/**
* Disable civicrm key for all forms that interact with the CMS.
*
* We do not control the CMS form generation and hence should suppress
* qfKey
*/
function civicrm_key_disable() {
if (!civicrm_initialize()) {
return FALSE;
}
CRM_Core_Config::singleton()->keyDisable = TRUE;
}
/**
* Enable CiviCRM.
*/
function civicrm_enable() {
db_query("UPDATE {system} SET weight = 100 WHERE name = 'civicrm'");
menu_rebuild();
menu_link_maintain('civicrm', 'delete', 'civicrm', 'CiviCRM');
$options = array(
'link_title' => 'CiviCRM',
'link_path' => 'civicrm/dashboard',
'module' => 'civicrm',
'options' => array('alter' => TRUE),
);
menu_link_save($options);
if (!civicrm_initialize()) {
return;
}
// also invoke civicrm menu rebuild
CRM_Core_Menu::store();
// Update the 'blocks' DB table with the blocks.
if (module_exists('block')) {
_block_rehash();
}
}
/**
* Implements hook_translated_menu_item_alter().
*
* This is a hack
* to hide the CiviCRM menu from the drupal navigation block for folks
* who don't have access CiviCRM permissions
*/
function civicrm_translated_menu_link_alter(&$item) {
if ($item['router_path'] == 'civicrm' &&
$item['module'] == 'civicrm' &&
!user_access('access CiviCRM')
) {
$item['access_callback'] = $item['access'] = FALSE;
}
}
/**
* Implements hook_admin_menu_output_alter().
*/
function civicrm_admin_menu_output_alter(&$content) {
if (!civicrm_initialize()) {
return;
}
$weight = 10;
$content['menu']['civicrm'] = array(
'#title' => t('CiviCRM'),
'#attributes' => array('class' => array('civicrm')),
'#href' => 'civicrm',
'#options' => array(
'query' => array('reset' => 1),
),
// #weight controls the order of links in the resulting item list.
'#weight' => $weight,
);
}
/**
* Implements hook_views_api().
*/
function civicrm_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'civicrm') . '/modules/views',
);
}
function civicrm_views_query_alter(&$view, &$query) {
if (!civicrm_initialize()) {
return;
}
// check if we are in multilingual mode, otherwise return
// TODO: should be a simple call - is_multiligual ?
$domain = new CRM_Core_DAO_Domain();
$domain->find(TRUE);
$multilingual = (bool) $domain->locales;
if ($multilingual) {
global $dbLocale;
$columns = CRM_Core_I18n_SchemaStructure::columns();
// TODO: for better performance, loop on $query->fields instead
foreach ($columns as $table => $hash) {
foreach ($hash as $column => $type) {
if (array_key_exists("{$table}_{$column}", $query->fields)) {
$query->fields["{$table}_{$column}"]['field'] = "{$column}{$dbLocale}";
}
}
}
}
}
function civicrm_add_jquery(&$html) {
CRM_Core_Resources::singleton()->addCoreResources('html-header');
// JS/CSS markup will be rendered in theme('page') by preprocess function
return $html;
}
function civicrm_form_alter(&$form, $formValues, $formID) {
switch ($formID) {
case 'user_admin_permissions':
$form['#submit'][] = 'civicrm_user_admin_permissions_submit';
case 'system_clean_url_settings':
if (!empty($formValues['input'])) {
// reset navigation for permissions changed and clean url
if (!civicrm_initialize()) {
return;
}
CRM_Core_BAO_Navigation::resetNavigation();
}
break;
case 'user_register_form':
$form['#attributes']['enctype'] = 'multipart/form-data';
$form['#validate'][] = 'civicrm_user_form_validate';
$output = civicrm_register_data($form,
$form['#user'],
NULL, TRUE, FALSE
);
$form = array_merge($form, $output);
break;
case 'user_profile_form':
$inCategory = TRUE;
if ($form['#user_category']) {
$inCategory = FALSE;
$categories = civicrm_user_categories();
foreach ($categories as $cat) {
if ($form['#user_category'] == $cat['name']) {
$inCategory = TRUE;
break;
}
}
}
// only return a form to drupal my account page
$output = array();
if ($inCategory &&
arg(0) == 'user' &&
arg(2) == 'edit' &&
arg(3)
) {
$form['#validate'][] = 'civicrm_user_form_validate';
$output = civicrm_form_data($form, $form['#user'], $form['#user_category'], TRUE);
if (!empty($output)) {
$form['#attributes']['enctype'] = 'multipart/form-data';
$form = array_merge($form, $output);
}
}
break;
default:
break;
}
}
/**
* Custom submit handler for CiviCRM to warn about unsafe permission configs.
*
* @param $form
* @param $form_state
*/
function civicrm_user_admin_permissions_submit($form, &$form_state) {
$rid = array_search('anonymous user', $form_state['values']['role_names']);
if ($rid === FALSE) {
return;
}
civicrm_initialize();
$roles = user_roles();
$permissions = array_filter($form_state['values'][$rid]);
$warning_permissions = CRM_Core_Permission::validateForPermissionWarnings($permissions);
$warning_permission_names = array();
foreach (module_implements('permission') as $module) {
if ($permissions = module_invoke($module, 'permission')) {
foreach ($permissions as $key => $permission) {
if (in_array($key, $warning_permissions)) {
$warning_permission_names[$key] = $permission['title'];
}
}
}
}
if (!empty($warning_permission_names)) {
drupal_set_message(t('The %1 role was assigned one or more permissions that may prove dangerous for users of that role to have. Please reconsider assigning %2 to them.',
array(
'%1' => $roles[$rid],
'%2' => implode(', ', $warning_permission_names),
)), 'warning');
}
}
/*
*
* Implements hook_theme_registry_alter().
*
* Based on the jquery_update module.
*
* Make sure this page preprocess function runs last
* so that a theme can't call drupal_get_js().
*
* Also, add civicrm parameters to links so they are not truncated by the
* language switcher.
*
* @param array $theme_registry
* Registry theme metadata.
*/
function civicrm_theme_registry_alter(&$theme_registry) {
if (isset($theme_registry['page'])) {
// See if our preprocess function is loaded, if so remove it.
if ($key = array_search('civicrm_preprocess_page', $theme_registry['page']['preprocess functions'])) {
unset($theme_registry['page']['preprocess functions'][$key]);
}
// Now add it on at the end of the array so that it runs last.
$theme_registry['page']['preprocess functions'][] = 'civicrm_preprocess_page';
}
// Rewrite the links in order to add back CiviCRM parameters for the language switcher.
$theme_registry['links__locale_block']['theme path'] = drupal_get_path('module', 'civicrm');
$theme_registry['links__locale_block']['function'] = 'civicrm_links__locale_block';
}
/**
* Implements moduleName_preprocess_hook().
*
* Based on the jquery_update module functions.
*
* Strips out JS and CSS for a path.
*
* @param array $variables
*/
function civicrm_preprocess_page(&$variables) {
// to increase it's flexibility.
if (module_exists('date_popup') && (in_array(arg(0), array(
'civicrm', 'user')))) {
/**
* Have hidden this function as it is not needed,
* but left as an example when we need to unset js
* in the future...
*
* // Only do this for pages that have JavaScript on them.
* if (!empty($variables['scripts'])) {
* $path = drupal_get_path('module', 'date_popup');
* unset($scripts['module'][$path . '/lib/ui.datepicker.js']);
* $variables['scripts'] = drupal_get_js('header', $scripts);
* }
*/
// Similar process for CSS but there are 2 CSS related variables.
// $variables['css'] and $variables['styles'] are both used.
if (!empty($variables['css'])) {
$path = drupal_get_path('module', 'date_popup');
unset($variables['css']['all']['module'][$path . '/themes/datepicker.css']);
$variables['styles'] = drupal_get_css($variables['css']);
}
}
}
/**
* Implements hook_filter_tips().
*/
function civicrm_filter_info() {
$filters = array();
$filters['civicrm_smarty'] = array(
'title' => t('CiviCRM-Smarty filter'),
'description' => t("Evaluate Smarty and CiviCRM API codes with CiviCRM's embedded Smarty engine"),
'cache' => FALSE,
'process callback' => '_civicrm_filter_process',
'tips callback' => '_civicrm_filter_tips',
'weight' => 10,
);
return $filters;
}
/**
* Filter tips callback.
*
* More explanation required.
*
* @param $filter
* @param $format
* @param bool $long
*
* @return array
*/
function _civicrm_filter_tips($filter, $format, $long = FALSE) {
return t('Evaluate <a target="_blank" href="@smarty">Smarty</a> and <a target="_blank" href="@civiapi">CiviCRM API</a> codes',
array(
'@smarty' => 'http://www.smarty.net/docsv2/en/',
'@civiapi' => 'http://wiki.civicrm.org/confluence/display/CRMDOC/CiviCRM+Public+APIs',
)
);
}
/**
* Filter process callback.
*
* More explanation required.
*
* @param $text
* @param $filter
* @param $format
* @param $langcode
* @param $cache
* @param $cache_id
*
* @return bool|mixed|string
*/
function _civicrm_filter_process($text, $filter, $format, $langcode, $cache, $cache_id) {
civicrm_initialize();
$config = CRM_Core_Config::singleton();
$smarty = CRM_Core_Smarty::singleton();
// as this file is not a class (not sure why) we need the require once
require_once 'CRM/Core/Smarty/resources/String.php';
civicrm_smarty_register_string_resource();
$was_secure = $smarty->security;
$smarty->security = TRUE;
$text = $smarty->fetch("string:${text}");
$smarty->security = $was_secure;
// In the use-case of embedding Smarty codes inside a Drupal page, one is likely
// to load data using 'pull MVC' instead of 'push MVC', so the interesting
// data isn't loaded until after processing the main content. Therefore, we
// evaluate debug codes after the main content.
if ($config->debug) {
$text .= $smarty->fetch('CRM/common/debug.tpl');
}
return $text;
}
/**
* Implements hook_civicrm_postProcess().
*/
function civicrm_civicrm_postProcess($formName, $form) {
// Reset Views Cache when CiviCRM components page is saved
if ($form instanceof CRM_Admin_Form_Setting_Component || $form instanceof CRM_Custom_Form_Group || $form instanceof CRM_Custom_Form_Field) {
if (module_exists('views')) {
views_invalidate_cache();
}
}
}
/**
* Implements hook_reviews().
*
* This implementation is invoked by coder_review module, and defines
* specific checks for the CiviCRM Coding Standard.
*/
function civicrm_reviews() {
$br = 'br';
$rules = array(
array(
'#type' => 'regex',
'#value' => '\t',
'#warning' => 'Use an indent of 2 spaces, with no tabs',
),
array(
'#type' => 'regex',
'#never' => '<\?php',
'#value' => '^ ( )*[^ \'".]',
'#warning' => 'Use an indent of 2 spaces, with no tabs',
'#severity' => 'minor',
),
array(
'#type' => 'regex',
'#value' => '\s(if|elseif|while|foreach|switch|case|return|for|catch)\(',
'#warning' => 'Control statements should have one space between the control keyword and opening parenthesis',
),
array(
'#type' => 'regex',
'#value' => '[\s\(](\w+)\s\(',
'#not' => '^(if|elseif|while|foreach|switch|case|return|for|list|catch)$',
'#warning' => 'Functions should be called with no spaces between the function name and opening parentheses',
),
array(
'#type' => 'regex',
'#value' => '\){',
'#warning' => 'use a space between the closing parenthesis and the open bracket',
),
array(
'#type' => 'regex',
'#value' => '(\S=>|=>\S)',
'#source' => 'php',
'#warning' => 'Arrays should be formatted with a space separating each element and assignment operator',
),
array(
'#type' => 'regex',
'#value' => '(\.(?:|\s{2,})[^\)\=\s0-9]|[^\(\s0-9](?:|\s{2,})\.)',
'#warning' => 'String concatenation should be formatted with a space separating the operators (dot .) and the surrounding terms',
),
array(
'#type' => 'regex',
'#value' => '<\?(\w+)',
'#not' => '^(php|xml)$',
'#warning' => 'Always use &lt;?php ?&gt; to delimit PHP code, not the &lt;? ?&gt; shorthand',
),
array(
'#type' => 'regex',
'#value' => 'global\s+\$(\w+)(,\s\$(\w+))*',
'#not' => '^_|^(' . _coder_review_style_core_global_regex() . ')$',
'#warning' => 'global variables should start with a single underscore followed by the module and another underscore',
),
array(
'#type' => 'callback',
'#source' => 'all',
'#value' => '_coder_review_style_closing_php_callback',
'#warning' => 'the final ?> should be omitted from all code files',
),
array(
'#type' => 'regex',
'#value' => '}\s*else',
'#warning' => 'else statements should begin on a new line',
),
array(
'#type' => 'regex',
'#value' => '[,][^ \n\r]',
'#warning' => 'missing space after comma',
),
array(
'#type' => 'regex',
'#value' => '^\s*{',
'#warning' => 'curly braces { should end a line, not start one',
),
array(
'#type' => 'regex',
'#source' => 'html',
// NOTE: use $br only to avoid a warning.
'#value' => '<' . $br . '>',
'#warning' => 'use &lt;br /&gt; instead of &lt;br&gt;',
'#severity' => 'minor',
),
array(
'#type' => 'regex',
'#source' => 'html',
'#value' => '(?-i)<[A-Z]+',
'#warning_callback' => '_coder_review_style_xhtml_warning',
'#severity' => 'minor',
),
array(
'#type' => 'regex',
'#value' => '\s(if|elseif|while|foreach|switch|return|for|catch)\s*\(.*\) \s*{\s*[^\s]+',
'#warning' => 'The control statement should be on a separate line from the control conditional',
),
array(
'#type' => 'regex',
'#filename' => array('tpl.php'),
'#value' => '\s(if|elseif)\s*\(.*\) \s*{\s*[^\s]+',
'#warning' => 'The control statement should use ":" syntax instead of curly braces.',
),
array(
'#type' => 'regex',
'#source' => 'all',
'#value' => '[ \t]+$',
'#warning' => 'There should be no trailing spaces',
'#severity' => 'minor',
),
array(
'#type' => 'regex',
'#value' => '[\s\(](strlen|strtolower|strtoupper|substr|ucfirst)\s*\(',
'#warning' => 'in most cases, replace the string function with the drupal_ equivalent string functions',
'#severity' => 'minor',
),
array(
'#type' => 'regex',
'#value' => '\[\s*[A-Za-z][A-Za-z0-9_]*\s*]',
'#not' => '\[\s*[A-Z][A-Z0-9_]*\s*]',
'#warning' => 'use quotes around a string literal array index, this is not only a style issue, but a known performance problem',
'#case-sensitive' => TRUE,
),
array(
'#type' => 'regex',
'#value' => '[\s=>]+(true|false|null)[\)\s;,\n\r]+',
'#case-sensitive' => TRUE,
'#warning' => 'Use uppercase for PHP constants, e.g. NULL, TRUE, FALSE',
),
array(
'#type' => 'regex',
'#value' => '\s+else\s+if\s*\(',
'#warning' => 'Use "elseif" in place of "else if"',
),
array(
'#type' => 'regex',
'#value' => '\s*[\'"]#value[\'"]\s*=>\s*t\s*\(\s*[\'"]Submit[\'"]\s*\)',
'#source' => 'allphp',
'#warning' => 'When labelling buttons, make it clear what the button does, "Submit" is too generic.',
'#severity' => 'minor',
),
);
$review = array(
'#title' => t('CiviCRM Coding Standards'),
'#link' => 'http://civicrm.org',
'#rules' => $rules,
'#description' => t('Should apply to all CiviCRM code.'),
);
return array('civicrm_coder' => $review);
}
/**
* Implements hook_modules_installed().
*
* @param array $modules
*/
function civicrm_modules_installed($modules) {
civicrm_initialize();
CRM_Core_Invoke::rebuildMenuAndCaches(TRUE);
}
/**
* Implements hook_modules_enabled().
*/
function civicrm_modules_enabled($modules) {
civicrm_initialize();
CRM_Core_Invoke::rebuildMenuAndCaches(TRUE);
}
/**
* Implements hook_modules_disabled().
*/
function civicrm_modules_disabled($modules) {
civicrm_initialize();
CRM_Core_Invoke::rebuildMenuAndCaches(TRUE);
}
/**
* Implements hook_modules_uninstalled().
*/
function civicrm_modules_uninstalled($modules) {
civicrm_initialize();
CRM_Core_Invoke::rebuildMenuAndCaches(TRUE);
}
/**
* Implements hook_metatag_page_cache_cid_parts_alter().
*
* Append the query string to the key under which the page title is cached to
* prevent cross-contamination from URLs such as civicrm/contribute/transact.
*
* If the metatag module is not installed this function will not fire.
*
* @param array $cid_parts
*/
function civicrm_metatag_page_cache_cid_parts_alter(&$cid_parts) {
// Avoid substr() because it will not work with Drupal language prefixes.
if (!empty($cid_parts['path']) && strpos($cid_parts['path'], 'civicrm') !== FALSE) {
$get = $_GET;
ksort($get);
$cid_parts['path'] .= '/' . md5(print_r($get, TRUE));
}
}