diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore index d607a621127..3566606ab6d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,31 +2,37 @@ !.gitignore !.htaccess !.gitkeep +/composer.phar + /app/bootstrap* /app/tests.bootstrap* +/app/phpunit.xml + /app/bundles/CoreBundle/Assets/css/app/less/**/*.css /app/bundles/CoreBundle/Assets/css/libraries/**/*.css /app/bundles/CoreBundle/Assets/css/libraries/Froala/plugins/*.css !/app/bundles/CoreBundle/Assets/css/libraries/builder.css !/app/bundles/CoreBundle/Assets/css/libraries/froala/plugins/gated_video.css !/app/bundles/CoreBundle/Assets/css/*.css + /app/cache/* !/app/cache/.gitkeep -/app/logs/* -!/app/logs/.gitkeep + /app/config/local*.php /app/config/config_local.php /app/config/paths_local.php + +/app/logs/* +!/app/logs/.gitkeep + /app/Resources/SensioGeneratorBundle -/app/phpunit.xml /app/spool/* + /vendor/* !/vendor/.htaccess + /bin/* -/composer.phar /bundles -/media/files/* -!/media/files/.htaccess /node_modules /build/packaging /build/packages/* @@ -34,7 +40,11 @@ /mockup /upgrade /upgrade_errors.txt -/translations + +/media/dashboards/* +!/media/dashboards/*.json +/media/files/* +!/media/files/.htaccess /media/images/* !/media/images/flags !/media/images/apple-touch-icon.png @@ -42,5 +52,31 @@ !/media/images/favicon.ico !/media/images/mautic_logo* !/media/images/.htaccess -/media/dashboards/* -!/media/dashboards/*.json \ No newline at end of file + +/plugins/* +!/plugins/MauticCitrixBundle +!/plugins/MauticClearbitBundle +!/plugins/MauticCloudStorageBundle +!/plugins/MauticCrmBundle +!/plugins/MauticEmailMarketingBundle +!/plugins/MauticFocusBundle +!/plugins/MauticFullContactBundle +!/plugins/MauticGmailBundle +!/plugins/MauticOutlookBundle +!/plugins/MauticSocialBundle +!/plugins/index.html + +/themes/* +!/themes/blank +!/themes/coffee +!/themes/goldstar +!/themes/Mauve +!/themes/nature +!/themes/neopolitan +!/themes/oxygen +!/themes/skyline +!/themes/sunday +!/themes/blank.png +!/themes/blank-big.png + +/translations diff --git a/.travis.yml b/.travis.yml index dc57895ccc7..b71b9eef83c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,10 @@ php: #- 5.5 - 5.6 - 7.0 - - 7.1 + +matrix: + allow_failures: + - php: 7.0 before_script: - composer install diff --git a/Gruntfile.js b/Gruntfile.js index e01445a8f9b..88a2c6ba423 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,7 @@ module.exports = function (grunt) { mautic: { // configurable paths bundleAssets: 'app/bundles/**/Assets/css', + pluginAssets: 'plugins/**/Assets/css', rootAssets: 'media/css' }, @@ -31,7 +32,7 @@ module.exports = function (grunt) { // Compiles less files in bundle's Assets/css root and single level directory to CSS less: { files: { - src: ['<%= mautic.bundleAssets %>/*.less', '<%= mautic.bundleAssets %>/*/*.less', '<%= mautic.bundleAssets %>/../builder/*.less'], + src: ['<%= mautic.bundleAssets %>/*.less', '<%= mautic.pluginAssets %>/*.less', '<%= mautic.bundleAssets %>/*/*.less', '<%= mautic.bundleAssets %>/../builder/*.less'], expand: true, rename: function (dest, src) { return dest + src.replace('.less', '.css') diff --git a/README.md b/README.md index 6b06a7fa2f6..178f5a6737e 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Installing from source is only recommended if you are comfortable using the comm #### Mautic requirements 1. See [Mautic requirements](https://www.mautic.org/download/requirements). -2. PHP modules: +2. PHP modules: - required: `zip`, `xml`, `mcrypt`, `imap`, `mailparse` - recommended: `openssl`, `opcache` / `apcu` / `memcached` - recommended for development: `xdebug` @@ -76,32 +76,32 @@ Before running these commands, please make a backup of your database. If updating from a tagged release to a tagged release, schema changes will be included in a migrations file. To apply the changes, run - `$ php app/console doctrine:migrations:migrate` + $ php app/console doctrine:migrations:migrate If you are updating to the latest source (remember this is alpha), first run - `$ php app/console doctrine:schema:update --dump-sql` + $ php app/console doctrine:schema:update --dump-sql This will list out the queries Doctrine wants to execute in order to get the schema up-to-date (no queries are actually executed). Review the queries to ensure there is nothing detrimental to your data. If you have doubts about a query, submit an issue here and we'll verify it. If you're satisfied with the queries, execute them with - `$ php app/console doctrine:schema:update --force` + $ php app/console doctrine:schema:update --force Your schema should now be up-to-date with the source. # Usage -Learning how to use marketing automation can be challenging. The first step is to understand what marketing automation is and how it can help your business be more successful. This quick usage outline is not meant to be comprehensive but will outline a few key areas of Mautic and how to use each of them. +Learning how to use marketing automation can be challenging. The first step is to understand what marketing automation is and how it can help your business be more successful. This quick usage outline is not meant to be comprehensive but will outline a few key areas of Mautic and how to use each of them. *You can find more detailed information at https://docs.mautic.org* ### 1. Monitoring -The act of monitoring website traffic and visitors is often the first step in a marketing automation system. This step involves collecting details and information about each visitor to your website. +The act of monitoring website traffic and visitors is often the first step in a marketing automation system. This step involves collecting details and information about each visitor to your website. #### Visitor Types -There are two types of visitor, **anonymous** and **known**. +There are two types of visitor, **anonymous** and **known**. **Anonymous visitors** are all visitors which browse to your website. These visitors are monitored and certain key pieces of information are collected. This information includes: @@ -143,16 +143,16 @@ There are several ways to connect with your leads. The three most common are **e **Landing pages** are usually the first step in the connection process as these are used to make initial contact with leads and collect the information to move them from an anonymous visitor to a known visitor. These pages are used to funnel visitors to a specific call to action. This call to action is usually a form to collect the visitor's information. -###3. Automating +### 3. Automating -One of Mautic's main purposes is to enable automation of specific tasks. The task of connecting with leads is one such area where automation becomes increasingly valuable. Mautic allows you to define specific times, events, or actions when a connection should be triggered. Here is an example of an automation process. +One of Mautic's main purposes is to enable automation of specific tasks. The task of connecting with leads is one such area where automation becomes increasingly valuable. Mautic allows you to define specific times, events, or actions when a connection should be triggered. Here is an example of an automation process. **Example** -A visitor fills out a call-to-action form on your landing page. This form collects their email address and automatically moves them from an **anonymous** to a **known** visitor. As a known visitor they are now added as a new lead to a specific campaign. This campaign will send the new lead an email you have pre-defined. You can then define additional actions to be taken based on the lead's response to your email. +A visitor fills out a call-to-action form on your landing page. This form collects their email address and automatically moves them from an **anonymous** to a **known** visitor. As a known visitor they are now added as a new lead to a specific campaign. This campaign will send the new lead an email you have pre-defined. You can then define additional actions to be taken based on the lead's response to your email. -This example demonstrates several uses of automation. First, the visitor is *automatically* moved from anonymous to known status. Second, the visitor is *automatically* added to a particular campaign. Lastly the visitor is sent an email *automatically* as a new lead. +This example demonstrates several uses of automation. First, the visitor is *automatically* moved from anonymous to known status. Second, the visitor is *automatically* added to a particular campaign. Lastly the visitor is sent an email *automatically* as a new lead. -There are many more ways in which automation can be used throughout Mautic to improve efficiency and reduce the time you spend connecting with your leads. As mentioned earlier, refer to [https://docs.mautic.org](https://docs.mautic.org) for more details. +There are many more ways in which automation can be used throughout Mautic to improve efficiency and reduce the time you spend connecting with your leads. As mentioned earlier, refer to [https://docs.mautic.org](https://docs.mautic.org) for more details. ## Customizing - Plugins, Themes @@ -168,7 +168,7 @@ Read more about API and webhooks in the [Mautic Developer Docummentation](https: ## Translations -One benefit of using Mautic is the ability to modify and customize the solution to fit your needs. Mautic allows you to quickly change to your preferred language, or modify any string through the language files. These language files are available for the translation by the community at [Transifex](https://www.transifex.com/mautic/mautic/dashboard) and if you are interested you can add more languages, or help to translate the current ones. +One benefit of using Mautic is the ability to modify and customize the solution to fit your needs. Mautic allows you to quickly change to your preferred language, or modify any string through the language files. These language files are available for the translation by the community at [Transifex](https://www.transifex.com/mautic/mautic/dashboard) and if you are interested you can add more languages, or help to translate the current ones. ## How to test a pull request @@ -207,7 +207,7 @@ Every change to Mautic core happens via PRs. Every PR must have 2 successful tes ## Unit Tests -The unit tests can be executed in the Mautic root directory with `phpunit --bootstrap vendor/autoload.php --configuration app/phpunit.xml.dist app/bundles` command. +The unit tests can be executed in the Mautic root directory with `composer test` command. # FAQ and Contact Information Marketing automation has historically been a difficult tool to implement in a business. The Mautic community is a rich environment for you to learn from others and share your knowledge as well. Open source means more than open code. Open source is providing equality for all and a chance to improve. If you have questions then the Mautic community can help provide the answers. diff --git a/app/AppKernel.php b/app/AppKernel.php index 1d31f40e67f..7f4eb1133c7 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -34,7 +34,7 @@ class AppKernel extends Kernel * * @const integer */ - const MINOR_VERSION = 6; + const MINOR_VERSION = 8; /** * Patch version number. @@ -181,6 +181,7 @@ public function registerBundles() new Mautic\WebhookBundle\MauticWebhookBundle(), new LightSaml\SymfonyBridgeBundle\LightSamlSymfonyBridgeBundle(), new LightSaml\SpBundle\LightSamlSpBundle(), + new Ivory\OrderedFormBundle\IvoryOrderedFormBundle(), ]; //dynamically register Mautic Plugin Bundles diff --git a/app/bundles/ApiBundle/Translations/en_US/messages.ini b/app/bundles/ApiBundle/Translations/en_US/messages.ini index 170b41fbf80..64d4c62b5df 100644 --- a/app/bundles/ApiBundle/Translations/en_US/messages.ini +++ b/app/bundles/ApiBundle/Translations/en_US/messages.ini @@ -7,7 +7,7 @@ mautic.api.auth.error.signature_method_rejected="The signature method used is un mautic.api.auth.error.signature_invalid="The signature provided does not match the one calculated by the service." mautic.api.auth.error.consumer_key_unknown="The consumer key provided is unsupported." mautic.api.auth.error.token_expired="The access token provided is valid, but has expired." -mautic.api.auth.error.token_rejected="The token provided does not have the right format."; +mautic.api.auth.error.token_rejected="The token provided does not have the right format." mautic.api.auth.error.additional_authorization_required="The access token does not have the correct access scopes." permission_denied="The access session handle (ASH) has expired or is invalid." mautic.api.call.notfound="Object not found." diff --git a/app/bundles/AssetBundle/EventListener/ReportSubscriber.php b/app/bundles/AssetBundle/EventListener/ReportSubscriber.php index 79bfcada4d1..af5b9081016 100644 --- a/app/bundles/AssetBundle/EventListener/ReportSubscriber.php +++ b/app/bundles/AssetBundle/EventListener/ReportSubscriber.php @@ -68,7 +68,13 @@ public function onReportBuilder(ReportBuilderEvent $event) ], ]; - $columns = array_merge($columns, $event->getStandardColumns($prefix, ['name'], 'mautic_asset_action'), $event->getCategoryColumns()); + $columns = array_merge( + $columns, + $event->getStandardColumns($prefix, ['name'], 'mautic_asset_action'), + $event->getCategoryColumns(), + $event->getCampaignByChannelColumns() + ); + $event->addTable( 'assets', [ @@ -107,7 +113,12 @@ public function onReportBuilder(ReportBuilderEvent $event) 'asset.downloads', [ 'display_name' => 'mautic.asset.report.downloads.table', - 'columns' => array_merge($columns, $downloadColumns, $event->getLeadColumns(), $event->getIpColumn()), + 'columns' => array_merge( + $columns, + $downloadColumns, + $event->getLeadColumns(), + $event->getIpColumn() + ), ], 'assets' ); @@ -135,6 +146,7 @@ public function onReportGenerate(ReportGeneratorEvent $event) if ($context == 'assets') { $queryBuilder->from(MAUTIC_TABLE_PREFIX.'assets', 'a'); $event->addCategoryLeftJoin($queryBuilder, 'a'); + $event->addCampaignByChannelJoin($queryBuilder, 'a', 'asset'); } elseif ($context == 'asset.downloads') { $event->applyDateFilters($queryBuilder, 'date_download', 'ad'); @@ -143,6 +155,7 @@ public function onReportGenerate(ReportGeneratorEvent $event) $event->addCategoryLeftJoin($queryBuilder, 'a'); $event->addLeadLeftJoin($queryBuilder, 'ad'); $event->addIpAddressLeftJoin($queryBuilder, 'ad'); + $event->addCampaignByChannelJoin($queryBuilder, 'a', 'asset'); } $event->setQueryBuilder($queryBuilder); diff --git a/app/bundles/CampaignBundle/Config/config.php b/app/bundles/CampaignBundle/Config/config.php index 2ed53c09267..b3edd8d31ed 100644 --- a/app/bundles/CampaignBundle/Config/config.php +++ b/app/bundles/CampaignBundle/Config/config.php @@ -200,7 +200,7 @@ ], 'mautic.form.type.campaignconfig' => [ 'class' => 'Mautic\CampaignBundle\Form\Type\ConfigType', - 'arguments' => 'mautic.factory', + 'arguments' => 'translator', 'alias' => 'campaignconfig', ], ], diff --git a/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php b/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php index 969b199722a..2166efd4064 100644 --- a/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php +++ b/app/bundles/CampaignBundle/Entity/LeadEventLogRepository.php @@ -92,8 +92,8 @@ public function getLeadLogs($leadId, array $options = []) ->leftJoin('ll', MAUTIC_TABLE_PREFIX.'campaign_events', 'e', 'll.event_id = e.id') ->leftJoin('ll', MAUTIC_TABLE_PREFIX.'campaigns', 'c', 'll.campaign_id = c.id') ->where('ll.lead_id = '.(int) $leadId) - ->andWhere('e.event_type = :eventType') - ->setParameter('eventType', 'action'); + ->andWhere('e.event_type != :eventType') + ->setParameter('eventType', 'decision'); if (isset($options['scheduledState'])) { if ($options['scheduledState']) { @@ -176,8 +176,14 @@ public function getUpcomingEvents(array $options = null) } if (isset($options['eventType'])) { - $query->andwhere('e.event_type = :eventType') - ->setParameter('eventType', $options['eventType']); + if (is_array($options['eventType'])) { + $query->andWhere( + $query->expr()->in('e.event_type', array_map([$query->expr(), 'literal'], $options['eventType'])) + ); + } else { + $query->andwhere('e.event_type = :eventTypes') + ->setParameter('eventTypes', $options['eventType']); + } } if (isset($options['limit'])) { diff --git a/app/bundles/CampaignBundle/Form/Type/ConfigType.php b/app/bundles/CampaignBundle/Form/Type/ConfigType.php index a6b35b8b08c..79c0f15d823 100644 --- a/app/bundles/CampaignBundle/Form/Type/ConfigType.php +++ b/app/bundles/CampaignBundle/Form/Type/ConfigType.php @@ -1,5 +1,14 @@ logger->debug('CAMPAIGN: '.ucfirst($child['eventType']).' ID# '.$child['id'].' has a decision path of no'); } else { $this->logger->debug('CAMPAIGN: '.ucfirst($child['eventType']).' ID# '.$child['id'].' is being processed'); } @@ -626,8 +627,9 @@ public function triggerStartingEvents( ++$totalEventCount; $event['campaign'] = [ - 'id' => $campaign->getId(), - 'name' => $campaign->getName(), + 'id' => $campaign->getId(), + 'name' => $campaign->getName(), + 'createdBy' => $campaign->getCreatedBy(), ]; $decisionEvent = [ @@ -769,8 +771,7 @@ public function triggerScheduledEvents( ) { defined('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED') or define('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED', 1); - $campaignId = $campaign->getId(); - $campaignName = $campaign->getName(); + $campaignId = $campaign->getId(); $this->logger->debug('CAMPAIGN: Triggering scheduled events'); @@ -903,8 +904,9 @@ public function triggerScheduledEvents( // Set campaign ID $event['campaign'] = [ - 'id' => $campaignId, - 'name' => $campaignName, + 'id' => $campaign->getId(), + 'name' => $campaign->getName(), + 'createdBy' => $campaign->getCreatedBy(), ]; // Execute event @@ -1011,8 +1013,6 @@ public function triggerNegativeEvents( $this->logger->debug('CAMPAIGN: Triggering negative events'); $campaignId = $campaign->getId(); - $campaignName = $campaign->getName(); - $repo = $this->getRepository(); $campaignRepo = $this->getCampaignRepository(); $logRepo = $this->getLeadEventLogRepository(); @@ -1262,8 +1262,9 @@ public function triggerNegativeEvents( // Set event $event = $events[$id]; $event['campaign'] = [ - 'id' => $campaignId, - 'name' => $campaignName, + 'id' => $campaign->getId(), + 'name' => $campaign->getName(), + 'createdBy' => $campaign->getCreatedBy(), ]; // Set lead in case this is triggered by the system @@ -1542,9 +1543,11 @@ public function executeEvent( } // Set campaign ID + $event['campaign'] = [ - 'id' => $campaign->getId(), - 'name' => $campaign->getName(), + 'id' => $campaign->getId(), + 'name' => $campaign->getName(), + 'createdBy' => $campaign->getCreatedBy(), ]; // Ensure properties is an array @@ -1648,6 +1651,17 @@ public function executeEvent( //trigger the action $response = $this->invokeEventCallback($event, $thisEventSettings, $lead, null, true, $log); + // Check if the lead wasn't deleted during the event callback + if (null === $lead->getId() && $response === true) { + ++$executedEventCount; + + $this->logger->debug( + 'CAMPAIGN: Contact was deleted while executing '.ucfirst($event['eventType']).' ID# '.$event['id'] + ); + + return true; + } + $eventTriggered = false; if ($response instanceof LeadEventLog) { // Listener handled the event and returned a log entry @@ -1691,31 +1705,7 @@ public function executeEvent( $repo->deleteEntity($log); } - // Notify the lead owner if there is one otherwise campaign creator that there was a failure - if (!$owner = $lead->getOwner()) { - $ownerId = $campaign->getCreatedBy(); - $owner = $this->userModel->getEntity($ownerId); - } - - if ($owner && $owner->getId()) { - $this->notificationModel->addNotification( - $campaign->getName().' / '.$event['name'], - 'error', - false, - $this->translator->trans( - 'mautic.campaign.event.failed', - [ - '%contact%' => ''.$lead->getPrimaryIdentifier().'', - ] - ), - null, - null, - $owner - ); - } + $this->notifyOfFailure($lead, $campaign->getCreatedBy(), $campaign->getName().' / '.$event['name']); $this->logger->debug($debug); } else { @@ -1818,7 +1808,9 @@ public function invokeEventCallback($event, $settings, $lead = null, $eventDetai 'lead' => $lead, 'systemTriggered' => $systemTriggered, 'config' => $event['properties'], - ], true, $log + ], + true, + $log ); $eventName = array_key_exists('eventName', $settings) ? $settings['eventName'] : null; @@ -2079,6 +2071,40 @@ public function getEventLineChartData($unit, \DateTime $dateFrom, \DateTime $dat return $chart->render(); } + /** + * @param Lead $lead + * @param $campaignCreatedBy + * @param $header + */ + public function notifyOfFailure(Lead $lead, $campaignCreatedBy, $header) + { + // Notify the lead owner if there is one otherwise campaign creator that there was a failure + if (!$owner = $lead->getOwner()) { + $ownerId = (int) $campaignCreatedBy; + $owner = $this->userModel->getEntity($ownerId); + } + + if ($owner && $owner->getId()) { + $this->notificationModel->addNotification( + $header, + 'error', + false, + $this->translator->trans( + 'mautic.campaign.event.failed', + [ + '%contact%' => ''.$lead->getPrimaryIdentifier().'', + ] + ), + null, + null, + $owner + ); + } + } + /** * Handles condition type events. * diff --git a/app/bundles/CampaignBundle/Translations/en_US/messages.ini b/app/bundles/CampaignBundle/Translations/en_US/messages.ini index 999ec51208d..51eb31af5de 100644 --- a/app/bundles/CampaignBundle/Translations/en_US/messages.ini +++ b/app/bundles/CampaignBundle/Translations/en_US/messages.ini @@ -1,5 +1,6 @@ mautic.campaign.add_new_source="Add a contact source..." mautic.campaign.campaign="Campaign" +mautic.campaign.campaign.id="Campaign ID" mautic.campaign.campaign.addremovelead="Add / remove contact" mautic.campaign.campaign.description="Campaign description: %description%" mautic.campaign.campaign.launch.builder="Launch Campaign Builder" @@ -24,6 +25,7 @@ mautic.campaign.event.inline.triggerimmediately="immediately" mautic.campaign.event.inline.triggerinterval="+ %interval% %unit%" mautic.campaign.event.last_error="Last execution error" mautic.campaign.event.failed="Failed to execute campaign event for %contact%." +mautic.campaign.event.failed.timeline="Generic error." mautic.campaign.event.intervalunit.choice.d="day(s)" mautic.campaign.event.intervalunit.choice.h="hour(s)" mautic.campaign.event.intervalunit.choice.i="minute(s)" @@ -94,7 +96,7 @@ mautic.campaign.rebuild.no_lists="There are no lists to rebuild from." mautic.campaign.rebuild.not_found="Campaign #%id% does not exist" mautic.campaign.rebuild.to_be_added="%leads% total contact(s) to be added in batches of %batch%" mautic.campaign.rebuild.to_be_removed="%leads% total contact(s) to be removed in batches of %batch%" -mautic.campaign.scheduled="Campaign action scheduled" +mautic.campaign.scheduled="Campaign event scheduled" mautic.campaign.trigger.event_count="%events% total events(s) to be processed in batches of %batch%" mautic.campaign.trigger.events_executed="%events% event(s) executed" mautic.campaign.trigger.lead_count_processed="%leads% total contact(s) to be processed in batches of %batch%" diff --git a/app/bundles/CategoryBundle/Entity/Category.php b/app/bundles/CategoryBundle/Entity/Category.php index 6daa200a014..3b0c88a86e5 100644 --- a/app/bundles/CategoryBundle/Entity/Category.php +++ b/app/bundles/CategoryBundle/Entity/Category.php @@ -117,10 +117,6 @@ public static function loadApiMetadata(ApiMetadataDriver $metadata) 'alias', 'description', 'color', - ] - ) - ->addProperties( - [ 'bundle', ] ) diff --git a/app/bundles/CategoryBundle/Helper/MenuHelper.php b/app/bundles/CategoryBundle/Helper/MenuHelper.php index b0eec836275..9441dda3b46 100644 --- a/app/bundles/CategoryBundle/Helper/MenuHelper.php +++ b/app/bundles/CategoryBundle/Helper/MenuHelper.php @@ -26,6 +26,6 @@ class MenuHelper */ public static function addCategoryMenuItems(&$items, $bundleName, CorePermissions $security) { - @trigger_error('Individual category menu items are no longer used.', E_DEPRECATED); + @trigger_error('Individual category menu items are no longer used.', E_USER_DEPRECATED); } } diff --git a/app/bundles/ChannelBundle/Entity/Message.php b/app/bundles/ChannelBundle/Entity/Message.php index 2e9b44f39f6..e0afe6c06ca 100644 --- a/app/bundles/ChannelBundle/Entity/Message.php +++ b/app/bundles/ChannelBundle/Entity/Message.php @@ -115,6 +115,7 @@ public static function loadApiMetadata(ApiMetadataDriver $metadata) 'publishUp', 'publishDown', 'channels', + 'category', ] ) ->build(); diff --git a/app/bundles/ChannelBundle/Entity/MessageQueueRepository.php b/app/bundles/ChannelBundle/Entity/MessageQueueRepository.php index 44025fe4f24..e81c8122523 100644 --- a/app/bundles/ChannelBundle/Entity/MessageQueueRepository.php +++ b/app/bundles/ChannelBundle/Entity/MessageQueueRepository.php @@ -18,6 +18,11 @@ */ class MessageQueueRepository extends CommonRepository { + /** + * @param $channel + * @param $channelId + * @param $leadId + */ public function findMessage($channel, $channelId, $leadId) { $results = $this->createQueryBuilder('mq') @@ -72,4 +77,38 @@ public function getQueuedMessages($limit, $processStarted, $channel = null, $cha return $results; } + + /** + * @param $channel + * @param array|null $ids + * + * @return bool|string + */ + public function getQueuedChannelCount($channel, array $ids = null) + { + $q = $this->getEntityManager()->getConnection()->createQueryBuilder(); + + $expr = $q->expr()->andX( + $q->expr()->eq($this->getTableAlias().'.channel', ':channel'), + $q->expr()->neq($this->getTableAlias().'.status', ':status') + ); + + if (!empty($ids)) { + $expr->add( + $q->expr()->in($this->getTableAlias().'.channel_id', $ids) + ); + } + + return (int) $q->select('count(*)') + ->from(MAUTIC_TABLE_PREFIX.'message_queue', $this->getTableAlias()) + ->where($expr) + ->setParameters( + [ + 'channel' => $channel, + 'status' => MessageQueue::STATUS_SENT, + ] + ) + ->execute() + ->fetchColumn(); + } } diff --git a/app/bundles/ChannelBundle/Model/MessageModel.php b/app/bundles/ChannelBundle/Model/MessageModel.php index 899fb7172db..38c127f6d2d 100644 --- a/app/bundles/ChannelBundle/Model/MessageModel.php +++ b/app/bundles/ChannelBundle/Model/MessageModel.php @@ -57,6 +57,26 @@ public function __construct(ChannelListHelper $channelListHelper, CampaignModel $this->campaignModel = $campaignModel; } + /** + * @param Message $entity + * @param bool $unlock + */ + public function saveEntity($entity, $unlock = true) + { + $isNew = $entity->isNew(); + + parent::saveEntity($entity, $unlock); + + if (!$isNew) { + // Update the channels + $channels = $entity->getChannels(); + foreach ($channels as $channel) { + $channel->setMessage($entity); + } + $this->getRepository()->saveEntities($channels); + } + } + /** * @return string */ @@ -220,6 +240,13 @@ public function getLeadStatsPost($messageId, $dateFrom = null, $dateTo = null, $ ); } + /** + * @param $messageId + * @param null $dateFrom + * @param null $dateTo + * + * @return mixed + */ public function getMarketingMessagesEventLogs($messageId, $dateFrom = null, $dateTo = null) { $eventLog = $this->campaignModel->getCampaignLeadEventLogRepository(); diff --git a/app/bundles/ChannelBundle/Model/MessageQueueModel.php b/app/bundles/ChannelBundle/Model/MessageQueueModel.php index 1a5a280b73d..cdc63a50825 100644 --- a/app/bundles/ChannelBundle/Model/MessageQueueModel.php +++ b/app/bundles/ChannelBundle/Model/MessageQueueModel.php @@ -362,6 +362,15 @@ public function rescheduleMessage($message, $rescheduleInterval = null, $leadId } } + /** + * @param $channel + * @param array $channelIds + */ + public function getQueuedChannelCount($channel, $channelIds = []) + { + return $this->getRepository()->getQueuedChannelCount($channel, $channelIds); + } + /** * {@inheritdoc} * @@ -411,8 +420,12 @@ protected function dispatchEvent($action, &$entity, $isNew = false, Event $event * * @param $action * @param $event + * @param $entity + * @param $isNew * * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException + * + * @return Event|null */ protected function dispatchDeprecatedEvent($action, Event $event = null, $entity = null, $isNew = null) { diff --git a/app/bundles/ChannelBundle/Translations/en_US/messages.ini b/app/bundles/ChannelBundle/Translations/en_US/messages.ini index f68eb2dd4a9..32914fe981a 100644 --- a/app/bundles/ChannelBundle/Translations/en_US/messages.ini +++ b/app/bundles/ChannelBundle/Translations/en_US/messages.ini @@ -20,4 +20,5 @@ mautic.channel.choose.messages_descr="Marketing messages" mautic.email.send.edit.message="Edit Message" mautic.channel.message.send.marketing.message="Send marketing message" mautic.channel.message.send.marketing.message.descr="Send a message through the configured channels withing the marketing message selected." -mautic.messages.processed.messages="Messages Sent by Channel" \ No newline at end of file +mautic.messages.processed.messages="Messages Sent by Channel" +mautic.channel.message.form.confirmdelete="Delete this message?" diff --git a/app/bundles/ChannelBundle/Views/Message/list_item.html.php b/app/bundles/ChannelBundle/Views/Message/list_item.html.php index 9afc2328b38..512eeb09dc2 100644 --- a/app/bundles/ChannelBundle/Views/Message/list_item.html.php +++ b/app/bundles/ChannelBundle/Views/Message/list_item.html.php @@ -14,7 +14,7 @@ $channels = []; if ($messageChannels) { foreach ($messageChannels as $channelName => $channel) { - if (!$channel->getChannelId()) { + if (!$channel->isEnabled()) { continue; } diff --git a/app/bundles/CoreBundle/Assets/css/app.css b/app/bundles/CoreBundle/Assets/css/app.css index 46d7935b55b..fc4ea9897e3 100644 --- a/app/bundles/CoreBundle/Assets/css/app.css +++ b/app/bundles/CoreBundle/Assets/css/app.css @@ -4258,23 +4258,29 @@ lesshat-selector { -lh-property: 0 ; -moz-hyphens: auto; hyphens: auto; } -.builder { +.builder, +.builder-slot { position: relative; } -.builder [data-token] { +.builder [data-token], +.builder-slot [data-token] { cursor: pointer; } -.builder .btn-block.ui-draggable-dragging { +.builder .btn-block.ui-draggable-dragging, +.builder-slot .btn-block.ui-draggable-dragging { width: 104px; height: 68px; margin: 6px 12px; white-space: normal; } -.builder .builder-panel-top { +.builder .builder-panel-top, +.builder-slot .builder-panel-top { margin-bottom: 10px; } .builder .template-dnd-help, -.builder .custom-dnd-help { +.builder-slot .template-dnd-help, +.builder .custom-dnd-help, +.builder-slot .custom-dnd-help { display: table-cell; vertical-align: middle; width: 100%; @@ -4325,6 +4331,38 @@ lesshat-selector { -lh-property: 0 ; .builder-panel .panel a.btn { white-space: normal; } +/********** SLOT ************/ +.builder-active-slot { + background-color: #fff; + z-index: 1030; +} +.builder-panel-slot { + width: 50%; + padding: 15px; + background-color: #d5d4d4; + overflow-y: auto; +} +.builder-panel-slot .btn-close-builder { + width: 100%; +} +.builder-content-slot { + left: 50%; + width: 50%; +} +.code-mode .builder-panel-slot { + width: 50%; +} +.code-mode .builder-content-slot { + width: 50%; +} +.code-mode .btn-close-builder { + width: 150px; + float: right; +} +.builder-panel-slot .panel a.btn { + white-space: normal; +} +/************* END SLOT ******************/ .ui-draggable-iframeFix { z-index: 9999 !important; } @@ -4332,6 +4370,9 @@ lesshat-selector { -lh-property: 0 ; border: 1px solid #eee; height: auto; } +#slot_codemode .CodeMirror { + height: 200px; +} .CodeMirror-hints { position: absolute; z-index: 9999 !important; @@ -4692,3 +4733,21 @@ td.col-id, th.col-id { width: 75px; } +.badge-wrapper { + float: right; + vertical-align: middle; + margin-right: -10px; +} +span.slot-caption { + font-size: 12px; +} +.imagecard-caption, +figcaption { + font-size: 16px; +} +.imagecard { + background-color: #ddd !important; +} +.imagecard .imagecard-caption { + background-color: #bbb !important; +} diff --git a/app/bundles/CoreBundle/Assets/css/app/less/components/builder.less b/app/bundles/CoreBundle/Assets/css/app/less/components/builder.less index 382859b254a..0f83752d588 100644 --- a/app/bundles/CoreBundle/Assets/css/app/less/components/builder.less +++ b/app/bundles/CoreBundle/Assets/css/app/less/components/builder.less @@ -1,5 +1,5 @@ -.builder { +.builder, .builder-slot { position: relative; [data-token] { @@ -78,6 +78,48 @@ white-space: normal; } +/********** SLOT ************/ + +.builder-active-slot { + background-color: #fff; + z-index: 1030; +} + +.builder-panel-slot { + width: 50%; + padding: 15px; + background-color: #d5d4d4; + overflow-y: auto; + + .btn-close-builder { + width: 100%; + } +} + +.builder-content-slot { + left: 50%; + width: 50%; +} + +.code-mode { + .builder-panel-slot { + width: 50%; + } + .builder-content-slot { + width: 50%; + } + .btn-close-builder { + width: 150px; + float: right; + } +} + +.builder-panel-slot .panel a.btn { + white-space: normal; +} + +/************* END SLOT ******************/ + .ui-draggable-iframeFix { z-index: 9999 !important; } diff --git a/app/bundles/CoreBundle/Assets/css/app/less/custom.less b/app/bundles/CoreBundle/Assets/css/app/less/custom.less index b552246fe59..b6822d53309 100644 --- a/app/bundles/CoreBundle/Assets/css/app/less/custom.less +++ b/app/bundles/CoreBundle/Assets/css/app/less/custom.less @@ -264,4 +264,22 @@ div[data-filter-container] .panel.in-group { td.col-id, th.col-id { width: 75px; -} \ No newline at end of file +} + +.badge-wrapper { + float: right; + vertical-align: middle; + margin-right: -10px; +} +span.slot-caption { + font-size: 12px; +} +.imagecard-caption, figcaption { + font-size: 16px; +} +.imagecard { + background-color: #ddd !important; +} +.imagecard .imagecard-caption { + background-color: #bbb !important; +} diff --git a/app/bundles/CoreBundle/Assets/css/libraries/builder.css b/app/bundles/CoreBundle/Assets/css/libraries/builder.css index 48f5096151a..1e4fee71fbf 100644 --- a/app/bundles/CoreBundle/Assets/css/libraries/builder.css +++ b/app/bundles/CoreBundle/Assets/css/libraries/builder.css @@ -5354,6 +5354,7 @@ div[data-slot-toolbar] { background-color: #4e5e9e; border-left: 1px solid #4e5e9e; border-right: 1px solid #4e5e9e; + padding-top: 2px; } div[data-slot-toolbar] .btn { width: 20px; @@ -5375,8 +5376,8 @@ div[data-slot-toolbar] .btn { font-size: 11px; line-height: 1.456; float: right; - margin-right: 5px; - color: #ffffff; + margin-right: 2px; + color: #fff; } div[data-slot-toolbar] .btn .fa { padding-top: 4px; @@ -5385,11 +5386,11 @@ div[data-slot], [data-section-wrapper] { position: relative; } -div[data-slot="image"] { +div[data-slot^="image"] { padding-top: 1px; padding-bottom: 1px; } -div[data-slot="image"] img { +div[data-slot^="image"] img { z-index: 2; position: relative; } @@ -5433,13 +5434,19 @@ div[data-slot].ui-sortable-helper { color: inherit !important; min-height: inherit !important; } -.slot-type-handle.btn { +.slot-type-handle.btn, +.section-type-handle.btn { float: left; width: 111px; margin: 2px; - height: 62px; + height: 75px; + padding-left: 5px; + padding-right: 5px; + text-align: center; + word-wrap: break-word; } -.slot-type-handle.ui-draggable-dragging { +.slot-type-handle.ui-draggable-dragging, +.section-type-handle.ui-draggable-dragging { color: #5d6c7c; background-color: #f5f5f5; border-color: #d3d3d3; @@ -5456,3 +5463,9 @@ div[data-slot].ui-sortable-helper { .theme-list .select-theme-link { margin-top: 5px; } +.theme-list .select-theme-selected { + margin-top: 5px; +} +[data-slot="dynamicContent"] { + z-index: 50; +} diff --git a/app/bundles/CoreBundle/Assets/css/libraries/builder.less b/app/bundles/CoreBundle/Assets/css/libraries/builder.less index a2694bddf9d..f19be330424 100644 --- a/app/bundles/CoreBundle/Assets/css/libraries/builder.less +++ b/app/bundles/CoreBundle/Assets/css/libraries/builder.less @@ -109,6 +109,7 @@ div[data-slot-toolbar] { background-color: @mautic-primary; border-left: 1px solid @mautic-primary; border-right: 1px solid @mautic-primary; + padding-top:2px; } div[data-slot-toolbar] .btn { @@ -131,8 +132,8 @@ div[data-slot-toolbar] .btn { font-size: 11px; line-height: 1.456; float: right; - margin-right: 5px; - color: #ffffff; + margin-right: 2px; + color: #fff; } div[data-slot-toolbar] .btn .fa { @@ -143,12 +144,12 @@ div[data-slot], [data-section-wrapper] { position: relative; } -div[data-slot="image"] { +div[data-slot^="image"] { padding-top: 1px; // prevent z-index of image from hiding the bottom border of a slot with focus above it padding-bottom: 1px; } -div[data-slot="image"] img { +div[data-slot^="image"] img { z-index: 2; position: relative; } @@ -201,14 +202,18 @@ div[data-slot].ui-sortable-helper { } } -.slot-type-handle.btn { +.slot-type-handle.btn, .section-type-handle.btn { float: left; width: 111px; margin: 2px; - height: 62px; + height: 75px; + padding-left:5px; + padding-right:5px; + text-align:center; + word-wrap: break-word; } -.slot-type-handle.ui-draggable-dragging { +.slot-type-handle.ui-draggable-dragging, .section-type-handle.ui-draggable-dragging{ color: #5d6c7c; background-color: #f5f5f5; border-color: #d3d3d3; @@ -227,4 +232,11 @@ div[data-slot].ui-sortable-helper { .select-theme-link { margin-top: 5px; } + .select-theme-selected { + margin-top:5px; + } } + +[data-slot="dynamicContent"] { + z-index:50; +} \ No newline at end of file diff --git a/app/bundles/CoreBundle/Assets/js/1.core.js b/app/bundles/CoreBundle/Assets/js/1.core.js index ca3cdde9132..1b6857fd354 100644 --- a/app/bundles/CoreBundle/Assets/js/1.core.js +++ b/app/bundles/CoreBundle/Assets/js/1.core.js @@ -394,10 +394,11 @@ var Mautic = { if (mQuery(target).length) { var hasBtn = mQuery(target).hasClass('btn'); var hasIcon = mQuery(target).hasClass('fa'); + var dontspin = mQuery(target).hasClass('btn-nospin'); var i = (hasBtn && mQuery(target).find('i.fa').length) ? mQuery(target).find('i.fa') : target; - if ((hasBtn && mQuery(target).find('i.fa').length) || hasIcon) { + if (!dontspin && ((hasBtn && mQuery(target).find('i.fa').length) || hasIcon)) { var el = (hasIcon) ? target : mQuery(target).find('i.fa').first(); var identifierClass = (new Date).getTime(); MauticVars.iconClasses[identifierClass] = mQuery(el).attr('class'); diff --git a/app/bundles/CoreBundle/Assets/js/1a.content.js b/app/bundles/CoreBundle/Assets/js/1a.content.js index edc6af920d5..e6dc8efa96a 100644 --- a/app/bundles/CoreBundle/Assets/js/1a.content.js +++ b/app/bundles/CoreBundle/Assets/js/1a.content.js @@ -517,7 +517,7 @@ Mautic.onPageLoad = function (container, response, inModal) { } if (textarea.hasClass('editor-dynamic-content')) { - minButtons = ['undo', 'redo', '|', 'bold', 'italic', 'underline', 'fontFamily', 'fontSize', 'color', 'align', 'formatOL', 'formatUL', 'quote', 'clearFormatting', 'insertLink', 'insertImage']; + minButtons = ['undo', 'redo', '|', 'bold', 'italic', 'underline', 'paragraphFormat', 'fontFamily', 'fontSize', 'color', 'align', 'formatOL', 'formatUL', 'quote', 'clearFormatting', 'insertLink', 'insertImage', 'insertGatedVideo', 'insertTable', 'html', 'fullscreen']; } if (textarea.hasClass('editor-advanced') || textarea.hasClass('editor-basic-fullpage')) { @@ -604,6 +604,31 @@ Mautic.onPageLoad = function (container, response, inModal) { contentSpecific = mauticContent; } + if (response && response.sidebar) { + var sidebarContent = mQuery('.app-sidebar.sidebar-left'); + var newSidebar = mQuery(response.sidebar); + var nav = sidebarContent.find('li'); + + if (nav.length) { + var openNavIndex; + + nav.each(function(i, el) { + var $el = mQuery(el); + + if ($el.hasClass('open')) { + openNavIndex = i; + } + }); + + var openNav = mQuery(newSidebar.find('li')[openNavIndex]); + + openNav.addClass('open'); + openNav.find('ul').removeClass('collapse'); + } + + sidebarContent.html(newSidebar); + } + if (container == '#app-content' || container == 'body') { //register global keyboard shortcuts Mautic.bindGlobalKeyboardShortcuts(); @@ -617,11 +642,9 @@ Mautic.onPageLoad = function (container, response, inModal) { } if (contentSpecific && typeof Mautic[contentSpecific + "OnLoad"] == 'function') { - if (typeof Mautic[contentSpecific + "OnLoad"] == 'function') { - if (typeof Mautic.loadedContent[contentSpecific] == 'undefined') { - Mautic.loadedContent[contentSpecific] = true; - Mautic[contentSpecific + "OnLoad"](container, response); - } + if (typeof Mautic.loadedContent[contentSpecific] == 'undefined') { + Mautic.loadedContent[contentSpecific] = true; + Mautic[contentSpecific + "OnLoad"](container, response); } } @@ -735,7 +758,7 @@ Mautic.onPageUnload = function (container, response) { Mautic[contentSpecific + "OnUnload"](container, response); } - if (typeof (Mautic.loadedContent[contentSpecific])) { + if (typeof Mautic.loadedContent[contentSpecific] !== 'undefined') { delete Mautic.loadedContent[contentSpecific]; } } @@ -810,7 +833,8 @@ Mautic.ajaxifyLink = function (el, event) { * * @param el */ -Mautic.activateChosenSelect = function(el, ignoreGlobal) { +Mautic.activateChosenSelect = function(el, ignoreGlobal, jQueryVariant) { + var mQuery = (typeof jQueryVariant != 'undefined') ? jQueryVariant : window.mQuery; if (mQuery(el).parents('.no-chosen').length && !ignoreGlobal) { // Globally ignored chosens because they are handled manually due to hidden elements, etc return; @@ -891,9 +915,9 @@ Mautic.activateChosenSelect = function(el, ignoreGlobal) { * @param options */ Mautic.activateFieldTypeahead = function (field, target, options, action) { - if (options) { + if (options && typeof options === 'String') { var keys = values = []; - //check to see if there is a key/value split + options = options.split('||'); if (options.length == 2) { keys = options[1].split('|'); @@ -915,15 +939,18 @@ Mautic.activateFieldTypeahead = function (field, target, options, action) { }); } - mQuery(fieldTypeahead).on('typeahead:selected', function (event, datum) { - if (mQuery("#" + field).length && datum["value"]) { - mQuery("#" + field).val(datum["value"]); - } - }).on('typeahead:autocompleted', function (event, datum) { + var callback = function (event, datum) { if (mQuery("#" + field).length && datum["value"]) { mQuery("#" + field).val(datum["value"]); + + var lookupCallback = mQuery('#' + field).data("lookup-callback"); + if (lookupCallback && typeof Mautic[lookupCallback] == 'function') { + Mautic[lookupCallback](field, datum); + } } - }); + }; + + mQuery(fieldTypeahead).on('typeahead:selected', callback).on('typeahead:autocompleted', callback); }; /** @@ -1346,7 +1373,7 @@ Mautic.activateListFilterSelect = function(el) { * Converts an input to a color picker * @param el */ -Mautic.activateColorPicker = function(el) { +Mautic.activateColorPicker = function(el, options) { var pickerOptions = mQuery(el).data('color-options'); if (!pickerOptions) { pickerOptions = { @@ -1357,6 +1384,10 @@ Mautic.activateColorPicker = function(el) { }; } + if (typeof options == 'object') { + pickerOptions = mQuery.extend(pickerOptions, options); + } + mQuery(el).minicolors(pickerOptions); }; diff --git a/app/bundles/CoreBundle/Assets/js/4.builder.js b/app/bundles/CoreBundle/Assets/js/4.builder.js index b18d7405ee1..41134c201c2 100644 --- a/app/bundles/CoreBundle/Assets/js/4.builder.js +++ b/app/bundles/CoreBundle/Assets/js/4.builder.js @@ -2,6 +2,7 @@ * Launch builder * * @param formName + * @param actionName */ Mautic.launchBuilder = function (formName, actionName) { var builder = mQuery('.builder'); @@ -64,17 +65,17 @@ Mautic.launchBuilder = function (formName, actionName) { Mautic.keepPreviewAlive('builder-template-content'); } - var builderPanel = mQuery('.builder-panel') - builderContent = mQuery('.builder-content') - btnCloseBuilder = mQuery('.btn-close-builder') - panelHeight = (builderContent.css('right') == '0px') ? builderPanel.height() : 0, - panelWidth = (builderContent.css('right') == '0px') ? 0 : builderPanel.width(), - spinnerLeft = (mQuery(window).width() - panelWidth - 60) / 2, - spinnerTop = (mQuery(window).height() - panelHeight - 60) / 2; + var builderPanel = mQuery('.builder-panel'); + var builderContent = mQuery('.builder-content'); + var btnCloseBuilder = mQuery('.btn-close-builder'); + var panelHeight = (builderContent.css('right') == '0px') ? builderPanel.height() : 0; + var panelWidth = (builderContent.css('right') == '0px') ? 0 : builderPanel.width(); + var spinnerLeft = (mQuery(window).width() - panelWidth - 60) / 2; + var spinnerTop = (mQuery(window).height() - panelHeight - 60) / 2; // Blur and focus the focussed inputs to fix the browser autocomplete bug on scroll builderPanel.on('scroll', function(e) { - builderPanel.find('input:focus').blur().focus(); + builderPanel.find('input:focus').blur(); }); var overlay = mQuery('').css(builderCss).appendTo('.builder-content'); @@ -86,6 +87,9 @@ Mautic.launchBuilder = function (formName, actionName) { var assets = Mautic.htmlspecialchars_decode(mQuery('[data-builder-assets]').html()); themeHtml = themeHtml.replace('', assets+''); + // Turn Dynamic Content Tokens into builder slots + themeHtml = Mautic.prepareDynamicContentBlocksForBuilder(themeHtml); + Mautic.buildBuilderIframe(themeHtml, 'builder-template-content', function() { mQuery('#builder-overlay').addClass('hide'); btnCloseBuilder.prop('disabled', false); @@ -144,8 +148,9 @@ Mautic.openServerBrowser = function(url, width, height) { * Creates an iframe and keeps its content live from CodeMirror changes * * @param iframeId + * @param slot */ -Mautic.keepPreviewAlive = function(iframeId) { +Mautic.keepPreviewAlive = function(iframeId, slot) { var codeChanged = false; // Watch for code changes Mautic.builderCodeMirror.on('change', function(cm, change) { @@ -154,7 +159,8 @@ Mautic.keepPreviewAlive = function(iframeId) { window.setInterval(function() { if (codeChanged) { - Mautic.livePreviewInterval = Mautic.updateIframeContent(iframeId, Mautic.builderCodeMirror.getValue()); + var value = (Mautic.builderCodeMirror)?Mautic.builderCodeMirror.getValue():''; + Mautic.livePreviewInterval = Mautic.updateIframeContent(iframeId, value, slot); codeChanged = false; } }, 2000); @@ -289,15 +295,20 @@ Mautic.initSelectTheme = function(themeField) { /** * Updates content of an iframe * - * @param iframe ID - * @param HTML content + * @param iframeId ID + * @param content HTML content + * @param slot */ -Mautic.updateIframeContent = function(iframeId, content) { - var iframe = document.getElementById(iframeId); - var doc = iframe.contentDocument || iframe.contentWindow.document; - doc.open(); - doc.write(content); - doc.close(); +Mautic.updateIframeContent = function(iframeId, content, slot) { + if (iframeId) { + var iframe = document.getElementById(iframeId); + var doc = iframe.contentDocument || iframe.contentWindow.document; + doc.open(); + doc.write(content); + doc.close(); + } else if (slot) { + slot.html(content); + } }; /** @@ -356,6 +367,9 @@ Mautic.closeBuilder = function(model) { customHtml = themeHtml.find('html').get(0).outerHTML } + // Convert dynamic slot definitions into tokens + customHtml = Mautic.convertDynamicContentSlotsToTokens(customHtml); + // Store the HTML content to the HTML textarea mQuery('.builder-html').val(customHtml); } catch (error) { @@ -642,7 +656,7 @@ Mautic.initSections = function() { mQuery('#builder-template-content', parent.document).css('overflow', 'visible'); mQuery('#builder-template-content', parent.document).attr('scrolling', 'yes'); - }, + } }).disableSelection(); // Initialize the slots @@ -667,7 +681,7 @@ Mautic.sectionBackgroundChanged = function(element, color) { Mautic.setTextSlotEditorStyle(parent.mQuery('#slot_text_content'), focusedSlot); } }); -} +}; Mautic.rgb2hex = function(orig) { var rgb = orig.replace(/\s/g,'').match(/^rgba?\((\d+),(\d+),(\d+)/i); @@ -675,7 +689,7 @@ Mautic.rgb2hex = function(orig) { ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : orig; -} +}; Mautic.initSlots = function(slotContainers) { if (!slotContainers) { @@ -689,6 +703,7 @@ Mautic.initSlots = function(slotContainers) { // Make slots sortable var bodyOverflow = {}; Mautic.sortActive = false; + Mautic.parentDocument = parent.document; slotContainers.sortable({ helper: function(e, ui) { @@ -740,7 +755,7 @@ Mautic.initSlots = function(slotContainers) { } Mautic.sortActive = false; - }, + } }); // Allow to drag&drop new slots from the slot type menu @@ -751,46 +766,132 @@ Mautic.initSlots = function(slotContainers) { revert: 'invalid', iframeOffset: iframe.offset(), helper: function(e, ui) { + // fix for Uncaught TypeError: Cannot read property 'document' of null // Fix body overflow that messes sortable up - bodyOverflow.overflowX = mQuery('body', parent.document).css('overflow-x'); - bodyOverflow.overflowY = mQuery('body', parent.document).css('overflow-y'); - mQuery('body', parent.document).css({ + bodyOverflow.overflowX = mQuery('body', Mautic.parentDocument).css('overflow-x'); + bodyOverflow.overflowY = mQuery('body', Mautic.parentDocument).css('overflow-y'); + mQuery('body', Mautic.parentDocument).css({ overflowX: 'hidden', overflowY: 'hidden' }); - var helper = mQuery(this).clone() + return mQuery(this).clone() .css('height', mQuery(this).height()) .css('width', mQuery(this).width()); - - return helper; }, zIndex: 8000, cursorAt: {top: 15, left: 15}, start: function(event, ui) { - mQuery('#builder-template-content', parent.document).css('overflow', 'hidden'); - mQuery('#builder-template-content', parent.document).attr('scrolling', 'no'); - slotContainers.sortable('option', 'scroll', false); + mQuery('#builder-template-content', Mautic.parentDocument).css('overflow', 'hidden'); + mQuery('#builder-template-content', Mautic.parentDocument).attr('scrolling', 'no'); + // check if it is initialized first to prevent error + if (slotContainers.data('sortable')) slotContainers.sortable('option', 'scroll', false); }, stop: function(event, ui) { // Restore original overflow - mQuery('body', parent.document).css(bodyOverflow); + mQuery('body', Mautic.parentDocument).css(bodyOverflow); - mQuery('#builder-template-content', parent.document).css('overflow', 'visible'); - mQuery('#builder-template-content', parent.document).attr('scrolling', 'yes'); - slotContainers.sortable('option', 'scroll', true); - }, + mQuery('#builder-template-content', Mautic.parentDocument).css('overflow', 'visible'); + mQuery('#builder-template-content', Mautic.parentDocument).attr('scrolling', 'yes'); + // check if it is initialized first to prevent error + if (slotContainers.data('sortable')) slotContainers.sortable('option', 'scroll', true); + } }).disableSelection(); iframe.on('scroll', function() { - mQuery('#slot-type-container .slot-type-handle', parent.document).draggable("option", "cursorAt", { top: -1 * iframe.scrollTop() + 15 }); + mQuery('#slot-type-container .slot-type-handle', Mautic.parentDocument).draggable("option", "cursorAt", { top: -1 * iframe.scrollTop() + 15 }); }); // Initialize the slots slotContainers.find('[data-slot]').each(function() { mQuery(this).trigger('slot:init', this); }); -} +}; + +Mautic.getSlotToolbar = function() { + Mautic.builderContents.find('[data-slot-toolbar]').remove(); + + var slotToolbar = mQuery('
').attr('data-slot-toolbar', true); + var deleteLink = Mautic.getSlotDeleteLink(); + + deleteLink.appendTo(slotToolbar); + + return slotToolbar; +}; + +Mautic.getSlotDeleteLink = function() { + if (typeof Mautic.deleteLink == 'undefined') { + Mautic.deleteLink = mQuery('') + .attr('data-slot-action', 'delete') + .attr('alt', 'delete') + .addClass('btn btn-delete btn-default'); + } + + return Mautic.deleteLink; +}; + +Mautic.getSlotFocus = function() { + Mautic.builderContents.find('[data-slot-focus]').remove(); + + return mQuery('
').attr('data-slot-focus', true); +}; + +Mautic.cloneFocusForm = function(decId, removeFroala) { + // reattach DEC + if (typeof Mautic.activeDEC !== 'undefined') { + var element = Mautic.activeDEC.detach(); + element.hide(); + Mautic.activeDECParent.append(element); + } + var focusForm = parent.mQuery('#emailform_dynamicContent_' + decId); + Mautic.activeDECParent = focusForm.parent(); + // show if hidden + focusForm.removeClass('fade'); + // remove delete default button + focusForm.find('.tab-pane:first').find('.remove-item').hide(); + var element =focusForm.detach(); + element.show(); + Mautic.activeDEC = element; + return element; +}; + +Mautic.initEmailDynamicContentSlotEdit = function (clickedSlot) { + var decId = clickedSlot.attr('data-param-dec-id'); + + var focusForm; + + if (decId || decId === 0) { + focusForm = Mautic.cloneFocusForm(decId); + } + + var focusFormHeader = parent.mQuery('#customize-slot-panel').find('.panel-heading h4'); + var newDynConButton = mQuery(' +
+
\ No newline at end of file diff --git a/app/bundles/ReportBundle/Views/FormTheme/Report/aggregator_row.html.php b/app/bundles/ReportBundle/Views/FormTheme/Report/aggregator_row.html.php new file mode 100644 index 00000000000..4ca591a6a7c --- /dev/null +++ b/app/bundles/ReportBundle/Views/FormTheme/Report/aggregator_row.html.php @@ -0,0 +1,10 @@ +vars['label_attr']['class'])) ? 'control-label' : $form->vars['label_attr']['class']; +?> +
+ row($form->vars['form']->children['function']); ?> + row($form->vars['form']->children['column']); ?> +
+ +
+
\ No newline at end of file diff --git a/app/bundles/ReportBundle/Views/Report/details_data.html.php b/app/bundles/ReportBundle/Views/Report/details_data.html.php index 2365e07135e..3f1ec0d26f2 100644 --- a/app/bundles/ReportBundle/Views/Report/details_data.html.php +++ b/app/bundles/ReportBundle/Views/Report/details_data.html.php @@ -12,13 +12,33 @@ $view->extend('MauticReportBundle:Report:details.html.php'); } -$dataCount = count($data); -$columnOrder = $report->getColumns(); -$graphOrder = $report->getGraphs(); -$startCount = ($dataCount > $limit) ? ($reportPage * $limit) - ($dataCount - 1) : 1; +$dataCount = count($data); +$columnOrder = $report->getColumns(); +$graphOrder = $report->getGraphs(); +$aggregatorOrder = $report->getAggregators(); +$aggregatorCount = count($aggregatorOrder); +$groupBy = $report->getGroupBy(); +$groupByCount = count($groupBy); +$startCount = ($totalResults > $limit) ? ($reportPage * $limit) - $limit + 1 : 1; +function getTotal($a, $f, $t, $allrows, $ac) +{ + switch ($f) { + case 'COUNT': + case 'SUM': + return (int) $t + (int) $a; + case 'AVG': + return ($ac == $allrows) ? round(((int) $t + (int) $a) / (int) $allrows, 2) : (int) $t + (int) $a; + case 'MAX': + return ((int) $a >= (int) $t) ? (int) $a : (int) $t; + case 'MIN': + return ((int) $a <= (int) $t) ? (int) $a : (int) $t; + default: + return (int) $t; + } +} ?> - +
@@ -27,6 +47,7 @@ + + + render('MauticCoreBundle:Helper:tableheader.html.php', [ + 'sessionVar' => 'report.'.$report->getId(), + 'orderBy' => $aggregator['function'], + 'text' => $aggregator['function'].' '.$columnName, + 'dataToggle' => '', + 'target' => '.report-content', + ]); + ?> + - + - - - + + + - - - _($row[$columns[$key]['alias']], $columns[$key]['type']); ?> - - + + + _($row[$columns[$key]['alias']], $columns[$key]['type']); ?> + + + + + _($row[$aggregator['function'].' '.$aggregator['column']], 'text'); + $total[$index] = getTotal($row[$aggregator['function'].' '.$aggregator['column']], $aggregator['function'], (isset($total[$index])) ? $total[$index] : 0, $dataCount, $avgCounter); + } + ?> + + + @@ -80,6 +142,27 @@ + + trans('mautic.report.report.groupby.totals'); ?> + +   + + + _($total[$index], 'text'); + endif; + ?> + + +
diff --git a/app/bundles/ReportBundle/Views/Report/form.html.php b/app/bundles/ReportBundle/Views/Report/form.html.php index f2fee786354..79aa977ee72 100644 --- a/app/bundles/ReportBundle/Views/Report/form.html.php +++ b/app/bundles/ReportBundle/Views/Report/form.html.php @@ -34,9 +34,7 @@
  • trans('mautic.report.tab.data'); ?>
  • -
  • +
  • trans('mautic.report.tab.graphs'); ?>
  • @@ -74,7 +72,7 @@
    -
    +

    trans('mautic.core.order'); ?>

    row($form['tableOrder']); ?>
    @@ -93,11 +91,22 @@
    +
    +
    +
    +

    trans('mautic.report.form.groupby'); ?>

    + row($form['groupBy']); ?> +
    +
    +
    +
    +

    trans('mautic.core.calculated.fields'); ?>

    + row($form['aggregators']); ?> +
    +
    +
    - -
    +
    diff --git a/app/bundles/SmsBundle/Api/TwilioApi.php b/app/bundles/SmsBundle/Api/TwilioApi.php index b23aa58a76d..513746cb303 100644 --- a/app/bundles/SmsBundle/Api/TwilioApi.php +++ b/app/bundles/SmsBundle/Api/TwilioApi.php @@ -16,6 +16,7 @@ use libphonenumber\PhoneNumberUtil; use Mautic\CoreBundle\Helper\PhoneNumberHelper; use Mautic\PageBundle\Model\TrackableModel; +use Mautic\PluginBundle\Helper\IntegrationHelper; use Monolog\Logger; class TwilioApi extends AbstractSmsApi @@ -39,18 +40,22 @@ class TwilioApi extends AbstractSmsApi * TwilioApi constructor. * * @param TrackableModel $pageTrackableModel - * @param \Services_Twilio $client * @param PhoneNumberHelper $phoneNumberHelper - * @param $sendingPhoneNumber + * @param IntegrationHelper $integrationHelper * @param Logger $logger */ - public function __construct(TrackableModel $pageTrackableModel, \Services_Twilio $client, PhoneNumberHelper $phoneNumberHelper, $sendingPhoneNumber, Logger $logger) + public function __construct(TrackableModel $pageTrackableModel, PhoneNumberHelper $phoneNumberHelper, IntegrationHelper $integrationHelper, Logger $logger) { - $this->client = $client; $this->logger = $logger; - if ($sendingPhoneNumber) { - $this->sendingPhoneNumber = $phoneNumberHelper->format($sendingPhoneNumber); + $integration = $integrationHelper->getIntegrationObject('Twilio'); + + if ($integration && $integration->getIntegrationSettings()->getIsPublished()) { + $this->sendingPhoneNumber = $integration->getIntegrationSettings()->getFeatureSettings()['sending_phone_number']; + + $keys = $integration->getDecryptedApiKeys(); + + $this->client = new \Services_Twilio($keys['username'], $keys['password']); } parent::__construct($pageTrackableModel); diff --git a/app/bundles/SmsBundle/Assets/img/Twilio.png b/app/bundles/SmsBundle/Assets/img/Twilio.png new file mode 100644 index 00000000000..96bdf83a55f Binary files /dev/null and b/app/bundles/SmsBundle/Assets/img/Twilio.png differ diff --git a/app/bundles/SmsBundle/Config/config.php b/app/bundles/SmsBundle/Config/config.php index 13ed1e8884e..61e7ee60c86 100644 --- a/app/bundles/SmsBundle/Config/config.php +++ b/app/bundles/SmsBundle/Config/config.php @@ -15,13 +15,10 @@ 'mautic.sms.campaignbundle.subscriber' => [ 'class' => 'Mautic\SmsBundle\EventListener\CampaignSubscriber', 'arguments' => [ - 'mautic.helper.core_parameters', + 'mautic.helper.integration', 'mautic.sms.model.sms', ], ], - 'mautic.sms.configbundle.subscriber' => [ - 'class' => 'Mautic\SmsBundle\EventListener\ConfigSubscriber', - ], 'mautic.sms.smsbundle.subscriber' => [ 'class' => 'Mautic\SmsBundle\EventListener\SmsSubscriber', 'arguments' => [ @@ -32,7 +29,10 @@ ], ], 'mautic.sms.channel.subscriber' => [ - 'class' => \Mautic\SmsBundle\EventListener\ChannelSubscriber::class, + 'class' => \Mautic\SmsBundle\EventListener\ChannelSubscriber::class, + 'arguments' => [ + 'mautic.helper.integration', + ], ], 'mautic.sms.message_queue.subscriber' => [ 'class' => \Mautic\SmsBundle\EventListener\MessageQueueSubscriber::class, @@ -75,7 +75,7 @@ 'mautic.lead.model.lead', 'mautic.helper.phone_number', 'mautic.sms.model.sms', - '%mautic.sms_frequency_number%', + 'mautic.helper.integration', ], 'alias' => 'sms_helper', ], @@ -85,21 +85,12 @@ 'class' => 'Mautic\SmsBundle\Api\TwilioApi', 'arguments' => [ 'mautic.page.model.trackable', - 'mautic.twilio.service', 'mautic.helper.phone_number', - '%mautic.sms_sending_phone_number%', + 'mautic.helper.integration', 'monolog.logger.mautic', ], 'alias' => 'sms_api', ], - 'mautic.twilio.service' => [ - 'class' => 'Services_Twilio', - 'arguments' => [ - '%mautic.sms_username%', - '%mautic.sms_password%', - ], - 'alias' => 'twilio_service', - ], ], 'models' => [ 'mautic.sms.model.sms' => [ @@ -151,8 +142,10 @@ 'access' => ['sms:smses:viewown', 'sms:smses:viewother'], 'parent' => 'mautic.core.channels', 'checks' => [ - 'parameters' => [ - 'sms_enabled' => true, + 'integration' => [ + 'Twilio' => [ + 'enabled' => true, + ], ], ], 'priority' => 70, diff --git a/app/bundles/SmsBundle/Controller/SmsController.php b/app/bundles/SmsBundle/Controller/SmsController.php index db00c20e78c..1f30b60b570 100644 --- a/app/bundles/SmsBundle/Controller/SmsController.php +++ b/app/bundles/SmsBundle/Controller/SmsController.php @@ -58,15 +58,6 @@ public function indexAction($page = 1) $session = $this->get('session'); - $listFilters = [ - 'filters' => [ - 'multiple' => true, - ], - ]; - - // Reset available groups - $listFilters['filters']['groups'] = []; - //set limits $limit = $session->get('mautic.sms.limit', $this->coreParametersHelper->getParameter('default_pagelimit')); $start = ($page === 1) ? 0 : (($page - 1) * $limit); @@ -75,90 +66,17 @@ public function indexAction($page = 1) } $search = $this->request->get('search', $session->get('mautic.sms.filter', '')); - $session->set('mautic.email.filter', $search); + $session->set('mautic.sms.filter', $search); $filter = ['string' => $search]; if (!$permissions['sms:smses:viewother']) { $filter['force'][] = - ['column' => 'e.createdBy', 'expr' => 'eq', 'value' => $this->user->getId()]; - } - - //retrieve a list of categories - $listFilters['filters']['groups']['mautic.core.filter.categories'] = [ - 'options' => $this->getModel('category')->getLookupResults('email', '', 0), - 'prefix' => 'category', - ]; - - //retrieve a list of Lead Lists - $listFilters['filters']['groups']['mautic.core.filter.lists'] = [ - 'options' => $this->getModel('lead.list')->getUserLists(), - 'prefix' => 'list', - ]; - - //retrieve a list of themes - $listFilters['filters']['groups']['mautic.core.filter.themes'] = [ - 'options' => $this->factory->getInstalledThemes('email'), - 'prefix' => 'theme', - ]; - - $currentFilters = $session->get('mautic.sms.list_filters', []); - $updatedFilters = $this->request->get('filters', false); - - if ($updatedFilters) { - // Filters have been updated - - // Parse the selected values - $newFilters = []; - $updatedFilters = json_decode($updatedFilters, true); - - if ($updatedFilters) { - foreach ($updatedFilters as $updatedFilter) { - list($clmn, $fltr) = explode(':', $updatedFilter); - - $newFilters[$clmn][] = $fltr; - } - - $currentFilters = $newFilters; - } else { - $currentFilters = []; - } - } - $session->set('mautic.sms.list_filters', $currentFilters); - - if (!empty($currentFilters)) { - $listIds = $catIds = []; - foreach ($currentFilters as $type => $typeFilters) { - switch ($type) { - case 'list': - $key = 'lists'; - break; - case 'category': - $key = 'categories'; - break; - } - - $listFilters['filters']['groups']['mautic.core.filter.'.$key]['values'] = $typeFilters; - - foreach ($typeFilters as $fltr) { - switch ($type) { - case 'list': - $listIds[] = (int) $fltr; - break; - case 'category': - $catIds[] = (int) $fltr; - break; - } - } - } - - if (!empty($listIds)) { - $filter['force'][] = ['column' => 'l.id', 'expr' => 'in', 'value' => $listIds]; - } - - if (!empty($catIds)) { - $filter['force'][] = ['column' => 'c.id', 'expr' => 'in', 'value' => $catIds]; - } + [ + 'column' => 'e.createdBy', + 'expr' => 'eq', + 'value' => $this->user->getId(), + ]; } $orderBy = $session->get('mautic.sms.orderby', 'e.name'); @@ -196,10 +114,11 @@ public function indexAction($page = 1) } $session->set('mautic.sms.page', $page); + $integration = $this->get('mautic.helper.integration')->getIntegrationObject('Twilio'); + return $this->delegateView([ 'viewParameters' => [ 'searchValue' => $search, - 'filters' => $listFilters, 'items' => $smss, 'totalItems' => $count, 'page' => $page, @@ -208,7 +127,7 @@ public function indexAction($page = 1) 'permissions' => $permissions, 'model' => $model, 'security' => $this->get('mautic.security'), - 'configured' => $this->coreParametersHelper->getParameter('sms_enabled'), + 'configured' => ($integration && $integration->getIntegrationSettings()->getIsPublished()), ], 'contentTemplate' => 'MauticSmsBundle:Sms:list.html.php', 'passthroughVars' => [ diff --git a/app/bundles/SmsBundle/EventListener/CampaignSubscriber.php b/app/bundles/SmsBundle/EventListener/CampaignSubscriber.php index 8fdd323d57d..bd3e1c6a8e9 100644 --- a/app/bundles/SmsBundle/EventListener/CampaignSubscriber.php +++ b/app/bundles/SmsBundle/EventListener/CampaignSubscriber.php @@ -15,7 +15,7 @@ use Mautic\CampaignBundle\Event\CampaignBuilderEvent; use Mautic\CampaignBundle\Event\CampaignExecutionEvent; use Mautic\CoreBundle\EventListener\CommonSubscriber; -use Mautic\CoreBundle\Helper\CoreParametersHelper; +use Mautic\PluginBundle\Helper\IntegrationHelper; use Mautic\SmsBundle\Model\SmsModel; use Mautic\SmsBundle\SmsEvents; @@ -25,9 +25,9 @@ class CampaignSubscriber extends CommonSubscriber { /** - * @var CoreParametersHelper + * @var IntegrationHelper */ - protected $coreParametersHelper; + protected $integrationHelper; /** * @var SmsModel @@ -37,15 +37,15 @@ class CampaignSubscriber extends CommonSubscriber /** * CampaignSubscriber constructor. * - * @param CoreParametersHelper $coreParametersHelper - * @param SmsModel $smsModel + * @param IntegrationHelper $integrationHelper + * @param SmsModel $smsModel */ public function __construct( - CoreParametersHelper $coreParametersHelper, + IntegrationHelper $integrationHelper, SmsModel $smsModel ) { - $this->coreParametersHelper = $coreParametersHelper; - $this->smsModel = $smsModel; + $this->integrationHelper = $integrationHelper; + $this->smsModel = $smsModel; } /** @@ -64,7 +64,9 @@ public static function getSubscribedEvents() */ public function onCampaignBuild(CampaignBuilderEvent $event) { - if ($this->coreParametersHelper->getParameter('sms_enabled')) { + $integration = $this->integrationHelper->getIntegrationObject('Twilio'); + + if ($integration && $integration->getIntegrationSettings()->getIsPublished()) { $event->addAction( 'sms.send_text_sms', [ @@ -84,6 +86,8 @@ public function onCampaignBuild(CampaignBuilderEvent $event) /** * @param CampaignExecutionEvent $event + * + * @return mixed */ public function onCampaignTriggerAction(CampaignExecutionEvent $event) { diff --git a/app/bundles/SmsBundle/EventListener/ChannelSubscriber.php b/app/bundles/SmsBundle/EventListener/ChannelSubscriber.php index 3a5e4f1473d..5006b1ce4a0 100644 --- a/app/bundles/SmsBundle/EventListener/ChannelSubscriber.php +++ b/app/bundles/SmsBundle/EventListener/ChannelSubscriber.php @@ -16,6 +16,7 @@ use Mautic\ChannelBundle\Model\MessageModel; use Mautic\CoreBundle\EventListener\CommonSubscriber; use Mautic\LeadBundle\Model\LeadModel; +use Mautic\PluginBundle\Helper\IntegrationHelper; use Mautic\ReportBundle\Model\ReportModel; /** @@ -23,6 +24,21 @@ */ class ChannelSubscriber extends CommonSubscriber { + /** + * @var IntegrationHelper + */ + protected $integrationHelper; + + /** + * ChannelSubscriber constructor. + * + * @param IntegrationHelper $integrationHelper + */ + public function __construct(IntegrationHelper $integrationHelper) + { + $this->integrationHelper = $integrationHelper; + } + /** * @return array */ @@ -38,7 +54,9 @@ public static function getSubscribedEvents() */ public function onAddChannel(ChannelEvent $event) { - if (!empty($this->params['sms_enabled'])) { + $integration = $this->integrationHelper->getIntegrationObject('Twilio'); + + if ($integration && $integration->getIntegrationSettings()->getIsPublished()) { $event->addChannel( 'sms', [ diff --git a/app/bundles/SmsBundle/EventListener/ConfigSubscriber.php b/app/bundles/SmsBundle/EventListener/ConfigSubscriber.php deleted file mode 100644 index 5a403c374ad..00000000000 --- a/app/bundles/SmsBundle/EventListener/ConfigSubscriber.php +++ /dev/null @@ -1,42 +0,0 @@ - ['onConfigGenerate', 0], - ]; - } - - public function onConfigGenerate(ConfigBuilderEvent $event) - { - $event->addForm([ - 'bundle' => 'SmsBundle', - 'formAlias' => 'smsconfig', - 'formTheme' => 'MauticSmsBundle:FormTheme\Config', - 'parameters' => $event->getParametersFromConfig('MauticSmsBundle'), - ]); - } -} diff --git a/app/bundles/SmsBundle/EventListener/MessageQueueSubscriber.php b/app/bundles/SmsBundle/EventListener/MessageQueueSubscriber.php index 2c08423b9c0..11634feb03c 100644 --- a/app/bundles/SmsBundle/EventListener/MessageQueueSubscriber.php +++ b/app/bundles/SmsBundle/EventListener/MessageQueueSubscriber.php @@ -18,7 +18,7 @@ use Mautic\SmsBundle\Model\SmsModel; /** - * Class CalendarSubscriber. + * Class MessageQueueSubscriber. */ class MessageQueueSubscriber extends CommonSubscriber { diff --git a/app/bundles/SmsBundle/Form/Type/ConfigType.php b/app/bundles/SmsBundle/Form/Type/ConfigType.php deleted file mode 100644 index f2afb149de7..00000000000 --- a/app/bundles/SmsBundle/Form/Type/ConfigType.php +++ /dev/null @@ -1,146 +0,0 @@ -add( - 'sms_enabled', - 'yesno_button_group', - [ - 'label' => 'mautic.sms.config.form.sms.enabled', - 'data' => (bool) $options['data']['sms_enabled'], - 'attr' => [ - 'tooltip' => 'mautic.sms.config.form.sms.enabled.tooltip', - ], - ] - ); - - $formModifier = function (FormEvent $event) { - $form = $event->getForm(); - $data = $event->getData(); - - // Add required restraints if sms is enabled - $constraints = (empty($data['sms_enabled'])) ? - [] : - [ - new NotBlank( - [ - 'message' => 'mautic.core.value.required', - ] - ), - ]; - - $form->add( - 'sms_username', - 'text', - [ - 'label' => 'mautic.sms.config.form.sms.username', - 'attr' => [ - 'tooltip' => 'mautic.sms.config.form.sms.username.tooltip', - 'class' => 'form-control', - 'data-show-on' => '{"config_smsconfig_sms_enabled_1":"checked"}', - ], - 'constraints' => $constraints, - ] - ); - - $form->add( - 'sms_password', - 'text', - [ - 'label' => 'mautic.sms.config.form.sms.password', - 'attr' => [ - 'tooltip' => 'mautic.sms.config.form.sms.password.tooltip', - 'class' => 'form-control', - 'data-show-on' => '{"config_smsconfig_sms_enabled_1":"checked"}', - ], - 'constraints' => $constraints, - ] - ); - - $form->add( - 'sms_sending_phone_number', - 'text', - [ - 'label' => 'mautic.sms.config.form.sms.sending_phone_number', - 'attr' => [ - 'tooltip' => 'mautic.sms.config.form.sms.sending_phone_number.tooltip', - 'class' => 'form-control', - 'data-show-on' => '{"config_smsconfig_sms_enabled_1":"checked"}', - ], - 'constraints' => $constraints, - ] - ); - }; - $builder->add('sms_frequency_number', 'number', - [ - 'precision' => 0, - 'label' => 'mautic.sms.list.frequency.number', - 'label_attr' => ['class' => 'control-label'], - 'required' => false, - 'attr' => [ - 'class' => 'form-control frequency', - ], - ]); - $builder->add('sms_frequency_time', 'choice', - [ - 'choices' => [ - 'DAY' => 'day', - 'WEEK' => 'week', - 'MONTH' => 'month', - ], - 'label' => 'mautic.lead.list.frequency.times', - 'label_attr' => ['class' => 'control-label'], - 'required' => false, - 'multiple' => false, - 'attr' => [ - 'class' => 'form-control frequency', - ], - ]); - - // Before submit - $builder->addEventListener( - FormEvents::PRE_SUBMIT, - $formModifier - ); - - // After submit - $builder->addEventListener( - FormEvents::PRE_SET_DATA, - $formModifier - ); - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'smsconfig'; - } -} diff --git a/app/bundles/SmsBundle/Helper/SmsHelper.php b/app/bundles/SmsBundle/Helper/SmsHelper.php index 49c40cfa370..0bb4cf7d94a 100644 --- a/app/bundles/SmsBundle/Helper/SmsHelper.php +++ b/app/bundles/SmsBundle/Helper/SmsHelper.php @@ -15,8 +15,8 @@ use libphonenumber\PhoneNumberFormat; use Mautic\CoreBundle\Helper\PhoneNumberHelper; use Mautic\LeadBundle\Entity\DoNotContact; -use Mautic\LeadBundle\Entity\Lead; use Mautic\LeadBundle\Model\LeadModel; +use Mautic\PluginBundle\Helper\IntegrationHelper; use Mautic\SmsBundle\Model\SmsModel; class SmsHelper @@ -42,9 +42,9 @@ class SmsHelper protected $smsModel; /** - * @var int + * @var IntegrationHelper */ - protected $smsFrequencyNumber; + protected $integrationHelper; /** * SmsHelper constructor. @@ -53,15 +53,18 @@ class SmsHelper * @param LeadModel $leadModel * @param PhoneNumberHelper $phoneNumberHelper * @param SmsModel $smsModel - * @param int $smsFrequencyNumber + * @param IntegrationHelper $integrationHelper */ - public function __construct(EntityManager $em, LeadModel $leadModel, PhoneNumberHelper $phoneNumberHelper, SmsModel $smsModel, $smsFrequencyNumber) + public function __construct(EntityManager $em, LeadModel $leadModel, PhoneNumberHelper $phoneNumberHelper, SmsModel $smsModel, IntegrationHelper $integrationHelper) { $this->em = $em; $this->leadModel = $leadModel; $this->phoneNumberHelper = $phoneNumberHelper; $this->smsModel = $smsModel; - $this->smsFrequencyNumber = $smsFrequencyNumber; + $this->integrationHelper = $integrationHelper; + $integration = $integrationHelper->getIntegrationObject('Twilio'); + $settings = $integration->getIntegrationSettings()->getFeatureSettings(); + $this->smsFrequencyNumber = $settings['frequency_number']; } public function unsubscribe($number) diff --git a/app/bundles/SmsBundle/Integration/TwilioIntegration.php b/app/bundles/SmsBundle/Integration/TwilioIntegration.php new file mode 100644 index 00000000000..d34b0b45a75 --- /dev/null +++ b/app/bundles/SmsBundle/Integration/TwilioIntegration.php @@ -0,0 +1,127 @@ + 'mautic.sms.config.form.sms.username', + 'password' => 'mautic.sms.config.form.sms.password', + ]; + } + + /** + * @return array + */ + public function getFormSettings() + { + return [ + 'requires_callback' => false, + 'requires_authorization' => false, + ]; + } + /** + * {@inheritdoc} + * + * @return string + */ + public function getAuthenticationType() + { + return 'none'; + } + + /** + * @param \Mautic\PluginBundle\Integration\Form|FormBuilder $builder + * @param array $data + * @param string $formArea + */ + public function appendToForm(&$builder, $data, $formArea) + { + if ($formArea == 'features') { + $builder->add( + 'sending_phone_number', + 'text', + [ + 'label' => 'mautic.sms.config.form.sms.sending_phone_number', + 'label_attr' => ['class' => 'control-label'], + 'required' => false, + 'attr' => [ + 'class' => 'form-control', + ], + ] + ); + $builder->add('frequency_number', 'number', + [ + 'precision' => 0, + 'label' => 'mautic.sms.list.frequency.number', + 'label_attr' => ['class' => 'control-label'], + 'required' => false, + 'attr' => [ + 'class' => 'form-control frequency', + ], + ]); + $builder->add('frequency_time', 'choice', + [ + 'choices' => [ + 'DAY' => 'day', + 'WEEK' => 'week', + 'MONTH' => 'month', + ], + 'label' => 'mautic.lead.list.frequency.times', + 'label_attr' => ['class' => 'control-label'], + 'required' => false, + 'multiple' => false, + 'attr' => [ + 'class' => 'form-control frequency', + ], + ]); + } + } +} diff --git a/app/bundles/SmsBundle/Translations/en_US/messages.ini b/app/bundles/SmsBundle/Translations/en_US/messages.ini index a804c1e79bd..6f8b24e2698 100644 --- a/app/bundles/SmsBundle/Translations/en_US/messages.ini +++ b/app/bundles/SmsBundle/Translations/en_US/messages.ini @@ -19,12 +19,6 @@ mautic.sms.smses="Text Messages" mautic.sms.campaign.send_sms="Send Push Text Message" mautic.sms.campaign.send_sms.tooltip="Sends a push sms to the user." mautic.sms.choose.smss="Select a text message to send." -mautic.sms.config.form.sms.app_id="Text Messages Provider App ID" -mautic.sms.config.form.sms.app_id.tooltip="One Signal App ID" -mautic.sms.config.form.sms.rest_api_key="Text Messages Provider API Key" -mautic.sms.config.form.sms.rest_api_key.tooltip="One Signal Rest API Key" -mautic.sms.config.form.sms.sms_safari_web_id="Text Messages Provider Safari Web ID" -mautic.sms.config.form.sms.sms_safari_web_id.tooltip="One Signal Safari Web ID for your One Signal App" mautic.sms.form.action.sendsms.admin="Send sms to user" mautic.sms.form.action.sendsms.admin.descr="Send the selected sms to the selected user(s) upon form submission." diff --git a/app/bundles/SmsBundle/Views/FormTheme/Config/_config_smsconfig_widget.html.php b/app/bundles/SmsBundle/Views/FormTheme/Config/_config_smsconfig_widget.html.php deleted file mode 100644 index 3f6ea3f8690..00000000000 --- a/app/bundles/SmsBundle/Views/FormTheme/Config/_config_smsconfig_widget.html.php +++ /dev/null @@ -1,45 +0,0 @@ - - -
    -
    -

    trans('mautic.config.tab.smsconfig'); ?>

    -
    -
    - children as $key => $f): ?> - -
    -
    - row($f); ?> -
    -
    - -
    -
    - -
    -
    -

    trans('mautic.config.tab.frequency_rules'); ?>

    -
    -
    -
    -
    - row($form->children['sms_frequency_number']); ?> -
    -
    - row($form->children['sms_frequency_time']); ?> -
    -
    -
    -
    diff --git a/app/bundles/UserBundle/Security/Provider/UserProvider.php b/app/bundles/UserBundle/Security/Provider/UserProvider.php index 44663a4fb52..987617cd3b9 100644 --- a/app/bundles/UserBundle/Security/Provider/UserProvider.php +++ b/app/bundles/UserBundle/Security/Provider/UserProvider.php @@ -92,6 +92,8 @@ public function loadUserByUsername($username) ->select('u, r') ->leftJoin('u.role', 'r') ->where('u.username = :username OR u.email = :username') + ->andWhere('u.isPublished = :true') + ->setParameter('true', true, 'boolean') ->setParameter('username', $username); if (func_num_args() > 1 && $email = func_get_arg(1)) { diff --git a/app/bundles/WebhookBundle/Config/config.php b/app/bundles/WebhookBundle/Config/config.php index 56ed433c44a..896c912dab5 100644 --- a/app/bundles/WebhookBundle/Config/config.php +++ b/app/bundles/WebhookBundle/Config/config.php @@ -49,18 +49,6 @@ ], ], 'events' => [ - 'mautic.webhook.lead.subscriber' => [ - 'class' => 'Mautic\WebhookBundle\EventListener\LeadSubscriber', - ], - 'mautic.webhook.form.subscriber' => [ - 'class' => 'Mautic\WebhookBundle\EventListener\FormSubscriber', - ], - 'mautic.webhook.email.subscriber' => [ - 'class' => 'Mautic\WebhookBundle\EventListener\EmailSubscriber', - ], - 'mautic.webhook.page.hit.subscriber' => [ - 'class' => 'Mautic\WebhookBundle\EventListener\PageSubscriber', - ], 'mautic.webhook.config.subscriber' => [ 'class' => 'Mautic\WebhookBundle\EventListener\ConfigSubscriber', ], diff --git a/app/bundles/WebhookBundle/Entity/EventRepository.php b/app/bundles/WebhookBundle/Entity/EventRepository.php index bd0837e7701..742daf646d6 100644 --- a/app/bundles/WebhookBundle/Entity/EventRepository.php +++ b/app/bundles/WebhookBundle/Entity/EventRepository.php @@ -16,11 +16,9 @@ class EventRepository extends CommonRepository { /** - * Get a list of events with the webhook. + * @param $type * - * @param array $args - * - * @return Paginator + * @return array */ public function getEntitiesByEventType($type) { diff --git a/app/bundles/WebhookBundle/EventListener/EmailSubscriber.php b/app/bundles/WebhookBundle/EventListener/EmailSubscriber.php deleted file mode 100644 index 4bdb2cc5b77..00000000000 --- a/app/bundles/WebhookBundle/EventListener/EmailSubscriber.php +++ /dev/null @@ -1,46 +0,0 @@ - ['onEmailOpen', 0], - ]; - } - - public function onEmailOpen(EmailOpenEvent $event) - { - $types = [EmailEvents::EMAIL_ON_OPEN]; - - $groups = ['statDetails', 'leadList', 'emailDetails']; - $stat = ($event->getStat()); - - $payload = [ - 'stat' => $stat, - ]; - - $webhooks = $this->getEventWebooksByType($types); - $this->webhookModel->QueueWebhooks($webhooks, $payload, $groups, true); - } -} diff --git a/app/bundles/WebhookBundle/EventListener/FormSubscriber.php b/app/bundles/WebhookBundle/EventListener/FormSubscriber.php deleted file mode 100644 index 77a57f5e00c..00000000000 --- a/app/bundles/WebhookBundle/EventListener/FormSubscriber.php +++ /dev/null @@ -1,52 +0,0 @@ - ['onFormSubmit', 0], - ]; - } - - /* - * Form submit event - * - * @param SubmissionEvent $event - */ - public function onFormSubmit(SubmissionEvent $event) - { - $types = [FormEvents::FORM_ON_SUBMIT]; - - $groups = ['submissionDetails', 'ipAddress', 'leadList', 'pageList', 'formList']; - - $form = $event->getSubmission(); - - $payload = [ - 'submission' => $form, - ]; - - $webhooks = $this->getEventWebooksByType($types); - $this->webhookModel->QueueWebhooks($webhooks, $payload, $groups, true); - } -} diff --git a/app/bundles/WebhookBundle/EventListener/LeadSubscriber.php b/app/bundles/WebhookBundle/EventListener/LeadSubscriber.php deleted file mode 100644 index 24bda43b727..00000000000 --- a/app/bundles/WebhookBundle/EventListener/LeadSubscriber.php +++ /dev/null @@ -1,108 +0,0 @@ - ['onLeadNewUpdate', 0], - LeadEvents::LEAD_POINTS_CHANGE => ['onLeadPointChange', 0], - LeadEvents::LEAD_POST_DELETE => ['onLeadDelete', 0], - //LeadEvents::LEAD_LIST_BATCH_CHANGE => array('onLeadEvent', 0), - //LeadEvents::LEAD_POST_MERGE => array('onLeadEvent', 0), - //LeadEvents::LEAD_IDENTIFIED => array('onLeadEvent', 0), - //LeadEvents::CURRENT_LEAD_CHANGED => array('onLeadEvent', 0) - ]; - } - - /* - * Generic method to execute when a lead does something - */ - public function onLeadNewUpdate(LeadEvent $event) - { - $serializerGroups = ['leadDetails', 'userList', 'publishDetails', 'ipAddress']; - - $entity = $event->getLead(); - - $payload = [ - 'lead' => $entity, - ]; - - // get the leads - if ($event->isNew()) { - // get our new lead webhook events first - $webhookEvents = $this->getEventWebooksByType(LeadEvents::LEAD_POST_SAVE.'_new'); - $this->webhookModel->QueueWebhooks($webhookEvents, $payload, $serializerGroups, true); - } - - // now deal with webhooks for the update event - if (!$event->isNew()) { - $webhookEvents = $this->getEventWebooksByType(LeadEvents::LEAD_POST_SAVE.'_update'); - $this->webhookModel->QueueWebhooks($webhookEvents, $payload, $serializerGroups, true); - } - } - - /* - * Method to execute when the lead point value changes.abstract - * Queues the event payload into the webhook queue so it can be processed - */ - public function onLeadPointChange(PointsChangeEvent $event) - { - /** @var \Mautic\LeadBundle\Entity\Lead $lead */ - $lead = $event->getLead(); - - $serializerGroups = ['leadDetails', 'userList', 'publishDetails', 'ipAddress']; - - $payload = [ - 'lead' => $lead, - 'points' => [ - 'old_points' => $event->getOldPoints(), - 'new_points' => $event->getNewPoints(), - ], - ]; - - $types = [LeadEvents::LEAD_POINTS_CHANGE]; - $webhooks = $this->getEventWebooksByType($types); - $this->webhookModel->QueueWebhooks($webhooks, $payload, $serializerGroups, true); - } - - /* - * Delete lead event - */ - public function onLeadDelete(LeadEvent $event) - { - /** @var \Mautic\LeadBundle\Entity\Lead $lead */ - $lead = $event->getLead(); - - $serializerGroups = ['leadDetails', 'userList', 'publishDetails', 'ipAddress']; - - $payload = [ - 'lead' => $lead, - ]; - - $types = [LeadEvents::LEAD_POST_DELETE]; - $webhooks = $this->getEventWebooksByType($types); - $this->webhookModel->QueueWebhooks($webhooks, $payload, $serializerGroups, true); - } -} diff --git a/app/bundles/WebhookBundle/EventListener/PageSubscriber.php b/app/bundles/WebhookBundle/EventListener/PageSubscriber.php deleted file mode 100644 index 77ff15b3e78..00000000000 --- a/app/bundles/WebhookBundle/EventListener/PageSubscriber.php +++ /dev/null @@ -1,47 +0,0 @@ - ['onPageHit', 0], - ]; - } - - public function onPageHit(PageHitEvent $event) - { - $types = [PageEvents::PAGE_ON_HIT]; - - $groups = ['hitDetails', 'emailDetails', 'pageList', 'leadList']; - - $hit = $event->getHit(); - - $payload = [ - 'hit' => $hit, - ]; - - $webhooks = $this->getEventWebooksByType($types); - $this->webhookModel->QueueWebhooks($webhooks, $payload, $groups, true); - } -} diff --git a/app/bundles/WebhookBundle/EventListener/WebhookModelTrait.php b/app/bundles/WebhookBundle/EventListener/WebhookModelTrait.php new file mode 100644 index 00000000000..462d05fae06 --- /dev/null +++ b/app/bundles/WebhookBundle/EventListener/WebhookModelTrait.php @@ -0,0 +1,33 @@ +webhookModel = $webhookModel; + } +} diff --git a/app/bundles/WebhookBundle/EventListener/WebhookSubscriberBase.php b/app/bundles/WebhookBundle/EventListener/WebhookSubscriberBase.php index f8142c34f2d..cdb9b347592 100644 --- a/app/bundles/WebhookBundle/EventListener/WebhookSubscriberBase.php +++ b/app/bundles/WebhookBundle/EventListener/WebhookSubscriberBase.php @@ -12,22 +12,20 @@ namespace Mautic\WebhookBundle\EventListener; use Mautic\CoreBundle\EventListener\CommonSubscriber; -use Mautic\WebhookBundle\Model\WebhookModel; /** * Class WebhookSubscriberBase. */ class WebhookSubscriberBase extends CommonSubscriber { - /** @var \Mautic\WebhookBundle\Model\WebhookModel $model */ - protected $webhookModel; + use WebhookModelTrait; /** - * @param WebhookModel $webhookModel + * WebhookSubscriberBase constructor. */ - public function setWebhookModel(WebhookModel $webhookModel) + public function __construct() { - $this->webhookModel = $webhookModel; + @trigger_error(self::class.' has been deprecated as of 2.7.1; use trait '.WebhookModelTrait::class.' instead', E_USER_DEPRECATED); } /** @@ -39,8 +37,6 @@ public function setWebhookModel(WebhookModel $webhookModel) */ public function getEventWebooksByType($type) { - $eventWebhooks = $this->webhookModel->getEventWebooksByType($type); - - return $eventWebhooks; + return $this->webhookModel->getEventWebooksByType($type); } } diff --git a/app/bundles/WebhookBundle/Model/WebhookModel.php b/app/bundles/WebhookBundle/Model/WebhookModel.php index 7598a8da8e6..06eb8f2be76 100644 --- a/app/bundles/WebhookBundle/Model/WebhookModel.php +++ b/app/bundles/WebhookBundle/Model/WebhookModel.php @@ -144,12 +144,26 @@ public function getEventWebooksByType($type) return $results; } + /** + * @param $type + * @param $payload + * @param $groups + */ + public function queueWebhooksByType($type, $payload, array $groups = []) + { + return $this->queueWebhooks( + $this->getEventWebooksByType($type), + $payload, + $groups + ); + } + /** * @param $webhookEvents * @param $payload * @param array $serializationGroups */ - public function QueueWebhooks($webhookEvents, $payload, array $serializationGroups = []) + public function queueWebhooks($webhookEvents, $payload, array $serializationGroups = []) { if (!count($webhookEvents) || !is_array($webhookEvents)) { return; diff --git a/app/config/config_prod.php b/app/config/config_prod.php index 9d57037c29e..b651e891d44 100644 --- a/app/config/config_prod.php +++ b/app/config/config_prod.php @@ -60,6 +60,12 @@ ], ]); +//Twig Configuration +$container->loadFromExtension('twig', [ + 'cache' => '%mautic.tmp_path%/%kernel.environment%/twig', + 'auto_reload' => true, +]); + // Allow overriding config without a requiring a full bundle or hacks if (file_exists(__DIR__.'/config_override.php')) { $loader->import('config_override.php'); diff --git a/app/logs/.gitkeep b/app/logs/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/middlewares/VersionCheckMiddleware.php b/app/middlewares/VersionCheckMiddleware.php index 6b96d96b68e..93f3d07d4c7 100644 --- a/app/middlewares/VersionCheckMiddleware.php +++ b/app/middlewares/VersionCheckMiddleware.php @@ -20,7 +20,7 @@ class VersionCheckMiddleware implements HttpKernelInterface, PrioritizedMiddlewa const PRIORITY = 10; const MAUTIC_MINIMUM_PHP = '5.6.19'; - const MAUTIC_MAXIMUM_PHP = '7.1.999'; + const MAUTIC_MAXIMUM_PHP = '7.0.999'; /** * @var HttpKernelInterface diff --git a/app/migrations/Version20170323111702.php b/app/migrations/Version20170323111702.php new file mode 100644 index 00000000000..a298ee34b4d --- /dev/null +++ b/app/migrations/Version20170323111702.php @@ -0,0 +1,267 @@ +tableName = sprintf('%s%s', $this->prefix, 'tweets'); + } + + /** + * @param Schema $schema + * + * @throws SkipMigrationException + * @throws \Doctrine\DBAL\Schema\SchemaException + */ + public function preUp(Schema $schema) + { + if ($schema->hasTable($this->tableName)) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql("CREATE TABLE {$this->tableName} (id INT AUTO_INCREMENT NOT NULL, category_id INT DEFAULT NULL, page_id INT DEFAULT NULL, asset_id INT DEFAULT NULL, is_published TINYINT(1) NOT NULL, date_added DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)', created_by INT DEFAULT NULL, created_by_user VARCHAR(255) DEFAULT NULL, date_modified DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)', modified_by INT DEFAULT NULL, modified_by_user VARCHAR(255) DEFAULT NULL, checked_out DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)', checked_out_by INT DEFAULT NULL, checked_out_by_user VARCHAR(255) DEFAULT NULL, name VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, media_id VARCHAR(255) DEFAULT NULL, media_path VARCHAR(255) DEFAULT NULL, text VARCHAR(255) NOT NULL, sent_count INT DEFAULT NULL, favorite_count INT DEFAULT NULL, retweet_count INT DEFAULT NULL, lang VARCHAR(255) DEFAULT NULL, INDEX tweet_text_index (text), INDEX favorite_count_index (favorite_count), INDEX sent_count_index (sent_count), INDEX retweet_count_index (retweet_count), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"); + + $this->addSql("CREATE TABLE {$this->prefix}tweet_stats (id INT AUTO_INCREMENT NOT NULL, tweet_id INT DEFAULT NULL, lead_id INT DEFAULT NULL, twitter_tweet_id VARCHAR(255) DEFAULT NULL, handle VARCHAR(255) NOT NULL, date_sent DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime)', is_failed TINYINT(1) DEFAULT NULL, retry_count INT DEFAULT NULL, source VARCHAR(255) DEFAULT NULL, source_id INT DEFAULT NULL, favorite_count INT DEFAULT NULL, retweet_count INT DEFAULT NULL, response_details LONGTEXT DEFAULT NULL COMMENT '(DC2Type:json_array)', INDEX stat_tweet_search (tweet_id, lead_id), INDEX stat_tweet_search2 (lead_id, tweet_id), INDEX stat_tweet_failed_search (is_failed), INDEX stat_tweet_source_search (source, source_id), INDEX favorite_count_index (favorite_count), INDEX retweet_count_index (retweet_count), INDEX tweet_date_sent (date_sent), INDEX twitter_tweet_id_index (twitter_tweet_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"); + + $this->addSql("ALTER TABLE {$this->tableName} ADD CONSTRAINT ".$this->generatePropertyName('tweets', 'fk', ['category_id'])." FOREIGN KEY (category_id) REFERENCES {$this->prefix}categories (id) ON DELETE SET NULL"); + $this->addSql("ALTER TABLE {$this->tableName} ADD CONSTRAINT ".$this->generatePropertyName('tweets', 'fk', ['page_id'])." FOREIGN KEY (page_id) REFERENCES {$this->prefix}pages (id) ON DELETE SET NULL"); + $this->addSql("ALTER TABLE {$this->tableName} ADD CONSTRAINT ".$this->generatePropertyName('tweets', 'fk', ['asset_id'])." FOREIGN KEY (asset_id) REFERENCES {$this->prefix}assets (id) ON DELETE SET NULL"); + + $this->addSql("ALTER TABLE {$this->prefix}tweet_stats ADD CONSTRAINT ".$this->generatePropertyName('tweet_stats', 'fk', ['tweet_id'])." FOREIGN KEY (tweet_id) REFERENCES {$this->prefix}tweets (id) ON DELETE SET NULL"); + $this->addSql("ALTER TABLE {$this->prefix}tweet_stats ADD CONSTRAINT ".$this->generatePropertyName('tweet_stats', 'fk', ['lead_id'])." FOREIGN KEY (lead_id) REFERENCES {$this->prefix}leads (id) ON DELETE SET NULL"); + + $this->addSql('CREATE INDEX '.$this->generatePropertyName('tweets', 'idx', ['category_id']).' ON '.$this->tableName.' (category_id)'); + $this->addSql('CREATE INDEX '.$this->generatePropertyName('tweets', 'idx', ['page_id']).' ON '.$this->tableName.' (page_id)'); + $this->addSql('CREATE INDEX '.$this->generatePropertyName('tweets', 'idx', ['asset_id']).' ON '.$this->tableName.' (asset_id)'); + } + + /** + * Create tweet entities from the channel properties. + * + * @param Schema $schema + */ + public function postUp(Schema $schema) + { + $qb = $this->connection->createQueryBuilder(); + $logger = $this->container->get('monolog.logger.mautic'); + $start = 0; + $batch = 500; + + // Migrate tweets stored in Marketing Message params + $qb->select('mc.id, mc.properties, mc.message_id, m.is_published, m.date_added, m.created_by, m.created_by_user') + ->from($this->prefix.'message_channels', 'mc') + ->leftJoin('mc', $this->prefix.'messages', 'm', 'm.id = mc.message_id') + ->where('mc.channel = :tweet') + ->andWhere('mc.properties <> :emptyTweet') + ->andWhere('mc.properties <> :emptyProps') + ->setParameter('emptyTweet', '{"tweet_text":null,"asset_link":null,"page_link":null}') + ->setParameter('emptyProps', '[]') + ->setParameter('tweet', 'tweet') + ->setMaxResults($batch); + + while ($results = $qb->execute()->fetchAll()) { + + // Start a transaction + $this->connection->beginTransaction(); + + foreach ($results as $row) { + $row['properties'] = json_decode($row['properties'], true); + $row['description'] = 'Migrated from marketing message ('.$row['message_id'].') channel ('.$row['id'].')'; + $tweetData = $this->buildTweetData($row); + if (!$tweetId = $this->getIdFromCache($tweetData['text'])) { + $this->connection->insert($this->tableName, $tweetData); + + $tweetId = $this->connection->lastInsertId(); + $this->addToCache($tweetData['text'], $tweetId); + } + + $this->connection->update( + $this->prefix.'message_channels', + [ + 'channel_id' => $tweetId, + ], + [ + 'id' => $row['id'], + ] + ); + } + + try { + $this->connection->commit(); + } catch (\Exception $e) { + $this->connection->rollBack(); + + $logger->addError($e->getMessage(), ['exception' => $e]); + } + + // Increase the start + $start += $batch; + $qb->setFirstResult($start); + } + + // Migrate tweets stored in campaign event params + $qb = $this->connection->createQueryBuilder(); + $start = 0; + + $qb->select('ce.id, ce.properties, ce.campaign_id, c.is_published, c.date_added, c.created_by, c.created_by_user') + ->from($this->prefix.'campaign_events', 'ce') + ->leftJoin('ce', $this->prefix.'campaigns', 'c', 'c.id = ce.campaign_id') + ->andWhere('ce.channel = :tweet') + ->setParameter('tweet', 'social.tweet') + ->setMaxResults($batch); + + while ($results = $qb->execute()->fetchAll()) { + + // Start a transaction + $this->connection->beginTransaction(); + + foreach ($results as $row) { + $row['properties'] = unserialize($row['properties']); + $row['description'] = 'Migrated from campaign ('.$row['campaign_id'].') event ('.$row['id'].')'; + $tweetData = $this->buildTweetData($row); + + if (!$tweetId = $this->getIdFromCache($tweetData['text'])) { + $this->connection->insert($this->tableName, $tweetData); + + $tweetId = $this->connection->lastInsertId(); + $this->addToCache($tweetData['text'], $tweetId); + } + + $this->connection->update( + $this->prefix.'campaign_events', + [ + 'channel_id' => $tweetId, + ], + [ + 'id' => $row['id'], + ] + ); + } + + try { + $this->connection->commit(); + } catch (\Exception $e) { + $this->connection->rollBack(); + + $logger->addError($e->getMessage(), ['exception' => $e]); + } + + // Increase the start + $start += $batch; + $qb->setFirstResult($start); + } + } + + /** + * Generates tweet name from the tweet text. + * + * @param array $properties + * + * @return string + */ + protected function generateTweetName(array $properties) + { + $name = isset($properties['tweet_text']) ? str_replace(PHP_EOL, '', $properties['tweet_text']) : 'Tweet'; + + return (strlen($name) > 53) ? substr($name, 0, 50).'...' : $name; + } + + /** + * Builds the tweet data to be stored from the channel/event rows. + * + * @param array $row + * + * @return array + */ + protected function buildTweetData(array $row) + { + $now = (new \DateTime())->format('Y-m-d H:i:s'); + $properties = $row['properties']; + + return [ + 'text' => isset($properties['tweet_text']) ? $properties['tweet_text'] : '', + 'name' => $this->generateTweetName($properties), + 'asset_id' => isset($properties['asset_link']) ? $properties['asset_link'] : null, + 'page_id' => isset($properties['page_link']) ? $properties['page_link'] : null, + 'is_published' => (bool) $row['is_published'], + 'date_added' => !empty($row['date_added']) ? $row['date_added'] : $now, + 'description' => !empty($row['description']) ? $row['description'] : 'Created by migration', + 'created_by' => (int) $row['created_by'], + 'created_by_user' => $row['created_by_user'], + 'lang' => 'en', + ]; + } + + /** + * Builds the tweet cache so we could join duplicated tweets. + * + * @param string $text + * @param int $id + */ + protected function addToCache($text, $id) + { + $this->tweetCache[md5($text)] = $id; + } + + /** + * Check tweetCache if the tweet exists. If so, returns tweet ID, if not false. + * + * @param string $text + * + * @return int|false + */ + protected function getIdFromCache($text) + { + $hash = md5($text); + + if (isset($this->tweetCache[$hash])) { + return $this->tweetCache[$hash]; + } + + return false; + } +} diff --git a/app/migrations/Version20170324112219.php b/app/migrations/Version20170324112219.php new file mode 100644 index 00000000000..38da5b9d99f --- /dev/null +++ b/app/migrations/Version20170324112219.php @@ -0,0 +1,115 @@ +connection->createQueryBuilder() + ->select('*') + ->from($this->prefix.'plugin_integration_settings') + ->where('name = "Twilio"') + ->execute() + ->fetch() + ; + + if ($row !== false) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->suppressNoSQLStatementError(); + + $integrationRepo = $this->container->get('doctrine.orm.entity_manager')->getRepository(Integration::class); + $coreParametersHelper = $this->container->get('mautic.helper.core_parameters'); + $twilioKeySettings = [ + 'username' => $coreParametersHelper->getParameter('sms_username'), + 'password' => $coreParametersHelper->getParameter('sms_password'), + ]; + $twilioFeatureSettings = [ + 'sending_phone_number' => $coreParametersHelper->getParameter('sms_sending_phone_number'), + 'frequency_number' => $coreParametersHelper->getParameter('sms_frequency_number'), + 'frequency_time' => $coreParametersHelper->getParameter('sms_frequency_time'), + ]; + $oneSignalKeySettings = [ + 'app_id' => $coreParametersHelper->getParameter('notification_app_id'), + 'rest_api_key' => $coreParametersHelper->getParameter('notification_rest_api_key'), + 'safari_web_id' => $coreParametersHelper->getParameter('notification_safari_web_id'), + 'gcm_sender_id' => $coreParametersHelper->getParameter('gcm_sender_id'), + ]; + $osFeatureSettings = [ + 'features' => [], + ]; + + // Ensure an empty string doesn't get persisted, as a numeric or null is required. + if (empty($twilioFeatureSettings['frequency_number'])) { + $twilioFeatureSettings['frequency_number'] = null; + } + + if ($coreParametersHelper->getParameter('notification_landing_page_enabled')) { + $osFeatureSettings['features'][] = 'landing_page_enabled'; + } + + if ($coreParametersHelper->getParameter('welcomenotification_enabled')) { + $osFeatureSettings['features'][] = 'welcome_notification_enabled'; + } + + if (!empty($twilioKeySettings['username'])) { + $twilioIntegration = new Integration(); + $twilioIntegration->setName('Twilio'); + $twilioIntegration->setIsPublished($coreParametersHelper->getParameter('sms_enabled')); + $twilioIntegration->setApiKeys([]); + $twilioIntegration->setFeatureSettings($twilioFeatureSettings); + $twilioIntegration->setSupportedFeatures([]); + + $twilInt = new TwilioIntegration($this->container->get('mautic.factory')); + $twilInt->encryptAndSetApiKeys($twilioKeySettings, $twilioIntegration); + + $integrationRepo->saveEntities([$twilioIntegration]); + } + + if (!empty($oneSignalKeySettings['rest_api_key'])) { + $oneSignalIntegration = new Integration(); + $oneSignalIntegration->setName('OneSignal'); + $oneSignalIntegration->setIsPublished($coreParametersHelper->getParameter('notification_enabled')); + $oneSignalIntegration->setApiKeys([]); + $oneSignalIntegration->setFeatureSettings($osFeatureSettings); + $oneSignalIntegration->setSupportedFeatures([]); + + $osInt = new OneSignalIntegration($this->container->get('mautic.factory')); + $osInt->encryptAndSetApiKeys($oneSignalKeySettings, $oneSignalIntegration); + + $integrationRepo->saveEntities([$oneSignalIntegration]); + } + } +} diff --git a/app/migrations/Version20170330165232.php b/app/migrations/Version20170330165232.php new file mode 100644 index 00000000000..9ecdf363cc4 --- /dev/null +++ b/app/migrations/Version20170330165232.php @@ -0,0 +1,44 @@ +getTable($this->prefix.'push_ids')->hasColumn('enabled')) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('ALTER TABLE '.$this->prefix.'push_ids ADD enabled TINYINT(1) NOT NULL;'); + $this->addSql('ALTER TABLE '.$this->prefix.'push_ids ADD mobile TINYINT(1) NOT NULL;'); + $this->addSql('ALTER TABLE '.$this->prefix.'push_notifications ADD mobile TINYINT(1) NOT NULL;'); + } +} diff --git a/app/migrations/Version20170404173451.php b/app/migrations/Version20170404173451.php new file mode 100644 index 00000000000..77ed0db2a49 --- /dev/null +++ b/app/migrations/Version20170404173451.php @@ -0,0 +1,44 @@ +getTable($this->prefix.'reports'); + if ($table->hasColumn('group_by')) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql("ALTER TABLE {$this->prefix}reports ADD group_by LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)';"); + $this->addSql("ALTER TABLE {$this->prefix}reports ADD aggregators LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)';"); + } +} diff --git a/app/migrations/Version20170405194627.php b/app/migrations/Version20170405194627.php new file mode 100644 index 00000000000..44ddf4f3ad9 --- /dev/null +++ b/app/migrations/Version20170405194627.php @@ -0,0 +1,42 @@ +getTable($this->prefix.'push_notifications')->hasColumn('mobileSettings')) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('ALTER TABLE '.$this->prefix."push_notifications ADD mobileSettings LONGTEXT NOT NULL COMMENT '(DC2Type:array)';"); + } +} diff --git a/app/migrations/Version20170410210055.php b/app/migrations/Version20170410210055.php new file mode 100644 index 00000000000..828bbc9b4a4 --- /dev/null +++ b/app/migrations/Version20170410210055.php @@ -0,0 +1,42 @@ +getTable($this->prefix.'push_notification_stats')->hasColumn('date_read')) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('ALTER TABLE '.$this->prefix."push_notification_stats ADD date_read DATETIME NOT NULL COMMENT '(DC2Type:datetime)';"); + } +} diff --git a/app/migrations/Version20170420122443.php b/app/migrations/Version20170420122443.php new file mode 100644 index 00000000000..1fe380d7379 --- /dev/null +++ b/app/migrations/Version20170420122443.php @@ -0,0 +1,43 @@ +getTable($this->prefix.'integration_entity'); + if ($table->hasIndex(MAUTIC_TABLE_PREFIX.'internal_integration_entity')) { + throw new SkipMigrationException('Schema includes this migration'); + } + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('CREATE INDEX '.$this->prefix.'internal_integration_entity ON '.$this->prefix.'integration_entity (internal_entity_id, integration_entity_id, internal_entity, integration_entity)'); + } +} diff --git a/app/version.txt b/app/version.txt index c708446f793..9e231446465 100644 --- a/app/version.txt +++ b/app/version.txt @@ -1 +1 @@ -2.6.1-dev \ No newline at end of file +2.8.1-dev \ No newline at end of file diff --git a/composer.json b/composer.json index dc5158a6663..12f5a293552 100644 --- a/composer.json +++ b/composer.json @@ -105,7 +105,8 @@ "doctrine/data-fixtures": "1.2.1", "sonata-project/exporter": "^1.7", "lightsaml/sp-bundle": "~1.0.3", - "symfony/expression-language": "~2.8" + "symfony/expression-language": "~2.8", + "egeloen/ordered-form-bundle": "^3.0" }, "require-dev": { "symfony/web-profiler-bundle": "~2.8", @@ -133,7 +134,8 @@ "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", "cp ./build/hooks/pre-commit ./.git/hooks/pre-commit" - ] + ], + "test": "phpunit --bootstrap vendor/autoload.php --configuration app/phpunit.xml.dist app/bundles" }, "config": { "bin-dir": "bin" diff --git a/composer.lock b/composer.lock index 9d7a6161029..7255a1ec278 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "26bf4bf7322c68ffed988995dea71fae", + "hash": "79c9882052953bdc08dcb66f0c206d6b", + "content-hash": "84f6f30701b5f4539b3a833c211a5705", "packages": [ { "name": "aws/aws-sdk-php", @@ -67,7 +68,7 @@ "s3", "sdk" ], - "time": "2016-07-25T18:03:20+00:00" + "time": "2016-07-25 18:03:20" }, { "name": "clue/stream-filter", @@ -116,7 +117,7 @@ "stream_filter_append", "stream_filter_register" ], - "time": "2015-11-08T23:41:30+00:00" + "time": "2015-11-08 23:41:30" }, { "name": "composer/ca-bundle", @@ -174,7 +175,7 @@ "ssl", "tls" ], - "time": "2016-11-02T18:11:27+00:00" + "time": "2016-11-02 18:11:27" }, { "name": "debril/rss-atom-bundle", @@ -229,7 +230,7 @@ "atom", "rss" ], - "time": "2016-09-13T11:46:21+00:00" + "time": "2016-09-13 11:46:21" }, { "name": "doctrine/annotations", @@ -297,7 +298,7 @@ "docblock", "parser" ], - "time": "2015-08-31T12:32:49+00:00" + "time": "2015-08-31 12:32:49" }, { "name": "doctrine/cache", @@ -367,7 +368,7 @@ "cache", "caching" ], - "time": "2015-12-19T05:03:47+00:00" + "time": "2015-12-19 05:03:47" }, { "name": "doctrine/collections", @@ -433,7 +434,7 @@ "collections", "iterator" ], - "time": "2015-04-14T22:21:58+00:00" + "time": "2015-04-14 22:21:58" }, { "name": "doctrine/common", @@ -506,7 +507,7 @@ "persistence", "spl" ], - "time": "2015-12-25T13:10:16+00:00" + "time": "2015-12-25 13:10:16" }, { "name": "doctrine/data-fixtures", @@ -565,7 +566,7 @@ "keywords": [ "database" ], - "time": "2016-06-20T18:08:26+00:00" + "time": "2016-06-20 18:08:26" }, { "name": "doctrine/dbal", @@ -636,7 +637,7 @@ "persistence", "queryobject" ], - "time": "2016-09-09T19:13:33+00:00" + "time": "2016-09-09 19:13:33" }, { "name": "doctrine/doctrine-bundle", @@ -717,7 +718,7 @@ "orm", "persistence" ], - "time": "2016-08-10T15:35:22+00:00" + "time": "2016-08-10 15:35:22" }, { "name": "doctrine/doctrine-cache-bundle", @@ -805,7 +806,7 @@ "cache", "caching" ], - "time": "2016-01-26T17:28:51+00:00" + "time": "2016-01-26 17:28:51" }, { "name": "doctrine/doctrine-fixtures-bundle", @@ -862,7 +863,7 @@ "Fixture", "persistence" ], - "time": "2015-11-04T21:23:23+00:00" + "time": "2015-11-04 21:23:23" }, { "name": "doctrine/doctrine-migrations-bundle", @@ -920,7 +921,7 @@ "migrations", "schema" ], - "time": "2015-11-04T13:45:30+00:00" + "time": "2015-11-04 13:45:30" }, { "name": "doctrine/inflector", @@ -987,7 +988,7 @@ "singularize", "string" ], - "time": "2015-11-06T14:35:42+00:00" + "time": "2015-11-06 14:35:42" }, { "name": "doctrine/instantiator", @@ -1041,7 +1042,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2015-06-14 21:17:01" }, { "name": "doctrine/lexer", @@ -1095,7 +1096,7 @@ "lexer", "parser" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2014-09-09 13:34:57" }, { "name": "doctrine/migrations", @@ -1163,7 +1164,7 @@ "database", "migrations" ], - "time": "2016-01-07T21:28:50+00:00" + "time": "2016-01-07 21:28:50" }, { "name": "doctrine/orm", @@ -1239,7 +1240,111 @@ "database", "orm" ], - "time": "2016-12-18T15:42:34+00:00" + "time": "2016-12-18 15:42:34" + }, + { + "name": "egeloen/ordered-form", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/egeloen/ivory-ordered-form.git", + "reference": "bd2cd20210aa53c036af2bcbafe8d11612e2cea9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egeloen/ivory-ordered-form/zipball/bd2cd20210aa53c036af2bcbafe8d11612e2cea9", + "reference": "bd2cd20210aa53c036af2bcbafe8d11612e2cea9", + "shasum": "" + }, + "require": { + "php": "^5.6|^7.0", + "symfony/form": "^2.7|^3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "phpunit/phpunit": "^5.0", + "symfony/phpunit-bridge": "^2.7|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Ivory\\OrderedForm\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + } + ], + "description": "Provides a form ordering support with the Symfony2 form component.", + "keywords": [ + "Symfony2", + "form", + "order" + ], + "time": "2017-02-27 21:20:30" + }, + { + "name": "egeloen/ordered-form-bundle", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/egeloen/IvoryOrderedFormBundle.git", + "reference": "e6c2399103be8cffeac96dd48dd0990afa0a54b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egeloen/IvoryOrderedFormBundle/zipball/e6c2399103be8cffeac96dd48dd0990afa0a54b2", + "reference": "e6c2399103be8cffeac96dd48dd0990afa0a54b2", + "shasum": "" + }, + "require": { + "egeloen/ordered-form": "^3.0", + "php": "^5.6|^7.0", + "symfony/framework-bundle": "^2.7|^3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "phpunit/phpunit": "^5.0", + "symfony/phpunit-bridge": "^2.7|^3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Ivory\\OrderedFormBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + } + ], + "description": "Provides a form ordering support for your Symfony2 project.", + "keywords": [ + "form", + "order" + ], + "time": "2017-02-27 21:30:37" }, { "name": "friendsofsymfony/oauth-server-bundle", @@ -1311,7 +1416,7 @@ "oauth2", "server" ], - "time": "2016-02-22T13:57:55+00:00" + "time": "2016-02-22 13:57:55" }, { "name": "friendsofsymfony/oauth2-php", @@ -1365,7 +1470,7 @@ "oauth", "oauth2" ], - "time": "2016-03-31T14:24:17+00:00" + "time": "2016-03-31 14:24:17" }, { "name": "friendsofsymfony/rest-bundle", @@ -1453,7 +1558,7 @@ "keywords": [ "rest" ], - "time": "2016-06-21T08:42:59+00:00" + "time": "2016-06-21 08:42:59" }, { "name": "geoip2/geoip2", @@ -1504,7 +1609,7 @@ "geolocation", "maxmind" ], - "time": "2016-10-11T21:58:42+00:00" + "time": "2016-10-11 21:58:42" }, { "name": "giggsey/libphonenumber-for-php", @@ -1566,7 +1671,7 @@ "phonenumber", "validation" ], - "time": "2016-12-12T09:08:42+00:00" + "time": "2016-12-12 09:08:42" }, { "name": "giggsey/locale", @@ -1615,7 +1720,7 @@ } ], "description": "Locale functions required by libphonenumber-for-php", - "time": "2016-10-24T20:49:55+00:00" + "time": "2016-10-24 20:49:55" }, { "name": "guzzle/guzzle", @@ -1711,7 +1816,7 @@ "web service" ], "abandoned": "guzzlehttp/guzzle", - "time": "2015-03-18T18:23:50+00:00" + "time": "2015-03-18 18:23:50" }, { "name": "guzzlehttp/guzzle", @@ -1773,7 +1878,7 @@ "rest", "web service" ], - "time": "2016-10-08T15:01:37+00:00" + "time": "2016-10-08 15:01:37" }, { "name": "guzzlehttp/promises", @@ -1824,7 +1929,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "time": "2016-12-20 10:07:11" }, { "name": "guzzlehttp/psr7", @@ -1882,7 +1987,7 @@ "stream", "uri" ], - "time": "2016-06-24T23:00:38+00:00" + "time": "2016-06-24 23:00:38" }, { "name": "ip2location/ip2location-php", @@ -1922,7 +2027,7 @@ "ip2location", "ip2locationlite" ], - "time": "2016-06-10T07:21:52+00:00" + "time": "2016-06-10 07:21:52" }, { "name": "ircmaxell/password-compat", @@ -1964,7 +2069,7 @@ "hashing", "password" ], - "time": "2014-11-20T16:49:30+00:00" + "time": "2014-11-20 16:49:30" }, { "name": "jbroadway/urlify", @@ -2018,7 +2123,7 @@ "url", "urlify" ], - "time": "2017-01-03T20:12:54+00:00" + "time": "2017-01-03 20:12:54" }, { "name": "jdorn/sql-formatter", @@ -2068,7 +2173,7 @@ "highlight", "sql" ], - "time": "2014-01-12T16:20:24+00:00" + "time": "2014-01-12 16:20:24" }, { "name": "jms/metadata", @@ -2119,7 +2224,7 @@ "xml", "yaml" ], - "time": "2016-12-05T10:18:33+00:00" + "time": "2016-12-05 10:18:33" }, { "name": "jms/parser-lib", @@ -2154,7 +2259,7 @@ "Apache2" ], "description": "A library for easily creating recursive-descent parsers.", - "time": "2012-11-18T18:08:43+00:00" + "time": "2012-11-18 18:08:43" }, { "name": "jms/serializer", @@ -2229,7 +2334,7 @@ "serialization", "xml" ], - "time": "2016-11-13T10:20:11+00:00" + "time": "2016-11-13 10:20:11" }, { "name": "jms/serializer-bundle", @@ -2299,7 +2404,7 @@ "serialization", "xml" ], - "time": "2015-11-10T12:26:42+00:00" + "time": "2015-11-10 12:26:42" }, { "name": "joomla/filter", @@ -2350,7 +2455,7 @@ "framework", "joomla" ], - "time": "2016-01-08T17:27:47+00:00" + "time": "2016-01-08 17:27:47" }, { "name": "joomla/http", @@ -2401,7 +2506,7 @@ "http", "joomla" ], - "time": "2016-03-01T17:48:32+00:00" + "time": "2016-03-01 17:48:32" }, { "name": "joomla/string", @@ -2467,7 +2572,7 @@ "joomla", "string" ], - "time": "2016-12-10T18:13:42+00:00" + "time": "2016-12-10 18:13:42" }, { "name": "joomla/uri", @@ -2504,7 +2609,7 @@ "joomla", "uri" ], - "time": "2014-02-09T02:57:17+00:00" + "time": "2014-02-09 02:57:17" }, { "name": "knplabs/gaufrette", @@ -2588,7 +2693,7 @@ "filesystem", "media" ], - "time": "2015-05-26T08:25:40+00:00" + "time": "2015-05-26 08:25:40" }, { "name": "knplabs/knp-menu", @@ -2654,7 +2759,7 @@ "menu", "tree" ], - "time": "2016-09-22T07:36:19+00:00" + "time": "2016-09-22 07:36:19" }, { "name": "knplabs/knp-menu-bundle", @@ -2711,7 +2816,7 @@ "keywords": [ "menu" ], - "time": "2016-09-22T12:24:40+00:00" + "time": "2016-09-22 12:24:40" }, { "name": "lightsaml/lightsaml", @@ -2774,7 +2879,7 @@ "lightSAML", "php" ], - "time": "2016-11-18T08:30:01+00:00" + "time": "2016-11-18 08:30:01" }, { "name": "lightsaml/sp-bundle", @@ -2823,7 +2928,7 @@ ], "description": "Light SAML2 SP Symfony Bundle", "homepage": "http://www.lightsaml.com/SP-Bundle/", - "time": "2016-11-04T13:58:13+00:00" + "time": "2016-11-04 13:58:13" }, { "name": "lightsaml/symfony-bridge", @@ -2874,7 +2979,7 @@ ], "description": "Light SAML Symfony bridge bundle", "homepage": "http://www.lightsaml.com", - "time": "2016-11-18T15:11:05+00:00" + "time": "2016-11-18 15:11:05" }, { "name": "maxmind-db/reader", @@ -2929,7 +3034,7 @@ "geolocation", "maxmind" ], - "time": "2016-11-21T21:33:24+00:00" + "time": "2016-11-21 21:33:24" }, { "name": "maxmind/web-service-common", @@ -2973,7 +3078,7 @@ ], "description": "Internal MaxMind Web Service API", "homepage": "https://github.com/maxmind/mm-web-service-api-php", - "time": "2016-08-18T16:36:52+00:00" + "time": "2016-08-18 16:36:52" }, { "name": "misd/phone-number-bundle", @@ -3040,7 +3145,7 @@ "phonenumber", "telephone number" ], - "time": "2016-12-17T17:15:49+00:00" + "time": "2016-12-17 17:15:49" }, { "name": "monolog/monolog", @@ -3118,7 +3223,7 @@ "logging", "psr-3" ], - "time": "2016-11-26T00:15:39+00:00" + "time": "2016-11-26 00:15:39" }, { "name": "mrclay/minify", @@ -3158,7 +3263,7 @@ ], "description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers", "homepage": "http://code.google.com/p/minify/", - "time": "2014-03-12T12:54:23+00:00" + "time": "2014-03-12 12:54:23" }, { "name": "mustangostang/spyc", @@ -3208,7 +3313,7 @@ "yaml", "yml" ], - "time": "2016-10-21T00:03:34+00:00" + "time": "2016-10-21 00:03:34" }, { "name": "oneup/uploader-bundle", @@ -3277,7 +3382,7 @@ "plupload", "upload" ], - "time": "2016-12-15T08:33:29+00:00" + "time": "2016-12-15 08:33:29" }, { "name": "paragonie/random_compat", @@ -3325,7 +3430,7 @@ "pseudorandom", "random" ], - "time": "2016-11-07T23:38:38+00:00" + "time": "2016-11-07 23:38:38" }, { "name": "php-http/discovery", @@ -3387,7 +3492,7 @@ "message", "psr7" ], - "time": "2016-11-27T12:21:25+00:00" + "time": "2016-11-27 12:21:25" }, { "name": "php-http/guzzle6-adapter", @@ -3447,7 +3552,7 @@ "Guzzle", "http" ], - "time": "2016-05-10T06:13:32+00:00" + "time": "2016-05-10 06:13:32" }, { "name": "php-http/httplug", @@ -3503,7 +3608,7 @@ "client", "http" ], - "time": "2016-08-31T08:30:17+00:00" + "time": "2016-08-31 08:30:17" }, { "name": "php-http/message", @@ -3572,7 +3677,7 @@ "message", "psr-7" ], - "time": "2016-12-16T21:09:21+00:00" + "time": "2016-12-16 21:09:21" }, { "name": "php-http/message-factory", @@ -3622,7 +3727,7 @@ "stream", "uri" ], - "time": "2015-12-19T14:08:53+00:00" + "time": "2015-12-19 14:08:53" }, { "name": "php-http/promise", @@ -3672,7 +3777,7 @@ "keywords": [ "promise" ], - "time": "2016-01-26T13:27:02+00:00" + "time": "2016-01-26 13:27:02" }, { "name": "phpcollection/phpcollection", @@ -3720,7 +3825,7 @@ "sequence", "set" ], - "time": "2015-05-17T12:39:23+00:00" + "time": "2015-05-17 12:39:23" }, { "name": "phpoffice/phpexcel", @@ -3777,7 +3882,7 @@ "xls", "xlsx" ], - "time": "2015-05-01T07:00:55+00:00" + "time": "2015-05-01 07:00:55" }, { "name": "phpoption/phpoption", @@ -3827,7 +3932,7 @@ "php", "type" ], - "time": "2015-07-25T16:39:46+00:00" + "time": "2015-07-25 16:39:46" }, { "name": "piwik/device-detector", @@ -3878,7 +3983,7 @@ "parser", "useragent" ], - "time": "2016-12-06T20:57:53+00:00" + "time": "2016-12-06 20:57:53" }, { "name": "psr/cache", @@ -3924,7 +4029,7 @@ "psr", "psr-6" ], - "time": "2016-08-06T20:24:11+00:00" + "time": "2016-08-06 20:24:11" }, { "name": "psr/http-message", @@ -3974,7 +4079,7 @@ "request", "response" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2016-08-06 14:39:51" }, { "name": "psr/log", @@ -4021,7 +4126,7 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2016-10-10 12:19:37" }, { "name": "rackspace/php-opencloud", @@ -4086,7 +4191,7 @@ "rackspace", "swift" ], - "time": "2015-03-16T23:57:58+00:00" + "time": "2015-03-16 23:57:58" }, { "name": "robrichards/xmlseclibs", @@ -4127,7 +4232,7 @@ "xml", "xmldsig" ], - "time": "2016-09-08T13:15:00+00:00" + "time": "2016-09-08 13:15:00" }, { "name": "sensio/distribution-bundle", @@ -4179,7 +4284,7 @@ "configuration", "distribution" ], - "time": "2017-01-04T13:35:35+00:00" + "time": "2017-01-04 13:35:35" }, { "name": "sensiolabs/security-checker", @@ -4223,7 +4328,7 @@ } ], "description": "A security checker for your composer.lock", - "time": "2016-09-23T18:09:57+00:00" + "time": "2016-09-23 18:09:57" }, { "name": "sonata-project/exporter", @@ -4292,7 +4397,7 @@ "export", "xls" ], - "time": "2016-08-17T08:25:58+00:00" + "time": "2016-08-17 08:25:58" }, { "name": "sparkpost/sparkpost", @@ -4337,7 +4442,7 @@ } ], "description": "Client library for interfacing with the SparkPost API.", - "time": "2016-07-29T05:08:27+00:00" + "time": "2016-07-29 05:08:27" }, { "name": "stack/builder", @@ -4386,7 +4491,7 @@ "keywords": [ "stack" ], - "time": "2016-06-02T06:58:42+00:00" + "time": "2016-06-02 06:58:42" }, { "name": "stack/run", @@ -4436,7 +4541,7 @@ "keywords": [ "stack" ], - "time": "2013-10-25T14:31:28+00:00" + "time": "2013-10-25 14:31:28" }, { "name": "swiftmailer/swiftmailer", @@ -4490,7 +4595,7 @@ "mail", "mailer" ], - "time": "2016-12-29T10:02:40+00:00" + "time": "2016-12-29 10:02:40" }, { "name": "symfony/asset", @@ -4545,7 +4650,7 @@ ], "description": "Symfony Asset Component", "homepage": "https://symfony.com", - "time": "2016-09-24T15:56:40+00:00" + "time": "2016-09-24 15:56:40" }, { "name": "symfony/browser-kit", @@ -4602,7 +4707,7 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2016-09-06T10:55:00+00:00" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/cache", @@ -4669,7 +4774,7 @@ "caching", "psr6" ], - "time": "2016-12-13T08:24:57+00:00" + "time": "2016-12-13 08:24:57" }, { "name": "symfony/class-loader", @@ -4722,7 +4827,7 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2016-11-29T08:25:54+00:00" + "time": "2016-11-29 08:25:54" }, { "name": "symfony/config", @@ -4778,7 +4883,7 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2016-12-10T08:21:45+00:00" + "time": "2016-12-10 08:21:45" }, { "name": "symfony/console", @@ -4839,7 +4944,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-12-06T11:59:35+00:00" + "time": "2016-12-06 11:59:35" }, { "name": "symfony/debug", @@ -4896,7 +5001,7 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2016-11-15T12:53:17+00:00" + "time": "2016-11-15 12:53:17" }, { "name": "symfony/dependency-injection", @@ -4959,7 +5064,7 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2016-12-08T14:41:31+00:00" + "time": "2016-12-08 14:41:31" }, { "name": "symfony/doctrine-bridge", @@ -5033,7 +5138,7 @@ ], "description": "Symfony Doctrine Bridge", "homepage": "https://symfony.com", - "time": "2016-11-24T00:43:03+00:00" + "time": "2016-11-24 00:43:03" }, { "name": "symfony/dom-crawler", @@ -5089,7 +5194,7 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2016-12-10T14:24:35+00:00" + "time": "2016-12-10 14:24:35" }, { "name": "symfony/event-dispatcher", @@ -5149,7 +5254,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-10-13T01:43:15+00:00" + "time": "2016-10-13 01:43:15" }, { "name": "symfony/expression-language", @@ -5198,7 +5303,7 @@ ], "description": "Symfony ExpressionLanguage Component", "homepage": "https://symfony.com", - "time": "2016-11-03T07:52:58+00:00" + "time": "2016-11-03 07:52:58" }, { "name": "symfony/filesystem", @@ -5247,7 +5352,7 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2016-10-18T04:28:30+00:00" + "time": "2016-10-18 04:28:30" }, { "name": "symfony/finder", @@ -5296,7 +5401,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-12-13T09:38:12+00:00" + "time": "2016-12-13 09:38:12" }, { "name": "symfony/form", @@ -5370,7 +5475,7 @@ ], "description": "Symfony Form Component", "homepage": "https://symfony.com", - "time": "2016-12-06T14:06:08+00:00" + "time": "2016-12-06 14:06:08" }, { "name": "symfony/framework-bundle", @@ -5462,7 +5567,7 @@ ], "description": "Symfony FrameworkBundle", "homepage": "https://symfony.com", - "time": "2016-12-13T09:38:12+00:00" + "time": "2016-12-13 09:38:12" }, { "name": "symfony/http-foundation", @@ -5517,7 +5622,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2016-11-27T04:20:28+00:00" + "time": "2016-11-27 04:20:28" }, { "name": "symfony/http-kernel", @@ -5599,7 +5704,7 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2016-12-13T12:16:15+00:00" + "time": "2016-12-13 12:16:15" }, { "name": "symfony/intl", @@ -5675,7 +5780,7 @@ "l10n", "localization" ], - "time": "2016-11-18T21:10:01+00:00" + "time": "2016-11-18 21:10:01" }, { "name": "symfony/monolog-bridge", @@ -5738,7 +5843,7 @@ ], "description": "Symfony Monolog Bridge", "homepage": "https://symfony.com", - "time": "2016-06-29T05:29:29+00:00" + "time": "2016-06-29 05:29:29" }, { "name": "symfony/monolog-bundle", @@ -5798,7 +5903,7 @@ "log", "logging" ], - "time": "2017-01-02T19:04:26+00:00" + "time": "2017-01-02 19:04:26" }, { "name": "symfony/options-resolver", @@ -5852,7 +5957,7 @@ "configuration", "options" ], - "time": "2016-10-18T04:28:30+00:00" + "time": "2016-10-18 04:28:30" }, { "name": "symfony/polyfill-apcu", @@ -5905,7 +6010,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-intl-icu", @@ -5963,7 +6068,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-mbstring", @@ -6022,7 +6127,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php54", @@ -6080,7 +6185,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php55", @@ -6136,7 +6241,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php56", @@ -6192,7 +6297,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-php70", @@ -6251,7 +6356,7 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/polyfill-util", @@ -6303,7 +6408,7 @@ "polyfill", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2016-11-14 01:06:16" }, { "name": "symfony/process", @@ -6352,7 +6457,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2016-11-24T00:43:03+00:00" + "time": "2016-11-24 00:43:03" }, { "name": "symfony/property-access", @@ -6412,7 +6517,7 @@ "property path", "reflection" ], - "time": "2016-07-30T07:20:35+00:00" + "time": "2016-07-30 07:20:35" }, { "name": "symfony/routing", @@ -6487,7 +6592,7 @@ "uri", "url" ], - "time": "2016-11-25T12:26:42+00:00" + "time": "2016-11-25 12:26:42" }, { "name": "symfony/security", @@ -6567,7 +6672,7 @@ ], "description": "Symfony Security Component", "homepage": "https://symfony.com", - "time": "2016-12-08T14:41:31+00:00" + "time": "2016-12-08 14:41:31" }, { "name": "symfony/security-acl", @@ -6628,7 +6733,7 @@ ], "description": "Symfony Security Component - ACL (Access Control List)", "homepage": "https://symfony.com", - "time": "2015-12-28T09:39:09+00:00" + "time": "2015-12-28 09:39:09" }, { "name": "symfony/security-bundle", @@ -6698,7 +6803,7 @@ ], "description": "Symfony SecurityBundle", "homepage": "https://symfony.com", - "time": "2016-11-25T12:26:42+00:00" + "time": "2016-11-25 12:26:42" }, { "name": "symfony/stopwatch", @@ -6747,7 +6852,7 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2016-06-29T05:29:29+00:00" + "time": "2016-06-29 05:29:29" }, { "name": "symfony/swiftmailer-bundle", @@ -6806,7 +6911,7 @@ ], "description": "Symfony SwiftmailerBundle", "homepage": "http://symfony.com", - "time": "2016-12-20T04:44:33+00:00" + "time": "2016-12-20 04:44:33" }, { "name": "symfony/templating", @@ -6861,7 +6966,7 @@ ], "description": "Symfony Templating Component", "homepage": "https://symfony.com", - "time": "2016-09-06T10:55:00+00:00" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/translation", @@ -6925,7 +7030,7 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2016-09-06T10:55:00+00:00" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/twig-bridge", @@ -7006,7 +7111,7 @@ ], "description": "Symfony Twig Bridge", "homepage": "https://symfony.com", - "time": "2016-07-28T11:13:34+00:00" + "time": "2016-07-28 11:13:34" }, { "name": "symfony/twig-bundle", @@ -7072,7 +7177,7 @@ ], "description": "Symfony TwigBundle", "homepage": "https://symfony.com", - "time": "2016-12-13T09:38:12+00:00" + "time": "2016-12-13 09:38:12" }, { "name": "symfony/validator", @@ -7145,7 +7250,7 @@ ], "description": "Symfony Validator Component", "homepage": "https://symfony.com", - "time": "2016-12-08T15:59:39+00:00" + "time": "2016-12-08 15:59:39" }, { "name": "symfony/yaml", @@ -7194,7 +7299,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-11-14T16:15:57+00:00" + "time": "2016-11-14 16:15:57" }, { "name": "twig/twig", @@ -7255,7 +7360,7 @@ "keywords": [ "templating" ], - "time": "2016-12-23T11:06:22+00:00" + "time": "2016-12-23 11:06:22" }, { "name": "twilio/sdk", @@ -7305,7 +7410,7 @@ "sms", "twilio" ], - "time": "2016-09-01T18:42:52+00:00" + "time": "2016-09-01 18:42:52" }, { "name": "willdurand/jsonp-callback-validator", @@ -7345,7 +7450,7 @@ } ], "description": "JSONP callback validator.", - "time": "2014-01-20T22:35:06+00:00" + "time": "2014-01-20 22:35:06" }, { "name": "willdurand/negotiation", @@ -7394,7 +7499,7 @@ "header", "negotiation" ], - "time": "2015-10-01T07:42:40+00:00" + "time": "2015-10-01 07:42:40" }, { "name": "willdurand/oauth-server-bundle", @@ -7492,7 +7597,7 @@ "php", "transifex" ], - "time": "2016-06-10T02:57:54+00:00" + "time": "2016-06-10 02:57:54" }, { "name": "friendsofphp/php-cs-fixer", @@ -7550,7 +7655,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2016-12-01T00:05:05+00:00" + "time": "2016-12-01 00:05:05" }, { "name": "liip/functional-test-bundle", @@ -7625,7 +7730,7 @@ "keywords": [ "Symfony2" ], - "time": "2016-05-10T22:04:27+00:00" + "time": "2016-05-10 22:04:27" }, { "name": "phpdocumentor/reflection-docblock", @@ -7674,7 +7779,7 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2015-02-03T12:10:50+00:00" + "time": "2015-02-03 12:10:50" }, { "name": "phpspec/prophecy", @@ -7733,7 +7838,7 @@ "spy", "stub" ], - "time": "2014-11-17T16:23:49+00:00" + "time": "2014-11-17 16:23:49" }, { "name": "phpunit/php-code-coverage", @@ -7795,7 +7900,7 @@ "testing", "xunit" ], - "time": "2015-10-06T15:47:00+00:00" + "time": "2015-10-06 15:47:00" }, { "name": "phpunit/php-file-iterator", @@ -7840,7 +7945,7 @@ "filesystem", "iterator" ], - "time": "2013-10-10T15:34:57+00:00" + "time": "2013-10-10 15:34:57" }, { "name": "phpunit/php-text-template", @@ -7881,7 +7986,7 @@ "keywords": [ "template" ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2015-06-21 13:50:34" }, { "name": "phpunit/php-timer", @@ -7925,7 +8030,7 @@ "keywords": [ "timer" ], - "time": "2016-05-12T18:03:57+00:00" + "time": "2016-05-12 18:03:57" }, { "name": "phpunit/php-token-stream", @@ -7974,7 +8079,7 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15T14:06:22+00:00" + "time": "2016-11-15 14:06:22" }, { "name": "phpunit/phpunit", @@ -8046,7 +8151,7 @@ "testing", "xunit" ], - "time": "2015-02-05T15:51:19+00:00" + "time": "2015-02-05 15:51:19" }, { "name": "phpunit/phpunit-mock-objects", @@ -8102,7 +8207,7 @@ "mock", "xunit" ], - "time": "2015-10-02T06:51:40+00:00" + "time": "2015-10-02 06:51:40" }, { "name": "sebastian/comparator", @@ -8166,7 +8271,7 @@ "compare", "equality" ], - "time": "2016-11-19T09:18:40+00:00" + "time": "2016-11-19 09:18:40" }, { "name": "sebastian/diff", @@ -8218,7 +8323,7 @@ "keywords": [ "diff" ], - "time": "2015-12-08T07:14:41+00:00" + "time": "2015-12-08 07:14:41" }, { "name": "sebastian/environment", @@ -8268,7 +8373,7 @@ "environment", "hhvm" ], - "time": "2016-08-18T05:49:44+00:00" + "time": "2016-08-18 05:49:44" }, { "name": "sebastian/exporter", @@ -8335,7 +8440,7 @@ "export", "exporter" ], - "time": "2016-06-17T09:04:28+00:00" + "time": "2016-06-17 09:04:28" }, { "name": "sebastian/global-state", @@ -8386,7 +8491,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", @@ -8439,7 +8544,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-11-11T19:50:13+00:00" + "time": "2015-11-11 19:50:13" }, { "name": "sebastian/version", @@ -8474,7 +8579,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21T13:59:46+00:00" + "time": "2015-06-21 13:59:46" }, { "name": "sensio/generator-bundle", @@ -8526,7 +8631,7 @@ } ], "description": "This bundle generates code for you", - "time": "2016-12-05T16:01:19+00:00" + "time": "2016-12-05 16:01:19" }, { "name": "symfony/var-dumper", @@ -8589,7 +8694,7 @@ "debug", "dump" ], - "time": "2017-01-03T08:53:57+00:00" + "time": "2017-01-03 08:53:57" }, { "name": "symfony/web-profiler-bundle", @@ -8648,7 +8753,7 @@ ], "description": "Symfony WebProfilerBundle", "homepage": "https://symfony.com", - "time": "2016-12-09T07:40:14+00:00" + "time": "2016-12-09 07:40:14" }, { "name": "webfactory/exceptions-bundle", @@ -8696,7 +8801,7 @@ ], "description": "A simple controller to display error pages during development plus some building blocks for more user-friendly error pages.", "homepage": "http://inside.webfactory.de/de/blog/symfony2-exception-handling-and-custom-error-pages-explained.html", - "time": "2016-01-19T14:46:51+00:00" + "time": "2016-01-19 14:46:51" } ], "aliases": [], diff --git a/media/css/app.css b/media/css/app.css index c202068de81..90abb41221d 100644 --- a/media/css/app.css +++ b/media/css/app.css @@ -439,11 +439,18 @@ solid #ebedf0 !important}.bdr-l{border-left:1px solid #ebedf0 !important}.bdr-r{ !important}.np{padding:0px !important}.pt-20,.pt-lg{padding-top:20px !important}.pt-15,.pt-md{padding-top:15px !important}.pt-10,.pt-sm{padding-top:10px !important}.pt-5,.pt-xs{padding-top:5px !important}.pt-4{padding-top:4px !important}.pt-3{padding-top:3px !important}.pt-2{padding-top:2px !important}.pt-1{padding-top:1px !important}.pt-0{padding-top:0px !important}.pr-20,.pr-lg{padding-right:20px !important}.pr-15,.pr-md{padding-right:15px !important}.pr-10,.pr-sm{padding-right:10px !important}.pr-5,.pr-xs{padding-right:5px !important}.pr-4{padding-right:4px !important}.pr-3{padding-right:3px !important}.pr-2{padding-right:2px !important}.pr-1{padding-right:1px !important}.pr-0{padding-right:0px !important}.pb-20,.pb-lg{padding-bottom:20px !important}.pb-15,.pb-md{padding-bottom:15px !important}.pb-10,.pb-sm{padding-bottom:10px !important}.pb-5,.pb-xs{padding-bottom:5px !important}.pb-4{padding-bottom:4px !important}.pb-3{padding-bottom:3px !important}.pb-2{padding-bottom:2px !important}.pb-1{padding-bottom:1px !important}.pb-0{padding-bottom:0px !important}.pl-20,.pl-lg{padding-left:20px !important}.pl-15,.pl-md{padding-left:15px !important}.pl-10,.pl-sm{padding-left:10px !important}.pl-5,.pl-xs{padding-left:5px !important}.pl-4{padding-left:4px !important}.pl-3{padding-left:3px !important}.pl-2{padding-left:2px !important}.pl-1{padding-left:1px !important}.pl-0{padding-left:0px !important}.w-44{width:44px}.no-focus.form-control, .form-control .no-focus{border-color:transparent}.no-focus.form-control:focus, -.form-control .no-focus:focus{outline:0}.ovf-h{overflow:hidden}.break-word{-ms-word-break:break-all;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;hyphens:auto}.builder{position:relative}.builder [data-token]{cursor:pointer}.builder .btn-block.ui-draggable-dragging{width:104px;height:68px;margin:6px -12px;white-space:normal}.builder .builder-panel-top{margin-bottom:10px}.builder .template-dnd-help, -.builder .custom-dnd-help{display:table-cell;vertical-align:middle;width:100%}.builder-active{background-color:#fff;position:absolute;top:0;bottom:0;right:0;left:0;width:100%;height:100%;z-index:1030}.builder-panel{position:fixed;top:0;bottom:0;right:0;width:525px;height:100%;padding:15px;background-color:#d5d4d4;overflow-y:auto}.builder-panel .btn-close-builder{width:100%}.builder-content{position:fixed;left:0;top:0;right:525px;height:100%}.code-mode .builder-panel{width:50%;position:fixed}.code-mode .builder-content{width:50%}.code-mode .btn-close-builder{width:150px;float:right}.builder-panel .panel +.form-control .no-focus:focus{outline:0}.ovf-h{overflow:hidden}.break-word{-ms-word-break:break-all;word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;hyphens:auto}.builder,.builder-slot{position:relative}.builder [data-token], +.builder-slot [data-token]{cursor:pointer}.builder .btn-block.ui-draggable-dragging, +.builder-slot .btn-block.ui-draggable-dragging{width:104px;height:68px;margin:6px +12px;white-space:normal}.builder .builder-panel-top, +.builder-slot .builder-panel-top{margin-bottom:10px}.builder .template-dnd-help, +.builder-slot .template-dnd-help, +.builder .custom-dnd-help, +.builder-slot .custom-dnd-help{display:table-cell;vertical-align:middle;width:100%}.builder-active{background-color:#fff;position:absolute;top:0;bottom:0;right:0;left:0;width:100%;height:100%;z-index:1030}.builder-panel{position:fixed;top:0;bottom:0;right:0;width:525px;height:100%;padding:15px;background-color:#d5d4d4;overflow-y:auto}.builder-panel .btn-close-builder{width:100%}.builder-content{position:fixed;left:0;top:0;right:525px;height:100%}.code-mode .builder-panel{width:50%;position:fixed}.code-mode .builder-content{width:50%}.code-mode .btn-close-builder{width:150px;float:right}.builder-panel .panel +a.btn{white-space:normal}.builder-active-slot{background-color:#fff;z-index:1030}.builder-panel-slot{width:50%;padding:15px;background-color:#d5d4d4;overflow-y:auto}.builder-panel-slot .btn-close-builder{width:100%}.builder-content-slot{left:50%;width:50%}.code-mode .builder-panel-slot{width:50%}.code-mode .builder-content-slot{width:50%}.code-mode .btn-close-builder{width:150px;float:right}.builder-panel-slot .panel a.btn{white-space:normal}.ui-draggable-iframeFix{z-index:9999 !important}.CodeMirror{border:1px -solid #eee;height:auto}.CodeMirror-hints{position:absolute;z-index:9999 !important;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0, 0, 0, 0.2);-moz-box-shadow:2px 3px 5px rgba(0, 0, 0, 0.2);box-shadow:2px 3px 5px rgba(0, 0, 0, 0.2);border-radius:3px;border:1px +solid #eee;height:auto}#slot_codemode +.CodeMirror{height:200px}.CodeMirror-hints{position:absolute;z-index:9999 !important;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0, 0, 0, 0.2);-moz-box-shadow:2px 3px 5px rgba(0, 0, 0, 0.2);box-shadow:2px 3px 5px rgba(0, 0, 0, 0.2);border-radius:3px;border:1px solid silver;background:white;font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:black;cursor:pointer}li.CodeMirror-hint-active{background:#08f;color:white}@media (max-width: 767px){.builder .builder-panel{width:100%;bottom:0;top:auto;height:35%;padding-left:30px !important;padding-right:30px !important;border-top:1px solid #757575;position:fixed}.builder .builder-content{right:0}.builder .builder-panel-top{position:relative;width:100%}.builder .builder-tokens{margin-top:10px}}.mautic-editable{min-height:75px;width:100%;border:dashed 1px #000;margin-top:3px;margin-bottom:3px}.mautic-editable.over-droppable{border:dashed 2px #4e5e9e;-webkit-box-shadow:0px 0px 2px 2px rgba(0, 0, 0, 0.75);-moz-box-shadow:0px 0px 2px 2px rgba(0, 0, 0, 0.75);box-shadow:0px 0px 2px 2px rgba(0,0,0,0.75)}.mautic-editable-placeholder{height:100%;width:100%;text-align:center;margin-top:25px}div[contentEditable=true]:empty:not(:focus):before{content:attr(data-placeholder);padding:5px;display:table-cell}.inline-token-list{max-height:200px;overflow-y:auto;overflow-x:hidden;margin-bottom:20px;padding-left:0;width:240px}.inline-token{position:relative;display:block;padding:5px;margin-bottom:-1px;background-color:#fff;border:1px solid #f7f7f7;color:#f7f7f7;font-size:12px}.inline-token:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.inline-token:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.inline-token{color:#000;text-decoration:none;font-family:"Open Sans",Helvetica,Arial,sans-serif;line-height:1.3856}a.inline-token:hover,a.inline-token:focus{background-color:#DBDFEC}a.inline-token .text-muted{color:#8393a2}.sortable-panels @@ -469,7 +476,7 @@ i{color:#4e5d9d}.col-actions{width:100px}.shuffle-item button{height:32px}.theme-selected{border:1px solid #4e5e9e;-webkit-box-shadow:0px 0px 40px #f3f3f3;box-shadow:0px 0px 40px #f3f3f3}#app-content.content-only .toolbar-form-buttons.pull-right{padding-right:20px}.fr-toolbar{border-top:2px solid #4e5d9d}.modal-body-content iframe.fr-iframe{min-height:300px}.CodeMirror{border:1px solid #d5d5d5;border-radius:3px}table.table>tbody>tr>td.long-text{max-width:500px;min-width:300px;-ms-word-break:break-all;word-break:break-word;overflow-wrap:break-word;word-wrap:break-word;white-space:normal}.dynamicContentFilterContainer .btn-primary:hover{color:#5d6c7c}#dynamicContentContainer .remove-item{display:block}#dynamicContentContainer>.tab-pane>.panel>ul{white-space:nowrap;overflow:auto;overflow-y:hidden;max-width:100%}#dynamicContentContainer > .tab-pane > .panel > ul -li{display:inline-block;vertical-align:top;float:none}div[data-filter-container] .in-group{margin-left:20px}div[data-filter-container] .panel{margin-bottom:0;margin-top:20px}div[data-filter-container] .panel.in-group{margin-top:0;border-top:0;border-top-left-radius:0;border-top-right-radius:0}td.col-id,th.col-id{width:75px}.col-client-id{width:75px}.thumbnail-preview +li{display:inline-block;vertical-align:top;float:none}div[data-filter-container] .in-group{margin-left:20px}div[data-filter-container] .panel{margin-bottom:0;margin-top:20px}div[data-filter-container] .panel.in-group{margin-top:0;border-top:0;border-top-left-radius:0;border-top-right-radius:0}td.col-id,th.col-id{width:75px}.badge-wrapper{float:right;vertical-align:middle;margin-right:-10px}span.slot-caption{font-size:12px}.imagecard-caption,figcaption{font-size:16px}.imagecard{background-color:#ddd !important}.imagecard .imagecard-caption{background-color:#bbb !important}.col-client-id{width:75px}.thumbnail-preview img{height:100px}.modal-body-content iframe, .preview-detail iframe{height:400px}.form-group.preview img{max-height:260px}@-webkit-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-moz-keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@keyframes passing-through{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%,70%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}100%{opacity:0;-webkit-transform:translateY(-40px);-moz-transform:translateY(-40px);-ms-transform:translateY(-40px);-o-transform:translateY(-40px);transform:translateY(-40px)}}@-webkit-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-moz-keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@keyframes slide-in{0%{opacity:0;-webkit-transform:translateY(40px);-moz-transform:translateY(40px);-ms-transform:translateY(40px);-o-transform:translateY(40px);transform:translateY(40px)}30%{opacity:1;-webkit-transform:translateY(0px);-moz-transform:translateY(0px);-ms-transform:translateY(0px);-o-transform:translateY(0px);transform:translateY(0px)}}@-webkit-keyframes @@ -535,7 +542,7 @@ span{display:inline-block;width:14px;height:8px;margin-right:5px;-moz-border-rad .widget{width:100% !important}}.col-email-id{width:75px}.email-builder .builder-panel .panel-body{padding:5px 0}.table-bordered{border-left:0}.table-bordered.email-list>thead>tr>th:first-child,.table-bordered.email-list>tbody>tr>td:first-child,.table-bordered.email-template>thead>tr>th:first-child,.table-bordered.email-template>tbody>tr>td:first-child{border-left:0px}.table-bordered.email-list>thead>tr>th:last-child,.table-bordered.email-list>tbody>tr>td:last-child,.table-bordered.email-template>thead>tr>th:last-child,.table-bordered.email-template>tbody>tr>td:last-child{border-right:0px}.email-filters{}.form-submitaction-group-header{font-size:1.1em;font-weight:bold}.form-field-wrapper{position:relative}.form-field-wrapper img{max-width:100%}#mauticforms_fields .mauticform-row, #mauticforms_actions .mauticform-row{padding:10px}#mauticforms_fields .mauticform-row{margin-bottom:0 !important}#mauticforms_actions .mauticform-row .action-label{font-size:1.1em;font-weight:bold;display:block}#mauticforms_actions .mauticform-row .action-descr{font-size:0.9em;display:block}#mauticforms_actions .form-buttons{float:right;margin-top:3px}#mauticforms_fields .form-buttons{position:absolute;right:8px;top:8px}#mauticforms_fields input, #mauticforms_fields textarea, #mauticforms_fields -select{background-color:#fff;box-shadow:0px 0px 0px #fff inset;padding:0.5em 0.5em;width:50%}.mauticform-row{min-height:60px}#mauticforms_fields .panel-footer{padding:3px +select{background-color:#fff;box-shadow:0px 0px 0px #fff inset;padding:0.5em 0.5em;width:50%}#mauticforms_fields .chosen-container-single .chosen-search input[type="text"]{width:100%}.mauticform-row{min-height:60px}#mauticforms_fields .panel-footer{padding:3px 15px}textarea.form-html{height:200px}.col-form-id,.col-formresult-id{width:75px}.col-form-submissions{width:100px}.formresult-list th .input-group{margin-top:10px}.formresult-list th:not(.col-formresult-id):not(.col-actions){min-width:125px}.formresult-list th.col-formresult-id{min-width:125px}.inline-spacer{padding-right:20px}#mauticforms_fields input[type="date"], #mauticforms_fields input[type="time"], #mauticforms_fields input[type="datetime-local"], @@ -555,10 +562,12 @@ dt{width:auto;text-align:left;margin-right:10px}#contact-timeline .dl-horizontal dd{margin-left:0px}#contact-timeline tr.timeline-row-highlighted{background-color:#fafafa}#contact-timeline tr.timeline-details img{max-height:200px}.frequency{width:45px!important}.fa-user:before,.fa-building:before{margin-right:6px}.inline-spacer{text-transform:capitalize}.contact{display:inline !important}.frequency-table, .frequency-table td{border-bottom:none!important;border-top:none!important}.frequency-values{padding-left:10px;padding-top:27px;padding-bottom:15px}.frequency-select{width:145px !important;float:right;padding-right:5px}.frequency-label{padding:8px}.frequency-date{width:100px;float:left}.panel-companies -.primary{color:#fdb933}.col-page-id{width:75px}.page-builder .builder-panel .panel-body{padding:5px +.primary{color:#fdb933}.building::before{content:"\f1ad";font-family:"FontAwesome";padding-right:3px}.user::before{content:"\f007";font-family:"FontAwesome";padding-right:3px}.col-page-id{width:75px}.page-builder .builder-panel .panel-body{padding:5px 0}.symbol-hashtag:before{content:'#'}.shuffle-item.integration{width:100px}.integration-disabled -img{filter:url("data:image/svg+xml;utf8,#grayscale");filter:gray;-webkit-filter:grayscale(100%);-webkit-transition:all .6s ease;-webkit-backface-visibility:hidden}.integration-disabled img:hover{filter:url("data:image/svg+xml;utf8,#grayscale");-webkit-filter:grayscale(0%)}.col-point-id,.col-pointtrigger-id{width:75px}.trigger-event-group-header{font-size:1.1em;font-weight:bold}#triggerEvents .trigger-event-row{padding:20px;margin-bottom:0 !important;border:1px -solid #ecf0f1}#triggerEvents .trigger-event-row .event-label{font-size:1.1em;font-weight:bold;display:block}#triggerEvents .trigger-event-row .event-descr{font-size:0.9em;display:block}#triggerEvents .trigger-event-row:hover{background-color:#ecf0f1}#triggerEvents .trigger-event-row.bg-danger:hover{background-color:#f2d4d2}#triggerEvents .form-buttons{float:right}.col-pointtrigger-color{width:50px}.col-report-actions,.col-report-count{width:25px}#reportTable th:not(.col-report-count){min-width:125px}.columnSelectorButtons{padding-top:40px}.columnSelectorButtons div:not(:last-child){margin-bottom:10px}#report_columns_chosen{width:100% !important}.profile-details{margin-bottom:20px}.col-user-id,.col-role-id{width:75px}.col-user-avatar{width:75px}.mautic-logo{width:150px;margin:0 +img{filter:url("data:image/svg+xml;utf8,#grayscale");filter:gray;-webkit-filter:grayscale(100%);-webkit-transition:all .6s ease;-webkit-backface-visibility:hidden}.integration-disabled img:hover{filter:url("data:image/svg+xml;utf8,#grayscale");-webkit-filter:grayscale(0%)}.field-selector{width:500px}.col-centered{margin:0 +auto;float:none}.placeholder{position:relative}.placeholder::after{position:absolute;right:3px;top:19px;content:attr(data-placeholder);pointer-events:none;opacity:0.3;font-size:9px}.integration-fields{font-size:12px;padding-left:7px}.col-point-id,.col-pointtrigger-id{width:75px}.trigger-event-group-header{font-size:1.1em;font-weight:bold}#triggerEvents .trigger-event-row{padding:20px;margin-bottom:0 !important;border:1px +solid #ecf0f1}#triggerEvents .trigger-event-row .event-label{font-size:1.1em;font-weight:bold;display:block}#triggerEvents .trigger-event-row .event-descr{font-size:0.9em;display:block}#triggerEvents .trigger-event-row:hover{background-color:#ecf0f1}#triggerEvents .trigger-event-row.bg-danger:hover{background-color:#f2d4d2}#triggerEvents .form-buttons{float:right}.col-pointtrigger-color{width:50px}.col-report-actions,.col-report-count{width:25px}#reportTable th:not(.col-report-count){min-width:125px}.columnSelectorButtons{padding-top:40px}.columnSelectorButtons div:not(:last-child){margin-bottom:10px}#report_columns_chosen{width:100% !important}#ms-report_groupBy +ul{height:115px}.profile-details{margin-bottom:20px}.col-user-id,.col-role-id{width:75px}.col-user-avatar{width:75px}.mautic-logo{width:150px;margin:0 auto;background:#fff}.mautic-logo > svg.mautic-logo-figure .circle{fill:#4e5e9e}.mautic-logo > svg.mautic-logo-figure .m, .mautic-logo > svg.mautic-logo-figure .m-arrow{fill:#fdb933}input:-webkit-autofill{-webkit-box-shadow:0 0 0px 1000px white inset}span.input-group-addon diff --git a/media/css/libraries.css b/media/css/libraries.css index f73c7f6c8bd..11aad5760e4 100644 --- a/media/css/libraries.css +++ b/media/css/libraries.css @@ -1,11 +1,11 @@ /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}:focus{outline:0}.fr-element,.fr-element:focus{outline:0px -solid transparent}.fr-box.fr-basic .fr-element{text-align:initial;color:#000;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow-x:auto;min-height:40px}.fr-element{background:transparent;position:relative;z-index:2;-webkit-user-select:auto}.fr-element +solid transparent}.fr-box.fr-basic .fr-element{color:#000;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow-x:auto;min-height:40px}.fr-element{background:transparent;position:relative;z-index:2;-webkit-user-select:auto}.fr-element a{user-select:auto;-o-user-select:auto;-moz-user-select:auto;-khtml-user-select:auto;-webkit-user-select:auto;-ms-user-select:auto}.fr-element.fr-disabled{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element [contenteditable="false"]{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element [contenteditable="true"]{outline:0px solid transparent}.fr-box a.fr-floating-btn{-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);border-radius:100%;-moz-border-radius:100%;-webkit-border-radius:100%;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;height:32px;width:32px;background:#fff;color:#1e88e5;-webkit-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;-moz-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;-ms-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;-o-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;outline:none;left:0;top:0;line-height:32px;-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);text-align:center;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none}.fr-box a.fr-floating-btn svg{-webkit-transition:transform 0.2s ease 0s;-moz-transition:transform 0.2s ease 0s;-ms-transition:transform 0.2s ease 0s;-o-transition:transform 0.2s ease 0s;fill:#1e88e5}.fr-box a.fr-floating-btn @@ -78,7 +78,7 @@ textarea{width:100%;margin:0px 0 1px 0;border:none;border-bottom:solid 1px #bdbdbd;color:#222;font-size:14px;padding:6px 0 2px;background:rgba(0, 0, 0, 0);position:relative;z-index:2;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-popup .fr-input-line input[type="text"]:focus, .fr-popup .fr-input-line textarea:focus{border-bottom:solid 2px #1e88e5;margin-bottom:0px}.fr-popup .fr-input-line input + label, -.fr-popup .fr-input-line textarea+label{position:absolute;top:0;left:0;font-size:12px;color:rgba(0, 0, 0, 0);-webkit-transition:color 0.2s ease 0s;-moz-transition:color 0.2s ease 0s;-ms-transition:color 0.2s ease 0s;-o-transition:color 0.2s ease 0s;z-index:1}.fr-popup .fr-input-line input.fr-not-empty:focus + label, +.fr-popup .fr-input-line textarea+label{position:absolute;top:0;left:0;font-size:12px;color:rgba(0, 0, 0, 0);-webkit-transition:color 0.2s ease 0s;-moz-transition:color 0.2s ease 0s;-ms-transition:color 0.2s ease 0s;-o-transition:color 0.2s ease 0s;z-index:3;width:100%;display:block;background:#fff}.fr-popup .fr-input-line input.fr-not-empty:focus + label, .fr-popup .fr-input-line textarea.fr-not-empty:focus+label{color:#1e88e5}.fr-popup .fr-input-line input.fr-not-empty + label, .fr-popup .fr-input-line textarea.fr-not-empty+label{color:#808080}.fr-popup input, .fr-popup @@ -98,40 +98,47 @@ label{cursor:pointer;margin:0 2px;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);text-align:left;border:0px;border-top:5px solid #222;text-rendering:optimizelegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fr-toolbar::after{clear:both;display:block;content:"";height:0}.fr-toolbar.fr-rtl{text-align:right}.fr-toolbar.fr-inline{display:none;white-space:nowrap;position:absolute;margin-top:10px}.fr-toolbar.fr-inline .fr-arrow{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #222;position:absolute;top:-9px;left:50%;margin-left:-5px;display:inline-block}.fr-toolbar.fr-inline.fr-above{margin-top:-10px;-webkit-box-shadow:0 -1px 3px rgba(0, 0, 0, 0.12), 0 -1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 -1px 3px rgba(0, 0, 0, 0.12), 0 -1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 -1px 3px rgba(0, 0, 0, 0.12), 0 -1px 1px 1px rgba(0, 0, 0, 0.16);border-bottom:5px solid #222;border-top:0}.fr-toolbar.fr-inline.fr-above .fr-arrow{top:auto;bottom:-9px;border-bottom:0;border-top-color:inherit;border-top-style:solid;border-top-width:5px}.fr-toolbar.fr-top{top:0;border-radius:2px 2px 0 0;-moz-border-radius:2px 2px 0 0;-webkit-border-radius:2px 2px 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0,0,0,0.16)}.fr-toolbar.fr-bottom{bottom:0;border-radius:0 0 2px 2px;-moz-border-radius:0 0 2px 2px;-webkit-border-radius:0 0 2px 2px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0,0,0,0.16)}.fr-separator{background:#ebebeb;display:block;vertical-align:top;float:left}.fr-separator+.fr-separator{display:none}.fr-separator.fr-vs{height:34px;width:1px;margin:2px}.fr-separator.fr-hs{clear:both;height:1px;width:calc(96%);margin:0 2px}.fr-separator.fr-hidden{display:none !important}.fr-rtl .fr-separator{float:right}.fr-toolbar.fr-inline .fr-separator.fr-hs{float:none}.fr-toolbar.fr-inline .fr-separator.fr-vs{float:none;display:inline-block}.fr-visibility-helper{display:none;margin-left:0px !important}@media (min-width: 768px){.fr-visibility-helper{margin-left:1px !important}}@media (min-width: 992px){.fr-visibility-helper{margin-left:2px !important}}@media (min-width: 1200px){.fr-visibility-helper{margin-left:3px !important}}.fr-opacity-0{-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"}.fr-box{position:relative}.fr-sticky{position:-webkit-sticky;position:-moz-sticky;position:-ms-sticky;position:-o-sticky;position:sticky}.fr-sticky-off{position:relative}.fr-sticky-on{position:fixed}.fr-sticky-on.fr-sticky-ios{position:absolute;left:0;right:0;width:auto !important}.fr-sticky-dummy{display:none}.fr-sticky-on+.fr-sticky-dummy,.fr-sticky-box>.fr-sticky-dummy{display:block}span.fr-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-box .fr-counter{position:absolute;bottom:0px;padding:5px;right:0px;color:#ccc;content:attr(data-chars);font-size:15px;font-family:"Times New Roman",Georgia,Serif;z-index:1;background:#fff;border-top:solid 1px #ebebeb;border-left:solid 1px #ebebeb;border-radius:2px 0 0 0;-moz-border-radius:2px 0 0 0;-webkit-border-radius:2px 0 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.fr-box.fr-rtl .fr-counter{left:0px;right:auto;border-left:none;border-right:solid 1px #ebebeb;border-radius:0 2px 0 0;-moz-border-radius:0 2px 0 0;-webkit-border-radius:0 2px 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.fr-box.fr-code-view .fr-counter{display:none} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-drag-helper{background:#1e88e5;height:2px;margin-top:-1px;-webkit-opacity:0.2;-moz-opacity:0.2;opacity:0.2;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";position:absolute;z-index:9999;display:none}.fr-drag-helper.fr-visible{display:block}.fr-dragging{-webkit-opacity:0.4;-moz-opacity:0.4;opacity:0.4;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ body.fr-fullscreen{overflow:hidden;height:100%;width:100%;position:fixed}.fr-box.fr-fullscreen{margin:0 !important;position:fixed;top:0;left:0;bottom:0;right:0;z-index:9990 !important;width:auto !important}.fr-box.fr-fullscreen .fr-toolbar.fr-top{top:0 !important}.fr-box.fr-fullscreen .fr-toolbar.fr-bottom{bottom:0 !important} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-line-breaker{cursor:text;border-top:1px solid #1e88e5;position:fixed;z-index:2;display:none}.fr-line-breaker.fr-visible{display:block}.fr-line-breaker a.fr-floating-btn{position:absolute;left:calc(34%);top:-16px} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ -.clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-element .fr-video{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element .fr-video::after{position:absolute;content:'';z-index:1;top:0;left:0;right:0;bottom:0;cursor:pointer;display:block;background:rgba(0, 0, 0, 0)}.fr-element .fr-video.fr-active>*{z-index:2;position:relative}.fr-element .fr-video>*{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;max-width:100%;border:none}.fr-box .fr-video-resizer{position:absolute;border:solid 1px #1e88e5;display:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box .fr-video-resizer.fr-active{display:block}.fr-box .fr-video-resizer .fr-handler{display:block;position:absolute;background:#1e88e5;border:solid 1px #fff;z-index:4;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-box .fr-video-resizer .fr-handler.fr-hnw{cursor:nw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hne{cursor:ne-resize}.fr-box .fr-video-resizer .fr-handler.fr-hsw{cursor:sw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hse{cursor:se-resize}.fr-box .fr-video-resizer .fr-handler{width:12px;height:12px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-6px;bottom:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-6px;bottom:-6px}@media (min-width: 1200px){.fr-box .fr-video-resizer .fr-handler{width:10px;height:10px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-5px;bottom:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-5px;bottom:-5px}}.fr-video-size-layer .fr-video-group .fr-input-line{width:calc(45%);display:inline-block}.fr-video-size-layer .fr-video-group .fr-input-line+.fr-input-line{margin-left:10px}.fr-video-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9999;display:none} +.clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-element .fr-video{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element .fr-video::after{position:absolute;content:'';z-index:1;top:0;left:0;right:0;bottom:0;cursor:pointer;display:block;background:rgba(0, 0, 0, 0)}.fr-element .fr-video.fr-active>*{z-index:2;position:relative}.fr-element .fr-video>*{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;max-width:100%;border:none}.fr-box .fr-video-resizer{position:absolute;border:solid 1px #1e88e5;display:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box .fr-video-resizer.fr-active{display:block}.fr-box .fr-video-resizer .fr-handler{display:block;position:absolute;background:#1e88e5;border:solid 1px #fff;z-index:4;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-box .fr-video-resizer .fr-handler.fr-hnw{cursor:nw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hne{cursor:ne-resize}.fr-box .fr-video-resizer .fr-handler.fr-hsw{cursor:sw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hse{cursor:se-resize}.fr-box .fr-video-resizer .fr-handler{width:12px;height:12px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-6px;bottom:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-6px;bottom:-6px}@media (min-width: 1200px){.fr-box .fr-video-resizer .fr-handler{width:10px;height:10px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-5px;bottom:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-5px;bottom:-5px}}.fr-video-size-layer .fr-video-group .fr-input-line{width:calc(45%);display:inline-block}.fr-video-size-layer .fr-video-group .fr-input-line+.fr-input-line{margin-left:10px}.fr-video-upload-layer{border:dashed 2px #bdbdbd;padding:25px +0;position:relative;font-size:14px;letter-spacing:1px;line-height:140%;text-align:center}.fr-video-upload-layer:hover{background:#ebebeb}.fr-video-upload-layer.fr-drop{background:#ebebeb;border-color:#1e88e5}.fr-video-upload-layer .fr-form{-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";position:absolute;top:0;bottom:0;left:0;right:0;z-index:9999;overflow:hidden;margin:0 +!important;padding:0 +!important;width:100% !important}.fr-video-upload-layer .fr-form +input{cursor:pointer;position:absolute;right:0px;top:0px;bottom:0px;width:500%;height:100%;margin:0px;font-size:400px}.fr-video-progress-bar-layer>h3{font-size:16px;margin:10px +0;font-weight:normal}.fr-video-progress-bar-layer>div.fr-action-buttons{display:none}.fr-video-progress-bar-layer>div.fr-loader{background:#bcdbf7;height:10px;width:100%;margin-top:20px;overflow:hidden;position:relative}.fr-video-progress-bar-layer > div.fr-loader +span{display:block;height:100%;width:0%;background:#1e88e5;-webkit-transition:width 0.2s ease 0s;-moz-transition:width 0.2s ease 0s;-ms-transition:width 0.2s ease 0s;-o-transition:width 0.2s ease 0s}.fr-video-progress-bar-layer > div.fr-loader.fr-indeterminate +span{width:30% !important;position:absolute;top:0;-webkit-animation:loading 2s linear infinite;-moz-animation:loading 2s linear infinite;-o-animation:loading 2s linear infinite;animation:loading 2s linear infinite}.fr-video-progress-bar-layer.fr-error>div.fr-loader{display:none}.fr-video-progress-bar-layer.fr-error>div.fr-action-buttons{display:block}.fr-video-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9999;display:none} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}textarea.fr-code{display:none;width:100%;resize:none;-moz-resize:none;-webkit-resize:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none;padding:10px;margin:0px;font-family:"Courier New",monospace;font-size:14px;background:#fff;color:#000;outline:none}.fr-box.fr-rtl textarea.fr-code{direction:rtl}.fr-box .CodeMirror{display:none}.fr-box.fr-code-view textarea.fr-code{display:block}.fr-box.fr-code-view.fr-inline{-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16)}.fr-box.fr-code-view .fr-element, @@ -141,16 +148,16 @@ body.fr-fullscreen{overflow:hidden;height:100%;width:100%;position:fixed}.fr-box 12px;-webkit-transition:background 0.2s ease 0s;-moz-transition:background 0.2s ease 0s;-ms-transition:background 0.2s ease 0s;-o-transition:background 0.2s ease 0s;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;z-index:2;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;text-decoration:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box.fr-inline .fr-command.fr-btn.html-switch i{font-size:14px;width:14px;text-align:center}.fr-box.fr-inline .fr-command.fr-btn.html-switch.fr-desktop:hover{background:#ebebeb} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-popup .fr-emoticon{display:inline-block;font-size:20px;width:20px;padding:5px;line-height:1;cursor:default;font-weight:normal;font-family:"Apple Color Emoji","Segoe UI Emoji","NotoColorEmoji","Segoe UI Symbol","Android Emoji","EmojiSymbols";-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-popup .fr-emoticon img{height:20px}.fr-popup .fr-link:focus{background:#ebebeb} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-element img{cursor:pointer}.fr-image-resizer{position:absolute;border:solid 1px #1e88e5;display:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-image-resizer.fr-active{display:block}.fr-image-resizer .fr-handler{display:block;position:absolute;background:#1e88e5;border:solid 1px #fff;z-index:4;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-image-resizer .fr-handler.fr-hnw{cursor:nw-resize}.fr-image-resizer .fr-handler.fr-hne{cursor:ne-resize}.fr-image-resizer .fr-handler.fr-hsw{cursor:sw-resize}.fr-image-resizer .fr-handler.fr-hse{cursor:se-resize}.fr-image-resizer .fr-handler{width:12px;height:12px}.fr-image-resizer .fr-handler.fr-hnw{left:-6px;top:-6px}.fr-image-resizer .fr-handler.fr-hne{right:-6px;top:-6px}.fr-image-resizer .fr-handler.fr-hsw{left:-6px;bottom:-6px}.fr-image-resizer .fr-handler.fr-hse{right:-6px;bottom:-6px}@media (min-width: 1200px){.fr-image-resizer .fr-handler{width:10px;height:10px}.fr-image-resizer .fr-handler.fr-hnw{left:-5px;top:-5px}.fr-image-resizer .fr-handler.fr-hne{right:-5px;top:-5px}.fr-image-resizer .fr-handler.fr-hsw{left:-5px;bottom:-5px}.fr-image-resizer .fr-handler.fr-hse{right:-5px;bottom:-5px}}.fr-image-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9999;display:none}.fr-image-upload-layer{border:dashed 2px #bdbdbd;padding:25px @@ -166,16 +173,16 @@ loading{from{left:-25%}to{left:100%}}@-moz-keyframes loading{from{left:-25%}to{left:100%}}@-o-keyframes loading{from{left:-25%}to{left:100%}} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-quick-insert{position:absolute;z-index:9998;white-space:nowrap;padding-right:5px;margin-left:-5px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-quick-insert.fr-on a.fr-floating-btn svg{-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg)}.fr-quick-insert.fr-hidden{display:none}.fr-qi-helper{position:absolute;z-index:3;padding-left:10px;white-space:nowrap}.fr-qi-helper a.fr-btn.fr-floating-btn{text-align:center;display:inline-block;color:#222;-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0)}.fr-qi-helper a.fr-btn.fr-floating-btn.fr-size-1{-webkit-opacity:1;-moz-opacity:1;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1)} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-modal-head .fr-modal-head-line::after{clear:both;display:block;content:"";height:0}.fr-modal-head .fr-modal-head-line i.fr-modal-more{float:left;opacity:1;-webkit-transition:padding 0.2s ease 0s, width 0.2s ease 0s, opacity 0.2s ease 0s;-moz-transition:padding 0.2s ease 0s, width 0.2s ease 0s, opacity 0.2s ease 0s;-ms-transition:padding 0.2s ease 0s, width 0.2s ease 0s, opacity 0.2s ease 0s;-o-transition:padding 0.2s ease 0s, width 0.2s ease 0s, opacity 0.2s ease 0s}.fr-modal-head .fr-modal-head-line i.fr-modal-more.fr-not-available{opacity:0;width:0;padding:12px 0}.fr-modal-head .fr-modal-tags{display:none}.fr-modal-head .fr-modal-tags @@ -191,9 +198,9 @@ div.fr-modal-body div.fr-image-list div.fr-image-container .fr-insert-img{displa img{-webkit-opacity:0.75;-moz-opacity:0.75;opacity:0.75;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"}.fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container:hover .fr-delete-img, .fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container:hover .fr-insert-img{display:inline-block}.fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container .fr-delete-img:hover{background:#bf4644;color:#fff}.fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container .fr-insert-img:hover{background:#ebebeb} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-popup .fr-colors-tabs{-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);margin-bottom:5px;line-height:16px;margin-left:-2px;margin-right:-2px}.fr-popup .fr-colors-tabs .fr-colors-tab{display:inline-block;width:50%;cursor:pointer;text-align:center;color:#222;font-size:13px;padding:8px 0;position:relative}.fr-popup .fr-colors-tabs .fr-colors-tab:hover, @@ -201,9 +208,9 @@ img{-webkit-opacity:0.75;-moz-opacity:0.75;opacity:0.75;-ms-filter:"progid:DXIma .fr-popup .fr-color-set>span:focus{outline:1px solid #222;z-index:2}.fr-rtl .fr-popup .fr-colors-tabs .fr-colors-tab.fr-selected-tab[data-param1="text"]~[data-param1="background"]::after{-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0)} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-file-upload-layer{border:dashed 2px #bdbdbd;padding:25px 0;position:relative;font-size:14px;letter-spacing:1px;line-height:140%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;text-align:center}.fr-file-upload-layer:hover{background:#ebebeb}.fr-file-upload-layer.fr-drop{background:#ebebeb;border-color:#1e88e5}.fr-file-upload-layer .fr-form{-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";position:absolute;top:0;bottom:0;left:0;right:0;z-index:9999;overflow:hidden;margin:0 @@ -218,9 +225,9 @@ loading{from{left:-25%}to{left:100%}}@-moz-keyframes loading{from{left:-25%}to{left:100%}}@-o-keyframes loading{from{left:-25%}to{left:100%}} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-element table td.fr-selected-cell, .fr-element table th.fr-selected-cell{border:1px @@ -262,13 +269,13 @@ li{display:block;padding:5px 10px;border-bottom:1px solid #DDD;cursor:pointer}.atwho-view small{font-size:smaller;color:#777;font-weight:normal}.fr-popup{z-index:9999 !important}div[data-slot-focus]{top:-1px;right:-1px;bottom:-1px;left:-1px;content:'';margin:0;padding:0;position:absolute;border:1px solid #4e5e9e;z-index:1}div[data-section-focus]{content:'';position:absolute;border:1px -solid #fdb933}div[data-section-focus="top"]{left:0px;right:0px;top:0px}div[data-section-focus="right"]{bottom:0px;right:0px;top:0px}div[data-section-focus="bottom"]{bottom:0px;right:0px;left:0px}div[data-section-focus="left"]{bottom:0px;top:0px;left:0px}div[data-slot-toolbar]{position:absolute;top:-24px;left:-1px;right:-1px;height:25px;width:100%;z-index:10;cursor:pointer;cursor:move;background-color:#4e5e9e;border-left:1px solid #4e5e9e;border-right:1px solid #4e5e9e}div[data-slot-toolbar] .btn{width:20px;height:20px;padding:0;display:inline-block;margin-bottom:0;font-weight:600;text-align:center;vertical-align:middle;cursor:pointer;border:1px -solid transparent;white-space:nowrap;line-height:1.3856;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:11px;line-height:1.456;float:right;margin-right:5px;color:#fff}div[data-slot-toolbar] .btn -.fa{padding-top:4px}div[data-slot],[data-section-wrapper]{position:relative}div[data-slot="image"]{padding-top:1px;padding-bottom:1px}div[data-slot="image"] img{z-index:2;position:relative}div[data-slot].ui-sortable-helper{border:1px +solid #fdb933}div[data-section-focus="top"]{left:0px;right:0px;top:0px}div[data-section-focus="right"]{bottom:0px;right:0px;top:0px}div[data-section-focus="bottom"]{bottom:0px;right:0px;left:0px}div[data-section-focus="left"]{bottom:0px;top:0px;left:0px}div[data-section-focus="handle"]{width:25px;height:25px;bottom:10px;left:10px;background:#4e5e9e;color:#fff;text-align:center;border:0;line-height:25px}div[data-section-focus="delete"]{width:25px;height:25px;bottom:36px;left:10px;background:#4e5e9e;color:#fff;text-align:center;border:0;line-height:25px}div[data-slot-toolbar]{position:absolute;top:-24px;left:-1px;right:-1px;height:25px;width:100%;z-index:10;cursor:pointer;cursor:move;background-color:#4e5e9e;border-left:1px solid #4e5e9e;border-right:1px solid #4e5e9e;padding-top:2px}div[data-slot-toolbar] .btn{width:20px;height:20px;padding:0;display:inline-block;margin-bottom:0;font-weight:600;text-align:center;vertical-align:middle;cursor:pointer;border:1px +solid transparent;white-space:nowrap;line-height:1.3856;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:11px;line-height:1.456;float:right;margin-right:2px;color:#fff}div[data-slot-toolbar] .btn +.fa{padding-top:4px}div[data-slot],[data-section-wrapper]{position:relative}div[data-slot^="image"]{padding-top:1px;padding-bottom:1px}div[data-slot^="image"] img{z-index:2;position:relative}div[data-slot].ui-sortable-helper{border:1px solid #4e5e9e}.slot-placeholder{border:2px dotted #4e5e9e}[data-slot="text"].fr-box{padding:initial}[data-slot="text"].fr-box .fr-toolbar{border-top:2px solid #4e5e9e;position:absolute;left:-15px;bottom:initial !important;min-width:385px}[data-slot="text"].fr-box .fr-toolbar.fr-top{top:-78px !important;bottom:initial !important}[data-slot="text"].fr-box .fr-toolbar.fr-bottom{bottom:-78px !important;top:initial !important}[data-slot="text"].fr-box .fr-wrapper{border-radius:0 0 0px 0px;-moz-border-radius:0 0 0px 0px;-webkit-border-radius:0 0 0px 0px;-webkit-box-shadow:none !important;-moz-box-shadow:none !important;box-shadow:none !important;background:transparent !important}[data-slot="text"].fr-box .fr-wrapper .fr-element{text-align:inherit !important;padding:0 -!important;overflow-x:initial !important;color:inherit !important;min-height:inherit !important}.slot-type-handle.btn{float:left;width:111px;margin:2px;height:62px}.slot-type-handle.ui-draggable-dragging{color:#5d6c7c;background-color:#f5f5f5;border-color:#d3d3d3;padding:10px -16px;font-size:16px;line-height:1.25;border-radius:4px;margin:2px;text-align:center}.theme-list .panel-body{height:350px}.theme-list .select-theme-link{margin-top:5px} +!important;overflow-x:initial !important;color:inherit !important;min-height:inherit !important}.slot-type-handle.btn,.section-type-handle.btn{float:left;width:111px;margin:2px;height:75px;padding-left:5px;padding-right:5px;text-align:center;word-wrap:break-word}.slot-type-handle.ui-draggable-dragging,.section-type-handle.ui-draggable-dragging{color:#5d6c7c;background-color:#f5f5f5;border-color:#d3d3d3;padding:10px +16px;font-size:16px;line-height:1.25;border-radius:4px;margin:2px;text-align:center}.theme-list .panel-body{height:350px}.theme-list .select-theme-link{margin-top:5px}.theme-list .select-theme-selected{margin-top:5px}[data-slot="dynamicContent"]{z-index:50} /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button, @@ -1110,9 +1117,9 @@ pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z span{*vertical-align:text-bottom}.cm-force-border{padding-right: .1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:none} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1120,7 +1127,7 @@ print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:af .container-fluid::after, .row::after, .form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}:focus{outline:0}.fr-element,.fr-element:focus{outline:0px -solid transparent}.fr-box.fr-basic .fr-element{text-align:initial;color:#000;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow-x:auto;min-height:40px}.fr-element{background:transparent;position:relative;z-index:2;-webkit-user-select:auto}.fr-element +solid transparent}.fr-box.fr-basic .fr-element{color:#000;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;overflow-x:auto;min-height:40px}.fr-element{background:transparent;position:relative;z-index:2;-webkit-user-select:auto}.fr-element a{user-select:auto;-o-user-select:auto;-moz-user-select:auto;-khtml-user-select:auto;-webkit-user-select:auto;-ms-user-select:auto}.fr-element.fr-disabled{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element [contenteditable="false"]{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element [contenteditable="true"]{outline:0px solid transparent}.fr-box a.fr-floating-btn{-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);border-radius:100%;-moz-border-radius:100%;-webkit-border-radius:100%;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;height:32px;width:32px;background:#fff;color:#1e88e5;-webkit-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;-moz-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;-ms-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;-o-transition:background 0.2s ease 0s, color 0.2s ease 0s, transform 0.2s ease 0s;outline:none;left:0;top:0;line-height:32px;-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);text-align:center;display:block;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border:none}.fr-box a.fr-floating-btn svg{-webkit-transition:transform 0.2s ease 0s;-moz-transition:transform 0.2s ease 0s;-ms-transition:transform 0.2s ease 0s;-o-transition:transform 0.2s ease 0s;fill:#1e88e5}.fr-box a.fr-floating-btn @@ -1193,7 +1200,7 @@ textarea{width:100%;margin:0px 0 1px 0;border:none;border-bottom:solid 1px #bdbdbd;color:#222;font-size:14px;padding:6px 0 2px;background:rgba(0, 0, 0, 0);position:relative;z-index:2;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-popup .fr-input-line input[type="text"]:focus, .fr-popup .fr-input-line textarea:focus{border-bottom:solid 2px #1e88e5;margin-bottom:0px}.fr-popup .fr-input-line input + label, -.fr-popup .fr-input-line textarea+label{position:absolute;top:0;left:0;font-size:12px;color:rgba(0, 0, 0, 0);-webkit-transition:color 0.2s ease 0s;-moz-transition:color 0.2s ease 0s;-ms-transition:color 0.2s ease 0s;-o-transition:color 0.2s ease 0s;z-index:1}.fr-popup .fr-input-line input.fr-not-empty:focus + label, +.fr-popup .fr-input-line textarea+label{position:absolute;top:0;left:0;font-size:12px;color:rgba(0, 0, 0, 0);-webkit-transition:color 0.2s ease 0s;-moz-transition:color 0.2s ease 0s;-ms-transition:color 0.2s ease 0s;-o-transition:color 0.2s ease 0s;z-index:3;width:100%;display:block;background:#fff}.fr-popup .fr-input-line input.fr-not-empty:focus + label, .fr-popup .fr-input-line textarea.fr-not-empty:focus+label{color:#1e88e5}.fr-popup .fr-input-line input.fr-not-empty + label, .fr-popup .fr-input-line textarea.fr-not-empty+label{color:#808080}.fr-popup input, .fr-popup @@ -1213,9 +1220,9 @@ label{cursor:pointer;margin:0 2px;border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);text-align:left;border:0px;border-top:5px solid #222;text-rendering:optimizelegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fr-toolbar::after{clear:both;display:block;content:"";height:0}.fr-toolbar.fr-rtl{text-align:right}.fr-toolbar.fr-inline{display:none;white-space:nowrap;position:absolute;margin-top:10px}.fr-toolbar.fr-inline .fr-arrow{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #222;position:absolute;top:-9px;left:50%;margin-left:-5px;display:inline-block}.fr-toolbar.fr-inline.fr-above{margin-top:-10px;-webkit-box-shadow:0 -1px 3px rgba(0, 0, 0, 0.12), 0 -1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 -1px 3px rgba(0, 0, 0, 0.12), 0 -1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 -1px 3px rgba(0, 0, 0, 0.12), 0 -1px 1px 1px rgba(0, 0, 0, 0.16);border-bottom:5px solid #222;border-top:0}.fr-toolbar.fr-inline.fr-above .fr-arrow{top:auto;bottom:-9px;border-bottom:0;border-top-color:inherit;border-top-style:solid;border-top-width:5px}.fr-toolbar.fr-top{top:0;border-radius:2px 2px 0 0;-moz-border-radius:2px 2px 0 0;-webkit-border-radius:2px 2px 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0,0,0,0.16)}.fr-toolbar.fr-bottom{bottom:0;border-radius:0 0 2px 2px;-moz-border-radius:0 0 2px 2px;-webkit-border-radius:0 0 2px 2px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16);box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0,0,0,0.16)}.fr-separator{background:#ebebeb;display:block;vertical-align:top;float:left}.fr-separator+.fr-separator{display:none}.fr-separator.fr-vs{height:34px;width:1px;margin:2px}.fr-separator.fr-hs{clear:both;height:1px;width:calc(96%);margin:0 2px}.fr-separator.fr-hidden{display:none !important}.fr-rtl .fr-separator{float:right}.fr-toolbar.fr-inline .fr-separator.fr-hs{float:none}.fr-toolbar.fr-inline .fr-separator.fr-vs{float:none;display:inline-block}.fr-visibility-helper{display:none;margin-left:0px !important}@media (min-width: 768px){.fr-visibility-helper{margin-left:1px !important}}@media (min-width: 992px){.fr-visibility-helper{margin-left:2px !important}}@media (min-width: 1200px){.fr-visibility-helper{margin-left:3px !important}}.fr-opacity-0{-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"}.fr-box{position:relative}.fr-sticky{position:-webkit-sticky;position:-moz-sticky;position:-ms-sticky;position:-o-sticky;position:sticky}.fr-sticky-off{position:relative}.fr-sticky-on{position:fixed}.fr-sticky-on.fr-sticky-ios{position:absolute;left:0;right:0;width:auto !important}.fr-sticky-dummy{display:none}.fr-sticky-on+.fr-sticky-dummy,.fr-sticky-box>.fr-sticky-dummy{display:block}span.fr-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1224,9 +1231,9 @@ label{cursor:pointer;margin:0 .row::after, .form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-box .fr-counter{position:absolute;bottom:0px;padding:5px;right:0px;color:#ccc;content:attr(data-chars);font-size:15px;font-family:"Times New Roman",Georgia,Serif;z-index:1;background:#fff;border-top:solid 1px #ebebeb;border-left:solid 1px #ebebeb;border-radius:2px 0 0 0;-moz-border-radius:2px 0 0 0;-webkit-border-radius:2px 0 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.fr-box.fr-rtl .fr-counter{left:0px;right:auto;border-left:none;border-right:solid 1px #ebebeb;border-radius:0 2px 0 0;-moz-border-radius:0 2px 0 0;-webkit-border-radius:0 2px 0 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box}.fr-box.fr-code-view .fr-counter{display:none} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1235,16 +1242,16 @@ label{cursor:pointer;margin:0 .row::after, .form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-drag-helper{background:#1e88e5;height:2px;margin-top:-1px;-webkit-opacity:0.2;-moz-opacity:0.2;opacity:0.2;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";position:absolute;z-index:9999;display:none}.fr-drag-helper.fr-visible{display:block}.fr-dragging{-webkit-opacity:0.4;-moz-opacity:0.4;opacity:0.4;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ body.fr-fullscreen{overflow:hidden;height:100%;width:100%;position:fixed}.fr-box.fr-fullscreen{margin:0 !important;position:fixed;top:0;left:0;bottom:0;right:0;z-index:9990 !important;width:auto !important}.fr-box.fr-fullscreen .fr-toolbar.fr-top{top:0 !important}.fr-box.fr-fullscreen .fr-toolbar.fr-bottom{bottom:0 !important} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1253,20 +1260,27 @@ body.fr-fullscreen{overflow:hidden;height:100%;width:100%;position:fixed}.fr-box .row::after, .form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-line-breaker{cursor:text;border-top:1px solid #1e88e5;position:fixed;z-index:2;display:none}.fr-line-breaker.fr-visible{display:block}.fr-line-breaker a.fr-floating-btn{position:absolute;left:calc(34%);top:-16px} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, .container::after, .container-fluid::after, .row::after, -.form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-element .fr-video{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element .fr-video::after{position:absolute;content:'';z-index:1;top:0;left:0;right:0;bottom:0;cursor:pointer;display:block;background:rgba(0, 0, 0, 0)}.fr-element .fr-video.fr-active>*{z-index:2;position:relative}.fr-element .fr-video>*{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;max-width:100%;border:none}.fr-box .fr-video-resizer{position:absolute;border:solid 1px #1e88e5;display:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box .fr-video-resizer.fr-active{display:block}.fr-box .fr-video-resizer .fr-handler{display:block;position:absolute;background:#1e88e5;border:solid 1px #fff;z-index:4;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-box .fr-video-resizer .fr-handler.fr-hnw{cursor:nw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hne{cursor:ne-resize}.fr-box .fr-video-resizer .fr-handler.fr-hsw{cursor:sw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hse{cursor:se-resize}.fr-box .fr-video-resizer .fr-handler{width:12px;height:12px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-6px;bottom:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-6px;bottom:-6px}@media (min-width: 1200px){.fr-box .fr-video-resizer .fr-handler{width:10px;height:10px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-5px;bottom:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-5px;bottom:-5px}}.fr-video-size-layer .fr-video-group .fr-input-line{width:calc(45%);display:inline-block}.fr-video-size-layer .fr-video-group .fr-input-line+.fr-input-line{margin-left:10px}.fr-video-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9999;display:none} +.form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-element .fr-video{user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-element .fr-video::after{position:absolute;content:'';z-index:1;top:0;left:0;right:0;bottom:0;cursor:pointer;display:block;background:rgba(0, 0, 0, 0)}.fr-element .fr-video.fr-active>*{z-index:2;position:relative}.fr-element .fr-video>*{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;max-width:100%;border:none}.fr-box .fr-video-resizer{position:absolute;border:solid 1px #1e88e5;display:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box .fr-video-resizer.fr-active{display:block}.fr-box .fr-video-resizer .fr-handler{display:block;position:absolute;background:#1e88e5;border:solid 1px #fff;z-index:4;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fr-box .fr-video-resizer .fr-handler.fr-hnw{cursor:nw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hne{cursor:ne-resize}.fr-box .fr-video-resizer .fr-handler.fr-hsw{cursor:sw-resize}.fr-box .fr-video-resizer .fr-handler.fr-hse{cursor:se-resize}.fr-box .fr-video-resizer .fr-handler{width:12px;height:12px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-6px;top:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-6px;bottom:-6px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-6px;bottom:-6px}@media (min-width: 1200px){.fr-box .fr-video-resizer .fr-handler{width:10px;height:10px}.fr-box .fr-video-resizer .fr-handler.fr-hnw{left:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hne{right:-5px;top:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hsw{left:-5px;bottom:-5px}.fr-box .fr-video-resizer .fr-handler.fr-hse{right:-5px;bottom:-5px}}.fr-video-size-layer .fr-video-group .fr-input-line{width:calc(45%);display:inline-block}.fr-video-size-layer .fr-video-group .fr-input-line+.fr-input-line{margin-left:10px}.fr-video-upload-layer{border:dashed 2px #bdbdbd;padding:25px +0;position:relative;font-size:14px;letter-spacing:1px;line-height:140%;text-align:center}.fr-video-upload-layer:hover{background:#ebebeb}.fr-video-upload-layer.fr-drop{background:#ebebeb;border-color:#1e88e5}.fr-video-upload-layer .fr-form{-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";position:absolute;top:0;bottom:0;left:0;right:0;z-index:9999;overflow:hidden;margin:0 +!important;padding:0 +!important;width:100% !important}.fr-video-upload-layer .fr-form +input{cursor:pointer;position:absolute;right:0px;top:0px;bottom:0px;width:500%;height:100%;margin:0px;font-size:400px}.fr-video-progress-bar-layer>h3{font-size:16px;margin:10px +0;font-weight:normal}.fr-video-progress-bar-layer>div.fr-action-buttons{display:none}.fr-video-progress-bar-layer>div.fr-loader{background:#bcdbf7;height:10px;width:100%;margin-top:20px;overflow:hidden;position:relative}.fr-video-progress-bar-layer > div.fr-loader +span{display:block;height:100%;width:0%;background:#1e88e5;-webkit-transition:width 0.2s ease 0s;-moz-transition:width 0.2s ease 0s;-ms-transition:width 0.2s ease 0s;-o-transition:width 0.2s ease 0s}.fr-video-progress-bar-layer > div.fr-loader.fr-indeterminate +span{width:30% !important;position:absolute;top:0;-webkit-animation:loading 2s linear infinite;-moz-animation:loading 2s linear infinite;-o-animation:loading 2s linear infinite;animation:loading 2s linear infinite}.fr-video-progress-bar-layer.fr-error>div.fr-loader{display:none}.fr-video-progress-bar-layer.fr-error>div.fr-action-buttons{display:block}.fr-video-overlay{position:fixed;top:0;left:0;bottom:0;right:0;z-index:9999;display:none} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1281,9 +1295,9 @@ body.fr-fullscreen{overflow:hidden;height:100%;width:100%;position:fixed}.fr-box 12px;-webkit-transition:background 0.2s ease 0s;-moz-transition:background 0.2s ease 0s;-ms-transition:background 0.2s ease 0s;-o-transition:background 0.2s ease 0s;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;z-index:2;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;text-decoration:none;user-select:none;-o-user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none}.fr-box.fr-inline .fr-command.fr-btn.html-switch i{font-size:14px;width:14px;text-align:center}.fr-box.fr-inline .fr-command.fr-btn.html-switch.fr-desktop:hover{background:#ebebeb} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1293,9 +1307,9 @@ i{font-size:14px;width:14px;text-align:center}.fr-box.fr-inline .fr-command.fr-b .form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.fr-popup .fr-emoticon{display:inline-block;font-size:20px;width:20px;padding:5px;line-height:1;cursor:default;font-weight:normal;font-family:"Apple Color Emoji","Segoe UI Emoji","NotoColorEmoji","Segoe UI Symbol","Android Emoji","EmojiSymbols";-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-popup .fr-emoticon img{height:20px}.fr-popup .fr-link:focus{background:#ebebeb} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1316,9 +1330,9 @@ loading{from{left:-25%}to{left:100%}}@-moz-keyframes loading{from{left:-25%}to{left:100%}}@-o-keyframes loading{from{left:-25%}to{left:100%}} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1328,9 +1342,9 @@ loading{from{left:-25%}to{left:100%}} .form-horizontal .form-group::after,.btn-toolbar::after,.btn-group-vertical>.btn-group::after,.nav::after,.navbar::after,.navbar-header::after,.navbar-collapse::after,.pager::after,.panel-body::after,.modal-footer::after{clear:both;display:block;content:"";height:0}.hide-by-clipping{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.fr-quick-insert{position:absolute;z-index:9998;white-space:nowrap;padding-right:5px;margin-left:-5px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fr-quick-insert.fr-on a.fr-floating-btn svg{-webkit-transform:rotate(135deg);-moz-transform:rotate(135deg);-ms-transform:rotate(135deg);-o-transform:rotate(135deg)}.fr-quick-insert.fr-hidden{display:none}.fr-qi-helper{position:absolute;z-index:3;padding-left:10px;white-space:nowrap}.fr-qi-helper a.fr-btn.fr-floating-btn{text-align:center;display:inline-block;color:#222;-webkit-opacity:0;-moz-opacity:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0)}.fr-qi-helper a.fr-btn.fr-floating-btn.fr-size-1{-webkit-opacity:1;-moz-opacity:1;opacity:1;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1)} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1351,9 +1365,9 @@ div.fr-modal-body div.fr-image-list div.fr-image-container .fr-insert-img{displa img{-webkit-opacity:0.75;-moz-opacity:0.75;opacity:0.75;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"}.fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container:hover .fr-delete-img, .fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container:hover .fr-insert-img{display:inline-block}.fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container .fr-delete-img:hover{background:#bf4644;color:#fff}.fr-desktop .fr-modal-wrapper div.fr-modal-body div.fr-image-list div.fr-image-container .fr-insert-img:hover{background:#ebebeb} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1366,9 +1380,9 @@ img{-webkit-opacity:0.75;-moz-opacity:0.75;opacity:0.75;-ms-filter:"progid:DXIma .fr-popup .fr-color-set>span:focus{outline:1px solid #222;z-index:2}.fr-rtl .fr-popup .fr-colors-tabs .fr-colors-tab.fr-selected-tab[data-param1="text"]~[data-param1="background"]::after{-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0)} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, @@ -1388,9 +1402,9 @@ loading{from{left:-25%}to{left:100%}}@-moz-keyframes loading{from{left:-25%}to{left:100%}}@-o-keyframes loading{from{left:-25%}to{left:100%}} /*! - * froala_editor v2.4.0 (https://www.froala.com/wysiwyg-editor) + * froala_editor v2.4.2 (https://www.froala.com/wysiwyg-editor) * License https://froala.com/wysiwyg-editor/terms/ - * Copyright 2014-2016 Froala Labs + * Copyright 2014-2017 Froala Labs */ .clearfix::after, .dl-horizontal dd::after, diff --git a/media/js/app.js b/media/js/app.js index 8ad61ff356d..3206d33bdf3 100644 --- a/media/js/app.js +++ b/media/js/app.js @@ -16,7 +16,7 @@ return;} Mautic.headLoadedAssets[url]=1;mQuery.getScript(url,function(data,textStatus,jqxhr){if(textStatus=='success'){if(onLoadCallback&&typeof Mautic[onLoadCallback]=='function'){Mautic[onLoadCallback]();}else if(typeof Mautic[mauticContent+"OnLoad"]=='function'){if(typeof Mautic.loadedContent[mauticContent]=='undefined'){Mautic.loadedContent[mauticContent]=true;Mautic[mauticContent+"OnLoad"]('#app-content',{});}}}});},loadStylesheet:function(url){if(typeof Mautic.headLoadedAssets=='undefined'){Mautic.headLoadedAssets={};}else if(typeof Mautic.headLoadedAssets[url]!='undefined'){return;} Mautic.headLoadedAssets[url]=1;var link=document.createElement("link");link.type="text/css";link.rel="stylesheet";link.href=url;mQuery('head').append(link);},startIconSpinOnEvent:function(target){if(MauticVars.ignoreIconSpin){MauticVars.ignoreIconSpin=false;return;} if(typeof target=='object'&&typeof(target.target)!=='undefined'){target=target.target;} -if(mQuery(target).length){var hasBtn=mQuery(target).hasClass('btn');var hasIcon=mQuery(target).hasClass('fa');var i=(hasBtn&&mQuery(target).find('i.fa').length)?mQuery(target).find('i.fa'):target;if((hasBtn&&mQuery(target).find('i.fa').length)||hasIcon){var el=(hasIcon)?target:mQuery(target).find('i.fa').first();var identifierClass=(new Date).getTime();MauticVars.iconClasses[identifierClass]=mQuery(el).attr('class');var specialClasses=['fa-fw','fa-lg','fa-2x','fa-3x','fa-4x','fa-5x','fa-li','text-white','text-muted'];var appendClasses="";for(var i=0;i',{id:'mautic-backdrop'});mQuery('
    ',{'class':'modal-backdrop fade in'}).appendTo(container);if(typeof hideWait=='undefined'){mQuery('
    ',{"class":'mautic-pleasewait'}).html(mauticLang.pleaseWait).appendTo(container);} container.appendTo('body');}},deactivateBackgroup:function(){if(mQuery('#mautic-backdrop').length){mQuery('#mautic-backdrop').remove();}},executeAction:function(action,callback){if(typeof Mautic.activeActions=='undefined'){Mautic.activeActions={};}else if(typeof Mautic.activeActions[action]!='undefined'){return;} Mautic.activeActions[action]=true;Mautic.dismissConfirmation();if(action.indexOf('batchExport')>=0){Mautic.initiateFileDownload(action);return;} @@ -62,15 +62,17 @@ event.preventDefault();if(!mQuery(this).hasClass('btn-nospin')){Mautic.startIcon mQuery('#'+id).click();});});}}else{mQuery('.toolbar-action-buttons').addClass('hide');if(mQuery('.toolbar-form-buttons').hasClass('hide')){mQuery(container+' .bottom-form-buttons').addClass('hide');var buttons=mQuery(container+" .bottom-form-buttons").html();mQuery(container+' .toolbar-form-buttons .toolbar-standard').html('');mQuery(container+' .toolbar-form-buttons .toolbar-dropdown .drop-menu').html('');var lastIndex=mQuery(buttons).filter("button").length-1;mQuery(buttons).filter("button").each(function(i,v){var id=mQuery(this).attr('id');var buttonClick=function(event){event.preventDefault();if(!mQuery(this).hasClass('btn-dnd')){mQuery(this).parent().find('button').prop('disabled',true);} Mautic.startIconSpinOnEvent(event);mQuery('#'+id).click();};mQuery("
    ').css(builderCss).appendTo('.builder-content');mQuery('.btn-close-builder').prop('disabled',true);var assets=Mautic.htmlspecialchars_decode(mQuery('[data-builder-assets]').html());themeHtml=themeHtml.replace('',assets+'');Mautic.buildBuilderIframe(themeHtml,'builder-template-content',function(){mQuery('#builder-overlay').addClass('hide');mQuery('.btn-close-builder').prop('disabled',false);});};Mautic.formatCode=function(){Mautic.builderCodeMirror.autoFormatRange({line:0,ch:0},{line:Mautic.builderCodeMirror.lineCount()});} +var builderPanel=mQuery('.builder-panel');var builderContent=mQuery('.builder-content');var btnCloseBuilder=mQuery('.btn-close-builder');var panelHeight=(builderContent.css('right')=='0px')?builderPanel.height():0;var panelWidth=(builderContent.css('right')=='0px')?0:builderPanel.width();var spinnerLeft=(mQuery(window).width()-panelWidth-60)/2;var spinnerTop=(mQuery(window).height()-panelHeight-60)/2;builderPanel.on('scroll',function(e){builderPanel.find('input:focus').blur();});var overlay=mQuery('').css(builderCss).appendTo('.builder-content');btnCloseBuilder.prop('disabled',true);var assets=Mautic.htmlspecialchars_decode(mQuery('[data-builder-assets]').html());themeHtml=themeHtml.replace('',assets+'');themeHtml=Mautic.prepareDynamicContentBlocksForBuilder(themeHtml);Mautic.buildBuilderIframe(themeHtml,'builder-template-content',function(){mQuery('#builder-overlay').addClass('hide');btnCloseBuilder.prop('disabled',false);});};Mautic.formatCode=function(){Mautic.builderCodeMirror.autoFormatRange({line:0,ch:0},{line:Mautic.builderCodeMirror.lineCount()});} Mautic.openMediaManager=function(){Mautic.openServerBrowser(mauticBasePath+'/'+mauticAssetPrefix+'app/bundles/CoreBundle/Assets/js/libraries/ckeditor/filemanager/index.html?type=Images',screen.width*0.7,screen.height*0.7);} Mautic.setFileUrl=function(url,width,height,alt){Mautic.insertTextAtCMCursor(url);} Mautic.insertTextAtCMCursor=function(text){var doc=Mautic.builderCodeMirror.getDoc();var cursor=doc.getCursor();doc.replaceRange(text,cursor);} Mautic.openServerBrowser=function(url,width,height){var iLeft=(screen.width-width)/2;var iTop=(screen.height-height)/2;var sOptions="toolbar=no,status=no,resizable=yes,dependent=yes";sOptions+=",width="+width;sOptions+=",height="+height;sOptions+=",left="+iLeft;sOptions+=",top="+iTop;var oWindow=window.open(url,"BrowseWindow",sOptions);} -Mautic.keepPreviewAlive=function(iframeId){var codeChanged=false;Mautic.builderCodeMirror.on('change',function(cm,change){codeChanged=true;});window.setInterval(function(){if(codeChanged){Mautic.livePreviewInterval=Mautic.updateIframeContent(iframeId,Mautic.builderCodeMirror.getValue());codeChanged=false;}},2000);};Mautic.killLivePreview=function(){window.clearInterval(Mautic.livePreviewInterval);};Mautic.destroyCodeMirror=function(){delete Mautic.builderCodeMirror;mQuery('#customHtmlContainer').empty();};Mautic.buildBuilderIframe=function(themeHtml,id,onLoadCallback){if(mQuery('iframe#'+id).length){var builder=mQuery('iframe#'+id);}else{var builder=mQuery("