3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
18 * This class manages creation and destruction of SQL triggers.
23 * The name of the output file.
30 * Build a list of triggers via hook and add them to (err, reconcile them
33 * @param string $tableName
34 * the specific table requiring a rebuild; or NULL to rebuild all tables.
39 public function rebuild($tableName = NULL, $force = FALSE) {
42 $logging = new \
CRM_Logging_Schema();
43 $logging->triggerInfo($info, $tableName, $force);
45 \CRM_Core_I18n_Schema
::triggerInfo($info, $tableName);
46 \CRM_Contact_BAO_Contact
::triggerInfo($info, $tableName);
48 \CRM_Utils_Hook
::triggerInfo($info, $tableName);
50 // drop all existing triggers on all tables
51 $logging->dropTriggers($tableName);
53 // now create the set of new triggers
54 $this->createTriggers($info, $tableName);
59 * per hook_civicrm_triggerInfo.
60 * @param string $onlyTableName
61 * the specific table requiring a rebuild; or NULL to rebuild all tables.
63 public function createTriggers($info, $onlyTableName = NULL) {
64 // Validate info array, should probably raise errors?
65 if (is_array($info) == FALSE) {
71 // now enumerate the tables and the events and collect the same set in a different format
72 foreach ($info as $value) {
74 // clean the incoming data, skip malformed entries
75 // TODO: malformed entries should raise errors or get logged.
76 if (isset($value['table']) == FALSE ||
77 isset($value['event']) == FALSE ||
78 isset($value['when']) == FALSE ||
79 isset($value['sql']) == FALSE
84 if (is_string($value['table']) == TRUE) {
85 $tables = [$value['table']];
88 $tables = $value['table'];
91 if (is_string($value['event']) == TRUE) {
92 $events = [strtolower($value['event'])];
95 $events = array_map('strtolower', $value['event']);
98 $whenName = strtolower($value['when']);
100 foreach ($tables as $tableName) {
101 if (!isset($triggers[$tableName])) {
102 $triggers[$tableName] = [];
105 foreach ($events as $eventName) {
106 $template_params = ['{tableName}', '{eventName}'];
107 $template_values = [$tableName, $eventName];
109 $sql = str_replace($template_params,
113 $variables = str_replace($template_params,
115 \CRM_Utils_Array
::value('variables', $value)
118 if (!isset($triggers[$tableName][$eventName])) {
119 $triggers[$tableName][$eventName] = [];
122 if (!isset($triggers[$tableName][$eventName][$whenName])) {
123 // We're leaving out cursors, conditions, and handlers for now
124 // they are kind of dangerous in this context anyway
125 // better off putting them in stored procedures
126 $triggers[$tableName][$eventName][$whenName] = [
133 $triggers[$tableName][$eventName][$whenName]['variables'][] = $variables;
136 $triggers[$tableName][$eventName][$whenName]['sql'][] = $sql;
141 // Sort tables alphabetically in order to output in a consistent order
142 // for sites that like to diff this output over time
143 // (ie. with the logging_no_trigger_permission setting in place).
145 // now spit out the sql
146 foreach ($triggers as $tableName => $tables) {
147 if ($onlyTableName != NULL && $onlyTableName != $tableName) {
150 foreach ($tables as $eventName => $events) {
151 foreach ($events as $whenName => $parts) {
152 $varString = implode("\n", $parts['variables']);
153 $sqlString = implode("\n", $parts['sql']);
154 $validName = \CRM_Core_DAO
::shortenSQLName($tableName, 48, TRUE);
155 $triggerName = "{$validName}_{$whenName}_{$eventName}";
156 $triggerSQL = "CREATE TRIGGER $triggerName $whenName $eventName ON $tableName FOR EACH ROW BEGIN $varString $sqlString END";
158 $this->enqueueQuery("DROP TRIGGER IF EXISTS $triggerName");
159 $this->enqueueQuery($triggerSQL);
166 * Wrapper function to drop triggers.
168 * @param string $tableName
169 * the specific table requiring a rebuild; or NULL to rebuild all tables.
171 public function dropTriggers($tableName = NULL) {
174 $logging = new \
CRM_Logging_Schema();
175 $logging->triggerInfo($info, $tableName);
177 // drop all existing triggers on all tables
178 $logging->dropTriggers($tableName);
182 * Enqueue a query which alters triggers.
184 * As this requires a high permission level we funnel the queries through here to
185 * facilitate them being taken 'offline'.
187 * @param string $triggerSQL
188 * The sql to run to create or drop the triggers.
189 * @param array $params
190 * Optional parameters to interpolate into the string.
192 public function enqueueQuery($triggerSQL, $params = []) {
193 if (\Civi
::settings()->get('logging_no_trigger_permission')) {
195 if (!file_exists($this->getFile())) {
196 // Ugh. Need to let user know somehow. This is the first change.
197 \CRM_Core_Session
::setStatus(ts('The mysql commands you need to run are stored in %1', [
198 1 => $this->getFile(),
207 $buf .= "DELIMITER //\n";
208 $buf .= \CRM_Core_DAO
::composeQuery($triggerSQL, $params) . " //\n";
209 $buf .= "DELIMITER ;\n";
210 file_put_contents($this->getFile(), $buf, FILE_APPEND
);
213 \CRM_Core_DAO
::executeQuery($triggerSQL, $params, TRUE, NULL, FALSE, FALSE);
218 * @return NULL|string
220 public function getFile() {
221 if ($this->file
=== NULL) {
222 $prefix = 'trigger' . \CRM_Utils_Request
::id();
223 $config = \CRM_Core_Config
::singleton();
224 $this->file
= "{$config->configAndLogDir}CiviCRM." . $prefix . md5($config->dsn
) . '.sql';