Файл: library/XenForo/DataWriter/EmailTemplate.php
Строк: 321
<?php
/**
* Data writer for email templates.
*
* @package XenForo_EmailTemplate
*/
class XenForo_DataWriter_EmailTemplate extends XenForo_DataWriter
{
/**
* Option that takes the path to the development template output directory.
* If not specified, output will not be written. Defaults determined based
* on config settings.
*
* @var string
*/
const OPTION_DEV_OUTPUT_DIR = 'devOutputDir';
/**
* Option that controls whether a full compile is performed when the template
* is modified. If false, the template is only parsed into segments. Defaults
* to true, but should be set to false for bulk imports; compilation should
* happen in the second pass.
*
* @var string
*/
const OPTION_FULL_COMPILE = 'fullCompile';
/**
* Option that controls whether a test compile will be performed when setting
* the value of a template. If false, the error will only be detected when a
* full compile is done.
*
* Note that this does not prevent the template from being parsed. That will
* always happen.
*
* @var string
*/
const OPTION_TEST_COMPILE = 'testCompile';
/**
* Title of the phrase that will be created when a call to set the
* existing data fails (when the data doesn't exist).
*
* @var string
*/
protected $_existingDataErrorPhrase = 'requested_email_template_not_found';
/**
* If an array, updates the template modification status list
*
* @var array|null
*/
protected $_modificationStatuses = null;
/**
* Gets the fields that are defined for the table. See parent for explanation.
*
* @return array
*/
protected function _getFields()
{
return array(
'xf_email_template' => array(
'template_id' => array('type' => self::TYPE_UINT, 'autoIncrement' => true),
'title' => array('type' => self::TYPE_STRING, 'required' => true, 'maxLength' => 50,
'verification' => array('$this', '_verifyTitle'), 'requiredError' => 'please_enter_valid_title'
),
'custom' => array('type' => self::TYPE_BOOLEAN, 'default' => 1),
'subject' => array('type' => self::TYPE_STRING, 'verification' => array('$this', '_verifySubject'), 'noTrim' => true),
'subject_parsed' => array('type' => self::TYPE_BINARY),
'body_text' => array('type' => self::TYPE_STRING, 'verification' => array('$this', '_verifyBodyText'), 'noTrim' => true),
'body_text_parsed' => array('type' => self::TYPE_BINARY),
'body_html' => array('type' => self::TYPE_STRING, 'verification' => array('$this', '_verifyBodyHtml'), 'noTrim' => true),
'body_html_parsed' => array('type' => self::TYPE_BINARY),
'addon_id' => array('type' => self::TYPE_STRING, 'maxLength' => 25, 'default' => '')
)
);
}
/**
* Gets the actual existing data out of data that was passed in. See parent for explanation.
*
* @param mixed
*
* @return array|false
*/
protected function _getExistingData($data)
{
if (!$templateId = $this->_getExistingPrimaryKey($data))
{
return false;
}
return array('xf_email_template' => $this->_getEmailTemplateModel()->getEmailTemplateById($templateId));
}
/**
* Gets SQL condition to update the existing record.
*
* @return string
*/
protected function _getUpdateCondition($tableName)
{
return 'template_id = ' . $this->_db->quote($this->getExisting('template_id'));
}
/**
* Gets the default set of options for this data writer.
* If in debug mode and we have a development directory config, we set the template
* dev output directory automatically.
*
* @return array
*/
protected function _getDefaultOptions()
{
$options = array(
self::OPTION_DEV_OUTPUT_DIR => '',
self::OPTION_FULL_COMPILE => true,
self::OPTION_TEST_COMPILE => true
);
$config = XenForo_Application::get('config');
if ($config->debug)
{
$options[self::OPTION_DEV_OUTPUT_DIR] = $this->_getEmailTemplateModel()->getEmailTemplateDevelopmentDirectory();
}
return $options;
}
/**
* Verifies that the provided template title contains only valid characters
*
* @param string Title
*
* @return boolean
*/
protected function _verifyTitle(&$title)
{
if (preg_match('/[^a-zA-Z0-9_.]/', $title))
{
$this->error(new XenForo_Phrase('please_enter_title_using_only_alphanumeric_dot'), 'title');
return false;
}
$this->_templateModifications = null;
return true;
}
/**
* Verification callback for an email template subject.
*
* @param string Uncompiled subject
*
* @return boolean
*/
protected function _verifySubject(&$subject)
{
return $this->_verifyTemplateField($subject, 'subject');
}
/**
* Verification callback for an email template plain text body.
*
* @param string Uncompiled body
*
* @return boolean
*/
protected function _verifyBodyText(&$body)
{
return $this->_verifyTemplateField($body, 'body_text');
}
/**
* Verification callback for an email template plain text body.
*
* @param string Uncompiled body
*
* @return boolean
*/
protected function _verifyBodyHtml(&$body)
{
return $this->_verifyTemplateField($body, 'body_html');
}
protected $_templateModifications = null;
protected function _getTemplateModifications()
{
if ($this->_templateModifications === null)
{
$this->_templateModifications = $this->_getModificationModel()->getActiveModificationsForTemplate($this->get('title'));
}
return $this->_templateModifications;
}
protected function _verifyTemplateField($string, $fieldName)
{
$modifications = $this->_getTemplateModifications();
foreach ($modifications AS $k => $modification)
{
if ($modification['search_location'] != $fieldName)
{
unset($modifications[$k]);
}
}
$templateWithModifications = $this->_getModificationModel()->applyTemplateModifications(
$string, $modifications, $modificationStatuses
);
$standardParse = true;
$parsed = null;
if ($modificationStatuses)
{
try
{
$compiler = new XenForo_Template_Compiler_Email($templateWithModifications);
$parsed = $compiler->lexAndParse();
if ($this->getOption(self::OPTION_TEST_COMPILE))
{
$compiler->setFollowExternal(false);
$compiler->compileParsed($parsed, $this->get('title'), 0, 0);
}
$standardParse = false;
}
catch (XenForo_Template_Compiler_Exception $e)
{
foreach ($modificationStatuses AS &$status)
{
if (is_int($status))
{
$status = 'error_compile';
}
}
}
}
if ($standardParse)
{
try
{
$compiler = new XenForo_Template_Compiler_Email($string);
$parsed = $compiler->lexAndParse();
if ($this->getOption(self::OPTION_TEST_COMPILE))
{
$compiler->setFollowExternal(false);
$compiler->compileParsed($parsed, $this->get('title'), 0, 0);
}
}
catch (XenForo_Template_Compiler_Exception $e)
{
$this->error($e->getMessage(), $fieldName);
return false;
}
}
$this->set($fieldName . '_parsed', serialize($parsed));
if (!is_array($this->_modificationStatuses))
{
$this->_modificationStatuses = array();
}
$this->_modificationStatuses += $modificationStatuses;
return true;
}
public function reparseTemplate()
{
$subject = $this->get('subject');
$this->_verifySubject($subject);
$bodyText = $this->get('body_text');
$this->_verifyBodyText($bodyText);
$bodyHtml = $this->get('body_html');
$this->_verifyBodyHtml($bodyHtml);
}
/**
* Pre-save handler.
*/
protected function _preSave()
{
if ($this->isChanged('title') || $this->isChanged('custom'))
{
$existingTemplate = $this->_getEmailTemplateModel()->getEmailTemplateByTitleAndType(
$this->get('title'), $this->get('custom')
);
if ($existingTemplate)
{
$this->error(new XenForo_Phrase('template_titles_must_be_unique'), 'title');
}
}
}
/**
* Post-save handler.
*/
protected function _postSave()
{
if (is_array($this->_modificationStatuses))
{
$this->_getModificationModel()->updateTemplateModificationLog($this->get('template_id'), $this->_modificationStatuses);
}
if ($this->getOption(self::OPTION_FULL_COMPILE))
{
XenForo_Template_Compiler_Email::removeTemplateFromCache($this->get('title'));
XenForo_Template_Compiler_Email::removeTemplateFromCache($this->getExisting('title'));
$this->_recompileTemplate();
}
if ($devDir = $this->_getDevOutputDir())
{
$this->_writeDevFileOutput($devDir);
}
}
/**
* Recompiles this template.
*/
protected function _recompileTemplate()
{
$this->_getEmailTemplateModel()->compileAndInsertNamedEmailTemplate($this->get('title'));
}
/**
* Helper to get the developer data output directory only if it is enabled
* and applicable to this situation.
*
* @return string
*/
protected function _getDevOutputDir()
{
if ($this->get('addon_id') == 'XenForo' && $this->get('custom') == 0)
{
return $this->getOption(self::OPTION_DEV_OUTPUT_DIR);
}
else
{
return '';
}
}
/**
* Writes the development files output to the specified directory. This will write
* each template into an individual file for easier tracking in source control.
*
* @param string Path to directory to write to
*/
protected function _writeDevFileOutput($dir)
{
if (!is_dir($dir) || !is_writable($dir))
{
throw new XenForo_Exception("Email template development directory $dir is not writable");
}
$filePrefix = $dir . '/' . $this->get('title');
$files = array(
$filePrefix . '.subject.txt' => $this->get('subject'),
$filePrefix . '.text.txt' => $this->get('body_text'),
$filePrefix . '.html.txt' => $this->get('body_html'),
);
foreach ($files AS $filePath => $data)
{
$fp = fopen($filePath, 'w');
if ($fp)
{
fwrite($fp, $data);
fclose($fp);
}
}
if ($this->isUpdate() && $this->isChanged('title'))
{
$this->_deleteExistingDevOutput($dir);
}
}
/**
* Post-delete handler.
*/
protected function _postDelete()
{
$this->_db->delete('xf_email_template_modification_log', 'template_id = ' . $this->_db->quote($this->get('template_id')));
if ($this->get('custom'))
{
$this->_recompileTemplate();
}
else
{
$titleQuoted = $this->_db->quote($this->get('title'));
$this->_db->delete('xf_email_template', 'title = ' . $titleQuoted);
$this->_db->delete('xf_email_template_compiled', 'title = ' . $titleQuoted);
$this->_db->delete('xf_email_template_phrase', 'title = ' . $titleQuoted);
}
if ($devDir = $this->_getDevOutputDir())
{
$this->_deleteExistingDevOutput($devDir);
}
}
/**
* Deletes the corresponding files when a template is deleted from the database
*
* @param string Path to email templates directory
*/
protected function _deleteExistingDevOutput($dir)
{
$filePrefix = $dir . '/' . $this->getExisting('title');
$files = array(
$filePrefix . '.subject.txt',
$filePrefix . '.text.txt',
$filePrefix . '.html.txt',
);
// ensure all files are writable before unlinking any
foreach ($files AS $file)
{
if (file_exists($file) && !is_writable($file))
{
throw new XenForo_Exception("Email template development file $dir is not writable");
}
}
foreach ($files AS $file)
{
if (file_exists($file) && is_writable($file))
{
unlink($file);
}
}
}
/**
* @return XenForo_Model_EmailTemplate
*/
protected function _getEmailTemplateModel()
{
return $this->getModelFromCache('XenForo_Model_EmailTemplate');
}
/**
* @return XenForo_Model_EmailTemplateModification
*/
protected function _getModificationModel()
{
return $this->getModelFromCache('XenForo_Model_EmailTemplateModification');
}
}