1025 lines
26 KiB
PHP
1025 lines
26 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Contains SimplenewsSource interface and implementations.
|
|
*/
|
|
|
|
/**
|
|
* The source used to build a newsletter mail.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
interface SimplenewsSourceInterface {
|
|
|
|
/**
|
|
* Returns the mail headers.
|
|
*
|
|
* @param $headers
|
|
* The default mail headers.
|
|
*
|
|
* @return
|
|
* Mail headers as an array.
|
|
*/
|
|
function getHeaders(array $headers);
|
|
|
|
/**
|
|
* Returns the mail subject.
|
|
*/
|
|
function getSubject();
|
|
|
|
/**
|
|
* Returns the mail body.
|
|
*
|
|
* The body should either be plaintext or html, depending on the format.
|
|
*/
|
|
function getBody();
|
|
|
|
/**
|
|
* Returns the plaintext body.
|
|
*/
|
|
function getPlainBody();
|
|
|
|
/**
|
|
* Returns the mail footer.
|
|
*
|
|
* The footer should either be plaintext or html, depending on the format.
|
|
*/
|
|
function getFooter();
|
|
|
|
/**
|
|
* Returns the plain footer.
|
|
*/
|
|
function getPlainFooter();
|
|
|
|
/**
|
|
* Returns the mail format.
|
|
*
|
|
* @return
|
|
* The mail format as string, either 'plain' or 'html'.
|
|
*/
|
|
function getFormat();
|
|
|
|
/**
|
|
* Returns the recipent of this newsletter mail.
|
|
*
|
|
* @return
|
|
* The recipient mail address(es) of this newsletter as a string.
|
|
*/
|
|
function getRecipient();
|
|
|
|
/**
|
|
* The language that should be used for this newsletter mail.
|
|
*/
|
|
function getLanguage();
|
|
|
|
/**
|
|
* Returns an array of attachments for this newsletter mail.
|
|
*
|
|
* @return
|
|
* An array of managed file objects with properties uri, filemime and so on.
|
|
*/
|
|
function getAttachments();
|
|
|
|
/**
|
|
* Returns the token context to be used with token replacements.
|
|
*
|
|
* @return
|
|
* An array of objects as required by token_replace().
|
|
*/
|
|
function getTokenContext();
|
|
|
|
/**
|
|
* Returns the mail key to be used for drupal_mail().
|
|
*
|
|
* @return
|
|
* The mail key, either test or node.
|
|
*/
|
|
function getKey();
|
|
|
|
/**
|
|
* Returns the formatted from mail address.
|
|
*/
|
|
function getFromFormatted();
|
|
|
|
/**
|
|
* Returns the plain mail address.
|
|
*/
|
|
function getFromAddress();
|
|
}
|
|
|
|
/**
|
|
* Source interface based on a node.
|
|
*
|
|
* This is the interface that needs to be implemented to be compatible with
|
|
* the default simplenews spool implementation and therefore exposed in
|
|
* hook_simplenews_source_cache_info().
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
interface SimplenewsSourceNodeInterface extends SimplenewsSourceInterface {
|
|
|
|
/**
|
|
* Create a source based on a node and subscriber.
|
|
*/
|
|
function __construct($node, $subscriber);
|
|
|
|
/**
|
|
* Returns the actually used node of this source.
|
|
*/
|
|
function getNode();
|
|
|
|
/**
|
|
* Returns the subscriber object.
|
|
*/
|
|
function getSubscriber();
|
|
}
|
|
|
|
/**
|
|
* Interface for a simplenews source cache implementation.
|
|
*
|
|
* This is only compatible with the SimplenewsSourceNodeInterface interface.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
interface SimplenewsSourceCacheInterface {
|
|
|
|
/**
|
|
* Create a new instance, allows to initialize based on the used
|
|
* source.
|
|
*/
|
|
function __construct(SimplenewsSourceNodeInterface $source);
|
|
|
|
/**
|
|
* Return a cached element, if existing.
|
|
*
|
|
* Although group and key can be used to identify the requested cache, the
|
|
* implementations are responsible to create a unique cache key themself using
|
|
* the $source. For example based on the node id and the language.
|
|
*
|
|
* @param $group
|
|
* Group of the cache key, which allows cache implementations to decide what
|
|
* they want to cache. Currently used groups:
|
|
* - data: Raw data, e.g. attachments.
|
|
* - build: Built and themed content, before personalizations like tokens.
|
|
* - final: The final returned data. Caching this means that newsletter
|
|
* can not be personalized anymore.
|
|
* @param $key
|
|
* Identifies the requested element, e.g. body, footer or attachments.
|
|
*/
|
|
function get($group, $key);
|
|
|
|
/**
|
|
* Write an element to the cache.
|
|
*
|
|
* Although group and key can be used to identify the requested cache, the
|
|
* implementations are responsible to create a unique cache key themself using
|
|
* the $source. For example based on the node id and the language.
|
|
*
|
|
* @param $group
|
|
* Group of the cache key, which allows cache implementations to decide what
|
|
* they want to cache. Currently used groups:
|
|
* - data: Raw data, e.g. attachments.
|
|
* - build: Built and themed content, before personalizations like tokens.
|
|
* - final: The final returned data. Caching this means that newsletter
|
|
* can not be personalized anymore.
|
|
* @param $key
|
|
* Identifies the requested element, e.g. body, footer or attachments.
|
|
* @param $data
|
|
* The data to be saved in the cache.
|
|
*/
|
|
function set($group, $key, $data);
|
|
}
|
|
|
|
/**
|
|
* A Simplenews spool implementation is a factory for Simplenews sources.
|
|
*
|
|
* Their main functionility is to return a number of sources based on the passed
|
|
* in array of mail spool rows. Additionally, it needs to return the processed
|
|
* mail rows after a source was sent.
|
|
*
|
|
* @todo: Move spool functions into this interface.
|
|
*
|
|
* @ingroup spool
|
|
*/
|
|
interface SimplenewsSpoolInterface {
|
|
|
|
/**
|
|
* Initalizes the spool implementation.
|
|
*
|
|
* @param $spool_list
|
|
* An array of rows from the {simplenews_mail_spool} table.
|
|
*/
|
|
function __construct($pool_list);
|
|
|
|
/**
|
|
* Returns a Simplenews source to be sent.
|
|
*
|
|
* A single source may represent any number of mail spool rows, e.g. by
|
|
* addressing them as BCC.
|
|
*/
|
|
function nextSource();
|
|
|
|
/**
|
|
* Returns the processed mail spool rows, keyed by the msid.
|
|
*
|
|
* Only rows that were processed while preparing the previously returned
|
|
* source must be returned.
|
|
*
|
|
* @return
|
|
* An array of mail spool rows, keyed by the msid. Can optionally have set
|
|
* the following additional properties.
|
|
* - actual_nid: In case of content translation, the source node that was
|
|
* used for this mail.
|
|
* - error: FALSE if the prepration for this row failed. For example set
|
|
* when the corresponding node failed to load.
|
|
* - status: A simplenews spool status to indicate the status.
|
|
*/
|
|
function getProcessed();
|
|
}
|
|
|
|
/**
|
|
* Simplenews Spool implementation.
|
|
*
|
|
* @ingroup spool
|
|
*/
|
|
class SimplenewsSpool implements SimplenewsSpoolInterface {
|
|
|
|
/**
|
|
* Array with mail spool rows being processed.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $spool_list;
|
|
|
|
/**
|
|
* Array of the processed mail spool rows.
|
|
*/
|
|
protected $processed = array();
|
|
|
|
/**
|
|
* Implements SimplenewsSpoolInterface::_construct($spool_list);
|
|
*/
|
|
public function __construct($spool_list) {
|
|
$this->spool_list = $spool_list;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSpoolInterface::nextSource();
|
|
*/
|
|
public function nextSource() {
|
|
// Get the current mail spool row and update the internal pointer to the
|
|
// next row.
|
|
$return = each($this->spool_list);
|
|
// If we're done, return false.
|
|
if (!$return) {
|
|
return FALSE;
|
|
}
|
|
$spool_data = $return['value'];
|
|
|
|
// Store this spool row as processed.
|
|
$this->processed[$spool_data->msid] = $spool_data;
|
|
|
|
$node = node_load($spool_data->nid);
|
|
if (!$node) {
|
|
// If node the load failed, set the processed status done and proceed with
|
|
// the next mail.
|
|
$this->processed[$spool_data->msid]->result = array(
|
|
'status' => SIMPLENEWS_SPOOL_DONE,
|
|
'error' => TRUE
|
|
);
|
|
return $this->prepareMail();
|
|
}
|
|
|
|
if ($spool_data->data) {
|
|
$subscriber = $spool_data->data;
|
|
}
|
|
else {
|
|
$subscriber = simplenews_subscriber_load_by_mail($spool_data->mail);
|
|
}
|
|
|
|
$source_class = $this->getSourceImplementation($spool_data);
|
|
$source = new $source_class($node, $subscriber);
|
|
|
|
// Set which node is actually used. In case of a translation set, this might
|
|
// not be the same node.
|
|
$this->processed[$spool_data->msid]->actual_nid = $source->getNode()->nid;
|
|
return $source;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSpoolInterface::getProcessed();
|
|
*/
|
|
function getProcessed() {
|
|
$processed = $this->processed;
|
|
$this->processed = array();
|
|
return $processed;
|
|
}
|
|
|
|
/**
|
|
* Return the Simplenews source implementation for the given mail spool row.
|
|
*/
|
|
protected function getSourceImplementation($spool_data) {
|
|
return variable_get('simplenews_source', 'SimplenewsSourceNode');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simplenews source implementation based on nodes for a single subscriber.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
class SimplenewsSourceNode implements SimplenewsSourceNodeInterface {
|
|
|
|
/**
|
|
* The node object.
|
|
*/
|
|
protected $node;
|
|
|
|
/**
|
|
* The cached build render array.
|
|
*/
|
|
protected $build;
|
|
|
|
/**
|
|
* The newsletter category.
|
|
*/
|
|
protected $category;
|
|
|
|
/**
|
|
* The subscriber and therefore recipient of this mail.
|
|
*/
|
|
protected $subscriber;
|
|
|
|
/**
|
|
* The mail key used for drupal_mail().
|
|
*/
|
|
protected $key = 'test';
|
|
|
|
/**
|
|
* The simplenews newsletter.
|
|
*/
|
|
protected $newsletter;
|
|
|
|
/**
|
|
* Cache implementation used for this source.
|
|
*
|
|
* @var SimplenewsSourceCacheInterface
|
|
*/
|
|
protected $cache;
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::_construct();
|
|
*/
|
|
public function __construct($node, $subscriber) {
|
|
$this->setSubscriber($subscriber);
|
|
$this->setNode($node);
|
|
$this->newsletter = simplenews_newsletter_load($node->nid);
|
|
$this->category = simplenews_category_load($this->newsletter->tid);
|
|
$this->initCache();
|
|
}
|
|
|
|
/**
|
|
* Set the node of this source.
|
|
*
|
|
* If the node is part of a translation set, switch to the node for the
|
|
* requested language, if existent.
|
|
*/
|
|
public function setNode($node) {
|
|
$langcode = $this->getLanguage();
|
|
$nid = $node->nid;
|
|
if (module_exists('translation')) {
|
|
// If the node has translations and a translation is required
|
|
// the equivalent of the node in the required language is used
|
|
// or the base node (nid == tnid) is used.
|
|
if ($tnid = $node->tnid) {
|
|
if ($langcode != $node->language) {
|
|
$translations = translation_node_get_translations($tnid);
|
|
// A translation is available in the preferred language.
|
|
if ($translation = $translations[$langcode]) {
|
|
$nid = $translation->nid;
|
|
$langcode = $translation->language;
|
|
}
|
|
else {
|
|
// No translation found which matches the preferred language.
|
|
foreach ($translations as $translation) {
|
|
if ($translation->nid == $tnid) {
|
|
$nid = $tnid;
|
|
$langcode = $translation->language;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// If a translation of the node is used, load this node.
|
|
if ($nid != $node->nid) {
|
|
$this->node = node_load($nid);
|
|
}
|
|
else {
|
|
$this->node = $node;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the cache implementation.
|
|
*/
|
|
protected function initCache() {
|
|
$class = variable_get('simplenews_source_cache', 'SimplenewsSourceCacheBuild');
|
|
$this->cache = new $class($this);
|
|
}
|
|
|
|
/**
|
|
* Returns the corresponding category.
|
|
*/
|
|
public function getCategory() {
|
|
return $this->category;
|
|
}
|
|
|
|
/**
|
|
* Set the active subscriber.
|
|
*/
|
|
public function setSubscriber($subscriber) {
|
|
$this->subscriber = $subscriber;
|
|
}
|
|
|
|
/**
|
|
* Return the subscriber object.
|
|
*/
|
|
public function getSubscriber() {
|
|
return $this->subscriber;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getHeaders().
|
|
*/
|
|
public function getHeaders(array $headers) {
|
|
|
|
// If receipt is requested, add headers.
|
|
if ($this->category->receipt) {
|
|
$headers['Disposition-Notification-To'] = $from;
|
|
$headers['X-Confirm-Reading-To'] = $from;
|
|
}
|
|
|
|
// Add priority if set.
|
|
switch ($this->category->priority) {
|
|
case SIMPLENEWS_PRIORITY_HIGHEST:
|
|
$headers['Priority'] = 'High';
|
|
$headers['X-Priority'] = '1';
|
|
$headers['X-MSMail-Priority'] = 'Highest';
|
|
break;
|
|
case SIMPLENEWS_PRIORITY_HIGH:
|
|
$headers['Priority'] = 'urgent';
|
|
$headers['X-Priority'] = '2';
|
|
$headers['X-MSMail-Priority'] = 'High';
|
|
break;
|
|
case SIMPLENEWS_PRIORITY_NORMAL:
|
|
$headers['Priority'] = 'normal';
|
|
$headers['X-Priority'] = '3';
|
|
$headers['X-MSMail-Priority'] = 'Normal';
|
|
break;
|
|
case SIMPLENEWS_PRIORITY_LOW:
|
|
$headers['Priority'] = 'non-urgent';
|
|
$headers['X-Priority'] = '4';
|
|
$headers['X-MSMail-Priority'] = 'Low';
|
|
break;
|
|
case SIMPLENEWS_PRIORITY_LOWEST:
|
|
$headers['Priority'] = 'non-urgent';
|
|
$headers['X-Priority'] = '5';
|
|
$headers['X-MSMail-Priority'] = 'Lowest';
|
|
break;
|
|
}
|
|
|
|
// Add user specific header data.
|
|
$message['headers']['From'] = $this->getFromFormatted();
|
|
$message['headers']['List-Unsubscribe'] = '<' . token_replace('[simplenews-subscriber:unsubscribe-url]', $this->getTokenContext(), array('sanitize' => FALSE)) . '>';
|
|
|
|
// Add general headers
|
|
$headers['Precedence'] = 'bulk';
|
|
return $headers;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getTokenContext().
|
|
*/
|
|
function getTokenContext() {
|
|
return array(
|
|
'category' => $this->getCategory(),
|
|
'simplenews_subscriber' => $this->getSubscriber(),
|
|
'node' => $this->getNode(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Set the mail key.
|
|
*/
|
|
function setKey($key) {
|
|
$this->key = $key;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getKey().
|
|
*/
|
|
function getKey() {
|
|
return $this->key;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getFromFormatted().
|
|
*/
|
|
function getFromFormatted() {
|
|
$name = $this->getCategory()->from_name;
|
|
|
|
// Windows based PHP systems don't accept formatted emails.
|
|
if (drupal_substr(PHP_OS, 0, 3) == 'WIN') {
|
|
return $this->getFromAddress();
|
|
}
|
|
else {
|
|
return '"' . $name . '" <' . $this->getFromAddress() . '>';
|
|
}
|
|
|
|
return $formatted_address;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getFromAddress().
|
|
*/
|
|
function getFromAddress() {
|
|
return $this->getCategory()->from_address;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getRecipient().
|
|
*/
|
|
function getRecipient() {
|
|
return $this->getSubscriber()->mail;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getFormat().
|
|
*/
|
|
function getFormat() {
|
|
return $this->getCategory()->format;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getLanguage().
|
|
*/
|
|
function getLanguage() {
|
|
return $this->getSubscriber()->language;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceSpoolInterface::getNode().
|
|
*/
|
|
function getNode() {
|
|
return $this->node;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getSubject().
|
|
*/
|
|
function getSubject() {
|
|
// Build email subject and perform some sanitizing.
|
|
$langcode = $this->getLanguage();
|
|
$language_list = language_list();
|
|
// Use the requested language if enabled.
|
|
$language = isset($language_list[$langcode]) ? $language_list[$langcode] : NULL;
|
|
$subject = token_replace($this->getCategory()->email_subject, $this->getTokenContext(), array('sanitize' => FALSE, 'language' => $language));
|
|
|
|
// Line breaks are removed from the email subject to prevent injection of
|
|
// malicious data into the email header.
|
|
$subject = str_replace(array("\r", "\n"), '', $subject);
|
|
return $subject;
|
|
}
|
|
|
|
/**
|
|
* Set up the necessary language and user context.
|
|
*/
|
|
protected function setContext() {
|
|
|
|
// Switch to the user
|
|
if ($this->uid = $this->getSubscriber()->uid) {
|
|
simplenews_impersonate_user($this->uid);
|
|
}
|
|
|
|
// Change language if the requested language is enabled.
|
|
$language = $this->getLanguage();
|
|
$languages = language_list();
|
|
if (isset($languages[$language])) {
|
|
$this->original_language = $GLOBALS['language'];
|
|
$GLOBALS['language'] = $languages[$language];
|
|
$GLOBALS['language_url'] = $languages[$language];
|
|
// Overwrites the current content language for i18n_select.
|
|
if (module_exists('i18n_select')) {
|
|
$GLOBALS['language_content'] = $languages[$language];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the context.
|
|
*/
|
|
protected function resetContext() {
|
|
|
|
// Switch back to the previous user.
|
|
if ($this->uid) {
|
|
simplenews_revert_user();
|
|
}
|
|
|
|
// Switch language back.
|
|
if (!empty($this->original_language)) {
|
|
$GLOBALS['language'] = $this->original_language;
|
|
$GLOBALS['language_url'] = $this->original_language;
|
|
if (module_exists('i18n_select')) {
|
|
$GLOBALS['language_content'] = $this->original_language;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build the node object.
|
|
*
|
|
* The resulting build array is cached as it is used in multiple places.
|
|
* @param $format
|
|
* (Optional) Override the default format. Defaults to getFormat().
|
|
*/
|
|
protected function build($format = NULL) {
|
|
if (empty($format)) {
|
|
$format = $this->getFormat();
|
|
}
|
|
if (!empty($this->build[$format])) {
|
|
return $this->build[$format];
|
|
}
|
|
|
|
// Build message body
|
|
// Supported view modes: 'email_plain', 'email_html', 'email_textalt'
|
|
$build = node_view($this->node, 'email_' . $format);
|
|
unset($build['#theme']);
|
|
|
|
foreach (field_info_instances('node', $this->node->type) as $field_name => $field) {
|
|
if (isset($build[$field_name])) {
|
|
$build[$field_name]['#theme'] = 'simplenews_field';
|
|
}
|
|
}
|
|
|
|
$this->build[$format] = $build;
|
|
return $this->build[$format];
|
|
}
|
|
|
|
/**
|
|
* Build the themed newsletter body.
|
|
*
|
|
* @param $format
|
|
* (Optional) Override the default format. Defaults to getFormat().
|
|
*/
|
|
protected function buildBody($format = NULL) {
|
|
if (empty($format)) {
|
|
$format = $this->getFormat();
|
|
}
|
|
if ($cache = $this->cache->get('build', 'body:' . $format)) {
|
|
return $cache;
|
|
}
|
|
$body = theme('simplenews_newsletter_body', array('build' => $this->build($format), 'category' => $this->getCategory(), 'language' => $this->getLanguage(), 'simplenews_subscriber' => $this->getSubscriber()));
|
|
$this->cache->set('build', 'body:' . $format, $body);
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getBody().
|
|
*/
|
|
public function getBody() {
|
|
return $this->getBodyWithFormat($this->getFormat());
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getBody().
|
|
*/
|
|
public function getPlainBody() {
|
|
return $this->getBodyWithFormat('plain');
|
|
}
|
|
|
|
/**
|
|
* Get the body with the requested format.
|
|
*
|
|
* @param $format
|
|
* Either html or plain.
|
|
*
|
|
* @return
|
|
* The rendered mail body as a string.
|
|
*/
|
|
protected function getBodyWithFormat($format) {
|
|
// Switch to correct user and language context.
|
|
$this->setContext();
|
|
|
|
if ($cache = $this->cache->get('final', 'body:' . $format)) {
|
|
return $cache;
|
|
}
|
|
|
|
$body = $this->buildBody($format);
|
|
|
|
// Build message body, replace tokens.
|
|
$body = token_replace($body, $this->getTokenContext(), array('sanitize' => FALSE));
|
|
if ($format == 'plain') {
|
|
// Convert HTML to text if requested to do so.
|
|
$body = simplenews_html_to_text($body, $this->getCategory()->hyperlinks);
|
|
}
|
|
$this->cache->set('final', 'body:' . $format, $body);
|
|
$this->resetContext();
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* Builds the themed footer.
|
|
*
|
|
* @param $format
|
|
* (Optional) Set the format of this footer build, overrides the default
|
|
* format.
|
|
*/
|
|
protected function buildFooter($format = NULL) {
|
|
if (empty($format)) {
|
|
$format = $this->getFormat();
|
|
}
|
|
|
|
if ($cache = $this->cache->get('build', 'footer:' . $format)) {
|
|
return $cache;
|
|
}
|
|
|
|
// Build and buffer message footer
|
|
$footer = theme('simplenews_newsletter_footer', array(
|
|
'build' => $this->build($format),
|
|
'category' => $this->getCategory(),
|
|
'context' => $this->getTokenContext(),
|
|
'key' => $this->getKey(),
|
|
'language' => $this->getLanguage(),
|
|
'format' => $format,
|
|
));
|
|
$this->cache->set('build', 'footer:' . $format, $footer);
|
|
return $footer;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getFooter().
|
|
*/
|
|
public function getFooter() {
|
|
return $this->getFooterWithFormat($this->getFormat());
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getPlainFooter().
|
|
*/
|
|
public function getPlainFooter() {
|
|
return $this->getFooterWithFormat('plain');
|
|
}
|
|
|
|
/**
|
|
* Get the footer in the specified format.
|
|
*
|
|
* @param $format
|
|
* Either html or plain.
|
|
*
|
|
* @return
|
|
* The footer for the requested format.
|
|
*/
|
|
protected function getFooterWithFormat($format) {
|
|
// Switch to correct user and language context.
|
|
$this->setContext();
|
|
if ($cache = $this->cache->get('final', 'footer:' . $format)) {
|
|
return $cache;
|
|
}
|
|
$final_footer = token_replace($this->buildFooter($format), $this->getTokenContext(), array('sanitize' => FALSE));
|
|
$this->cache->set('build', 'footer:' . $format, $final_footer);
|
|
$this->resetContext();
|
|
return $final_footer;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceInterface::getAttachments().
|
|
*/
|
|
function getAttachments() {
|
|
if ($cache = $this->cache->get('data', 'attachments')) {
|
|
return $cache;
|
|
}
|
|
|
|
$attachments = array();
|
|
$build = $this->build();
|
|
$fids = array();
|
|
foreach (field_info_instances('node', $this->node->type) as $field_name => $field_instance) {
|
|
// @todo: Find a better way to support more field types.
|
|
// Only add fields of type file which are enabled for the current view
|
|
// mode as attachments.
|
|
$field = field_info_field($field_name);
|
|
if ($field['type'] == 'file' && isset($build[$field_name])) {
|
|
|
|
if ($items = field_get_items('node', $this->node, $field_name)) {
|
|
foreach ($items as $item) {
|
|
$fids[] = $item['fid'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!empty($fids)) {
|
|
$attachments = file_load_multiple($fids);
|
|
}
|
|
|
|
$this->cache->set('data', 'attachments', $attachments);
|
|
return $attachments;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Abstract implementation of the source caching that does static caching.
|
|
*
|
|
* Subclasses need to implement the abstract function isCacheable() to decide
|
|
* what should be cached.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
abstract class SimplenewsSourceCacheStatic implements SimplenewsSourceCacheInterface {
|
|
|
|
/**
|
|
* The simplenews source for which this cache is used.
|
|
*
|
|
* @var SimplenewsSourceNodeInterface
|
|
*/
|
|
protected $source;
|
|
|
|
/**
|
|
* The cache identifier for the given source.
|
|
*/
|
|
protected $cid;
|
|
|
|
/**
|
|
* The static cache.
|
|
*/
|
|
protected static $cache = array();
|
|
|
|
/**
|
|
* Implements SimplenewsSourceNodeInterface::__construct().
|
|
*/
|
|
public function __construct(SimplenewsSourceNodeInterface $source) {
|
|
$this->source = $source;
|
|
|
|
self::$cache = &drupal_static(__CLASS__, array());
|
|
}
|
|
|
|
/**
|
|
* Returns the cache identifier for the current source.
|
|
*/
|
|
protected function getCid() {
|
|
if (empty($this->cid)) {
|
|
$this->cid = $this->source->getNode()->nid . ':' . $this->source->getLanguage();
|
|
}
|
|
return $this->cid;
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceNodeInterface::get().
|
|
*/
|
|
public function get($group, $key) {
|
|
if (!$this->isCacheable($group, $key)) {
|
|
return;
|
|
}
|
|
|
|
if (isset(self::$cache[$this->getCid()][$group][$key])) {
|
|
return self::$cache[$this->getCid()][$group][$key];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements SimplenewsSourceNodeInterface::set().
|
|
*/
|
|
public function set($group, $key, $data) {
|
|
if (!$this->isCacheable($group, $key)) {
|
|
return;
|
|
}
|
|
|
|
self::$cache[$this->getCid()][$group][$key] = $data;
|
|
}
|
|
|
|
/**
|
|
* Return if the requested element should be cached.
|
|
*
|
|
* @return
|
|
* TRUE if it should be cached, FALSE otherwise.
|
|
*/
|
|
abstract function isCacheable($group, $key);
|
|
}
|
|
|
|
/**
|
|
* Cache implementation that does not cache anything at all.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
class SimplenewsSourceCacheNone extends SimplenewsSourceCacheStatic {
|
|
|
|
/**
|
|
* Implements SimplenewsSourceCacheStatic::set().
|
|
*/
|
|
public function isCacheable($group, $key) {
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Source cache implementation that caches build and data element.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
class SimplenewsSourceCacheBuild extends SimplenewsSourceCacheStatic {
|
|
|
|
/**
|
|
* Implements SimplenewsSourceCacheStatic::set().
|
|
*/
|
|
function isCacheable($group, $key) {
|
|
|
|
// Only cache for anon users.
|
|
if (user_is_logged_in()) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Only cache data and build information.
|
|
return in_array($group, array('data', 'build'));
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Example source implementation used for tests.
|
|
*
|
|
* @ingroup source
|
|
*/
|
|
class SimplenewsSourceTest implements SimplenewsSourceInterface {
|
|
|
|
protected $format;
|
|
|
|
public function __construct($format) {
|
|
$this->format = $format;
|
|
}
|
|
|
|
public function getAttachments() {
|
|
return array(
|
|
array(
|
|
'uri' => 'example://test.png',
|
|
'filemime' => 'x-example',
|
|
'filename' => 'test.png',
|
|
),
|
|
);
|
|
}
|
|
|
|
public function getBody() {
|
|
return $this->getFormat() == 'plain' ? $this->getPlainBody() : 'the body';
|
|
}
|
|
|
|
public function getFooter() {
|
|
return $this->getFormat() == 'plain' ? $this->getPlainFooter() : 'the footer';
|
|
}
|
|
|
|
public function getPlainFooter() {
|
|
return 'the plain footer';
|
|
}
|
|
|
|
public function getFormat() {
|
|
return $this->format;
|
|
}
|
|
|
|
public function getFromAddress() {
|
|
return 'test@example.org';
|
|
}
|
|
|
|
public function getFromFormatted() {
|
|
return 'Test <test@example.org>';
|
|
}
|
|
|
|
public function getHeaders(array $headers) {
|
|
$headers['X-Simplenews-Test'] = 'OK';
|
|
return $headers;
|
|
}
|
|
|
|
public function getKey() {
|
|
return 'node';
|
|
}
|
|
|
|
public function getLanguage() {
|
|
return 'en';
|
|
}
|
|
|
|
public function getPlainBody() {
|
|
return 'the plain body';
|
|
}
|
|
|
|
public function getRecipient() {
|
|
return 'recipient@example.org';
|
|
}
|
|
|
|
public function getSubject() {
|
|
return 'the subject';
|
|
}
|
|
|
|
public function getTokenContext() {
|
|
return array();
|
|
}
|
|
}
|