Revert "Merge pull request #1371 from drumm/CRM-12930-smarty-safe-mode"
[civicrm-core.git] / CRM / Core / PseudoConstant.php
index 94b25c6ff61b3617ea84f77ac9c000d266b2ef1e..ef8b6daca7e38bb897c0da2054afb4c779d612b1 100644 (file)
@@ -57,19 +57,14 @@ class CRM_Core_PseudoConstant {
   private static $cache;
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * activity type
    * @var array
    * @static
    */
   private static $activityType;
 
-  /**
-   * payment processor billing modes
-   * @var array
-   * @static
-   */
-  private static $billingMode;
-
   /**
    * states, provinces
    * @var array
@@ -106,13 +101,8 @@ class CRM_Core_PseudoConstant {
   private static $countryIsoCode;
 
   /**
-   * tag
-   * @var array
-   * @static
-   */
-  private static $tag;
-
-  /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * group
    * @var array
    * @static
@@ -140,20 +130,6 @@ class CRM_Core_PseudoConstant {
    */
   private static $staticGroup;
 
-  /**
-   * user framework groups
-   * @var array
-   * @static
-   */
-  private static $ufGroup;
-
-  /**
-   * custom groups
-   * @var array
-   * @static
-   */
-  private static $customGroup;
-
   /**
    * currency codes
    * @var array
@@ -161,13 +137,6 @@ class CRM_Core_PseudoConstant {
    */
   private static $currencyCode;
 
-  /**
-   * currency Symbols
-   * @var array
-   * @static
-   */
-  private static $currencySymbols;
-
   /**
    * payment processor
    * @var array
@@ -190,6 +159,8 @@ class CRM_Core_PseudoConstant {
   private static $worldRegions;
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * activity status
    * @var array
    * @static
@@ -224,13 +195,6 @@ class CRM_Core_PseudoConstant {
    */
   private static $extensions;
 
-  /**
-   * contact Type
-   * @var array
-   * @static
-   */
-  private static $contactType;
-
   /**
    * Financial Account Type
    * @var array
@@ -239,7 +203,9 @@ class CRM_Core_PseudoConstant {
   private static $accountOptionValues;
 
   /**
-   * Get options for a given field.
+   * Low-level option getter, rarely accessed directly.
+   * NOTE: Rather than calling this function directly use CRM_*_BAO_*::buildOptions()
+   *
    * @param String $daoName
    * @param String $fieldName
    * @param Array $params
@@ -248,59 +214,116 @@ class CRM_Core_PseudoConstant {
    *                            if true, the results are reversed
    * - grouping   boolean if true, return the value in 'grouping' column (currently unsupported for tables other than option_value)
    * - localize   boolean if true, localize the results before returning
-   * - condition  string  add another condition to the sql query
+   * - condition  string|array add condition(s) to the sql query - will be concatenated using 'AND'
    * - keyColumn  string the column to use for 'id'
    * - labelColumn string the column to use for 'label'
+   * - orderColumn string the column to use for sorting, defaults to 'weight' column if one exists, else defaults to labelColumn
    * - onlyActive boolean return only the action option values
    * - fresh      boolean ignore cache entries and go back to DB
+   * @param String $context: Context string
    *
    * @return Array on success, FALSE on error.
    *
    * @static
    */
-  public static function get($daoName, $fieldName, $params = array()) {
+  public static function get($daoName, $fieldName, $params = array(), $context = NULL) {
+    CRM_Core_DAO::buildOptionsContext($context);
+    $flip = !empty($params['flip']);
+    // Merge params with defaults
+    $params += array(
+      'grouping' => FALSE,
+      'localize' => FALSE,
+      'onlyActive' => ($context == 'validate' || $context == 'create') ? FALSE : TRUE,
+      'fresh' => FALSE,
+    );
+
+    // Custom fields are not in the schema
+    if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {
+      $customField = new CRM_Core_DAO_CustomField();
+      $customField->id = (int) substr($fieldName, 7);
+      $customField->find(TRUE);
+      $options = FALSE;
+
+      if (!empty($customField->option_group_id)) {
+        $options = CRM_Core_OptionGroup::valuesByID($customField->option_group_id,
+          $flip,
+          $params['grouping'],
+          $params['localize'],
+          // Note: for custom fields the 'name' column is NULL
+          CRM_Utils_Array::value('labelColumn', $params, 'label'),
+          $params['onlyActive'],
+          $params['fresh']
+        );
+      }
+      else {
+        if ($customField->data_type === 'StateProvince') {
+          $options = self::stateProvince();
+        }
+        elseif ($customField->data_type === 'Country') {
+          $options = $context == 'validate' ? self::countryIsoCode() : self::country();
+        }
+        elseif ($customField->data_type === 'Boolean') {
+          $options = $context == 'validate' ? array(0, 1) : array(1 => ts('Yes'), 0 => ts('No'));
+        }
+        $options = $options && $flip ? array_flip($options) : $options;
+      }
+      if ($options !== FALSE) {
+        CRM_Utils_Hook::customFieldOptions($customField->id, $options, FALSE);
+      }
+      $customField->free();
+      return $options;
+    }
+
+    // Core field: load schema
     $dao = new $daoName;
     $fields = $dao->fields();
+    $fieldKeys = $dao->fieldKeys();
     $dao->free();
-    if (empty($fields[$fieldName])) {
+
+    // Support "unique names" as well as sql names
+    $fieldKey = $fieldName;
+    if (empty($fields[$fieldKey])) {
+      $fieldKey = $fieldKeys[$fieldName];
+    }
+    // If neither worked then this field doesn't exist. Return false.
+    if (empty($fields[$fieldKey])) {
       return FALSE;
     }
-    $fieldSpec = $fields[$fieldName];
-    $flip = !empty($params['flip']);
+    $fieldSpec = $fields[$fieldKey];
 
     // If the field is an enum, explode the enum definition and return the array.
     if (isset($fieldSpec['enumValues'])) {
       // use of a space after the comma is inconsistent in xml
       $enumStr = str_replace(', ', ',', $fieldSpec['enumValues']);
-      $values = explode(',', $enumStr);
-      return $flip ? array_flip($values) : $values;
+      $output = explode(',', $enumStr);
+      return array_combine($output, $output);
     }
 
     elseif (!empty($fieldSpec['pseudoconstant'])) {
       $pseudoconstant = $fieldSpec['pseudoconstant'];
-      // Merge params with defaults
+      // Merge params with schema defaults
       $params += array(
-        'grouping' => FALSE,
-        'localize' => FALSE,
-        'condition' => CRM_Utils_Array::value('condition', $pseudoconstant),
+        'condition' => CRM_Utils_Array::value('condition', $pseudoconstant, array()),
         'keyColumn' => CRM_Utils_Array::value('keyColumn', $pseudoconstant),
         'labelColumn' => CRM_Utils_Array::value('labelColumn', $pseudoconstant),
-        'onlyActive' => TRUE,
-        'fresh' => FALSE,
       );
 
       // Fetch option group from option_value table
       if(!empty($pseudoconstant['optionGroupName'])) {
+        if ($context == 'validate') {
+          $params['labelColumn'] = 'name';
+        }
         // Call our generic fn for retrieving from the option_value table
         return CRM_Core_OptionGroup::values(
           $pseudoconstant['optionGroupName'],
           $flip,
           $params['grouping'],
           $params['localize'],
-          $params['condition'] ? $params['condition'] : $pseudoconstant['condition'],
+          $params['condition'] ? ' AND ' . implode(' AND ', (array) $params['condition']) : NULL,
           $params['labelColumn'] ? $params['labelColumn'] : 'label',
           $params['onlyActive'],
-          $params['fresh']
+          $params['fresh'],
+          $params['keyColumn'] ? $params['keyColumn'] : 'value'
         );
       }
 
@@ -309,62 +332,178 @@ class CRM_Core_PseudoConstant {
         // Normalize params so the serialized cache string will be consistent.
         CRM_Utils_Array::remove($params, 'flip', 'fresh');
         ksort($params);
-        $cacheKey = "{$daoName}{$fieldName}" . serialize($params);
+        $cacheKey = $daoName . $fieldName . serialize($params);
 
-        // Return cached options
+        // Retrieve cached options
         if (isset(self::$cache[$cacheKey]) && empty($params['fresh'])) {
-          return $flip ? array_flip(self::$cache[$cacheKey]) : self::$cache[$cacheKey];
+          $output = self::$cache[$cacheKey];
         }
-
-        $select = "SELECT %1 AS id, %2 AS label";
-        $from = "FROM %3";
-        $wheres = array();
-        $order = "ORDER BY %2";
-        // Condition param can be passed as an sql string
-        if (!empty($params['condition'])) {
-          $wheres[] = $params['condition'];
-        }
-        // Support for onlyActive param if option table contains is_active field
-        if (!empty($params['onlyActive'])) {
-          $daoName = CRM_Core_AllCoreTables::getClassForTable($pseudoconstant['table']);
-          $dao = new $daoName;
-          if (in_array('is_active', $dao->fields())) {
-            $wheres[] = 'is_active = 1';
+        else {
+          $daoName = CRM_Core_DAO_AllCoreTables::getClassForTable($pseudoconstant['table']);
+          if (!class_exists($daoName)) {
+            return FALSE;
           }
+          // Get list of fields for the option table
+          $dao = new $daoName;
+          $availableFields = array_keys($dao->fieldKeys());
           $dao->free();
-        }
-        $queryParams = array(
-           1 => array($params['keyColumn'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES),
-           2 => array($params['labelColumn'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES),
-           3 => array($pseudoconstant['table'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES),
-        );
 
-        self::$cache[$cacheKey] = array();
-        $query = "$select $from";
-        if ($wheres) {
-          $query .= " WHERE " . implode($wheres, ' AND ');
-        }
-        $query .= ' ' . $order;
-        $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
-        while ($dao->fetch()) {
-          self::$cache[$cacheKey][$dao->id] = $dao->label;
-        }
-        $dao->free();
-        if (!empty($params['localize'])) {
-          $i18n = CRM_Core_I18n::singleton();
-          $i18n->localizeArray(self::$cache[$cacheKey]);
-        }
+          $select = "SELECT %1 AS id, %2 AS label";
+          $from = "FROM %3";
+          $wheres = array();
+          $order = "ORDER BY %2";
+
+          // Use machine name instead of label in validate context
+          if ($context == 'validate') {
+            if (!empty($pseudoconstant['nameColumn'])) {
+              $params['labelColumn'] = $pseudoconstant['nameColumn'];
+            }
+            elseif (in_array('name', $availableFields)) {
+              $params['labelColumn'] = 'name';
+            }
+          }
+          // Condition param can be passed as an sql clause string or an array of clauses
+          if (!empty($params['condition'])) {
+            $wheres[] = implode(' AND ', (array) $params['condition']);
+          }
+          // onlyActive param will automatically filter on common flags
+          if (!empty($params['onlyActive'])) {
+            foreach (array('is_active' => 1, 'is_deleted' => 0, 'is_test' => 0) as $flag => $val) {
+              if (in_array($flag, $availableFields)) {
+                $wheres[] = "$flag = $val";
+              }
+            }
+          }
+          // Filter domain specific options
+          if (in_array('domain_id', $availableFields)) {
+            $wheres[] = 'domain_id = ' . CRM_Core_Config::domainID();
+          }
+          $queryParams = array(
+             1 => array($params['keyColumn'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES),
+             2 => array($params['labelColumn'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES),
+             3 => array($pseudoconstant['table'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES),
+          );
+          // Add orderColumn param
+          if (!empty($params['orderColumn'])) {
+            $queryParams[4] = array($params['orderColumn'], 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES);
+            $order = "ORDER BY %4";
+          }
+          // Support no sorting if $params[orderColumn] is FALSE
+          elseif (isset($params['orderColumn']) && $params['orderColumn'] === FALSE) {
+            $order = '';
+          }
+          // Default to 'weight' if that column exists
+          elseif (in_array('weight', $availableFields)) {
+            $order = "ORDER BY weight";
+          }
 
-        return $flip ? array_flip(self::$cache[$cacheKey]) : self::$cache[$cacheKey];
+          $output = array();
+          $query = "$select $from";
+          if ($wheres) {
+            $query .= " WHERE " . implode($wheres, ' AND ');
+          }
+          $query .= ' ' . $order;
+          $dao = CRM_Core_DAO::executeQuery($query, $queryParams);
+          while ($dao->fetch()) {
+            $output[$dao->id] = $dao->label;
+          }
+          $dao->free();
+          // Localize results
+          if (!empty($params['localize']) || $pseudoconstant['table'] == 'civicrm_country' || $pseudoconstant['table'] == 'civicrm_state_province') {
+            $I18nParams = array();
+            if ($pseudoconstant['table'] == 'civicrm_country') {
+              $I18nParams['context'] = 'country';
+            }
+            if ($pseudoconstant['table'] == 'civicrm_state_province') {
+              $I18nParams['context'] = 'province';
+            }
+            $i18n = CRM_Core_I18n::singleton();
+            $i18n->localizeArray($output, $I18nParams);
+            // Maintain sort by label
+            if ($order == "ORDER BY %2") {
+              CRM_Utils_Array::asort($output);
+            }
+          }
+          self::$cache[$cacheKey] = $output;
+        }
+        return $flip ? array_flip($output) : $output;
       }
     }
+
+    // Return "Yes" and "No" for boolean fields
+    elseif (CRM_Utils_Array::value('type', $fieldSpec) === CRM_Utils_Type::T_BOOLEAN) {
+      $output = $context == 'validate' ? array(0, 1) : array(1 => ts('Yes'), 0 => ts('No'));
+      return $flip ? array_flip($output) : $output;
+    }
     // If we're still here, it's an error. Return FALSE.
     return FALSE;
   }
 
   /**
-   * populate the object from the database. generic populate
-   * method
+   * Fetch the translated label for a field given its key
+   *
+   * @param String $baoName
+   * @param String $fieldName
+   * @param String|Int $key
+   *
+   * TODO: Accept multivalued input?
+   *
+   * @return bool|null|string
+   *   FALSE if the given field has no associated option list
+   *   NULL if the given key has no corresponding option
+   *   String if label is found
+   */
+  static function getLabel($baoName, $fieldName, $key) {
+    $values = $baoName::buildOptions($fieldName, 'get');
+    if ($values === FALSE) {
+      return FALSE;
+    }
+    return CRM_Utils_Array::value($key, $values);
+  }
+
+  /**
+   * Fetch the machine name for a field given its key
+   *
+   * @param String $baoName
+   * @param String $fieldName
+   * @param String|Int $key
+   *
+   * @return bool|null|string
+   *   FALSE if the given field has no associated option list
+   *   NULL if the given key has no corresponding option
+   *   String if label is found
+   */
+  static function getName($baoName, $fieldName, $key) {
+    $values = $baoName::buildOptions($fieldName, 'validate');
+    if ($values === FALSE) {
+      return FALSE;
+    }
+    return CRM_Utils_Array::value($key, $values);
+  }
+
+  /**
+   * Fetch the key for a field option given its name
+   *
+   * @param String $baoName
+   * @param String $fieldName
+   * @param String|Int $value
+   *
+   * @return bool|null|string|number
+   *   FALSE if the given field has no associated option list
+   *   NULL if the given key has no corresponding option
+   *   String|Number if key is found
+   */
+  static function getKey($baoName, $fieldName, $value) {
+    $values = $baoName::buildOptions($fieldName, 'validate');
+    if ($values === FALSE) {
+      return FALSE;
+    }
+    return CRM_Utils_Array::key($value, $values);
+  }
+
+  /**
+   * DEPRECATED generic populate method
+   * All pseudoconstant functions that use this method are also deprecated.
    *
    * The static array $var is populated from the db
    * using the <b>$name DAO</b>.
@@ -439,12 +578,17 @@ class CRM_Core_PseudoConstant {
    *
    */
   public static function flush($name = 'cache') {
-    if (property_exists(self, $name)) {
+    if (isset(self::$$name)) {
       self::$$name = NULL;
     }
+    if ($name == 'cache') {
+      CRM_Core_OptionGroup::flushAll();
+    }
   }
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * Get all Activty types.
    *
    * The static array activityType is returned
@@ -517,25 +661,6 @@ class CRM_Core_PseudoConstant {
     return self::$activityType[$index];
   }
 
-  /**
-   * Get all payment-processor billing modes
-   *
-   * @access public
-   * @static
-   *
-   * @return array ($id => $name)
-   */
-  public static function billingMode() {
-    if (!self::$billingMode) {
-      self::$billingMode = array(
-        CRM_Core_Payment::BILLING_MODE_FORM => 'form',
-        CRM_Core_Payment::BILLING_MODE_BUTTON => 'button',
-        CRM_Core_Payment::BILLING_MODE_NOTIFY => 'notify',
-      );
-    }
-    return self::$billingMode;
-  }
   /**
    * Get all the State/Province from database.
    *
@@ -765,28 +890,8 @@ WHERE  id = %1";
   }
 
   /**
-   * Get all the categories from database.
-   *
-   * The static array tag is returned, and if it's
-   * called the first time, the <b>Tag DAO</b> is used
-   * to get all the categories.
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
    *
-   * Note: any database errors will be trapped by the DAO.
-   *
-   * @access public
-   * @static
-   *
-   * @return array - array reference of all categories.
-   *
-   */
-  public static function &tag() {
-    if (!self::$tag) {
-      self::populate(self::$tag, 'CRM_Core_DAO_Tag', TRUE);
-    }
-    return self::$tag;
-  }
-
-  /**
    * Get all groups from database
    *
    * The static array group is returned, and if it's
@@ -846,6 +951,8 @@ WHERE  id = %1";
   }
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * Get all permissioned groups from database
    *
    * The static array group is returned, and if it's
@@ -903,36 +1010,6 @@ WHERE  id = %1";
     return self::$staticGroup;
   }
 
-  /**
-   * Get all the custom groups
-   *
-   * @access public
-   *
-   * @return array - array reference of all groups.
-   * @static
-   */
-  public static function &customGroup($reset = FALSE) {
-    if (!self::$customGroup || $reset) {
-      self::populate(self::$customGroup, 'CRM_Core_DAO_CustomGroup', FALSE, 'title', 'is_active', NULL, 'title');
-    }
-    return self::$customGroup;
-  }
-
-  /**
-   * Get all the user framework groups
-   *
-   * @access public
-   *
-   * @return array - array reference of all groups.
-   * @static
-   */
-  public static function &ufGroup() {
-    if (!self::$ufGroup) {
-      self::populate(self::$ufGroup, 'CRM_Core_DAO_UFGroup', FALSE, 'title', 'is_active', NULL, 'title');
-    }
-    return self::$ufGroup;
-  }
-
   /**
    * Get all Relationship Types  from database.
    *
@@ -979,25 +1056,6 @@ WHERE  id = %1";
     return self::$relationshipType[$valueColumnName];
   }
 
-  /**
-   * Get all the Currency Symbols from Database
-   *
-   * @access public
-   *
-   * @return array - array reference of all Currency Symbols
-   * @static
-   *
-   * FIXME: this is not stored as an optionValue, and it's not tied to a single DB column;
-   * FIXME: It's used for a setting stored in option group 'currencies_enabled'. What to do?
-   */
-  public static function &currencySymbols($name = 'symbol', $key = 'id') {
-    $cacheKey = "{$name}_{$key}";
-    if (!isset(self::$currencySymbols[$cacheKey])) {
-      self::populate(self::$currencySymbols[$cacheKey], 'CRM_Financial_DAO_Currency', TRUE, $name, NULL, NULL, 'name', $key);
-    }
-    return self::$currencySymbols[$cacheKey];
-  }
-
   /**
    * get all the ISO 4217 currency codes
    *
@@ -1319,6 +1377,7 @@ WHERE  id = %1";
   }
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
    * Get all active payment processors
    *
    * The static array paymentProcessor is returned
@@ -1353,7 +1412,7 @@ WHERE  id = %1";
   }
 
   /**
-   * Get all active payment processors
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
    *
    * The static array paymentProcessorType is returned
    *
@@ -1402,6 +1461,8 @@ WHERE  id = %1";
   }
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * Get all Activity Statuses.
    *
    * The static array activityStatus is returned
@@ -1425,6 +1486,8 @@ WHERE  id = %1";
   }
 
   /**
+   * DEPRECATED. Please use the buildOptions() method in the appropriate BAO object.
+   *
    * Get all Visibility levels.
    *
    * The static array visibility is returned
@@ -1655,26 +1718,6 @@ WHERE  id = %1
     return self::$greetingDefaults;
   }
 
-  /**
-   * Get all the Languages from database.
-   *
-   * @access public
-   * @static
-   *
-   * @return array self::languages - array reference of all languages
-   *
-   */
-  public static function &languages() {
-    return CRM_Core_I18n_PseudoConstant::languages();
-  }
-
-  /**
-   * Alias of above
-   */
-  public static function &preferredLanguage() {
-    return CRM_Core_I18n_PseudoConstant::languages();
-  }
-
   /**
    * Get all extensions
    *
@@ -1705,19 +1748,6 @@ WHERE  id = %1
     return self::$extensions;
   }
 
-  /**
-   * Fetch the list of active extensions of type 'module'
-   *
-   * @param $fresh bool whether to forcibly reload extensions list from canonical store
-   * @access public
-   * @static
-   *
-   * @return array - array(array('prefix' => $, 'file' => $))
-   */
-  public static function getModuleExtensions($fresh = FALSE) {
-    return CRM_Extension_System::singleton()->getMapper()->getActiveModuleFiles($fresh);
-  }
-
   /**
    * Get all options values
    *
@@ -1742,5 +1772,18 @@ WHERE  id = %1
 
     return self::$accountOptionValues[$cacheKey];
   }
+
+  /**
+   * Fetch the list of active extensions of type 'module'
+   *
+   * @param $fresh bool whether to forcibly reload extensions list from canonical store
+   * @access public
+   * @static
+   *
+   * @return array - array(array('prefix' => $, 'file' => $))
+   */
+  public static function getModuleExtensions($fresh = FALSE) {
+    return CRM_Extension_System::singleton()->getMapper()->getActiveModuleFiles($fresh);
+  }
 }