Commit 2cda87f4 authored by bgm's avatar bgm Committed by Aegir user

Updated uk.co.vedaconsulting.mosaico (2.0-beta3.1511995766)

parent 5ae934e6
......@@ -27,21 +27,34 @@ class CRM_Mosaico_BAO_MosaicoTemplate extends CRM_Mosaico_DAO_MosaicoTemplate {
*/
public static function findBaseTemplates() {
if (!isset(Civi::$statics[__CLASS__]['bases'])) {
$templatesDir = CRM_Core_Resources::singleton()->getPath('uk.co.vedaconsulting.mosaico');
if (!$templatesDir) {
return FALSE;
}
$templatesDir .= '/packages/mosaico/templates';
if (!is_dir($templatesDir)) {
return FALSE;
}
$templatesUrl = CRM_Mosaico_Utils::getTemplatesUrl('absolute');
$records = array();
$records[] = array(
'name' => 'versafix-1',
'title' => 'Versafix',
'thumbnail' => CRM_Mosaico_Utils::getTemplatesUrl('absolute', 'versafix-1/edres/_full.png'),
'path' => 'templates/versafix-1/template-versafix-1.html',
);
// $records[] = array(
// 'name' => 'tedc15',
// 'title' => 'TEDC 15',
// 'thumbnail' => CRM_Mosaico_Utils::getTemplatesUrl('absolute', 'tedc15/edres/_full.png'),
// 'path' => 'templates/tedc15/template-tedc15.html',
// );
foreach (glob("{$templatesDir}/*", GLOB_ONLYDIR) as $dir) {
$template = basename($dir);
$templateHTML = "{$templatesUrl}/{$template}/template-{$template}.html";
$templateThumbnail = "{$templatesUrl}/{$template}/edres/_full.png";
$records[] = array(
'name' => $template,
'title' => $template,
'thumbnail' => $templateThumbnail,
'path' => $templateHTML,
);
}
Civi::$statics[__CLASS__]['bases'] = $records;
}
return Civi::$statics[__CLASS__]['bases'];
}
......
<?php
use CRM_Mosaico_ExtensionUtil as E;
/**
* Form controller class
*
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference
*/
class CRM_Mosaico_Form_MosaicoAdmin extends CRM_Admin_Form_Setting {
protected $_settings = array(
'mosaico_layout' => 'Mosaico Preferences',
);
/**
* Build the form object.
*/
public function buildQuickForm() {
parent::buildQuickForm();
}
}
<?php
use Civi\FlexMailer\Listener\RequiredTokens;
use CRM_Mosaico_ExtensionUtil as E;
/**
* Class CRM_Mosaico_MosaicoRequiredTokens
*
* The token format for Mosaico extends the traditional format -- all
* traditional tokens (eg "{action.unsubscribeUrl}") are supported, and
* a few additional aliases (eg "[unsubscribe_link]") are also
* supported.
*
* When validating required tokens, we should accept the aliases.
*/
class CRM_Mosaico_MosaicoRequiredTokens extends RequiredTokens {
public function __construct() {
parent::__construct(array('mosaico'), array());
}
public function getRequiredTokens() {
$requiredTokens = Civi::service('civi_flexmailer_required_tokens')
->getRequiredTokens();
// Mosaico's templates handle the mailing-address/contact-info
// differently than the CiviMail templates -- one of the standard
// blocks provides a section where it prompts you to fill in this info.
//
// Arguably, this makes the `{domain.address}` requirement redundant.
// Arguably, the `{domain.address}` approach is better.
//
// For the moment, it's convenient to go along with the Mosaico-way.
// But it's quite patch-welcome to change (eg inject `{domain.address}`
// to the default layout, and then enforce the requirement).
unset($requiredTokens['domain.address']);
return $requiredTokens;
}
public function findMissingTokens($str) {
$content = array('body_unspecified' => $str);
_mosaico_civicrm_alterMailContent($content);
return parent::findMissingTokens($content['body_unspecified']);
}
}
......@@ -3,7 +3,7 @@
class CRM_Mosaico_Page_Editor extends CRM_Core_Page {
const DEFAULT_MODULE_WEIGHT = 200;
function run() {
public function run() {
$smarty = CRM_Core_Smarty::singleton();
$smarty->assign('baseUrl', CRM_Mosaico_Utils::getMosaicoDistUrl('relative'));
$smarty->assign('scriptUrls', $this->getScriptUrls());
......@@ -67,6 +67,7 @@ class CRM_Mosaico_Page_Editor extends CRM_Core_Page {
'titleToken' => 'MOSAICO Responsive Email Designer',
'fileuploadConfig' => array(
'url' => $this->getUrl('civicrm/mosaico/upload', NULL, FALSE),
'maxFileSize' => $this->getMaxFileSize(),
// messages??
),
......@@ -76,6 +77,7 @@ class CRM_Mosaico_Page_Editor extends CRM_Core_Page {
// It extends "tinymceConfig" and adds more plugins/buttons.
// See also: https://www.tinymce.com/docs/configure/integration-and-setup/
'tinymceConfig' => array(
'convert_urls' => FALSE,
'external_plugins' => array(
'civicrmtoken' => $res->getUrl('uk.co.vedaconsulting.mosaico', 'js/tinymce-plugins/civicrmtoken/plugin.js', 1),
),
......@@ -111,7 +113,20 @@ class CRM_Mosaico_Page_Editor extends CRM_Core_Page {
protected function getUrl($path, $query, $frontend) {
// This function shouldn't really exist, but it's tiring to set `$htmlize`
// to false every.single.time we need a URL.
return CRM_Utils_System::url($path, $query, FALSE, NULL, FALSE, $frontend);
// These URLs should be absolute -- this influences the final URLs
// for any uploaded images, and those will need to be absolute to work
// correctly in all forms of composition/delivery.
return CRM_Utils_System::url($path, $query, TRUE, NULL, FALSE, $frontend);
}
/**
* @return int
*/
protected function getMaxFileSize() {
$fakeUnlimited = 25 * 1024 * 1024;
$iniVal = ini_get('upload_max_filesize') ? CRM_Utils_Number::formatUnitSize(ini_get('upload_max_filesize'), TRUE) : $fakeUnlimited;
$settingVal = Civi::settings()->get('maxFileSize') ? (1024 * 1024 * Civi::settings()->get('maxFileSize')) : $fakeUnlimited;
return (int) min($iniVal, $settingVal);
}
}
......@@ -22,6 +22,7 @@ class CRM_Mosaico_Services {
}
$container->setDefinition('mosaico_flexmail_composer', new Definition('CRM_Mosaico_MosaicoComposer'));
$container->setDefinition('mosaico_flexmail_url_filter', new Definition('CRM_Mosaico_UrlFilter'));
$container->setDefinition('mosaico_required_tokens', new Definition('CRM_Mosaico_MosaicoRequiredTokens'));
foreach (self::getListenerSpecs() as $listenerSpec) {
$container->findDefinition('dispatcher')->addMethodCall('addListenerService', $listenerSpec);
......@@ -31,6 +32,10 @@ class CRM_Mosaico_Services {
protected static function getListenerSpecs() {
$listenerSpecs = array();
if (class_exists('\Civi\FlexMailer\Validator')) {
// TODO Simplify by removing conditional. Wait until at least Feb 2018.
$listenerSpecs[] = array(\Civi\FlexMailer\Validator::EVENT_CHECK_SENDABLE, array('mosaico_required_tokens', 'onCheckSendable'), FM::WEIGHT_MAIN);
}
$listenerSpecs[] = array(FM::EVENT_COMPOSE, array('mosaico_flexmail_composer', 'onCompose'), FM::WEIGHT_MAIN);
$listenerSpecs[] = array(FM::EVENT_COMPOSE, array('mosaico_flexmail_url_filter', 'onCompose'), FM::WEIGHT_ALTER - 100);
......
......@@ -36,11 +36,11 @@ class CRM_Mosaico_Upgrader_Base {
* Obtain a reference to the active upgrade handler.
*/
static public function instance() {
if (! self::$instance) {
if (!self::$instance) {
// FIXME auto-generate
self::$instance = new CRM_Mosaico_Upgrader(
'uk.co.vedaconsulting.mosaico',
realpath(__DIR__ .'/../../../')
realpath(__DIR__ . '/../../../')
);
}
return self::$instance;
......@@ -205,7 +205,7 @@ class CRM_Mosaico_Upgrader_Base {
* @return array(revisionNumbers) sorted numerically
*/
public function getRevisions() {
if (! is_array($this->revisions)) {
if (!is_array($this->revisions)) {
$this->revisions = array();
$clazz = new ReflectionClass(get_class($this));
......@@ -240,6 +240,9 @@ class CRM_Mosaico_Upgrader_Base {
// ******** Hook delegates ********
/**
* @see https://wiki.civicrm.org/confluence/display/CRMDOC/hook_civicrm_install
*/
public function onInstall() {
$files = glob($this->extensionDir . '/sql/*_install.sql');
if (is_array($files)) {
......@@ -300,4 +303,5 @@ class CRM_Mosaico_Upgrader_Base {
default:
}
}
}
......@@ -3,6 +3,8 @@
//this may not be required as it doesn't appear to be used anywhere?
//require_once 'packages/premailer/premailer.php';
use CRM_Mosaico_ExtensionUtil as E;
/**
* Class CRM_Mosaico_Utils
*/
......@@ -16,9 +18,9 @@ class CRM_Mosaico_Utils {
*/
public static function getLayoutOptions() {
return array(
'auto' => 'Automatically select a layout',
'bootstrap-single' => 'Single Page (Bootstrap CSS)',
'bootstrap-wizard' => 'Wizard (Bootstrap CSS)',
'auto' => E::ts('Automatically select a layout'),
'bootstrap-single' => E::ts('Single Page (Bootstrap CSS)'),
'bootstrap-wizard' => E::ts('Wizard (Bootstrap CSS)'),
);
}
......@@ -402,7 +404,7 @@ class CRM_Mosaico_Utils {
/* perform the requested action */
switch (CRM_Utils_Type::escape($_POST['action'], 'String')) {
case "download": {
case "download":
// download
header("Content-Type: application/force-download");
header("Content-Disposition: attachment; filename=\"" . $_POST["filename"] . "\"");
......@@ -410,9 +412,8 @@ class CRM_Mosaico_Utils {
echo $html;
break;
}
case "save": {
case "save":
$result = array();
$msgTplId = NULL;
$hashKey = CRM_Utils_Type::escape($_POST['key'], 'String');
......@@ -465,9 +466,8 @@ class CRM_Mosaico_Utils {
}
CRM_Utils_JSON::output($result);
break;
}
case "email": {
case "email":
$result = array();
if (!CRM_Utils_Rule::email($_POST['rcpt'])) {
CRM_Core_Session::setStatus('Recipient Email address not found');
......@@ -497,7 +497,7 @@ class CRM_Mosaico_Utils {
}
break;
}
}
CRM_Utils_System::civiExit();
}
......
# uk.co.vedaconsulting.mosaico
##Extension title
Integration of Mosaico with CiviCRM
##Description
This extension integrates Mosaico a responsive email template editor, with CiviCRM.
* [Initial Blog Post](https://civicrm.org/blogs/parvez/a-new-beginning-for-civimail)
......@@ -65,7 +70,7 @@ cd uk.co.vedaconsulting.mosaico
./bin/setup.sh -D
```
## Usage
## Getting started (Usage)
If you haven't used Mosaico before, consult the the demo and tutorial materials from http://mosaico.io/index.html.
......
......@@ -18,79 +18,102 @@ phpunit4 --group e2e
## Manual Tests: Save/Load Mailing
1. Create a mailing
1. Navigate to "Mailings => New Mailing" (`civicrm/a/#/mailing/new` or `civicrm/a/#/mailing/new/mosaico`)
2. _Observe_: Redirect to `civicrm/a/#/mailing/123` and open with Mosaico layout
3. Enter a mailing name. (Make a mental note of the name.)
4. Under "Design", choose a template.
5. _Observe_: A full-screen dialog opens with Mosaico.
6. Create a block. Edit some text. (Make a mental note of the content.)
7. Save.
2. Immediately re-edit
1. Under "Design", open the template again.
2. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
3. Save or close
3. Try a full reload
1. Navigate to "Mailings => Draft and Unscheduled"
2. Find your mailing. Click "Continue".
3. Under "Design", open the template again.
4. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
1. Create a mailing
1. Navigate to "Mailings => New Mailing" (`civicrm/a/#/mailing/new` or `civicrm/a/#/mailing/new/mosaico`)
2. _Observe_: Redirect to `civicrm/a/#/mailing/123` and open with Mosaico layout
3. Enter a mailing name, etc. (Make a mental note of the name.)
4. Under "Design", choose a template.
5. _Observe_: A full-screen dialog opens with Mosaico.
6. Create a block. Edit some text. (Make a mental note of the content.)
7. Save.
2. Immediately re-edit
1. Under "Design", open the template again.
2. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
3. Save or close
3. Try a full reload
1. Navigate to "Mailings => Draft and Unscheduled"
2. Find your mailing. Click "Continue".
3. Under "Design", open the template again.
4. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
## Manual Tests: Tokens
This extension defines a TinyMCE plugin called "civicrmtoken". To test this
This extension defines a TinyMCE plugin called `civicrmtoken`. To test this
plugin, create a mailing with a block of content. Then try each of the following:
1. Edit a paragraph. In the toolbar, click the token dropdown and see a hotlist of 3-5 tokens. Pick one. Observe the new token in the paragraph.
2. Edit a paragraph. In the toolbar, click the token icon and see a dialog. Pick one. Observe the new token in the paragraph.
3. Edit a paragraph. Press Ctrl-Shift-T and see a dialog. Enter a filter and pick a token using the keyboard. Observe the new token in the paragraph.
4. Edit a heading or button. Ensure that the the token icon/dropdown/hotkey work as expected. Observe the new token in the heading or button.
1. Edit a paragraph. In the toolbar, click the token dropdown and see a hotlist of 3-5 tokens. Pick one. Observe the new token in the paragraph.
2. Edit a paragraph. In the toolbar, click the token icon and see a dialog. Pick one. Observe the new token in the paragraph.
3. Edit a paragraph. Press `Ctrl-Shift-T` and see a dialog. Enter a filter and pick a token using the keyboard. Observe the new token in the paragraph.
4. Edit a heading or button. Ensure that the the token icon/dropdown/hotkey work as expected. Observe the new token in the heading or button.
## Manual Tests: Images
## Manual Tests: Images and Links
Mosaico handles a few different kinds of images. To test these, create a
mailing then:
Mosaico handles a few different kinds of images and links. To test these, create a
mailing with content:
1. Add blocks with images
1. Add a block which supports an image. Upload an image.
2. Add a block which supports an image. Re-use the previously uplaoded image.
3. Add a footer block which uses the built-in Twitter/Facebook icons
2. Test the mailing. Check:
1. The first block should show the uploaded image. The URL should be absolute.
2. The second block should show the same uploaded image. The URL should be absolute.
3. The footer block should show the Twitter/Facebook icons. The URL should be absolute.
* (CON-1) Create a new mailing. Fill in placeholders for "Name", "Subject", etc.
* (CON-2) Choose "Empty Template (versafix-1)".
* (CON-3) Add a block which supports one image with text.
* Upload an image.
* Set the button's link to an external page (eg `https://www.google.com/search?q=asdf&oq=asdf`)
* Highlight some text. Make it a a hyperlink to a token (eg `{action.forward}`)
* (CON-4) Add a block which supports one image with text.
* Go to the "Gallery". Re-use the previously uploaded image.
* Set the button's link to an internal page (eg `http://dmaster.l/civicrm/event/info?reset=1&id=1`)
* (CON-5) Add a footer block which uses the built-in Twitter/Facebook icons.
* Set the link for at least one social media button. Disable any others.
Now, we're going test that content appears correctly in several scenarios. Each scenario references the "Message Evaluation Procedure" (defined further down):
* (SC-1) Using the "Test", open the "Preview" in HTML. Perform the "Message Evaluation Procedure" (in the browser).
* (SC-2) Using the "Test", send a message to an email address. Perform the "Message Evaluation Procedure" (in the email).
* (SC-3) Finalize and submit the mailing. Trigger cron (eg `cv api -U admin job.process_mailing`). Perform the "Message Evaluation Procedure" (in the email).
* (SC-4) In the email, click the link to "View in Browser". Perform the "Message Evaluation Procedure" (in the browser).
The "Message Evaluation Procedure" is:
* (MEP-1) Check the the first block:
* (a) The image should appear. Inspect it to see that the URL is absolute.
* (b) The button should appear. Inspect it to see that the URL is absolute. Click it and see that it opens.
* (c) The highlighted text should be a link. Inspect it to see that the URL is absolute. Click it and see that it opens.
* (MEP-2) Check the second block:
* (a) The image should appear. Inspect it to see that the URL is absolute.
* (b) The button should appear. Inspect it to see that the URL is absolute. Click it and see that it opens.
* (MEP-3) Check the footer block.
* (a) The social media icons should appear. Inspect one to see that the image URL is absolute.
* (b) The social media icons should be links. Inspect one to see that the link is absolute. Click it and see that it opens.
## Manual Tests: Save/Load Template
1. Create a new template
1. Navigate to "Mailings => Mosaico Templates"
2. _Observe_: There is a section for "Create new template from...".
3. _Observe_: There *may be* a section "Configured templates" if some templates exist.
4. Under "Create new template from...", select one of the base templates like "Versafix 1"
5. Enter a template name. (Make a mental note of the name.)
6. _Observe_: A full-screen dialog opens with Mosaico.
7. Create a block. Edit some text. (Make a mental note of the content.)
8. Save.
2. Immediately re-edit
1. Under "Configured templates", find your template and click the thumbnail.
2. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
3. Save
3. Try a full reload
1. Go to any other page (such as the dashboard).
2. Navigate to "Mailings => Mosaico Templates"
3. Under "Configured templates", find your template and click the thumbnail.
4. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
4. Copy a template
1. Under "Configured templates", find your template and click the copy icon.
2. Enter a new template name. (Make a mental note of the name.)
3. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
4. Create another block. Edit some text.
5. Save
6. Open both the old and new templates. Check that they have the right content.
5. Delete the copied template
1. Under "Configured templates", find your new copied template and click the red X.
2. _Observe_: The template goes away.
6. Rename a template
1. Under "Configured templates", find your template and click the wrench icon.
2. Enter a new template name.
3. _Observe_: The name updates.
1. Create a new template
1. Navigate to "Mailings => Mosaico Templates"
2. _Observe_: There is a section for "Create new template from...".
3. _Observe_: There *may be* a section "Configured templates" if some templates exist.
4. Under "Create new template from...", select one of the base templates like "Versafix 1"
5. Enter a template name. (Make a mental note of the name.)
6. _Observe_: A full-screen dialog opens with Mosaico.
7. Create a block. Edit some text. (Make a mental note of the content.)
8. Save.
2. Immediately re-edit
1. Under "Configured templates", find your template and click the thumbnail.
2. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
3. Save
3. Try a full reload
1. Go to any other page (such as the dashboard).
2. Navigate to "Mailings => Mosaico Templates"
3. Under "Configured templates", find your template and click the thumbnail.
4. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
4. Copy a template
1. Under "Configured templates", find your template and click the copy icon.
2. Enter a new template name. (Make a mental note of the name.)
3. _Observe_: A full-screen dialog opens with Mosaico. It restores the content from before.
4. Create another block. Edit some text.
5. Save
6. Open both the old and new templates. Check that they have the right content.
5. Delete the copied template
1. Under "Configured templates", find your new copied template and click the red X.
2. _Observe_: The template goes away.
6. Rename a template
1. Under "Configured templates", find your template and click the wrench icon.
2. Enter a new template name.
3. _Observe_: The name updates.
......@@ -24,6 +24,8 @@ $result = array (
// If there are any navbars that we should try to avoid, include them
// in these jQuery selectors.
'topNav' => '#civicrm-menu',
'drupalNav' => '#toolbar',
'joomlaNav' => '.com_civicrm > .navbar',
'leftNav' => '.wp-admin #adminmenu',
),
);
......
......@@ -3,14 +3,12 @@
<div class="panel panel-w-subheading panel-default crm-mosaico-modal-panel">
<div class="panel-subheading">
<ul class="nav nav-tabs">
<li class="active"><a href="#attachments" data-toggle="tab">{{ts('Attachments')}}</a></li>
<li class=""><a href="#responses" data-toggle="tab">{{ts('Responses')}}</a></li>
<li class=""><a href="#tracking" data-toggle="tab">{{ts('Tracking')}}</a></li>
<li><a href="#publication" data-toggle="tab">{{ts('Publication')}}</a></li>
</ul>
</div>
<ul class="nav nav-tabs">
<li class="active"><a href="#attachments" data-toggle="tab">{{ts('Attachments')}}</a></li>
<li class=""><a href="#responses" data-toggle="tab">{{ts('Responses')}}</a></li>
<li class=""><a href="#tracking" data-toggle="tab">{{ts('Tracking')}}</a></li>
<li><a href="#publication" data-toggle="tab">{{ts('Publication')}}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="attachments">
......
......@@ -61,8 +61,8 @@
<div class="form-group">
<label for="inputSubject" class="control-label required-mark">{{ts('Subject')}}</label>
<div>
<div style="float: right;">
<input crm-mailing-token on-select="$broadcast('insert:subject', token.name)" tabindex="-1"/>
<div style="float: right;" class="token-holder">
<input class="form-control" crm-mailing-token on-select="$broadcast('insert:subject', token.name)" tabindex="-1"/>
</div>
<input
id="inputSubject"
......@@ -83,11 +83,13 @@
<select
class="form-control"
id="inputUnsubscribeGroup"
crm-ui-select="{dropdownAutoWidth : true}"
name="baseGroup"
ng-model="mailing.recipients.groups.base[0]"
ng-required="true"
>
<option ng-repeat="grp in crmMailingConst.groupNames | filter:{is_hidden:0} | orderBy:'title'"
<option value=""></option>
<option ng-repeat="grp in groupNames | filter:{is_hidden:0} | orderBy:'title'"
value="{{grp.id}}">{{grp.title}}</option>
</select>
</div>
......
(function(angular, $, _) {
angular.module('crmMosaico').directive('crmMosaicoBlockMailing', function(crmMailingSimpleDirective) {
return crmMailingSimpleDirective('crmMosaicoBlockMailing', '~/crmMosaico/BlockMailing.html');
angular.module('crmMosaico').directive('crmMosaicoBlockMailing', function($q, crmMetadata, crmUiHelp) {
var directiveName = 'crmMosaicoBlockMailing', templateUrl = '~/crmMosaico/BlockMailing.html';
return {
scope: {
crmMailing: '@'
},
templateUrl: templateUrl,
link: function (scope, elm, attr) {
// Common elements - like crmMailingSimpleDirective
scope.$parent.$watch(attr.crmMailing, function(newValue){
scope.mailing = newValue;
});
scope.crmMailingConst = CRM.crmMailing;
scope.ts = CRM.ts(null);
scope.hs = crmUiHelp({file: 'CRM/Mailing/MailingUI'});
scope[directiveName] = attr[directiveName] ? scope.$parent.$eval(attr[directiveName]) : {};
$q.when(crmMetadata.getFields('Mailing'), function(fields) {
scope.mailingFields = fields;
});
// Unique elements
scope.groupNames = CRM.crmMailing.testGroupNames || CRM.crmMailing.groupNames;
}
};
});
})(angular, CRM.$, CRM._);
......@@ -37,7 +37,7 @@ Vars: mailing:obj, testContact:obj, testGroup:obj, crmMailing:FormController
ui-jq="crmSelect2"