[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/ -> messagelib.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Functions for interacting with the message system
  19   *
  20   * @package   core_message
  21   * @copyright 2008 Luis Rodrigues and Martin Dougiamas
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  require_once (__DIR__ . '/../message/lib.php');
  28  
  29  /**
  30   * Called when a message provider wants to send a message.
  31   * This functions checks the message recipient's message processor configuration then
  32   * sends the message to the configured processors
  33   *
  34   * Required parameters of the $eventdata object:
  35   *  component string component name. must exist in message_providers
  36   *  name string message type name. must exist in message_providers
  37   *  userfrom object|int the user sending the message
  38   *  userto object|int the message recipient
  39   *  subject string the message subject
  40   *  fullmessage string the full message in a given format
  41   *  fullmessageformat int the format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..)
  42   *  fullmessagehtml string the full version (the message processor will choose with one to use)
  43   *  smallmessage string the small version of the message
  44   *
  45   * Optional parameters of the $eventdata object:
  46   *  notification bool should the message be considered as a notification rather than a personal message
  47   *  contexturl string if this is a notification then you can specify a url to view the event. For example the forum post the user is being notified of.
  48   *  contexturlname string the display text for contexturl
  49   *
  50   * Note: processor failure is is not reported as false return value,
  51   *       earlier versions did not do it consistently either.
  52   *
  53   * @category message
  54   * @param stdClass|\core\message\message $eventdata information about the message (component, userfrom, userto, ...)
  55   * @return mixed the integer ID of the new message or false if there was a problem with submitted data
  56   */
  57  function message_send($eventdata) {
  58      global $CFG, $DB;
  59  
  60      //new message ID to return
  61      $messageid = false;
  62  
  63      // Fetch default (site) preferences
  64      $defaultpreferences = get_message_output_default_preferences();
  65      $preferencebase = $eventdata->component.'_'.$eventdata->name;
  66      // If message provider is disabled then don't do any processing.
  67      if (!empty($defaultpreferences->{$preferencebase.'_disable'})) {
  68          return $messageid;
  69      }
  70  
  71      // By default a message is a notification. Only personal/private messages aren't notifications.
  72      if (!isset($eventdata->notification)) {
  73          $eventdata->notification = 1;
  74      }
  75  
  76      if (!is_object($eventdata->userto)) {
  77          $eventdata->userto = core_user::get_user($eventdata->userto);
  78      }
  79      if (!is_object($eventdata->userfrom)) {
  80          $eventdata->userfrom = core_user::get_user($eventdata->userfrom);
  81      }
  82      if (!$eventdata->userto) {
  83          debugging('Attempt to send msg to unknown user', DEBUG_NORMAL);
  84          return false;
  85      }
  86      if (!$eventdata->userfrom) {
  87          debugging('Attempt to send msg from unknown user', DEBUG_NORMAL);
  88          return false;
  89      }
  90  
  91      // Verify all necessary data fields are present.
  92      if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended)
  93              or !isset($eventdata->userto->deleted) or !isset($eventdata->userto->emailstop)) {
  94  
  95          debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER);
  96          $eventdata->userto = core_user::get_user($eventdata->userto->id);
  97      }
  98  
  99      $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false);
 100      // If recipient is internal user (noreply user), and emailstop is set then don't send any msg.
 101      if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) {
 102          debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL);
 103          return false;
 104      }
 105  
 106      //after how long inactive should the user be considered logged off?
 107      if (isset($CFG->block_online_users_timetosee)) {
 108          $timetoshowusers = $CFG->block_online_users_timetosee * 60;
 109      } else {
 110          $timetoshowusers = 300;//5 minutes
 111      }
 112  
 113      // Work out if the user is logged in or not
 114      if (!empty($eventdata->userto->lastaccess) && (time()-$timetoshowusers) < $eventdata->userto->lastaccess) {
 115          $userstate = 'loggedin';
 116      } else {
 117          $userstate = 'loggedoff';
 118      }
 119  
 120      // Create the message object
 121      $savemessage = new stdClass();
 122      $savemessage->useridfrom        = $eventdata->userfrom->id;
 123      $savemessage->useridto          = $eventdata->userto->id;
 124      $savemessage->subject           = $eventdata->subject;
 125      $savemessage->fullmessage       = $eventdata->fullmessage;
 126      $savemessage->fullmessageformat = $eventdata->fullmessageformat;
 127      $savemessage->fullmessagehtml   = $eventdata->fullmessagehtml;
 128      $savemessage->smallmessage      = $eventdata->smallmessage;
 129      $savemessage->notification      = $eventdata->notification;
 130  
 131      if (!empty($eventdata->contexturl)) {
 132          $savemessage->contexturl = (string)$eventdata->contexturl;
 133      } else {
 134          $savemessage->contexturl = null;
 135      }
 136  
 137      if (!empty($eventdata->contexturlname)) {
 138          $savemessage->contexturlname = (string)$eventdata->contexturlname;
 139      } else {
 140          $savemessage->contexturlname = null;
 141      }
 142  
 143      $savemessage->timecreated = time();
 144  
 145      if (PHPUNIT_TEST and class_exists('phpunit_util')) {
 146          // Add some more tests to make sure the normal code can actually work.
 147          $componentdir = core_component::get_component_directory($eventdata->component);
 148          if (!$componentdir or !is_dir($componentdir)) {
 149              throw new coding_exception('Invalid component specified in message-send(): '.$eventdata->component);
 150          }
 151          if (!file_exists("$componentdir/db/messages.php")) {
 152              throw new coding_exception("$eventdata->component does not contain db/messages.php necessary for message_send()");
 153          }
 154          $messageproviders = null;
 155          include("$componentdir/db/messages.php");
 156          if (!isset($messageproviders[$eventdata->name])) {
 157              throw new coding_exception("Missing messaging defaults for event '$eventdata->name' in '$eventdata->component' messages.php file");
 158          }
 159          unset($componentdir);
 160          unset($messageproviders);
 161          // Now ask phpunit if it wants to catch this message.
 162          if (phpunit_util::is_redirecting_messages()) {
 163              $savemessage->timeread = time();
 164              $messageid = $DB->insert_record('message_read', $savemessage);
 165              $message = $DB->get_record('message_read', array('id'=>$messageid));
 166              phpunit_util::message_sent($message);
 167              return $messageid;
 168          }
 169      }
 170  
 171      // Fetch enabled processors
 172      $processors = get_message_processors(true);
 173  
 174      // Preset variables
 175      $processorlist = array();
 176      // Fill in the array of processors to be used based on default and user preferences
 177      foreach ($processors as $processor) {
 178          // Skip adding processors for internal user, if processor doesn't support sending message to internal user.
 179          if (!$usertoisrealuser && !$processor->object->can_send_to_any_users()) {
 180              continue;
 181          }
 182  
 183          // First find out permissions
 184          $defaultpreference = $processor->name.'_provider_'.$preferencebase.'_permitted';
 185          if (isset($defaultpreferences->{$defaultpreference})) {
 186              $permitted = $defaultpreferences->{$defaultpreference};
 187          } else {
 188              // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
 189              // exist in the message_provider table (thus there is no default settings for them).
 190              $preferrormsg = "Could not load preference $defaultpreference. Make sure the component and name you supplied
 191                      to message_send() are valid.";
 192              throw new coding_exception($preferrormsg);
 193          }
 194  
 195          // Find out if user has configured this output
 196          // Some processors cannot function without settings from the user
 197          $userisconfigured = $processor->object->is_user_configured($eventdata->userto);
 198  
 199          // DEBUG: notify if we are forcing unconfigured output
 200          if ($permitted == 'forced' && !$userisconfigured) {
 201              debugging('Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL);
 202          }
 203  
 204          // Populate the list of processors we will be using
 205          if ($permitted == 'forced' && $userisconfigured) {
 206              // An admin is forcing users to use this message processor. Use this processor unconditionally.
 207              $processorlist[] = $processor->name;
 208          } else if ($permitted == 'permitted' && $userisconfigured && !$eventdata->userto->emailstop) {
 209              // User has not disabled notifications
 210              // See if user set any notification preferences, otherwise use site default ones
 211              $userpreferencename = 'message_provider_'.$preferencebase.'_'.$userstate;
 212              if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto)) {
 213                  if (in_array($processor->name, explode(',', $userpreference))) {
 214                      $processorlist[] = $processor->name;
 215                  }
 216              } else if (isset($defaultpreferences->{$userpreferencename})) {
 217                  if (in_array($processor->name, explode(',', $defaultpreferences->{$userpreferencename}))) {
 218                      $processorlist[] = $processor->name;
 219                  }
 220              }
 221          }
 222      }
 223  
 224      // Store unread message just in case we get a fatal error any time later.
 225      $savemessage->id = $DB->insert_record('message', $savemessage);
 226      $eventdata->savedmessageid = $savemessage->id;
 227  
 228      // Let the manager do the sending or buffering when db transaction in progress.
 229      return \core\message\manager::send_message($eventdata, $savemessage, $processorlist);
 230  }
 231  
 232  
 233  /**
 234   * Updates the message_providers table with the current set of message providers
 235   *
 236   * @param string $component For example 'moodle', 'mod_forum' or 'block_quiz_results'
 237   * @return boolean True on success
 238   */
 239  function message_update_providers($component='moodle') {
 240      global $DB;
 241  
 242      // load message providers from files
 243      $fileproviders = message_get_providers_from_file($component);
 244  
 245      // load message providers from the database
 246      $dbproviders = message_get_providers_from_db($component);
 247  
 248      foreach ($fileproviders as $messagename => $fileprovider) {
 249  
 250          if (!empty($dbproviders[$messagename])) {   // Already exists in the database
 251              // check if capability has changed
 252              if ($dbproviders[$messagename]->capability == $fileprovider['capability']) {  // Same, so ignore
 253                  // exact same message provider already present in db, ignore this entry
 254                  unset($dbproviders[$messagename]);
 255                  continue;
 256  
 257              } else {                                // Update existing one
 258                  $provider = new stdClass();
 259                  $provider->id         = $dbproviders[$messagename]->id;
 260                  $provider->capability = $fileprovider['capability'];
 261                  $DB->update_record('message_providers', $provider);
 262                  unset($dbproviders[$messagename]);
 263                  continue;
 264              }
 265  
 266          } else {             // New message provider, add it
 267  
 268              $provider = new stdClass();
 269              $provider->name       = $messagename;
 270              $provider->component  = $component;
 271              $provider->capability = $fileprovider['capability'];
 272  
 273              $transaction = $DB->start_delegated_transaction();
 274              $DB->insert_record('message_providers', $provider);
 275              message_set_default_message_preference($component, $messagename, $fileprovider);
 276              $transaction->allow_commit();
 277          }
 278      }
 279  
 280      foreach ($dbproviders as $dbprovider) {  // Delete old ones
 281          $DB->delete_records('message_providers', array('id' => $dbprovider->id));
 282          $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("%_provider_{$component}_{$dbprovider->name}_%"));
 283          $DB->delete_records_select('user_preferences', $DB->sql_like('name', '?', false), array("message_provider_{$component}_{$dbprovider->name}_%"));
 284          cache_helper::invalidate_by_definition('core', 'config', array(), 'message');
 285      }
 286  
 287      return true;
 288  }
 289  
 290  /**
 291   * This function populates default message preferences for all existing providers
 292   * when the new message processor is added.
 293   *
 294   * @param string $processorname The name of message processor plugin (e.g. 'email', 'jabber')
 295   * @throws invalid_parameter_exception if $processorname does not exist in the database
 296   */
 297  function message_update_processors($processorname) {
 298      global $DB;
 299  
 300      // validate if our processor exists
 301      $processor = $DB->get_records('message_processors', array('name' => $processorname));
 302      if (empty($processor)) {
 303          throw new invalid_parameter_exception();
 304      }
 305  
 306      $providers = $DB->get_records_sql('SELECT DISTINCT component FROM {message_providers}');
 307  
 308      $transaction = $DB->start_delegated_transaction();
 309      foreach ($providers as $provider) {
 310          // load message providers from files
 311          $fileproviders = message_get_providers_from_file($provider->component);
 312          foreach ($fileproviders as $messagename => $fileprovider) {
 313              message_set_default_message_preference($provider->component, $messagename, $fileprovider, $processorname);
 314          }
 315      }
 316      $transaction->allow_commit();
 317  }
 318  
 319  /**
 320   * Setting default messaging preferences for particular message provider
 321   *
 322   * @param  string $component   The name of component (e.g. moodle, mod_forum, etc.)
 323   * @param  string $messagename The name of message provider
 324   * @param  array  $fileprovider The value of $messagename key in the array defined in plugin messages.php
 325   * @param  string $processorname The optional name of message processor
 326   */
 327  function message_set_default_message_preference($component, $messagename, $fileprovider, $processorname='') {
 328      global $DB;
 329  
 330      // Fetch message processors
 331      $condition = null;
 332      // If we need to process a particular processor, set the select condition
 333      if (!empty($processorname)) {
 334         $condition = array('name' => $processorname);
 335      }
 336      $processors = $DB->get_records('message_processors', $condition);
 337  
 338      // load default messaging preferences
 339      $defaultpreferences = get_message_output_default_preferences();
 340  
 341      // Setting default preference
 342      $componentproviderbase = $component.'_'.$messagename;
 343      $loggedinpref = array();
 344      $loggedoffpref = array();
 345      // set 'permitted' preference first for each messaging processor
 346      foreach ($processors as $processor) {
 347          $preferencename = $processor->name.'_provider_'.$componentproviderbase.'_permitted';
 348          // if we do not have this setting yet, set it
 349          if (!isset($defaultpreferences->{$preferencename})) {
 350              // determine plugin default settings
 351              $plugindefault = 0;
 352              if (isset($fileprovider['defaults'][$processor->name])) {
 353                  $plugindefault = $fileprovider['defaults'][$processor->name];
 354              }
 355              // get string values of the settings
 356              list($permitted, $loggedin, $loggedoff) = translate_message_default_setting($plugindefault, $processor->name);
 357              // store default preferences for current processor
 358              set_config($preferencename, $permitted, 'message');
 359              // save loggedin/loggedoff settings
 360              if ($loggedin) {
 361                  $loggedinpref[] = $processor->name;
 362              }
 363              if ($loggedoff) {
 364                  $loggedoffpref[] = $processor->name;
 365              }
 366          }
 367      }
 368      // now set loggedin/loggedoff preferences
 369      if (!empty($loggedinpref)) {
 370          $preferencename = 'message_provider_'.$componentproviderbase.'_loggedin';
 371          if (isset($defaultpreferences->{$preferencename})) {
 372              // We have the default preferences for this message provider, which
 373              // likely means that we have been adding a new processor. Add defaults
 374              // to exisitng preferences.
 375              $loggedinpref = array_merge($loggedinpref, explode(',', $defaultpreferences->{$preferencename}));
 376          }
 377          set_config($preferencename, join(',', $loggedinpref), 'message');
 378      }
 379      if (!empty($loggedoffpref)) {
 380          $preferencename = 'message_provider_'.$componentproviderbase.'_loggedoff';
 381          if (isset($defaultpreferences->{$preferencename})) {
 382              // We have the default preferences for this message provider, which
 383              // likely means that we have been adding a new processor. Add defaults
 384              // to exisitng preferences.
 385              $loggedoffpref = array_merge($loggedoffpref, explode(',', $defaultpreferences->{$preferencename}));
 386          }
 387          set_config($preferencename, join(',', $loggedoffpref), 'message');
 388      }
 389  }
 390  
 391  /**
 392   * Returns the active providers for the user specified, based on capability
 393   *
 394   * @param int $userid id of user
 395   * @return array An array of message providers
 396   */
 397  function message_get_providers_for_user($userid) {
 398      global $DB, $CFG;
 399  
 400      $providers = get_message_providers();
 401  
 402      // Ensure user is not allowed to configure instantmessage if it is globally disabled.
 403      if (!$CFG->messaging) {
 404          foreach ($providers as $providerid => $provider) {
 405              if ($provider->name == 'instantmessage') {
 406                  unset($providers[$providerid]);
 407                  break;
 408              }
 409          }
 410      }
 411  
 412      // If the component is an enrolment plugin, check it is enabled
 413      foreach ($providers as $providerid => $provider) {
 414          list($type, $name) = core_component::normalize_component($provider->component);
 415          if ($type == 'enrol' && !enrol_is_enabled($name)) {
 416              unset($providers[$providerid]);
 417          }
 418      }
 419  
 420      // Now we need to check capabilities. We need to eliminate the providers
 421      // where the user does not have the corresponding capability anywhere.
 422      // Here we deal with the common simple case of the user having the
 423      // capability in the system context. That handles $CFG->defaultuserroleid.
 424      // For the remaining providers/capabilities, we need to do a more complex
 425      // query involving all overrides everywhere.
 426      $unsureproviders = array();
 427      $unsurecapabilities = array();
 428      $systemcontext = context_system::instance();
 429      foreach ($providers as $providerid => $provider) {
 430          if (empty($provider->capability) || has_capability($provider->capability, $systemcontext, $userid)) {
 431              // The provider is relevant to this user.
 432              continue;
 433          }
 434  
 435          $unsureproviders[$providerid] = $provider;
 436          $unsurecapabilities[$provider->capability] = 1;
 437          unset($providers[$providerid]);
 438      }
 439  
 440      if (empty($unsureproviders)) {
 441          // More complex checks are not required.
 442          return $providers;
 443      }
 444  
 445      // Now check the unsure capabilities.
 446      list($capcondition, $params) = $DB->get_in_or_equal(
 447              array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
 448      $params['userid'] = $userid;
 449  
 450      $sql = "SELECT DISTINCT rc.capability, 1
 451  
 452                FROM {role_assignments} ra
 453                JOIN {context} actx ON actx.id = ra.contextid
 454                JOIN {role_capabilities} rc ON rc.roleid = ra.roleid
 455                JOIN {context} cctx ON cctx.id = rc.contextid
 456  
 457               WHERE ra.userid = :userid
 458                 AND rc.capability $capcondition
 459                 AND rc.permission > 0
 460                 AND (".$DB->sql_concat('actx.path', "'/'")." LIKE ".$DB->sql_concat('cctx.path', "'/%'").
 461                 " OR ".$DB->sql_concat('cctx.path', "'/'")." LIKE ".$DB->sql_concat('actx.path', "'/%'").")";
 462  
 463      if (!empty($CFG->defaultfrontpageroleid)) {
 464          $frontpagecontext = context_course::instance(SITEID);
 465  
 466          list($capcondition2, $params2) = $DB->get_in_or_equal(
 467                  array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
 468          $params = array_merge($params, $params2);
 469          $params['frontpageroleid'] = $CFG->defaultfrontpageroleid;
 470          $params['frontpagepathpattern'] = $frontpagecontext->path . '/';
 471  
 472          $sql .= "
 473               UNION
 474  
 475              SELECT DISTINCT rc.capability, 1
 476  
 477                FROM {role_capabilities} rc
 478                JOIN {context} cctx ON cctx.id = rc.contextid
 479  
 480               WHERE rc.roleid = :frontpageroleid
 481                 AND rc.capability $capcondition2
 482                 AND rc.permission > 0
 483                 AND ".$DB->sql_concat('cctx.path', "'/'")." LIKE :frontpagepathpattern";
 484      }
 485  
 486      $relevantcapabilities = $DB->get_records_sql_menu($sql, $params);
 487  
 488      // Add back any providers based on the detailed capability check.
 489      foreach ($unsureproviders as $providerid => $provider) {
 490          if (array_key_exists($provider->capability, $relevantcapabilities)) {
 491              $providers[$providerid] = $provider;
 492          }
 493      }
 494  
 495      return $providers;
 496  }
 497  
 498  /**
 499   * Gets the message providers that are in the database for this component.
 500   *
 501   * This is an internal function used within messagelib.php
 502   *
 503   * @see message_update_providers()
 504   * @param string $component A moodle component like 'moodle', 'mod_forum', 'block_quiz_results'
 505   * @return array An array of message providers
 506   */
 507  function message_get_providers_from_db($component) {
 508      global $DB;
 509  
 510      return $DB->get_records('message_providers', array('component'=>$component), '', 'name, id, component, capability');  // Name is unique per component
 511  }
 512  
 513  /**
 514   * Loads the messages definitions for a component from file
 515   *
 516   * If no messages are defined for the component, return an empty array.
 517   * This is an internal function used within messagelib.php
 518   *
 519   * @see message_update_providers()
 520   * @see message_update_processors()
 521   * @param string $component A moodle component like 'moodle', 'mod_forum', 'block_quiz_results'
 522   * @return array An array of message providers or empty array if not exists
 523   */
 524  function message_get_providers_from_file($component) {
 525      $defpath = core_component::get_component_directory($component).'/db/messages.php';
 526  
 527      $messageproviders = array();
 528  
 529      if (file_exists($defpath)) {
 530          require($defpath);
 531      }
 532  
 533      foreach ($messageproviders as $name => $messageprovider) {   // Fix up missing values if required
 534          if (empty($messageprovider['capability'])) {
 535              $messageproviders[$name]['capability'] = NULL;
 536          }
 537          if (empty($messageprovider['defaults'])) {
 538              $messageproviders[$name]['defaults'] = array();
 539          }
 540      }
 541  
 542      return $messageproviders;
 543  }
 544  
 545  /**
 546   * Remove all message providers for particular component and corresponding settings
 547   *
 548   * @param string $component A moodle component like 'moodle', 'mod_forum', 'block_quiz_results'
 549   * @return void
 550   */
 551  function message_provider_uninstall($component) {
 552      global $DB;
 553  
 554      $transaction = $DB->start_delegated_transaction();
 555      $DB->delete_records('message_providers', array('component' => $component));
 556      $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("%_provider_{$component}_%"));
 557      $DB->delete_records_select('user_preferences', $DB->sql_like('name', '?', false), array("message_provider_{$component}_%"));
 558      $transaction->allow_commit();
 559      // Purge all messaging settings from the caches. They are stored by plugin so we have to clear all message settings.
 560      cache_helper::invalidate_by_definition('core', 'config', array(), 'message');
 561  }
 562  
 563  /**
 564   * Uninstall a message processor
 565   *
 566   * @param string $name A message processor name like 'email', 'jabber'
 567   */
 568  function message_processor_uninstall($name) {
 569      global $DB;
 570  
 571      $transaction = $DB->start_delegated_transaction();
 572      $DB->delete_records('message_processors', array('name' => $name));
 573      $DB->delete_records_select('config_plugins', "plugin = ?", array("message_{$name}"));
 574      // delete permission preferences only, we do not care about loggedin/loggedoff
 575      // defaults, they will be removed on the next attempt to update the preferences
 576      $DB->delete_records_select('config_plugins', "plugin = 'message' AND ".$DB->sql_like('name', '?', false), array("{$name}_provider_%"));
 577      $transaction->allow_commit();
 578      // Purge all messaging settings from the caches. They are stored by plugin so we have to clear all message settings.
 579      cache_helper::invalidate_by_definition('core', 'config', array(), array('message', "message_{$name}"));
 580  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1