*/
class CRM_Core_DAO extends DB_DataObject {
+ /**
+ * Primary key field(s).
+ *
+ * @var string[]
+ */
+ public static $_primaryKey = ['id'];
+
/**
* How many times has this instance been cloned.
*
*/
public static $_factory = NULL;
- public static $_checkedSqlFunctionsExist = FALSE;
-
/**
* https://issues.civicrm.org/jira/browse/CRM-17748
* internal variable for DAO to hold per-query settings
return CRM_Core_DAO_AllCoreTables::getBriefName($className);
}
+ /**
+ * Returns user-friendly description of this entity.
+ *
+ * @return string|null
+ */
+ public static function getEntityDescription() {
+ return NULL;
+ }
+
public function __clone() {
if (!empty($this->_DB_resultid)) {
$this->resultCopies++;
if (empty($record['id'])) {
throw new CRM_Core_Exception("Cannot delete {$entityName} with no id.");
}
+ CRM_Utils_Type::validate($record['id'], 'Positive');
CRM_Utils_Hook::pre('delete', $entityName, $record['id'], $record);
$instance = new $className();
$newObject = new $daoName();
$fields = $object->fields();
- if (!is_array($fieldsFix)) {
- $fieldsToPrefix = [];
- $fieldsToSuffix = [];
- $fieldsToReplace = [];
- }
+ $fieldsToPrefix = [];
+ $fieldsToSuffix = [];
+ $fieldsToReplace = [];
if (!empty($fieldsFix['prefix'])) {
$fieldsToPrefix = $fieldsFix['prefix'];
}
$fieldsToReplace = $fieldsFix['replace'];
}
+ $localizableFields = FALSE;
foreach ($fields as $name => $value) {
if ($name == 'id' || $value['name'] == 'id') {
// copy everything but the id!
$newObject->$dbName = CRM_Utils_Date::isoToMysql($newObject->$dbName);
}
+ if (!empty($value['localizable'])) {
+ $localizableFields = TRUE;
+ }
+
if ($newData) {
$newObject->copyValues($newData);
}
}
$newObject->save();
+
+ // ensure we copy all localized fields as well
+ if (CRM_Core_I18n::isMultilingual() && $localizableFields) {
+ global $dbLocale;
+ $locales = CRM_Core_I18n::getMultilingual();
+ $curLocale = CRM_Core_I18n::getLocale();
+ // loop on other locales
+ foreach ($locales as $locale) {
+ if ($locale != $curLocale) {
+ // setLocale doesn't seems to be reliable to set dbLocale and we only need to change the db locale
+ $dbLocale = '_' . $locale;
+ $newObject->copyLocalizable($object->id, $newObject->id, $fieldsToPrefix, $fieldsToSuffix, $fieldsToReplace);
+ }
+ }
+ // restore dbLocale to starting value
+ $dbLocale = '_' . $curLocale;
+ }
+
if (!$blockCopyofCustomValues) {
$newObject->copyCustomFields($object->id, $newObject->id);
}
return $newObject;
}
+ /**
+ * Method that copies localizable fields from an old entity to a new one.
+ *
+ * Fixes bug dev/core#2479,
+ * where non current locale fields are copied from current locale losing translation when copying
+ *
+ * @param int $entityID
+ * @param int $newEntityID
+ * @param array $fieldsToPrefix
+ * @param array $fieldsToSuffix
+ * @param array $fieldsToReplace
+ */
+ protected function copyLocalizable($entityID, $newEntityID, $fieldsToPrefix, $fieldsToSuffix, $fieldsToReplace) {
+ $entity = get_class($this);
+ $object = new $entity();
+ $object->id = $entityID;
+ $object->find();
+
+ $newObject = new $entity();
+ $newObject->id = $newEntityID;
+
+ $newObject->find();
+
+ if ($object->fetch() && $newObject->fetch()) {
+
+ $fields = $object->fields();
+ foreach ($fields as $name => $value) {
+
+ if ($name == 'id' || $value['name'] == 'id') {
+ // copy everything but the id!
+ continue;
+ }
+
+ // only copy localizable fields
+ if (!$value['localizable']) {
+ continue;
+ }
+
+ $dbName = $value['name'];
+ $type = CRM_Utils_Type::typeToString($value['type']);
+ $newObject->$dbName = $object->$dbName;
+ if (isset($fieldsToPrefix[$dbName])) {
+ $newObject->$dbName = $fieldsToPrefix[$dbName] . $newObject->$dbName;
+ }
+ if (isset($fieldsToSuffix[$dbName])) {
+ $newObject->$dbName .= $fieldsToSuffix[$dbName];
+ }
+ if (isset($fieldsToReplace[$dbName])) {
+ $newObject->$dbName = $fieldsToReplace[$dbName];
+ }
+
+ if ($type == 'Timestamp' || $type == 'Date') {
+ $newObject->$dbName = CRM_Utils_Date::isoToMysql($newObject->$dbName);
+ }
+
+ }
+ $newObject->save();
+
+ }
+ }
+
/**
* Method that copies custom fields values from an old entity to a new one.
*
Civi::service('sql_triggers')->rebuild($tableName, $force);
}
- /**
- * Because sql functions are sometimes lost, esp during db migration, we check here to avoid numerous support requests
- * @see http://issues.civicrm.org/jira/browse/CRM-13822
- * TODO: Alternative solutions might be
- * * Stop using functions and find another way to strip numeric characters from phones
- * * Give better error messages (currently a missing fn fatals with "unknown error")
- */
- public static function checkSqlFunctionsExist() {
- if (!self::$_checkedSqlFunctionsExist) {
- self::$_checkedSqlFunctionsExist = TRUE;
- $dao = CRM_Core_DAO::executeQuery("SHOW function status WHERE db = database() AND name = 'civicrm_strip_non_numeric'");
- if (!$dao->fetch()) {
- self::triggerRebuild();
- }
- }
- }
-
/**
* Wrapper function to drop triggers.
*
* @param string $tableName
* Table referred to.
*
- * @return array
+ * @return CRM_Core_Reference_Interface[]
* structure of table and column, listing every table with a
* foreign key reference to $tableName, and the column where the key appears.
*/
$contactReferences = [];
$coreReferences = CRM_Core_DAO::getReferencesToTable('civicrm_contact');
foreach ($coreReferences as $coreReference) {
- if (!is_a($coreReference, 'CRM_Core_Reference_Dynamic')) {
+ if (
+ // Exclude option values
+ !is_a($coreReference, 'CRM_Core_Reference_Dynamic') &&
+ // Exclude references to other columns
+ $coreReference->getTargetKey() === 'id'
+ ) {
$contactReferences[$coreReference->getReferenceTable()][] = $coreReference->getReferenceKey();
}
}
*/
public static function createSQLFilter($fieldName, $filter, $type = NULL, $alias = NULL, $returnSanitisedArray = FALSE) {
foreach ($filter as $operator => $criteria) {
- if (!CRM_Core_BAO_SchemaHandler::databaseSupportsUTF8MB4()) {
- foreach ((array) $criteria as $criterion) {
- if (!empty($criterion) && !is_numeric($criterion)
- // The first 2 criteria are redundant but are added as they
- // seem like they would
- // be quicker than this 3rd check.
- && max(array_map('ord', str_split($criterion))) >= 240) {
- // String contains unsupported emojis.
- // We return a clause that resolves to false as an emoji string by definition cannot be saved.
- // note that if we return just 0 for false if gets lost in empty checks.
- // https://stackoverflow.com/questions/16496554/can-php-detect-4-byte-encoded-utf8-chars
- return '0 = 1';
- }
- }
+ $emojiFilter = CRM_Utils_SQL::handleEmojiInQuery($criteria);
+ if ($emojiFilter === '0 = 1') {
+ return $emojiFilter;
}
if (in_array($operator, self::acceptedSQLOperators(), TRUE)) {
return $clauses;
}
- /**
- * Check whether action can be performed on a given record.
- *
- * Dispatches to internal BAO function ('static::_checkAccess())` and `hook_civicrm_checkAccess`.
- *
- * @param string $entityName
- * Ex: 'Contact' or 'Custom_Foobar'
- * @param string $action
- * APIv4 action name.
- * Ex: 'create', 'get', 'delete'
- * @param array $record
- * All (known/loaded) values of individual record being accessed.
- * The record should provide an 'id' but may otherwise be incomplete; guard accordingly.
- * @param int|null $userID
- * Contact ID of the active user (whose access we must check). NULL for anonymous.
- * @param bool $granted
- * Initial value (usually TRUE, but the API might pass FALSE if gatekeeper permissions fail)
- *
- * @return bool
- */
- public static function checkAccess(string $entityName, string $action, array $record, $userID, $granted = TRUE): bool {
- // Ensure this function was either called on a BAO class or a DAO that has no BAO
- if (!$entityName ||
- (!strpos(static::class, '_BAO_') && CRM_Core_DAO_AllCoreTables::getBAOClassName(static::class) !== static::class)
- ) {
- throw new CRM_Core_Exception('Function checkAccess must be called on a BAO class');
- }
- // Dispatch to protected function _checkAccess in this BAO
- if ($granted && method_exists(static::class, '_checkAccess')) {
- $granted = static::_checkAccess($entityName, $action, $record, $userID);
- }
- // Dispatch to hook
- CRM_Utils_Hook::checkAccess($entityName, $action, $record, $userID, $granted);
- return $granted;
- }
-
/**
* ensure database name is 'safe', i.e. only contains word characters (includes underscores)
* and dashes, and contains at least one [a-z] case insensitive.