374 lines
12 KiB
Plaintext
374 lines
12 KiB
Plaintext
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Provide UI for controlling the mail_system variable.
|
|
*/
|
|
|
|
/**
|
|
* Implements hook_init().
|
|
*
|
|
* Caches the list of MailSystemInterface classes, and removes classes
|
|
* from the mail_system variable which are no longer available.
|
|
*
|
|
* @see mailsystem_get_classes()
|
|
*/
|
|
function mailsystem_init() {
|
|
mailsystem_get_classes();
|
|
// @todo Remove this when issue #299138 gets resolved.
|
|
if (!function_exists('mailsystem_html_to_text')) {
|
|
module_load_include('inc', 'mailsystem', 'html_to_text');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_permission().
|
|
*
|
|
* Defines a permission for managing the mail_system variable.
|
|
*/
|
|
function mailsystem_permission() {
|
|
return array(
|
|
'administer mailsystem' => array(
|
|
'title' => t('Administer Mail System'),
|
|
'description' => t(
|
|
'Select the default, per-module, and per-mailing <a href="!interface"><code>@interface</code></a> to use for formatting and sending email messages.',
|
|
array(
|
|
'!interface' => url('http://api.drupal.org/api/drupal/includes--mail.inc/interface/MailSystemInterface/7'),
|
|
'@interface' => 'MailSystemInterface',
|
|
)
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_menu().
|
|
*/
|
|
function mailsystem_menu() {
|
|
$items['admin/config/system/mailsystem'] = array(
|
|
'title' => 'Mail System',
|
|
'description' => 'Configure per-module Mail System settings.',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('mailsystem_admin_settings'),
|
|
'access arguments' => array('administer mailsystem'),
|
|
'file' => 'mailsystem.admin.inc',
|
|
);
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Returns the id for the default mail_system setting.
|
|
*/
|
|
function mailsystem_default_id() {
|
|
// @todo: Is there a way to get this from core?
|
|
return 'default-system';
|
|
}
|
|
|
|
/**
|
|
* Returns the value for the default mail_system setting.
|
|
*/
|
|
function mailsystem_default_value() {
|
|
// @todo: Is there a way to get this from core?
|
|
return 'DefaultMailSystem';
|
|
}
|
|
|
|
/**
|
|
* Returns the default settings for the mail_system variable.
|
|
*/
|
|
function mailsystem_defaults() {
|
|
return array(mailsystem_default_id() => mailsystem_default_value());
|
|
}
|
|
|
|
/**
|
|
* Returns the current mail_system settings.
|
|
*
|
|
* @return The contents of the mail_system variable merged with its defaults.
|
|
*/
|
|
function mailsystem_get() {
|
|
return array_merge(
|
|
mailsystem_defaults(),
|
|
variable_get('mail_system', mailsystem_defaults())
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns the default list of MailSystemInterface methods.
|
|
*
|
|
* @return
|
|
* An array whose keys are the names of the methods defined by
|
|
* MailSystemInterface and whose values are the default class used to
|
|
* provide that method.
|
|
*/
|
|
function mailsystem_default_methods() {
|
|
$mail_system = mailsystem_get();
|
|
$default_class = $mail_system[mailsystem_default_id()];
|
|
$methods = get_class_methods('MailSystemInterface');
|
|
return array_combine(
|
|
$methods,
|
|
array_fill(0, count($methods), $default_class)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Creates and registers a new MailSystemInterface class.
|
|
*
|
|
* The newly-created class gets its name and each of its class methods from the
|
|
* other classes specified by the $class parameter.
|
|
*
|
|
* @param $class An associative array of ($method_name => $class_name) tuples,
|
|
* where each $method_name is the name of a class method to be created, and
|
|
* each $class_name is the name of a class to use for that method.
|
|
*
|
|
* @return
|
|
* The name of the newly-created class if successful; otherwise FALSE.
|
|
*/
|
|
function mailsystem_create_class($classes) {
|
|
// Merge in defaults.
|
|
$classes += mailsystem_default_methods();
|
|
ksort($classes);
|
|
// Do not create a new class whose methods all derive from the same class.
|
|
if (count(array_unique($classes)) === 1) {
|
|
return FALSE;
|
|
}
|
|
$class_name = implode('__', $classes);
|
|
// Ensure that the mailsystem directory exists.
|
|
// First we try the private filesystem.
|
|
$private_files = variable_get('file_private_path', '');
|
|
$private_files_full = $private_files . '/mailsystem';
|
|
$public_files = variable_get('file_public_path', conf_path() . '/files');
|
|
$public_files_full = $public_files . '/mailsystem';
|
|
if ($private_files && file_prepare_directory($private_files_full, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
|
$class_dir = $private_files . '/mailsystem';
|
|
}
|
|
// If private filesystem is not defined or writable, we use the public filesystem.
|
|
elseif (file_prepare_directory($public_files_full, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
|
$class_dir = $public_files . '/mailsystem';
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
// Build the class filename.
|
|
$class_file = $class_dir . DIRECTORY_SEPARATOR . "$class_name.mail.inc";
|
|
// Build the class implementation as a string.
|
|
$class_contents = '<?php
|
|
class ' . $class_name . ' implements MailSystemInterface {';
|
|
// Create a protected variable to hold each method class.
|
|
foreach (array_keys($classes) as $method) {
|
|
$class_contents .= '
|
|
protected $' . $method . 'Class;';
|
|
}
|
|
// Create a class construction function to populate the variables.
|
|
$class_contents .= '
|
|
public function __construct() {';
|
|
foreach ($classes as $method => $class) {
|
|
$class_contents .= '
|
|
if (drupal_autoload_class(\'' . $class . '\')) {
|
|
$this->' . $method . 'Class = new ' . $class . ';
|
|
}
|
|
else {
|
|
$this->' . $method . 'Class = new ' . mailsystem_default_value() . ';
|
|
}';
|
|
}
|
|
$class_contents .= '
|
|
}';
|
|
// Create each class method.
|
|
foreach (array_keys($classes) as $method) {
|
|
$class_contents .= '
|
|
public function ' . $method . '(array $message) {
|
|
return $this->' . $method . 'Class->' . $method . '($message);
|
|
}';
|
|
}
|
|
$class_contents .= '
|
|
}
|
|
';
|
|
if (file_unmanaged_save_data($class_contents, $class_file, FILE_EXISTS_REPLACE)) {
|
|
// Remove any conflicting registry entries to avoid a database error.
|
|
$class_condition = db_and()
|
|
->condition('name', $class_name)
|
|
->condition('type', 'class');
|
|
$file_condition = db_and()
|
|
->condition('filename', $class_file);
|
|
db_delete('registry_file')
|
|
->condition($file_condition)
|
|
->execute();
|
|
db_delete('registry')->condition(
|
|
db_or()->condition($class_condition)
|
|
->condition($file_condition)
|
|
)->execute();
|
|
// Make sure that registry functions are available.
|
|
require_once 'includes/registry.inc';
|
|
// Parse the newly-created class file and add it to the registry.
|
|
_registry_parse_file($class_file, $class_contents, 'mailsystem');
|
|
// Clear the mailsystem caches so that it will pick up the new class.
|
|
drupal_static_reset('mailsystem_get_classes');
|
|
cache_clear_all('mailsystem_get_classes', 'cache');
|
|
drupal_set_message(
|
|
t('Class <code>%class</code> written to <code>%file</code>.',
|
|
array('%class' => $class_name, '%file' => $class_file)
|
|
)
|
|
);
|
|
}
|
|
return $class_name;
|
|
}
|
|
|
|
/**
|
|
* Helps other modules safely set their own key within mail_system. This
|
|
* function should be called from hook_enable() implementations.
|
|
*
|
|
* @param $setting An associative array ($id => $value) where:
|
|
* - $id is the machine-readable module name optionally followed by '_'
|
|
* and a key.
|
|
* - $value is one of
|
|
* - (string) The name of a class that implements MailSystemInterface.
|
|
* - (array) An associative array whose keys are the names of methods
|
|
* defined by MailSystemInterface and whose values are the names of
|
|
* the class to use for that method.
|
|
*
|
|
* @see drupal_mail(), mailsystem_default_methods()
|
|
*/
|
|
function mailsystem_set(array $setting) {
|
|
$mail_system = mailsystem_get();
|
|
foreach ($setting as $key => $class) {
|
|
if (is_array($class)) {
|
|
unset($setting[$key]);
|
|
if ($new_class = mailsystem_create_class($class)) {
|
|
$setting[$key] = $new_class;
|
|
}
|
|
}
|
|
}
|
|
variable_set('mail_system', array_merge(mailsystem_get(), $setting));
|
|
}
|
|
|
|
/**
|
|
* Helps other modules safely remove their settings from mail_system. This
|
|
* function should be called from the other module's hook_disable() function.
|
|
*
|
|
* @param $setting An associative array ($module => $classname) describing
|
|
* a module and associated MailSystemInterface class that are being disabled.
|
|
* - $module is the machine-readable module name.
|
|
* - $classname is a class that implements MailSystemInterface.
|
|
*
|
|
* If $classname is empty, only the $module entry is removed.
|
|
*
|
|
* @param $class
|
|
* The name of the class to be removed, if any.
|
|
*/
|
|
function mailsystem_clear(array $setting) {
|
|
variable_set(
|
|
'mail_system',
|
|
array_merge(
|
|
mailsystem_defaults(),
|
|
array_diff_key(array_diff(mailsystem_get(), $setting), $setting)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of classes which implement MailSystemInterface.
|
|
*/
|
|
function &mailsystem_get_classes() {
|
|
// Load static cache.
|
|
$mailsystem_classes = &drupal_static(__FUNCTION__);
|
|
// Check persistent cache if necessary.
|
|
if (!isset($mailsystem_classes) && $cache = cache_get('mailsystem_get_classes')) {
|
|
$mailsystem_classes = $cache->data;
|
|
}
|
|
// Load from db if no cache was hit.
|
|
if (!isset($mailsystem_classes)) {
|
|
$mailsystem_classes = array();
|
|
// @todo Is there a better way to find all mail-related classes?
|
|
$declared_classes = get_declared_classes();
|
|
$all_classes = array_combine(
|
|
$declared_classes,
|
|
array_fill(0, count($declared_classes), 0)
|
|
);
|
|
$mail_classes = db_select('registry', 'registry')
|
|
->distinct()
|
|
->fields('registry', array('name', 'filename'))
|
|
->where("type=:type AND ( filename like :filename OR name like :name )",
|
|
// Making the HUGE assumption that all classes which implement
|
|
// MailSystemInterface have filenames containing '.mail.' or
|
|
// classnames ending in 'MailSystem'.
|
|
array(
|
|
':type' => 'class',
|
|
':name' => '%MailSystem',
|
|
':filename' => '%.mail.%',
|
|
)
|
|
)
|
|
->execute()
|
|
->fetchAllKeyed();
|
|
foreach ($mail_classes as $classname => $classfile) {
|
|
if (file_exists($classfile)
|
|
&& drupal_autoload_class($classname)
|
|
) {
|
|
$all_classes[$classname] = 1;
|
|
}
|
|
}
|
|
foreach ($all_classes as $classname => $autoload) {
|
|
if (($autoload || preg_match('/MailSystem/', $classname))
|
|
&& ($object = new $classname)
|
|
&& ($object instanceof MailSystemInterface)
|
|
) {
|
|
$mailsystem_classes[$classname] = $classname;
|
|
}
|
|
elseif ($autoload) {
|
|
// Clear classes that are no longer available.
|
|
db_delete('registry')
|
|
->condition('name', $classname)
|
|
->execute();
|
|
}
|
|
}
|
|
foreach (array_unique(mailsystem_get()) as $classname) {
|
|
if (class_exists($classname)) {
|
|
$mailsystem_classes[$classname] = $classname;
|
|
}
|
|
else {
|
|
mailsystem_clear(array(mailsystem_default_id() => $classname));
|
|
}
|
|
}
|
|
ksort($mailsystem_classes);
|
|
// Store in persistent cache.
|
|
cache_set('mailsystem_get_classes', $mailsystem_classes, 'cache', CACHE_TEMPORARY);
|
|
}
|
|
return $mailsystem_classes;
|
|
}
|
|
|
|
/**
|
|
* Implements hook_theme_registry_alter().
|
|
*/
|
|
function mailsystem_theme_registry_alter(&$theme_registry) {
|
|
module_load_include('inc', 'mailsystem', 'mailsystem.theme');
|
|
return mailsystem_theme_theme_registry_alter($theme_registry);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the key of the theme used to render the emails.
|
|
*
|
|
* @todo Add some kind of hook to let other modules alter this behavior.
|
|
*/
|
|
function mailsystem_get_mail_theme() {
|
|
global $theme_key;
|
|
$theme = variable_get('mailsystem_theme', 'current');
|
|
switch ($theme) {
|
|
case 'default':
|
|
$theme = variable_get('theme_default', NULL);
|
|
break;
|
|
|
|
case 'current':
|
|
$theme = $theme_key;
|
|
break;
|
|
|
|
case 'domain':
|
|
// Fetch the theme for the current domain.
|
|
if (module_exists('domain_theme')) {
|
|
// Assign the selected theme, based on the active domain.
|
|
global $_domain;
|
|
$domain_theme = domain_theme_lookup($_domain['domain_id']);
|
|
// The above returns -1 on failure.
|
|
$theme = ($domain_theme != -1) ? $domain_theme['theme'] : $theme_key;
|
|
}
|
|
break;
|
|
}
|
|
return $theme;
|
|
}
|