diff --git a/CRM/Financial/BAO/FinancialType.php b/CRM/Financial/BAO/FinancialType.php index 538b01ccd072..919b221707d6 100644 --- a/CRM/Financial/BAO/FinancialType.php +++ b/CRM/Financial/BAO/FinancialType.php @@ -395,7 +395,33 @@ public static function checkPermissionedLineItems($id, $op, $force = TRUE) { } /** - * Check if FT-ACL is turned on or off + * Check if the logged in user has permission to edit the given financial type. + * + * This is called when determining if they can edit things like option values + * in price sets. At the moment it is not possible to change an option value from + * a type you do not have permission to to a type that you do. + * + * @todo it is currently not possible to edit disabled types if you have ACLs on. + * Do ACLs still apply once disabled? That question should be resolved if tackling + * that gap. + * + * @param int $financialTypeID + * + * @return bool + */ + public static function checkPermissionToEditFinancialType($financialTypeID) { + if (!self::isACLFinancialTypeStatus()) { + return TRUE; + } + // @todo consider adding back in disabled types here. + CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes, CRM_Core_Action::UPDATE); + return isset($financialTypes[$financialTypeID]); + } + + /** + * Check if FT-ACL is turned on or off. + * + * @todo rename this function e.g isFinancialTypeACLsEnabled. * * @return bool */ diff --git a/CRM/Price/BAO/PriceFieldValue.php b/CRM/Price/BAO/PriceFieldValue.php index b02b7cb4ce08..2f337386d5ce 100644 --- a/CRM/Price/BAO/PriceFieldValue.php +++ b/CRM/Price/BAO/PriceFieldValue.php @@ -29,8 +29,6 @@ * * @package CRM * @copyright CiviCRM LLC (c) 2004-2017 - * $Id$ - * */ /** @@ -43,9 +41,9 @@ class CRM_Price_BAO_PriceFieldValue extends CRM_Price_DAO_PriceFieldValue { * Insert/update a new entry in the database. * * @param array $params - * (reference), array $ids. * - * @param $ids + * @param array $ids + * Deprecated variable. * * @return CRM_Price_DAO_PriceFieldValue */ @@ -157,7 +155,7 @@ public static function retrieve(&$params, &$defaults) { } /** - * Retrive the all values for given field id. + * Retrieve all values for given field id. * * @param int $fieldId * Price_field_id. diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php index 39fa2d567cf8..be5c02d6bc41 100644 --- a/CRM/Price/BAO/PriceSet.php +++ b/CRM/Price/BAO/PriceSet.php @@ -29,12 +29,10 @@ * * @package CRM * @copyright CiviCRM LLC (c) 2004-2017 - * $Id$ - * */ /** - * Business object for managing price sets + * Business object for managing price sets. * */ class CRM_Price_BAO_PriceSet extends CRM_Price_DAO_PriceSet { @@ -191,23 +189,17 @@ public static function getTitle($id) { * * @return array */ - public static function &getUsedBy($id, $simpleReturn = FALSE) { - $usedBy = $forms = $tables = array(); - $queryString = " -SELECT entity_table, entity_id -FROM civicrm_price_set_entity -WHERE price_set_id = %1"; - $params = array(1 => array($id, 'Integer')); - $crmFormDAO = CRM_Core_DAO::executeQuery($queryString, $params); - - while ($crmFormDAO->fetch()) { - $forms[$crmFormDAO->entity_table][] = $crmFormDAO->entity_id; - $tables[] = $crmFormDAO->entity_table; - } - // Return only tables + public static function getUsedBy($id, $simpleReturn = FALSE) { + $usedBy = array(); + $forms = self::getFormsUsingPriceSet($id); + $tables = array_keys($forms); + // @todo - this is really clumsy overloading the signature like this. Instead + // move towards having a function that does not call reformatUsedByFormsWithEntityData + // and call that when that data is not used. if ($simpleReturn == 'table') { return $tables; } + // @todo - this is painfully slow in some cases. if (empty($forms)) { $queryString = " SELECT cli.entity_table, cli.entity_id @@ -224,74 +216,17 @@ public static function &getUsedBy($id, $simpleReturn = FALSE) { return $usedBy; } } - // Return only entity data + // @todo - this is really clumsy overloading the signature like this. See above. if ($simpleReturn == 'entity') { return $forms; } - foreach ($forms as $table => $entities) { - switch ($table) { - case 'civicrm_event': - $ids = implode(',', $entities); - $queryString = "SELECT ce.id as id, ce.title as title, ce.is_public as isPublic, ce.start_date as startDate, ce.end_date as endDate, civicrm_option_value.label as eventType, ce.is_template as isTemplate, ce.template_title as templateTitle -FROM civicrm_event ce -LEFT JOIN civicrm_option_value ON - ( ce.event_type_id = civicrm_option_value.value ) -LEFT JOIN civicrm_option_group ON - ( civicrm_option_group.id = civicrm_option_value.option_group_id ) -WHERE - civicrm_option_group.name = 'event_type' AND - ce.id IN ($ids) AND - ce.is_active = 1;"; - $crmDAO = CRM_Core_DAO::executeQuery($queryString); - while ($crmDAO->fetch()) { - if ($crmDAO->isTemplate) { - $usedBy['civicrm_event_template'][$crmDAO->id]['title'] = $crmDAO->templateTitle; - $usedBy['civicrm_event_template'][$crmDAO->id]['eventType'] = $crmDAO->eventType; - $usedBy['civicrm_event_template'][$crmDAO->id]['isPublic'] = $crmDAO->isPublic; - } - else { - $usedBy[$table][$crmDAO->id]['title'] = $crmDAO->title; - $usedBy[$table][$crmDAO->id]['eventType'] = $crmDAO->eventType; - $usedBy[$table][$crmDAO->id]['startDate'] = $crmDAO->startDate; - $usedBy[$table][$crmDAO->id]['endDate'] = $crmDAO->endDate; - $usedBy[$table][$crmDAO->id]['isPublic'] = $crmDAO->isPublic; - } - } - break; - - case 'civicrm_contribution_page': - $ids = implode(',', $entities); - $queryString = "SELECT cp.id as id, cp.title as title, cp.start_date as startDate, cp.end_date as endDate,ct.name as type -FROM civicrm_contribution_page cp, civicrm_financial_type ct -WHERE ct.id = cp.financial_type_id AND - cp.id IN ($ids) AND - cp.is_active = 1;"; - $crmDAO = CRM_Core_DAO::executeQuery($queryString); - while ($crmDAO->fetch()) { - $usedBy[$table][$crmDAO->id]['title'] = $crmDAO->title; - $usedBy[$table][$crmDAO->id]['type'] = $crmDAO->type; - $usedBy[$table][$crmDAO->id]['startDate'] = $crmDAO->startDate; - $usedBy[$table][$crmDAO->id]['endDate'] = $crmDAO->endDate; - } - break; - - case 'civicrm_contribution': - case 'civicrm_membership': - case 'civicrm_participant': - $usedBy[$table] = 1; - break; - - default: - CRM_Core_Error::fatal("$table is not supported in PriceSet::usedBy()"); - break; - } - } + $usedBy = self::reformatUsedByFormsWithEntityData($forms, $usedBy); return $usedBy; } /** - * Delete the price set. + * Delete the price set, including the fields. * * @param int $id * Price Set id. @@ -302,19 +237,6 @@ public static function &getUsedBy($id, $simpleReturn = FALSE) { * */ public static function deleteSet($id) { - // remove from all inactive forms - $usedBy = self::getUsedBy($id); - if (isset($usedBy['civicrm_event'])) { - foreach ($usedBy['civicrm_event'] as $eventId => $unused) { - $eventDAO = new CRM_Event_DAO_Event(); - $eventDAO->id = $eventId; - $eventDAO->find(); - while ($eventDAO->fetch()) { - self::removeFrom('civicrm_event', $eventDAO->id); - } - } - } - // delete price fields $priceField = new CRM_Price_DAO_PriceField(); $priceField->price_set_id = $id; @@ -374,7 +296,7 @@ public static function removeFrom($entityTable, $entityId) { } /** - * Find a price_set_id associatied with the given table, id and usedFor + * Find a price_set_id associated with the given table, id and usedFor * Used For value for events:1, contribution:2, membership:3 * * @param string $entityTable @@ -624,7 +546,7 @@ public static function getSetDetail($setID, $required = TRUE, $validOnly = FALSE */ public static function getOnlyPriceFieldID(array $priceSet) { if (count($priceSet['fields']) > 1) { - throw new CRM_Core_Exception(ts('expected only one price field to be in priceset but multiple are present')); + throw new CRM_Core_Exception(ts('expected only one price field to be in price set but multiple are present')); } return (int) implode('_', array_keys($priceSet['fields'])); } @@ -640,7 +562,7 @@ public static function getOnlyPriceFieldID(array $priceSet) { public static function getOnlyPriceFieldValueID(array $priceSet) { $priceFieldID = self::getOnlyPriceFieldID($priceSet); if (count($priceSet['fields'][$priceFieldID]['options']) > 1) { - throw new CRM_Core_Exception(ts('expected only one price field to be in priceset but multiple are present')); + throw new CRM_Core_Exception(ts('expected only one price field to be in price set but multiple are present')); } return (int) implode('_', array_keys($priceSet['fields'][$priceFieldID]['options'])); } @@ -666,7 +588,7 @@ public static function initSet(&$form, $id, $entityTable = 'civicrm_event', $val $priceSetId = self::getFor($entityTable, $id); } - //check if priceset is is_config + //check if price set is is_config if (is_numeric($priceSetId)) { if (CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $priceSetId, 'is_quick_config') && $form->getVar('_name') != 'Participant') { $form->assign('quickConfig', 1); @@ -714,16 +636,16 @@ public static function initSet(&$form, $id, $entityTable = 'civicrm_event', $val //get option count info. $form->_priceSet['optionsCountTotal'] = self::getPricesetCount($priceSetId); if ($form->_priceSet['optionsCountTotal']) { - $optionsCountDeails = array(); + $optionsCountDetails = array(); if (!empty($form->_priceSet['fields'])) { foreach ($form->_priceSet['fields'] as $field) { foreach ($field['options'] as $option) { $count = CRM_Utils_Array::value('count', $option, 0); - $optionsCountDeails['fields'][$field['id']]['options'][$option['id']] = $count; + $optionsCountDetails['fields'][$field['id']]['options'][$option['id']] = $count; } } } - $form->_priceSet['optionsCountDetails'] = $optionsCountDeails; + $form->_priceSet['optionsCountDetails'] = $optionsCountDetails; } //get option max value info. @@ -769,6 +691,7 @@ public static function initSet(&$form, $id, $entityTable = 'civicrm_event', $val * @param string $component * This parameter appears to only be relevant to determining whether memberships should be auto-renewed. * (and is effectively a boolean for 'is_membership' which could be calculated from the line items.) + * @param int $priceSetID */ public static function processAmount($fields, &$params, &$lineItem, $component = '', $priceSetID = NULL) { // using price set @@ -1190,7 +1113,7 @@ public static function setDefaultPriceSet(&$form, &$defaults) { return $defaults; } - foreach ($form->_priceSet['fields'] as $key => $val) { + foreach ($form->_priceSet['fields'] as $val) { foreach ($val['options'] as $keys => $values) { // build price field index which is passed via URL // url format will be appended by "&price_5=11" @@ -1744,7 +1667,7 @@ public static function parsePriceSetArrayFromParams($params) { public static function getNonDeductibleAmountFromPriceSet($priceSetId, $lineItem) { $nonDeductibleAmount = 0; if (!empty($lineItem[$priceSetId])) { - foreach ($lineItem[$priceSetId] as $fieldId => $options) { + foreach ($lineItem[$priceSetId] as $options) { $nonDeductibleAmount += $options['non_deductible_amount'] * $options['qty']; } } @@ -1752,4 +1675,111 @@ public static function getNonDeductibleAmountFromPriceSet($priceSetId, $lineItem return $nonDeductibleAmount; } + /** + * Get an array of all forms using a given price set. + * + * @param int $id + * + * @return array + * Pages using the price set, keyed by type. e.g + * array(' + * 'civicrm_contribution_page' => array(2,5,6), + * 'civicrm_event' => array(5,6), + * 'civicrm_event_template' => array(7), + * ) + */ + public static function getFormsUsingPriceSet($id) { + $forms = array(); + $queryString = " +SELECT entity_table, entity_id +FROM civicrm_price_set_entity +WHERE price_set_id = %1"; + $params = array(1 => array($id, 'Integer')); + $crmFormDAO = CRM_Core_DAO::executeQuery($queryString, $params); + + while ($crmFormDAO->fetch()) { + $forms[$crmFormDAO->entity_table][] = $crmFormDAO->entity_id; + } + return $forms; + } + + /** + * @param array $forms + * Array of forms that use a price set keyed by entity. e.g + * array(' + * 'civicrm_contribution_page' => array(2,5,6), + * 'civicrm_event' => array(5,6), + * 'civicrm_event_template' => array(7), + * ) + * + * @return mixed + * Array of entities suppliemented with per entity information. + * e.g + * array('civicrm_event' => array(7 => array('title' => 'x'...)) + * + * @throws \Exception + */ + protected static function reformatUsedByFormsWithEntityData($forms) { + $usedBy = array(); + foreach ($forms as $table => $entities) { + switch ($table) { + case 'civicrm_event': + $ids = implode(',', $entities); + $queryString = "SELECT ce.id as id, ce.title as title, ce.is_public as isPublic, ce.start_date as startDate, ce.end_date as endDate, civicrm_option_value.label as eventType, ce.is_template as isTemplate, ce.template_title as templateTitle +FROM civicrm_event ce +LEFT JOIN civicrm_option_value ON + ( ce.event_type_id = civicrm_option_value.value ) +LEFT JOIN civicrm_option_group ON + ( civicrm_option_group.id = civicrm_option_value.option_group_id ) +WHERE + civicrm_option_group.name = 'event_type' AND + ce.id IN ($ids) AND + ce.is_active = 1;"; + $crmDAO = CRM_Core_DAO::executeQuery($queryString); + while ($crmDAO->fetch()) { + if ($crmDAO->isTemplate) { + $usedBy['civicrm_event_template'][$crmDAO->id]['title'] = $crmDAO->templateTitle; + $usedBy['civicrm_event_template'][$crmDAO->id]['eventType'] = $crmDAO->eventType; + $usedBy['civicrm_event_template'][$crmDAO->id]['isPublic'] = $crmDAO->isPublic; + } + else { + $usedBy[$table][$crmDAO->id]['title'] = $crmDAO->title; + $usedBy[$table][$crmDAO->id]['eventType'] = $crmDAO->eventType; + $usedBy[$table][$crmDAO->id]['startDate'] = $crmDAO->startDate; + $usedBy[$table][$crmDAO->id]['endDate'] = $crmDAO->endDate; + $usedBy[$table][$crmDAO->id]['isPublic'] = $crmDAO->isPublic; + } + } + break; + + case 'civicrm_contribution_page': + $ids = implode(',', $entities); + $queryString = "SELECT cp.id as id, cp.title as title, cp.start_date as startDate, cp.end_date as endDate,ct.name as type +FROM civicrm_contribution_page cp, civicrm_financial_type ct +WHERE ct.id = cp.financial_type_id AND + cp.id IN ($ids) AND + cp.is_active = 1;"; + $crmDAO = CRM_Core_DAO::executeQuery($queryString); + while ($crmDAO->fetch()) { + $usedBy[$table][$crmDAO->id]['title'] = $crmDAO->title; + $usedBy[$table][$crmDAO->id]['type'] = $crmDAO->type; + $usedBy[$table][$crmDAO->id]['startDate'] = $crmDAO->startDate; + $usedBy[$table][$crmDAO->id]['endDate'] = $crmDAO->endDate; + } + break; + + case 'civicrm_contribution': + case 'civicrm_membership': + case 'civicrm_participant': + $usedBy[$table] = 1; + break; + + default: + CRM_Core_Error::fatal("$table is not supported in PriceSet::usedBy()"); + break; + } + } + return $usedBy; + } + } diff --git a/CRM/Price/Form/DeleteSet.php b/CRM/Price/Form/DeleteSet.php index 7adc42f22d5f..a4bb5a0d8a33 100644 --- a/CRM/Price/Form/DeleteSet.php +++ b/CRM/Price/Form/DeleteSet.php @@ -29,8 +29,6 @@ * * @package CRM * @copyright CiviCRM LLC (c) 2004-2017 - * $Id$ - * */ /** diff --git a/CRM/Price/Form/Option.php b/CRM/Price/Form/Option.php index 477ec36c9b62..99126381c8a9 100644 --- a/CRM/Price/Form/Option.php +++ b/CRM/Price/Form/Option.php @@ -115,8 +115,7 @@ public function setDefaultValues() { public function buildQuickForm() { if ($this->_action == CRM_Core_Action::UPDATE) { $finTypeId = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceFieldValue', $this->_oid, 'financial_type_id'); - CRM_Financial_BAO_FinancialType::getAvailableFinancialTypes($financialTypes, CRM_Core_Action::UPDATE); - if (!array_key_exists($finTypeId, $financialTypes)) { + if (!CRM_Financial_BAO_FinancialType::checkPermissionToEditFinancialType($finTypeId)) { CRM_Core_Error::fatal(ts("You do not have permission to access this page")); } } diff --git a/CRM/Price/Page/Option.php b/CRM/Price/Page/Option.php index 2a7f82f35113..5e3daafb184d 100644 --- a/CRM/Price/Page/Option.php +++ b/CRM/Price/Page/Option.php @@ -121,8 +121,13 @@ public static function &actionLinks() { * @return void */ public function browse() { - $customOption = array(); - CRM_Price_BAO_PriceFieldValue::getValues($this->_fid, $customOption); + $priceOptions = civicrm_api3('PriceFieldValue', 'get', array( + 'price_field_id' => $this->_fid, + // Explicitly do not check permissions so we are not + // restricted by financial type, so we can change them. + 'check_permissions' => FALSE, + )); + $customOption = $priceOptions['values']; // CRM-15378 - check if these price options are in an Event price set $isEvent = FALSE; @@ -134,7 +139,6 @@ public function browse() { } $config = CRM_Core_Config::singleton(); - $financialType = CRM_Contribute_PseudoConstant::financialType(); $taxRate = CRM_Core_PseudoConstant::getTaxRates(); // display taxTerm for priceFields $invoiceSettings = Civi::settings()->get('contribution_invoice_settings'); @@ -153,7 +157,7 @@ public function browse() { $customOption[$id]['tax_amount'] = $taxAmount['tax_amount']; } if (!empty($values['financial_type_id'])) { - $customOption[$id]['financial_type_id'] = $financialType[$values['financial_type_id']]; + $customOption[$id]['financial_type_id'] = CRM_Contribute_PseudoConstant::financialType($values['financial_type_id']); } // update enable/disable links depending on price_field properties. if ($this->_isSetReserved) { diff --git a/CRM/Price/Page/Set.php b/CRM/Price/Page/Set.php index 1a54f366036d..c3ec481e7b5f 100644 --- a/CRM/Price/Page/Set.php +++ b/CRM/Price/Page/Set.php @@ -143,7 +143,6 @@ public function run() { $this->preview($sid); } elseif ($action & CRM_Core_Action::COPY) { - $session = CRM_Core_Session::singleton(); CRM_Core_Session::setStatus(ts('A copy of the price set has been created'), ts('Saved'), 'success'); $this->copy(); } @@ -155,10 +154,8 @@ public function run() { if (empty($usedBy)) { // prompt to delete - $session = CRM_Core_Session::singleton(); - $session->pushUserContext(CRM_Utils_System::url('civicrm/admin/price', 'action=browse')); + CRM_Core_Session::singleton()->pushUserContext(CRM_Utils_System::url('civicrm/admin/price', 'action=browse')); $controller = new CRM_Core_Controller_Simple('CRM_Price_Form_DeleteSet', 'Delete Price Set', NULL); - // $id = CRM_Utils_Request::retrieve('sid', 'Positive', $this, false, 0); $controller->set('sid', $sid); $controller->setEmbedded(TRUE); $controller->process(); diff --git a/CRM/Utils/Check/Component/PriceFields.php b/CRM/Utils/Check/Component/PriceFields.php new file mode 100644 index 000000000000..a36d9a558427 --- /dev/null +++ b/CRM/Utils/Check/Component/PriceFields.php @@ -0,0 +1,76 @@ +fetch()) { + $count++; + $url = CRM_Utils_System::url('civicrm/admin/price/field', array( + 'reset' => 1, + 'action' => 'browse', + 'sid' => $dao->ps_id)); + $html .= "
the following Price Set Fields use disabled or invalid financial types and need to be fixed if they are to still be used.
+
Price Set | Price Set Field | Action Link | +
---|