/**
* Run the page.
*
- * This method is called after the page is created. It checks for the
- * type of action and executes that action.
- * Finally it calls the parent's run method.
+ * Set the breadcrumb before beginning the standard page run.
*/
public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String',
- // default to 'browse'
- $this, FALSE, 'browse'
- );
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive',
- $this, FALSE, 0
- );
-
// set breadcrumb to append to admin/access
$breadCrumb = array(
array(
),
);
CRM_Utils_System::appendBreadCrumb($breadCrumb);
- // what action to take ?
- if ($action & (CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) {
- $this->edit($action, $id);
- }
-
- if ($action & (CRM_Core_Action::UPDATE)) {
- $this->edit($action, $id);
-
- if (isset($id)) {
- $aclName = CRM_Core_DAO::getFieldValue('CRM_ACL_DAO_ACL', $id);
- CRM_Utils_System::setTitle(ts('Edit ACL - %1', array(1 => $aclName)));
- }
- }
-
- // finally browse the acl's
- if ($action & CRM_Core_Action::BROWSE) {
- $this->browse();
- }
// parent run
return parent::run();
switch ($acl[$dao->id]['object_table']) {
case 'civicrm_saved_search':
- $acl[$dao->id]['object'] = $group[$acl[$dao->id]['object_id']];
+ $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $group);
$acl[$dao->id]['object_name'] = ts('Group');
break;
case 'civicrm_uf_group':
- $acl[$dao->id]['object'] = $ufGroup[$acl[$dao->id]['object_id']];
+ $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $ufGroup);
$acl[$dao->id]['object_name'] = ts('Profile');
break;
case 'civicrm_custom_group':
- $acl[$dao->id]['object'] = $customGroup[$acl[$dao->id]['object_id']];
+ $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $customGroup);
$acl[$dao->id]['object_name'] = ts('Custom Group');
break;
case 'civicrm_event':
- $acl[$dao->id]['object'] = $event[$acl[$dao->id]['object_id']];
+ $acl[$dao->id]['object'] = CRM_Utils_Array::value($acl[$dao->id]['object_id'], $event);
$acl[$dao->id]['object_name'] = ts('Event');
break;
}
return 'civicrm/acl';
}
+ /**
+ * Edit an ACL.
+ *
+ * @param int $mode
+ * What mode for the form ?.
+ * @param int $id
+ * Id of the entity (for update, view operations).
+ * @param bool $imageUpload
+ * Not used in this case, but extended from CRM_Core_Page_Basic.
+ * @param bool $pushUserContext
+ * Not used in this case, but extended from CRM_Core_Page_Basic.
+ */
+ public function edit($mode, $id = NULL, $imageUpload = FALSE, $pushUserContext = TRUE) {
+ if ($mode & (CRM_Core_Action::UPDATE)) {
+ if (isset($id)) {
+ $aclName = CRM_Core_DAO::getFieldValue('CRM_ACL_DAO_ACL', $id);
+ CRM_Utils_System::setTitle(ts('Edit ACL – %1', array(1 => $aclName)));
+ }
+ }
+ parent::edit($mode, $id, $imageUpload, $pushUserContext);
+ }
+
}
* Finally it calls the parent's run method.
*/
public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String',
- // default to 'browse'
- $this, FALSE, 'browse'
- );
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive',
- $this, FALSE, 0
- );
+ $id = $this->getIdAndAction();
// set breadcrumb to append to admin/access
$breadCrumb = array(
CRM_Utils_System::appendBreadCrumb($breadCrumb);
// what action to take ?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) {
- $this->edit($action, $id);
+ if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) {
+ $this->edit($this->_action, $id);
}
// finally browse the acl's
$this->browse();
- // parent run
- return parent::run();
+ // This replaces parent run, but do parent's parent run
+ return CRM_Core_Page::run();
}
/**
* Finally it calls the parent's run method.
*/
public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String',
- // default to 'browse'
- $this, FALSE, 'browse'
- );
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive',
- $this, FALSE, 0
- );
+ $id = $this->getIdAndAction();
// set breadcrumb to append to admin/access
$breadCrumb = array(
CRM_Utils_System::setTitle(ts('Assign Users to Roles'));
// what action to take ?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) {
- $this->edit($action, $id);
+ if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::DELETE)) {
+ $this->edit($this->_action, $id);
}
// reset cache if enabled/disabled
- if ($action & (CRM_Core_Action::DISABLE | CRM_Core_Action::ENABLE)) {
+ if ($this->_action & (CRM_Core_Action::DISABLE | CRM_Core_Action::ENABLE)) {
CRM_ACL_BAO_Cache::resetCache();
}
// finally browse the acl's
- if ($action & CRM_Core_Action::BROWSE) {
+ if ($this->_action & CRM_Core_Action::BROWSE) {
}
- // parent run
- return parent::run();
+ // This replaces parent run, but do parent's parent run
+ return CRM_Core_Page::run();
}
/**
* method.
*/
public function run() {
- // get the requested action, default to 'browse'
- $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse');
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
+ $id = $this->getIdAndAction();
$context = CRM_Utils_Request::retrieve('context', 'String', $this, FALSE);
if ($context == 'nonDupe') {
$this->assign('hasperm_merge_duplicate_contacts', CRM_Core_Permission::check('merge duplicate contacts'));
// which action to take?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
- $this->edit($action, $id);
+ if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
+ $this->edit($this->_action, $id);
}
- if ($action & CRM_Core_Action::DELETE) {
+ if ($this->_action & CRM_Core_Action::DELETE) {
$this->delete($id);
}
// browse the rules
$this->browse();
- // parent run
- return parent::run();
+ // This replaces parent run, but do parent's parent run
+ return CRM_Core_Page::run();
}
/**
* Finally it calls the parent's run method.
*/
public function run() {
-
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String',
- // default to 'browse'
- $this, FALSE, 'browse'
- );
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive',
- $this, FALSE, 0
- );
+ $id = $this->getIdAndAction();
// what action to take ?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::PREVIEW)) {
- $this->edit($action, $id, TRUE);
+ if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD | CRM_Core_Action::PREVIEW)) {
+ $this->edit($this->_action, $id, TRUE);
}
// finally browse the custom groups
$this->browse();
// parent run
- return parent::run();
+ return CRM_Core_Page::run();
}
/**
$sort = ($n > 2) ? func_get_arg(2) : NULL;
// what action do we want to perform ? (store it for smarty too.. :)
- $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse');
- $this->assign('action', $this->_action);
-
- // get 'id' if present
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
-
- require_once str_replace('_', DIRECTORY_SEPARATOR, $this->getBAOName()) . ".php";
-
- if ($id) {
- if (!$this->checkPermission($id, NULL)) {
- CRM_Core_Error::fatal(ts('You do not have permission to make changes to the record'));
- }
- }
+ $id = $this->getIdAndAction();
if ($this->_action & (CRM_Core_Action::VIEW |
CRM_Core_Action::ADD |
return parent::run();
}
+ /**
+ * Retrieve the action and ID from the request.
+ *
+ * Action is assigned to the template while we're at it. This is pulled from
+ * the `run()` method above.
+ *
+ * @return int
+ * The ID if present, or 0.
+ */
+ public function getIdAndAction() {
+ $this->_action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse');
+ $this->assign('action', $this->_action);
+
+ // get 'id' if present
+ $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
+
+ require_once str_replace('_', DIRECTORY_SEPARATOR, $this->getBAOName()) . ".php";
+
+ if ($id) {
+ if (!$this->checkPermission($id, NULL)) {
+ CRM_Core_Error::fatal(ts('You do not have permission to make changes to the record'));
+ }
+ }
+
+ return $id;
+ }
+
/**
* @return string
*/
$hasDelete = $hasDisable = TRUE;
if (!empty($values['name']) && in_array($values['name'], array(
- 'encounter_medium',
- 'case_type',
- 'case_status',
- ))
- ) {
+ 'encounter_medium',
+ 'case_type',
+ 'case_status',
+ ))) {
static $caseCount = NULL;
if (!isset($caseCount)) {
$caseCount = CRM_Case_BAO_Case::caseCount(NULL, FALSE);
return self::$_links;
}
- /**
- * Run the page.
- *
- * This method is called after the page is created. It checks for the
- * type of action and executes that action.
- * Finally it calls the parent's run method.
- */
- public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); // default to 'browse'
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
-
- // what action to take ?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
- $this->edit($action, $id);
- }
-
- // parent run
- return parent::run();
- }
-
/**
* Browse all custom data groups.
*/
public function run() {
$context = CRM_Utils_Request::retrieve('context', 'String', $this);
$this->set("context", $context);
- // assign vars to templates
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
- $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); // default to 'browse'
+
+ $id = $this->getIdAndAction();
// what action to take ?
- if ($action & (CRM_Core_Action::UPDATE |
+ if ($this->_action & (CRM_Core_Action::UPDATE |
CRM_Core_Action::ADD |
CRM_Core_Action::CLOSE |
CRM_Core_Action::REOPEN |
CRM_Core_Action::EXPORT)
) {
- $this->edit($action, $id);
+ $this->edit($this->_action, $id);
}
// parent run
- return parent::run();
+ return CRM_Core_Page::run();
}
return self::$_links;
}
- /**
- * Run the page.
- *
- * This method is called after the page is created. It checks for the
- * type of action and executes that action.
- * Finally it calls the parent's run method.
- */
- public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 'browse'); // default to 'browse'
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE, 0);
-
- // what action to take ?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
- $this->edit($action, $id);
- }
-
- // parent run
- return parent::run();
- }
-
/**
* Browse all financial types.
*/
return self::$_links;
}
- /**
- * Run the page.
- *
- * This method is called after the page is created. It checks for the
- * type of action and executes that action.
- * Finally it calls the parent's run method.
- *
- * @return void
- */
- public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String',
- // default to 'browse'
- $this, FALSE, 'browse'
- );
-
- // assign vars to templates
- $this->assign('action', $action);
- $id = CRM_Utils_Request::retrieve('id', 'Positive',
- $this, FALSE, 0
- );
-
- // what action to take ?
- if ($action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
- $this->edit($action, $id);
- }
- // finally browse the custom groups
- $this->browse();
-
- // parent run
- return parent::run();
- }
-
/**
* Browse all custom data groups.
*
* @return void
*/
public function run() {
- // get the requested action
- $action = CRM_Utils_Request::retrieve('action', 'String',
- $this, FALSE,
- 'browse'
- );
- if ($action & CRM_Core_Action::REVERT) {
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE);
+ $id = $this->getIdAndAction();
+
+ if ($this->_action & CRM_Core_Action::REVERT) {
CRM_PCP_BAO_PCP::setIsActive($id, 0);
$session = CRM_Core_Session::singleton();
$session->pushUserContext(CRM_Utils_System::url(CRM_Utils_System::currentPath(), 'reset=1'));
}
- elseif ($action & CRM_Core_Action::RENEW) {
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE);
+ elseif ($this->_action & CRM_Core_Action::RENEW) {
CRM_PCP_BAO_PCP::setIsActive($id, 1);
$session = CRM_Core_Session::singleton();
$session->pushUserContext(CRM_Utils_System::url(CRM_Utils_System::currentPath(), 'reset=1'));
}
- elseif ($action & CRM_Core_Action::DELETE) {
- $id = CRM_Utils_Request::retrieve('id', 'Positive', $this, FALSE);
+ elseif ($this->_action & CRM_Core_Action::DELETE) {
$session = CRM_Core_Session::singleton();
$session->pushUserContext(CRM_Utils_System::url(CRM_Utils_System::currentPath(), 'reset=1&action=browse'));
$controller = new CRM_Core_Controller_Simple('CRM_PCP_Form_PCP',
$this->browse();
// parent run
- parent::run();
+ CRM_Core_Page::run();
}
/**
--- /dev/null
+<?php
+
+/**
+ * Test that page hooks only get invoked once per page run.
+ */
+class CRM_Core_Page_HookTest extends CiviUnitTestCase {
+ public $DBResetRequired = TRUE;
+
+ /**
+ * The list of classes extending CRM_Core_Page_Basic: the ones to try the
+ * `run()` method on.
+ *
+ * @var array
+ */
+ public $basicPages = array();
+
+ /**
+ * A place to hold the counts of hook invocations.
+ *
+ * @var array
+ */
+ public $hookCount = array();
+
+ /**
+ * Classes that should be skipped
+ *
+ * The main reason is that they look for URL parameters that we don't know to
+ * provide.
+ *
+ * TODO: track down what's needed (in a way that we can be confident for
+ * testing) and quit skipping them.
+ *
+ * @var array
+ */
+ public $skip = array(
+ 'CRM_Contact_Page_DedupeFind',
+ 'CRM_Mailing_Page_Report',
+ 'CRM_Financial_Page_BatchTransaction',
+ 'CRM_Admin_Page_PreferencesDate',
+ 'CRM_Admin_Page_Extensions',
+ 'CRM_Admin_Page_PaymentProcessor',
+ 'CRM_Admin_Page_LabelFormats',
+ // This is a page with no corresponding form:
+ 'CRM_Admin_Page_EventTemplate',
+ );
+
+ /**
+ * Set up the list of pages to evaluate by going through the menu.
+ */
+ public function setUp() {
+ // Get all of the menu items in CiviCRM.
+ $items = CRM_Core_Menu::items(TRUE);
+ // Check if they extend the class we care about; test if needed.
+ foreach ($items as $item) {
+ $class = is_array($item['page_callback']) ? $item['page_callback'][0] : $item['page_callback'];
+ if (in_array($class, $this->skip)) {
+ continue;
+ }
+ if (is_subclass_of($class, 'CRM_Core_Page_Basic')) {
+ $this->basicPages[] = $class;
+ }
+ }
+ parent::setUp();
+ }
+
+ /**
+ * Make sure form hooks are only invoked once.
+ */
+ public function testFormsCallBuildFormOnce() {
+ CRM_Utils_Hook_UnitTests::singleton()->setHook('civicrm_buildForm', array($this, 'onBuildForm'));
+ CRM_Utils_Hook_UnitTests::singleton()->setHook('civicrm_preProcess', array($this, 'onPreProcess'));
+ $_REQUEST = array('action' => 'add');
+ foreach ($this->basicPages as $pageName) {
+ // Reset the counters
+ $this->hookCount = array(
+ 'buildForm' => array(),
+ 'preProcess' => array(),
+ );
+ $page = new $pageName();
+ ob_start();
+ $page->run();
+ ob_end_clean();
+ $this->runTestAssert($pageName);
+ }
+ }
+
+ /**
+ * Go through the record of hook invocation and make sure that each hook has
+ * run once and no more than once per class.
+ *
+ * @param string $pageName
+ * The page/form evaluated.
+ */
+ private function runTestAssert($pageName) {
+ foreach ($this->hookCount as $hook => $hookUsage) {
+ $ran = FALSE;
+ foreach ($hookUsage as $class => $count) {
+ $ran = TRUE;
+ // The hook should have run once and only once.
+ $this->assertEquals(1, $count, "Checking $pageName: $hook invoked multiple times with $class");
+ }
+ $this->assertTrue($ran, "$hook never invoked for $pageName");
+ }
+ }
+
+ /**
+ * Make sure pageRun hook is only invoked once.
+ */
+ public function testPagesCallPageRunOnce() {
+ CRM_Utils_Hook_UnitTests::singleton()->setHook('civicrm_pageRun', array($this, 'onPageRun'));
+ $_REQUEST = array('action' => 'browse');
+ foreach ($this->basicPages as $pageName) {
+ // Reset the counters
+ $this->hookCount = array('pageRun' => array());
+ $page = new $pageName();
+ ob_start();
+ $page->run();
+ ob_end_clean();
+ $this->runTestAssert($pageName);
+ }
+ }
+
+ /**
+ * Implements hook_civicrm_buildForm().
+ *
+ * Increment the count of uses of this hook per formName.
+ */
+ public function onBuildForm($formName, &$form) {
+ $this->incrementHookCount('buildForm', $formName);
+ }
+
+ public function onPreProcess($formName, &$form) {
+ $this->incrementHookCount('preProcess', $formName);
+ }
+
+ /**
+ * Implements hook_civicrm_pageRun().
+ *
+ * Increment the count of uses of this hook per page class.
+ */
+ public function onPageRun(&$page) {
+ $this->incrementHookCount('pageRun', get_class($page));
+ }
+
+ /**
+ * Increment the count of uses of a hook in a class.
+ *
+ * @param string $hook
+ * The hook being used.
+ * @param string $class
+ * The class name of the page or form.
+ */
+ private function incrementHookCount($hook, $class) {
+ if (empty($this->hookCount[$hook][$class])) {
+ $this->hookCount[$hook][$class] = 0;
+ }
+ $this->hookCount[$hook][$class]++;
+ }
+
+}