3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
34 * This class manages creation and destruction of SQL triggers.
39 * The name of the output file.
46 * Build a list of triggers via hook and add them to (err, reconcile them
49 * @param string $tableName
50 * the specific table requiring a rebuild; or NULL to rebuild all tables.
55 public function rebuild($tableName = NULL, $force = FALSE) {
58 $logging = new \
CRM_Logging_Schema();
59 $logging->triggerInfo($info, $tableName, $force);
61 \CRM_Core_I18n_Schema
::triggerInfo($info, $tableName);
62 \CRM_Contact_BAO_Contact
::triggerInfo($info, $tableName);
64 \CRM_Utils_Hook
::triggerInfo($info, $tableName);
66 // drop all existing triggers on all tables
67 $logging->dropTriggers($tableName);
69 // now create the set of new triggers
70 $this->createTriggers($info, $tableName);
75 * per hook_civicrm_triggerInfo.
76 * @param string $onlyTableName
77 * the specific table requiring a rebuild; or NULL to rebuild all tables.
79 public function createTriggers(&$info, $onlyTableName = NULL) {
80 // Validate info array, should probably raise errors?
81 if (is_array($info) == FALSE) {
87 // now enumerate the tables and the events and collect the same set in a different format
88 foreach ($info as $value) {
90 // clean the incoming data, skip malformed entries
91 // TODO: malformed entries should raise errors or get logged.
92 if (isset($value['table']) == FALSE ||
93 isset($value['event']) == FALSE ||
94 isset($value['when']) == FALSE ||
95 isset($value['sql']) == FALSE
100 if (is_string($value['table']) == TRUE) {
101 $tables = [$value['table']];
104 $tables = $value['table'];
107 if (is_string($value['event']) == TRUE) {
108 $events = [strtolower($value['event'])];
111 $events = array_map('strtolower', $value['event']);
114 $whenName = strtolower($value['when']);
116 foreach ($tables as $tableName) {
117 if (!isset($triggers[$tableName])) {
118 $triggers[$tableName] = [];
121 foreach ($events as $eventName) {
122 $template_params = ['{tableName}', '{eventName}'];
123 $template_values = [$tableName, $eventName];
125 $sql = str_replace($template_params,
129 $variables = str_replace($template_params,
131 \CRM_Utils_Array
::value('variables', $value)
134 if (!isset($triggers[$tableName][$eventName])) {
135 $triggers[$tableName][$eventName] = [];
138 if (!isset($triggers[$tableName][$eventName][$whenName])) {
139 // We're leaving out cursors, conditions, and handlers for now
140 // they are kind of dangerous in this context anyway
141 // better off putting them in stored procedures
142 $triggers[$tableName][$eventName][$whenName] = [
149 $triggers[$tableName][$eventName][$whenName]['variables'][] = $variables;
152 $triggers[$tableName][$eventName][$whenName]['sql'][] = $sql;
157 // now spit out the sql
158 foreach ($triggers as $tableName => $tables) {
159 if ($onlyTableName != NULL && $onlyTableName != $tableName) {
162 foreach ($tables as $eventName => $events) {
163 foreach ($events as $whenName => $parts) {
164 $varString = implode("\n", $parts['variables']);
165 $sqlString = implode("\n", $parts['sql']);
166 $validName = \CRM_Core_DAO
::shortenSQLName($tableName, 48, TRUE);
167 $triggerName = "{$validName}_{$whenName}_{$eventName}";
168 $triggerSQL = "CREATE TRIGGER $triggerName $whenName $eventName ON $tableName FOR EACH ROW BEGIN $varString $sqlString END";
170 $this->enqueueQuery("DROP TRIGGER IF EXISTS $triggerName");
171 $this->enqueueQuery($triggerSQL);
178 * Wrapper function to drop triggers.
180 * @param string $tableName
181 * the specific table requiring a rebuild; or NULL to rebuild all tables.
183 public function dropTriggers($tableName = NULL) {
186 $logging = new \
CRM_Logging_Schema();
187 $logging->triggerInfo($info, $tableName);
189 // drop all existing triggers on all tables
190 $logging->dropTriggers($tableName);
194 * Enqueue a query which alters triggers.
196 * As this requires a high permission level we funnel the queries through here to
197 * facilitate them being taken 'offline'.
199 * @param string $triggerSQL
200 * The sql to run to create or drop the triggers.
201 * @param array $params
202 * Optional parameters to interpolate into the string.
204 public function enqueueQuery($triggerSQL, $params = []) {
205 if (\Civi
::settings()->get('logging_no_trigger_permission')) {
207 if (!file_exists($this->getFile())) {
208 // Ugh. Need to let user know somehow. This is the first change.
209 \CRM_Core_Session
::setStatus(ts('The mysql commands you need to run are stored in %1', [
210 1 => $this->getFile(),
219 $buf .= "DELIMITER //\n";
220 $buf .= \CRM_Core_DAO
::composeQuery($triggerSQL, $params) . " //\n";
221 $buf .= "DELIMITER ;\n";
222 file_put_contents($this->getFile(), $buf, FILE_APPEND
);
225 \CRM_Core_DAO
::executeQuery($triggerSQL, $params, TRUE, NULL, FALSE, FALSE);
230 * @return NULL|string
232 public function getFile() {
233 if ($this->file
=== NULL) {
234 $prefix = 'trigger' . \CRM_Utils_Request
::id();
235 $config = \CRM_Core_Config
::singleton();
236 $this->file
= "{$config->configAndLogDir}CiviCRM." . $prefix . md5($config->dsn
) . '.sql';