Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"stat":{"id":8,"emailAddress":"test@test.com","ipAddress":{},"dateSent":"2015-08-26T01:34:37+00:00","isRead":true,"isFailed":false,"dateRead":"2015-08-26T01:35:53+00:00","retryCount":0,"source":"email","openCount":1,"lastOpened":"2015-08-26T01:35:53+00:00","sourceId":5,"trackingHash":"55dd17adace91","viewedInBrowser":false,"lead":{"id":26,"points":10,"color":"","fields":{"core":{"title":{"id":"1","label":"Title","alias":"title","type":"lookup","group":"core","value":""},"firstname":{"id":"2","label":"First Name","alias":"firstname","type":"text","group":"core","value":""},"lastname":{"id":"3","label":"Last Name","alias":"lastname","type":"text","group":"core","value":""},"company":{"id":"4","label":"Company","alias":"company","type":"lookup","group":"core","value":""},"position":{"id":"5","label":"Position","alias":"position","type":"text","group":"core","value":""},"email":{"id":"6","label":"Email","alias":"email","type":"email","group":"core","value":"test@test.com"},"phone":{"id":"7","label":"Phone","alias":"phone","type":"tel","group":"core","value":""},"mobile":{"id":"8","label":"Mobile","alias":"mobile","type":"tel","group":"core","value":""},"fax":{"id":"9","label":"Fax","alias":"fax","type":"text","group":"core","value":""},"address1":{"id":"10","label":"Address Line 1","alias":"address1","type":"text","group":"core","value":""},"address2":{"id":"11","label":"Address Line 2","alias":"address2","type":"text","group":"core","value":""},"city":{"id":"12","label":"City","alias":"city","type":"lookup","group":"core","value":""},"state":{"id":"13","label":"State","alias":"state","type":"region","group":"core","value":""},"zipcode":{"id":"14","label":"Zipcode","alias":"zipcode","type":"lookup","group":"core","value":""},"country":{"id":"15","label":"Country","alias":"country","type":"country","group":"core","value":""},"website":{"id":"16","label":"Website","alias":"website","type":"text","group":"core","value":""}},"social":{"twitter":{"id":"17","label":"Twitter","alias":"twitter","type":"text","group":"social","value":""},"facebook":{"id":"18","label":"Facebook","alias":"facebook","type":"text","group":"social","value":""},"skype":{"id":"20","label":"Skype","alias":"skype","type":"text","group":"social","value":""},"instagram":{"id":"21","label":"Instagram","alias":"instagram","type":"text","group":"social","value":""},"foursquare":{"id":"22","label":"Foursquare","alias":"foursquare","type":"text","group":"social","value":""}},"personal":[],"professional":[]}},"email":{"id":5,"name":"Email","subject":"Email","language":"en","category":null,"fromAddress":null,"fromName":null,"replyToAddress":null,"bccAddress":null,"publishUp":null,"publishDown":null,"readCount":1,"sentCount":3,"revision":1,"assetAttachments":[],"variantStartDate":null,"variantSentCount":0,"variantReadCount":0,"variantParent":null,"variantChildren":[]}}}

View File

@@ -0,0 +1,45 @@
/* EmailBundle */
.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 {
}
.clickable-stat a { color: #fff; }
.clickable-stat a:hover { color: #fff; }
#emailGraphStats .spinner,
#reads-map-container .spinner{
text-align: center;
font-size: 32px;
}
.dynamic-content .nav-tabs {
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
flex-wrap: nowrap;
display: flex;
scrollbar-width: thin; /* Firefox */
}

View File

@@ -0,0 +1,170 @@
@media (min-width: 768px) {
.modal-dialog-heatmap {
width: 90%;
max-width: 1200px;
}
}
.modal-heatmap-close {
position: absolute;
top: 5px;
right: 20px;
color: #fff;
z-index: 1080;
float: none;
opacity: 0.75;
}
.modal-heatmap-close:hover,
.modal-heatmap-close:focus {
opacity: 1;
color: #fff;
}
.heatmap-iframe {
display: block;
width: 100%;
height: 600px;
border: 0;
margin: 0;
padding: 0;
}
@media (min-width: 768px) {
.heatmap-iframe {
height: 90vh;
}
}
body.heatmap-iframe-body a.heatmap-link {
position: relative;
z-index: 1010;
}
.heatmap-label {
position: absolute;
z-index: 1050;
box-shadow: 0 0 2px rgba(0, 0, 0, 0.45);
}
.heatmap-label > p {
padding: 6px 6px 4px 6px !important;
margin: 0 !important;
font-family: "Open Sans", Helvetica, Arial, sans-serif !important;
font-size: 12px !important;
line-height: 1 !important;
color: #fff !important;
}
.heatmap-legend {
display: none;
position: absolute;
z-index: 1060;
right: 20px;
top: 30px;
color: #fff;
padding: 15px;
background-color: rgba(0, 0, 0, 0.85);
border-radius: 4px;
}
@media (min-width: 768px) {
.heatmap-legend {
display: block;
}
}
.heatmap-legend a {
color: rgba(255, 255, 255, 0.6);
}
.heatmap-legend a.active,
.heatmap-legend a:focus,
.heatmap-legend a:hover {
color: rgba(255, 255, 255, 1);
text-decoration: underline;
}
.heatmap-legend > p:nth-child(1),
.heatmap-legend > p:nth-child(3) {
color: #cccccc;
font-size: 18px;
line-height: 1.1;
}
.heatmap-legend > p:nth-child(1) {
margin-top: 0;
}
.heatmap-legend > p:nth-child(2) {
color: #fff;
font-size: 32px;
margin-bottom: 20px;
}
.heatmap-legend > p:nth-child(3) {
margin-bottom: 5px;
}
.heatmap-scale {
display: flex;
}
.heatmap-scale-header,
.heatmap-scale-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
font-size: 12px;
}
.heatmap-scale-footer {
padding-bottom: 0;
}
.heatmap-scale-header > div,
.heatmap-scale-footer > div {
}
.heatmap-scale-bar {
position: relative;
width: 47px;
height: 16px;
background: #fff;
}
.heatmap-scale-bar:nth-child(1) {
background: linear-gradient(to right, #2c3bb6, #0a85ff);
border-radius: 2px 0 0 2px;
}
.heatmap-scale-bar:nth-child(2) {
background: linear-gradient(to right, #0a85ff, #f0df42);
}
.heatmap-scale-bar:nth-child(3) {
background: linear-gradient(to right, #f0df42, #f8c344);
}
.heatmap-scale-bar:nth-child(4) {
background: linear-gradient(to right, #f8c344, #ff843a);
}
.heatmap-scale-bar:nth-child(5) {
background: linear-gradient(to right, #ff843a, #f83834);
border-radius: 0 2px 2px 0;
}
.heatmap-scale-bar:nth-child(-n+4):after {
content: "";
display: block;
width: 1px;
height: 24px;
background-color: rgba(255, 255, 255, 0.8);
position: absolute;
top: -4px;
right: -1px;
z-index: 1;
}

View File

@@ -0,0 +1,35 @@
//EmailBundle (Copied from app/bundles/LeadBundle/Assets/js/lead.js)
Mautic.emailBatchSubmit = function() {
if (Mautic.batchActionPrecheck("")) {
if (mQuery('#email_batch_newCategory').val()) {
const $emailBatchIds = mQuery('#email_batch_ids');
if ($emailBatchIds.length) {
$emailBatchIds.val(Mautic.getCheckedListIds(false, true));
}
return true;
}
}
return false;
};
function setCategory(id, newCategory) {
const tr = document.querySelector("#row_email_" + id);
const div = tr.querySelector("div.d-flex.ai-center.gap-xs");
const span = div.querySelector("span");
div.textContent = newCategory.name;
span.style = "background: #" + newCategory.color + ";"
div.prepend(span);
}
Mautic.emailBatchSubmitCallback = function( response ) {
mQuery('#MauticSharedModal').modal('hide');
console.log("Received: " + JSON.stringify(response));
response.affected.forEach( function(id){
setCategory(id, response.newCategory);
});
}

View File

@@ -0,0 +1,68 @@
Mautic.testMonitoredEmailServerConnection = function(mailbox) {
var data = {
host: mQuery('#config_emailconfig_monitored_email_' + mailbox + '_host').val(),
port: mQuery('#config_emailconfig_monitored_email_' + mailbox + '_port').val(),
encryption: mQuery('#config_emailconfig_monitored_email_' + mailbox + '_encryption').val(),
user: mQuery('#config_emailconfig_monitored_email_' + mailbox + '_user').val(),
password: mQuery('#config_emailconfig_monitored_email_' + mailbox + '_password').val(),
mailbox: mailbox
};
var abortCall = false;
if (!data.host) {
mQuery('#config_emailconfig_monitored_email_' + mailbox + '_host').parent().addClass('has-error');
abortCall = true;
} else {
mQuery('#config_emailconfig_monitored_email_' + mailbox + '_host').parent().removeClass('has-error');
}
if (!data.port) {
mQuery('#config_emailconfig_monitored_email_' + mailbox + '_port').parent().addClass('has-error');
abortCall = true;
} else {
mQuery('#config_emailconfig_monitored_email_' + mailbox + '_port').parent().removeClass('has-error');
}
if (abortCall) {
return;
}
mQuery('#' + mailbox + 'TestButtonContainer .ri-loader-3-line').removeClass('hide');
Mautic.ajaxActionRequest('email:testMonitoredEmailServerConnection', data, function(response) {
var theClass = (response.success) ? 'has-success' : 'has-error';
var theMessage = response.message;
mQuery('#' + mailbox + 'TestButtonContainer').removeClass('has-success has-error').addClass(theClass);
mQuery('#' + mailbox + 'TestButtonContainer .help-block').html(theMessage);
mQuery('#' + mailbox + 'TestButtonContainer .ri-loader-3-line').addClass('hide');
if (response.folders) {
if (mailbox == 'general') {
// Update applicable folders
mQuery('select[data-imap-folders]').each(
function(index) {
var thisMailbox = mQuery(this).data('imap-folders');
if (mQuery('#config_emailconfig_monitored_email_' + thisMailbox + '_override_settings_0').is(':checked')) {
var folder = '#config_emailconfig_monitored_email_' + thisMailbox + '_folder';
var curVal = mQuery(folder).val();
mQuery(folder).html(response.folders);
mQuery(folder).val(curVal);
mQuery(folder).trigger('chosen:updated');
}
}
);
} else {
// Find and update folder lists
var folder = '#config_emailconfig_monitored_email_' + mailbox + '_folder';
var curVal = mQuery(folder).val();
mQuery(folder).html(response.folders);
mQuery(folder).val(curVal);
mQuery(folder).trigger('chosen:updated');
}
}
});
};
Mautic.disableSendTestEmailButton = function(element) {
mQuery(element).closest('.tab-pane').find('.config-dsn-test-container').each(function () {Mautic.configDsnTestDisable(this)});
};

View File

@@ -0,0 +1,755 @@
/** EmailBundle **/
Mautic.emailOnLoad = function (container, response) {
Mautic.internalDynamicContentItemCreateListeners = [];
Mautic.internalDynamicContentFilterCreateListeners = [];
if (mQuery('#emailform_plainText').length) {
// @todo initiate the token dropdown
var plaintext = mQuery('#emailform_plainText');
Mautic.initAtWho(plaintext, plaintext.attr('data-token-callback'));
Mautic.initSelectTheme(mQuery('#emailform_template'));
Mautic.initEmailDynamicContent();
Mautic.prepareVersioning(
function (content) {
console.log('undo');
},
function (content) {
console.log('redo');
}
);
// Open the builder directly when saved from the builder
if (response && response.inBuilder) {
Mautic.isInBuilder = true;
Mautic.launchBuilder('emailform');
Mautic.processBuilderErrors(response);
}
} else if (mQuery(container + ' #list-search').length) {
Mautic.activateSearchAutocomplete('list-search', 'email');
}
if (mQuery('table.email-list').length) {
var ids = [];
mQuery('td.col-stats').each(function () {
var id = mQuery(this).attr('data-stats');
ids.push(id);
});
// Get all stats numbers in batches of 10
while (ids.length > 0) {
let batchIds = ids.splice(0, 1);
Mautic.ajaxActionRequest(
'email:getEmailCountStats',
{ids: batchIds},
function (response) {
if (response.success && response.stats) {
for (var i = 0; i < response.stats.length; i++) {
var stat = response.stats[i];
if (mQuery('#sent-count-' + stat.id).length) {
if (stat.pending) {
mQuery('#pending-' + stat.id + ' > a').html(stat.pending);
mQuery('#pending-' + stat.id).removeClass('hide');
}
if (stat.queued) {
mQuery('#queued-' + stat.id + ' > a').html(stat.queued);
mQuery('#queued-' + stat.id).removeClass('hide');
}
mQuery('#sent-count-' + stat.id + ' > a').html(stat.sentCount);
mQuery('#read-count-' + stat.id + ' > a').html(stat.readCount);
mQuery('#read-percent-' + stat.id + ' > a').html(stat.readPercent);
}
}
}
},
false,
true,
"GET"
);
}
}
if (mQuery('#emailGraphStats').length) {
// Email detail graph - loaded via AJAX not to block loading a whole page
var graphUrl = mQuery('#emailGraphStats').attr('data-graph-url');
mQuery("#emailGraphStats").load(graphUrl, function () {
Mautic.renderCharts();
Mautic.initDateRangePicker('#emailGraphStats #daterange_date_from', '#emailGraphStats #daterange_date_to');
});
}
var $loadDeliveredElements = mQuery('[data-email-stat-delivered-for]');
if ($loadDeliveredElements.length) {
$loadDeliveredElements.each(function(i, el) {
Mautic.loadEmailDeliveredStat(mQuery(el));
});
}
var $loadEmailUsage = mQuery('[data-fetch-email-usages]');
if ($loadEmailUsage.length) {
$loadEmailUsage.each(function(i, el) {
Mautic.loadEmailUsages(mQuery(el));
});
}
Mautic.initMailerIsOwnerGlobalCheck();
};
Mautic.emailOnUnload = function(id) {
if (id === '#app-content') {
delete Mautic.listCompareChart;
}
if (typeof Mautic.ajaxActionXhrQueue !== 'undefined') {
delete Mautic.ajaxActionXhrQueue['email:getEmailCountStats'];
}
};
Mautic.insertEmailBuilderToken = function(editorId, token) {
var editor = Mautic.getEmailBuilderEditorInstances();
editor[instance].insertText(token);
};
Mautic.getEmailAbTestWinnerForm = function(abKey) {
if (abKey && mQuery(abKey).val() && mQuery(abKey).closest('.form-group').hasClass('has-error')) {
mQuery(abKey).closest('.form-group').removeClass('has-error');
if (mQuery(abKey).next().hasClass('help-block')) {
mQuery(abKey).next().remove();
}
}
Mautic.activateLabelLoadingIndicator('emailform_variantSettings_winnerCriteria');
var emailId = mQuery('#emailform_sessionId').val();
var query = "action=email:getAbTestForm&abKey=" + mQuery(abKey).val() + "&emailId=" + emailId;
mQuery.ajax({
url: mauticAjaxUrl,
type: "POST",
data: query,
dataType: "json",
success: function (response) {
if (typeof response.html != 'undefined') {
if (mQuery('#emailform_variantSettings_properties').length) {
mQuery('#emailform_variantSettings_properties').replaceWith(response.html);
} else {
mQuery('#emailform_variantSettings').append(response.html);
}
if (response.html != '') {
Mautic.onPageLoad('#emailform_variantSettings_properties', response);
}
}
},
error: function (request, textStatus, errorThrown) {
Mautic.processAjaxError(request, textStatus, errorThrown);
},
complete: function() {
Mautic.removeLabelLoadingIndicator();
}
});
};
Mautic.submitSendForm = function () {
Mautic.dismissConfirmation();
mQuery('.btn-send').prop('disabled', true);
mQuery('form[name=\'batch_send\']').submit();
};
Mautic.emailSendOnLoad = function (container, response) {
if (mQuery('.email-send-progress').length) {
if (!mQuery('#emailSendProgress').length) {
Mautic.clearModeratedInterval('emailSendProgress');
} else {
Mautic.setModeratedInterval('emailSendProgress', 'sendEmailBatch', 2000);
}
}
};
Mautic.emailSendOnUnload = function () {
if (mQuery('.email-send-progress').length) {
Mautic.clearModeratedInterval('emailSendProgress');
if (typeof Mautic.sendEmailBatchXhr != 'undefined') {
Mautic.sendEmailBatchXhr.abort();
delete Mautic.sendEmailBatchXhr;
}
}
};
Mautic.sendEmailBatch = function () {
var data = 'id=' + mQuery('.progress-bar-send').data('email') + '&pending=' + mQuery('.progress-bar-send').attr('aria-valuemax') + '&batchlimit=' + mQuery('.progress-bar-send').data('batchlimit');
Mautic.sendEmailBatchXhr = Mautic.ajaxActionRequest('email:sendBatch', data, function (response) {
if (response.progress) {
if (response.progress[0] > 0) {
mQuery('.imported-count').html(response.progress[0]);
mQuery('.progress-bar-send').attr('aria-valuenow', response.progress[0]).css('width', response.percent + '%');
mQuery('.progress-bar-send span.sr-only').html(response.percent + '%');
}
if (response.progress[0] >= response.progress[1]) {
Mautic.clearModeratedInterval('emailSendProgress');
setTimeout(function () {
mQuery.ajax({
type: 'POST',
showLoadingBar: false,
url: window.location,
data: 'complete=1',
success: function (response) {
if (response.newContent) {
// It's done so pass to process page
Mautic.processPageContent(response);
}
}
});
}, 1000);
}
}
Mautic.moderatedIntervalCallbackIsComplete('emailSendProgress');
});
};
Mautic.autoGeneratePlaintext = function() {
mQuery('.plaintext-spinner').removeClass('hide');
Mautic.ajaxActionRequest(
'email:generatePlaintText',
{
id: mQuery('#emailform_sessionId').val(),
custom: mQuery('#emailform_customHtml').val()
},
function (response) {
mQuery('#emailform_plainText').val(response.text);
mQuery('.plaintext-spinner').addClass('hide');
}
);
};
Mautic.selectEmailType = function(emailType) {
if (emailType == 'list') {
mQuery('#leadList').removeClass('hide');
mQuery('#segmentTranslationParent').removeClass('hide');
mQuery('#templateTranslationParent').addClass('hide');
mQuery('.page-header h3').text(mauticLang.newListEmail);
} else {
mQuery('#segmentTranslationParent').addClass('hide');
mQuery('#templateTranslationParent').removeClass('hide');
mQuery('#leadList').addClass('hide');
mQuery('.page-header h3').text(mauticLang.newTemplateEmail);
}
mQuery('#emailform_emailType').val(emailType);
mQuery('body').removeClass('noscroll');
mQuery('.email-type-modal').remove();
mQuery('.email-type-modal-backdrop').remove();
};
Mautic.getTotalAttachmentSize = function() {
var assets = mQuery('#emailform_assetAttachments').val();
if (assets) {
assets = {
'assets': assets
};
Mautic.ajaxActionRequest('email:getAttachmentsSize', assets, function(response) {
mQuery('#attachment-size').text(response.size);
}, false, false, "GET");
} else {
mQuery('#attachment-size').text('0');
}
};
Mautic.standardEmailUrl = function(options) {
if (options && options.windowUrl && options.origin) {
var url = options.windowUrl;
var editEmailKey = '/emails/edit/emailId';
var previewEmailKey = '/email/preview/emailId';
if (url.indexOf(editEmailKey) > -1 ||
url.indexOf(previewEmailKey) > -1) {
options.windowUrl = url.replace('emailId', mQuery(options.origin).val());
}
}
return options;
};
/**
* Enables/Disables email preview and edit. Can be triggered from campaign or form actions
* @param opener
* @param origin
*/
Mautic.disabledEmailAction = function(opener, origin) {
if (typeof opener == 'undefined') {
opener = window;
}
var email = opener.mQuery(origin);
if (email.length == 0) return;
var emailId = email.val();
var disabled = emailId === '' || emailId === null;
opener.mQuery('[id$=_editEmailButton]').prop('disabled', disabled);
opener.mQuery('[id$=_previewEmailButton]').prop('disabled', disabled);
};
Mautic.initEmailDynamicContent = function() {
if (mQuery('#dynamic-content-container').length) {
mQuery('#emailFilters .remove-selected').each( function (index, el) {
mQuery(el).on('click', function () {
mQuery(this).closest('.panel').animate(
{'opacity': 0},
'fast',
function () {
mQuery(this).remove();
}
);
if (!mQuery('#emailFilters li:not(.placeholder)').length) {
mQuery('#emailFilters li.placeholder').removeClass('hide');
} else {
mQuery('#emailFilters li.placeholder').addClass('hide');
}
});
});
mQuery('#addNewDynamicContent').on('click', function (e) {
e.preventDefault();
Mautic.createNewDynamicContentItem();
});
Mautic.initDynamicContentItem();
}
};
Mautic.createNewDynamicContentItem = function(jQueryVariant) {
// To support the parent.mQuery from the builder
var mQuery = (typeof jQueryVariant != 'undefined') ? jQueryVariant : window.mQuery;
var tabHolder = mQuery('#dynamicContentTabs');
var filterHolder = mQuery('#dynamicContentContainer');
var dynamicContentPrototype = mQuery('#dynamicContentPrototype').data('prototype');
var dynamicContentIndex = tabHolder.find('li').length - 1;
while (mQuery('#emailform_dynamicContent_' + dynamicContentIndex).length > 0) {
dynamicContentIndex++; // prevent duplicate ids
}
var tabId = '#emailform_dynamicContent_' + dynamicContentIndex;
var tokenName = 'Dynamic Content ' + (dynamicContentIndex + 1);
var newForm = dynamicContentPrototype.replace(/__name__/g, dynamicContentIndex);
var newTab = mQuery('<li><a role="tab" data-toggle="tab" href="' + tabId + '">' + tokenName + '</a></li>');
tabHolder.append(newTab);
filterHolder.append(newForm);
var itemContainer = mQuery(tabId);
var textarea = itemContainer.find('.editor');
var firstInput = itemContainer.find('input[type="text"]').first();
if (Mautic.internalDynamicContentItemCreateListeners) {
Mautic.internalDynamicContentItemCreateListeners.forEach(function(callback) {
callback(textarea);
});
}
tabHolder.find('i').first().removeClass('ri-loader-3-line ri-spin').addClass('ri-add-line text-success');
newTab.find('a').tab('show');
firstInput.focus();
Mautic.updateDynamicContentDropdown();
Mautic.initDynamicContentItem(tabId, mQuery, tokenName);
return tabId;
};
Mautic.dynamicContentAddNewItemListener = function(callback) {
Mautic.internalDynamicContentItemCreateListeners.push(callback);
}
Mautic.createNewDynamicContentFilter = function(el, jQueryVariant) {
// To support the parent.mQuery from the builder
var mQuery = (typeof jQueryVariant != 'undefined') ? jQueryVariant : window.mQuery;
var $this = mQuery(el);
var parentElement = $this.parents('.panel');
var tabHolder = parentElement.find('.nav');
var filterHolder = parentElement.find('.tab-content');
var filterBlockPrototype = mQuery('#filterBlockPrototype');
var filterIndex = filterHolder.find('.tab-pane').length - 1;
var dynamicContentIndex = $this.parents('.tab-pane').attr('id').match(/\d+$/)[0];
var filterPrototype = filterBlockPrototype.data('prototype');
var filterContainerId = '#emailform_dynamicContent_' + dynamicContentIndex + '_filters_' + filterIndex ;
// prevent duplicate ids
while (mQuery(filterContainerId).length > 0) {
filterIndex++;
filterContainerId = '#emailform_dynamicContent_' + dynamicContentIndex + '_filters_' + filterIndex ;
}
var newTab = mQuery('<li><a role="tab" data-toggle="tab" href="' + filterContainerId + '">Variation ' + (filterIndex + 1) + '</a></li>');
var newForm = filterPrototype.replace(/__name__/g, filterIndex)
.replace(/dynamicContent_0_filters/g, 'dynamicContent_' + dynamicContentIndex + '_filters')
.replace(/dynamicContent]\[0]\[filters/g, 'dynamicContent][' + dynamicContentIndex + '][filters');
tabHolder.append(newTab);
filterHolder.append(newForm);
var filterContainer = mQuery(filterContainerId);
var availableFilters = filterContainer.find('select[data-mautic="available_filters"]');
var altTextarea = filterContainer.find('.editor');
var removeButton = filterContainer.find('.remove-item');
Mautic.activateChosenSelect(availableFilters, false, mQuery);
availableFilters.on('change', function() {
var $this = mQuery(this);
if ($this.val()) {
Mautic.addDynamicContentFilter($this.val(), mQuery);
$this.val('');
$this.trigger('chosen:updated');
}
});
if (Mautic.internalDynamicContentFilterCreateListeners) {
Mautic.internalDynamicContentFilterCreateListeners.forEach(function(callback) {
callback(altTextarea);
});
}
Mautic.initRemoveEvents(removeButton, mQuery);
newTab.find('a').tab('show');
return filterContainerId;
};
Mautic.dynamicContentAddNewFilterListener = function(callback) {
Mautic.internalDynamicContentFilterCreateListeners.push(callback);
}
Mautic.initDynamicContentItem = function (tabId, jQueryVariant, tokenName) {
// To support the parent.mQuery from the builder
var mQuery = (typeof jQueryVariant != 'undefined') ? jQueryVariant : window.mQuery;
var $el = mQuery('#dynamic-content-container');
if ($el.length === 0){
mQuery = parent.mQuery;
$el = mQuery('#dynamic-content-container');
}
if (tabId || typeof tabId != "undefined") {
$el = mQuery(tabId);
}
// add a click event listener for adding a new dynamic content variant
$el.find('.addNewDynamicContentFilter').on('click', function (e) {
e.preventDefault();
Mautic.createNewDynamicContentFilter(this);
});
if (typeof tokenName != 'undefined') {
$el.find('.dynamic-content-token-name').val(tokenName);
}
if ($el.find('.dynamic-content-token-name').val() === '') {
var dynamicContent = $el.attr('id').match(/\d+$/);
if (dynamicContent) {
var dynamicContentIndex = dynamicContent[0];
$el.find('.dynamic-content-token-name').val('Dynamic Content ' + dynamicContentIndex);
}
}
$el.find('a.remove-selected').on('click', function() {
mQuery(this).closest('.panel').animate(
{'opacity': 0},
'fast',
function () {
mQuery(this).remove();
}
);
});
$el.find('select[data-mautic="available_filters"]').on('change', function() {
var $this = mQuery(this);
if ($this.val()) {
Mautic.addDynamicContentFilter($this.val(), mQuery);
$this.val('');
$this.trigger('chosen:updated');
}
});
Mautic.initRemoveEvents($el.find('.remove-item'), mQuery);
};
Mautic.updateDynamicContentDropdown = function () {
var options = [];
mQuery('#dynamicContentTabs').find('a[data-toggle="tab"]').each(function () {
var prototype = '<li><a class="fr-command" data-cmd="dynamicContent" data-param1="__tokenName__">__tokenName__</a></li>';
var newOption = prototype.replace(/__tokenName__/g, mQuery(this).text());
options.push(newOption);
});
mQuery('button[data-cmd="dynamicContent"]').next().find('ul').html(options.join(''));
};
Mautic.initMailerIsOwnerGlobalCheck = function() {
let radioSelector = '.mailer-is-owner-local';
Mautic.toggleMailerIsOwnerWarningMessage(radioSelector);
mQuery(radioSelector).on('change', function() {
Mautic.toggleMailerIsOwnerWarningMessage(radioSelector);
});
}
Mautic.toggleMailerIsOwnerWarningMessage = function(radioSelector) {
let checkedRadio = mQuery(radioSelector+':checked');
let globalMailerIsOwnerValue = checkedRadio.attr('data-global-mailer-is-onwer') ? '1' : '0';
let warningMessageId = 'mailer-is-owner-waring';
mQuery('#'+warningMessageId).remove();
if (checkedRadio.val() !== globalMailerIsOwnerValue) {
let warning = mQuery('<div/>');
warning.attr('id', warningMessageId);
warning.html(checkedRadio.attr('data-warning'));
warning.addClass('alert alert-warning mt-md');
checkedRadio.closest('.form-group').append(warning);
}
}
Mautic.initRemoveEvents = function (elements, jQueryVariant) {
var mQuery = (typeof jQueryVariant != 'undefined') ? jQueryVariant : window.mQuery;
if (elements.hasClass('remove-selected')) {
elements.on('click', function() {
mQuery(this).closest('.panel').animate(
{'opacity': 0},
'fast',
function () {
mQuery(this).remove();
}
);
});
} else {
elements.on('click', function (e) {
e.preventDefault();
var $this = mQuery(this);
var parentElement = $this.parents('.tab-pane.dynamic-content');
if ($this.hasClass('remove-filter')) {
parentElement = $this.parents('.tab-pane.dynamic-content-filter');
}
var tabLink = mQuery('a[href="#' + parentElement.attr('id') + '"]').parent();
var tabContainer = tabLink.parent();
parentElement.remove();
tabLink.remove();
// if tabContainer is for variants, show the first one, if it is the DEC vertical list, show the second one
if (tabContainer.hasClass('tabs-left') || $this.hasClass('remove-filter')) {
tabContainer.find('li').first().next().find('a').tab('show');
} else {
tabContainer.find('li').first().find('a').tab('show');
}
Mautic.updateDynamicContentDropdown();
});
}
};
Mautic.addDynamicContentFilter = function (selectedFilter, jQueryVariant) {
var mQuery = (typeof jQueryVariant != 'undefined') ? jQueryVariant : window.mQuery;
var dynamicContentItems = mQuery('.tab-pane.dynamic-content');
var activeDynamicContent = dynamicContentItems.filter(':visible');
var dynamicContentIndex = activeDynamicContent.attr('id').match(/\d+$/)[0]; //dynamicContentItems.index(activeDynamicContent);
var dynamicContentFilterContainers = activeDynamicContent.find('div[data-filter-container]');
var activeDynamicContentFilterContainer = dynamicContentFilterContainers.filter(':visible');
var dynamicContentFilterIndex = dynamicContentFilterContainers.index(activeDynamicContentFilterContainer);
var selectedOption = mQuery('option[data-mautic="available_' + selectedFilter + '"]').first();
var label = selectedOption.text();
// create a new filter
var filterNum = activeDynamicContentFilterContainer.children('.panel').length;
var prototype = mQuery('#filterSelectPrototype').data('prototype');
var fieldObject = selectedOption.data('field-object');
var fieldType = selectedOption.data('field-type');
var isSpecial = (mQuery.inArray(fieldType, ['leadlist', 'assets', 'lead_email_received', 'tags', 'multiselect', 'boolean', 'select', 'country', 'timezone', 'region', 'stage', 'locale']) != -1);
// Update the prototype settings
prototype = prototype.replace(/__name__/g, filterNum)
.replace(/__label__/g, label)
.replace(/dynamicContent_0_filters/g, 'dynamicContent_' + dynamicContentIndex + '_filters')
.replace(/dynamicContent]\[0]\[filters/g, 'dynamicContent][' + dynamicContentIndex + '][filters')
.replace(/filters_0_filters/g, 'filters_' + dynamicContentFilterIndex + '_filters')
.replace(/filters]\[0]\[filters/g, 'filters][' + dynamicContentFilterIndex + '][filters');
if (filterNum === 0) {
prototype = prototype.replace(/in-group/g, '');
}
// Convert to DOM
prototype = mQuery(prototype);
if (fieldObject == 'company') {
prototype.find('.object-icon').removeClass('ri-user-6-fill').addClass('ri-building-2-line');
} else {
prototype.find('.object-icon').removeClass('ri-building-2-line').addClass('ri-user-6-fill');
}
var filterBase = "emailform[dynamicContent][" + dynamicContentIndex + "][filters][" + dynamicContentFilterIndex + "][filters][" + filterNum + "]";
var filterIdBase = "emailform_dynamicContent_" + dynamicContentIndex + "_filters_" + dynamicContentFilterIndex + "_filters_" + filterNum;
if (isSpecial) {
var templateField = fieldType;
if (fieldType == 'boolean' || fieldType == 'multiselect' || fieldType == 'leadlist') {
templateField = 'select';
}
var template = mQuery('#templates .' + templateField + '-template').clone();
var $template = mQuery(template);
var templateNameAttr = $template.attr('name').replace(/__name__/g, filterNum)
.replace(/__dynamicContentIndex__/g, dynamicContentIndex)
.replace(/__dynamicContentFilterIndex__/g, dynamicContentFilterIndex);
var templateIdAttr = $template.attr('id').replace(/__name__/g, filterNum)
.replace(/__dynamicContentIndex__/g, dynamicContentIndex)
.replace(/__dynamicContentFilterIndex__/g, dynamicContentFilterIndex);
$template.attr('name', templateNameAttr);
$template.attr('id', templateIdAttr);
prototype.find('input[name="' + filterBase + '[filter]"]').replaceWith(template);
}
if (activeDynamicContentFilterContainer.find('.panel').length == 0) {
// First filter so hide the glue footer
prototype.find(".panel-footer").addClass('hide');
}
prototype.find("input[name='" + filterBase + "[field]']").val(selectedFilter);
prototype.find("input[name='" + filterBase + "[type]']").val(fieldType);
prototype.find("input[name='" + filterBase + "[object]']").val(fieldObject);
var filterEl = (isSpecial) ? "select[name='" + filterBase + "[filter]']" : "input[name='" + filterBase + "[filter]']";
activeDynamicContentFilterContainer.append(prototype);
Mautic.initRemoveEvents(activeDynamicContentFilterContainer.find("a.remove-selected"), mQuery);
var filter = '#' + filterIdBase + '_filter';
var fieldOptions = fieldCallback = '';
//activate fields
if (isSpecial) {
if (fieldType == 'select' || fieldType == 'boolean' || fieldType == 'multiselect' || fieldType == 'leadlist') {
// Generate the options
fieldOptions = selectedOption.data("field-list");
mQuery.each(fieldOptions, function(index, val) {
mQuery('<option>').val(index).text(val).appendTo(filterEl);
});
}
} else if (fieldType == 'lookup') {
fieldCallback = selectedOption.data("field-callback");
if (fieldCallback && typeof Mautic[fieldCallback] == 'function') {
fieldOptions = selectedOption.data("field-list");
Mautic[fieldCallback](filterIdBase + '_filter', selectedFilter, fieldOptions);
}
} else if (fieldType == 'datetime') {
mQuery(filter).datetimepicker({
format: 'Y-m-d H:i',
lazyInit: true,
validateOnBlur: false,
allowBlank: true,
scrollMonth: false,
scrollInput: false
});
} else if (fieldType == 'date') {
mQuery(filter).datetimepicker({
timepicker: false,
format: 'Y-m-d',
lazyInit: true,
validateOnBlur: false,
allowBlank: true,
scrollMonth: false,
scrollInput: false,
closeOnDateSelect: true
});
} else if (fieldType == 'time') {
mQuery(filter).datetimepicker({
datepicker: false,
format: 'H:i',
lazyInit: true,
validateOnBlur: false,
allowBlank: true,
scrollMonth: false,
scrollInput: false
});
} else if (fieldType == 'lookup_id') {
//switch the filter and display elements
var oldFilter = mQuery(filterEl);
var newDisplay = mQuery(oldFilter).clone();
mQuery(newDisplay).attr('name', filterBase + '[display]')
.attr('id', filterIdBase + '_display');
var oldDisplay = mQuery(prototype).find("input[name='" + filterBase + "[display]']");
var newFilter = mQuery(oldDisplay).clone();
mQuery(newFilter).attr('name', filterBase + '[filter]')
.attr('id', filterIdBase + '_filter');
mQuery(oldFilter).replaceWith(newFilter);
mQuery(oldDisplay).replaceWith(newDisplay);
var fieldCallback = selectedOption.data("field-callback");
if (fieldCallback && typeof Mautic[fieldCallback] == 'function') {
fieldOptions = selectedOption.data("field-list");
Mautic[fieldCallback](filterIdBase + '_display', selectedFilter, fieldOptions, mQuery);
}
} else {
mQuery(filter).attr('type', fieldType);
}
var operators = mQuery(selectedOption).data('field-operators');
mQuery('#' + filterIdBase + '_operator').html('');
mQuery.each(operators, function (label, value) {
var newOption = mQuery('<option/>').val(value).text(label);
newOption.appendTo(mQuery('#' + filterIdBase + '_operator'));
});
// Convert based on first option in list
Mautic.convertDwcFilterInput('#' + filterIdBase + '_operator', mQuery);
};
Mautic.copySubjectToName = function(elemSubject) {
let elemName = mQuery("#emailform_name");
if (elemName.val() === "") {
elemName.val(elemSubject.val());
}
};
Mautic.loadEmailDeliveredStat = function($el) {
var emailId = $el.data('email-stat-delivered-for');
Mautic.ajaxActionRequest('email:getEmailDeliveredCount', {id: emailId}, function(response){
if (response.success) {
var delivered = response.delivered;
$el.html(delivered);
}
}, false, true, "GET");
};
Mautic.loadEmailUsages = function($el) {
var emailId = $el.data('fetch-email-usages');
Mautic.ajaxActionRequest('email:getEmailUsages', {id: emailId}, function(response){
var usagesHtml = response.usagesHtml;
$el.html(usagesHtml);
}, false, true, "GET");
};

View File

@@ -0,0 +1,255 @@
(function(window, document, Mautic, $, Math) {
class Heatmap {
constructor(emailId) {
this.emailId = emailId;
this.mode = 'total';
this.content = null;
this.clickStats = null;
this.$modal = null;
this.$iframe = null;
this.$iframeBody = null;
this.iframeDocument = null;
this.totalClicks = null;
this.totalUniqueClicks = null;
this.legendTemplate = null;
this.links = [];
this.gradient = [
[44, 59, 182], // #2c3bb6
[10, 133, 255], // #0a85ff
[240, 223, 66], // #f0df42
[248, 195, 68], // #f8c344
[255, 132, 58], // #ff843a
[248, 56, 52] // #f83834
];
}
init() {
this.fetchHeatmap(function() {
this.render();
}.bind(this));
}
render() {
this.renderModal();
this.bindEvents();
this.$modal.modal('show');
}
fetchHeatmap(callback) {
Mautic.ajaxActionRequest('email:heatmap', {id: this.emailId}, function(response){
this.content = response.content;
this.clickStats = response.clickStats;
this.totalClicks = response.totalClicks;
this.totalUniqueClicks = response.totalUniqueClicks;
this.legendTemplate = response.legendTemplate;
callback();
}.bind(this), false, true, "GET");
}
waitForIframeContent(callback) {
const self = this;
const interval = setInterval(function () {
const height = self.$iframeBody.height();
if (height > 0 && self.lastHeight === height) {
callback();
clearInterval(interval);
} else {
self.lastHeight = height;
}
}, 100);
}
bindEvents() {
const self = this;
self.$iframe[0].addEventListener('load', function() {
self.waitForIframeContent(function() {
self.renderLabels();
self.bindMouseEvents();
});
});
$(window).on('resize', function() {
self.labelPositions();
});
self.$modal.on('hidden.bs.modal', function () {
$(this).remove();
});
$('[data-toggle="heatmap-total"]').click(function(e) {
e.preventDefault();
if (self.mode === 'total') return;
self.mode = 'total';
$('[data-heatmap-clicks]').html(self.totalClicks);
$('[data-toggle="heatmap-unique"]').removeClass('active');
$(this).addClass('active');
self.removeLabels();
self.renderLabels();
});
$('[data-toggle="heatmap-unique"]').click(function(e) {
e.preventDefault();
if (self.mode === 'unique') return;
self.mode = 'unique';
$('[data-heatmap-clicks]').html(self.totalUniqueClicks);
$('[data-toggle="heatmap-total"]').removeClass('active');
$(this).addClass('active');
self.removeLabels();
self.renderLabels();
});
$('div.heatmap-legend').on('scroll mousewheel touchmove', function(e) {
e.preventDefault();
});
}
bindMouseEvents() {
const self = this;
const moveUp = function() {
const $label = $(this).hasClass('heatmap-link') ? $(this).data('heatmap-label') : $(this);
$label.css('z-index', 2050);
}
const moveDown = function() {
const $label = $(this).hasClass('heatmap-link') ? $(this).data('heatmap-label') : $(this);
$label.css('z-index', 1050);
}
self.$iframeBody.on('mouseenter focus', '.heatmap-label, a.heatmap-link', moveUp);
self.$iframeBody.on('mouseleave blur', '.heatmap-label, a.heatmap-link', moveDown);
}
renderModal() {
this.$modal = $("<div />").attr({"class": "modal fade heatmap-modal"});
const $modalDialogDiv = $("<div />").attr({"class": "modal-dialog modal-dialog-heatmap"});
const $modalContentDiv = $("<div />").attr({"class": "modal-content"});
this.$iframe = $('<iframe class="heatmap-iframe">' + this.content + '</iframe>');
$modalContentDiv.append(this.$iframe);
this.$modal.append($modalDialogDiv.append($modalContentDiv));
$('body').append(this.$modal);
this.iframeDocument = this.$iframe[0].contentDocument || this.$iframe[0].contentWindow.document;
this.iframeDocument.open();
this.iframeDocument.write(this.content);
const cssLink = document.createElement("link");
cssLink.href = "/app/bundles/EmailBundle/Assets/css/heatmap.css";
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
this.iframeDocument.head.appendChild(cssLink);
this.$iframeBody = $('body', this.iframeDocument);
this.$iframeBody.addClass('heatmap-iframe-body');
$modalContentDiv.append(this.legendTemplate);
$modalContentDiv.append('<button type="button" class="modal-heatmap-close close" data-dismiss="modal"><span aria-hidden="true">×</span></button>');
this.iframeDocument.close();
}
renderLabels() {
const self = this;
self.clickStats.forEach(function(link) {
const $a = $('a[href="' + link.url + '"]', self.$iframeBody);
$a.addClass('heatmap-link');
$a.each(function() {
const $el = $(this);
self.links.push($el);
const rate = self.mode === 'total' ? link.hits_rate : link.unique_hits_rate;
const percent = Math.round(rate * 100);
const text = (self.mode === 'total' ? link.hits_text : link.unique_hits_text) + ' (' + percent.toString() + '%)';
const $label = $('<div class="heatmap-label"><p>' + text + '</p></div>');
const bgColor = self.interpolateColor(rate);
const bgColorLeft = self.interpolateColor(rate - 0.1);
const bgColorRight = self.interpolateColor(rate + 0.1);
$label.css({
'background-color': bgColor,
'background': 'linear-gradient(to right, ' +bgColorLeft+ ', '+ bgColorRight +')'
});
const $border = $('<div class="heatmap-label-border"></div>');
$border.css({
'border': '1px dashed ' + bgColor,
'border-bottom': 'none'
});
$label.append($border);
$label.attr('title', link.url);
self.$iframeBody.append($label);
$el.data('heatmap-label', $label);
$el.data('heatmap-label-border', $border);
$label.data('a', $a);
});
});
self.labelPositions();
}
removeLabels() {
if (!this.links.length) return;
$(this.links).each(function() {
$(this).data('heatmap-label').remove();
});
this.links = [];
}
labelPositions() {
const self = this;
$(self.links).each(function() {
const $el = $(this);
const $label = $el.data('heatmap-label');
const $border = $el.data('heatmap-label-border');
const position = $el.position();
$label.css({
position: 'absolute',
top: position.top + $el.outerHeight(),
left: position.left - 1,
'min-width': Math.max($el.outerWidth(), 60) + 2
});
$border.css({
position: 'absolute',
bottom: '100%',
left: 0,
width: $el.outerWidth(),
height: $el.outerHeight()
});
});
}
interpolateColor(rate) {
if (rate <= 0) {
return 'rgb(' + this.gradient[0].join(',') + ')';
}
if (rate >= 1) {
const lastIndex = this.gradient.length - 1;
return 'rgb(' + this.gradient[lastIndex].join(',') + ')';
}
const segmentCount = this.gradient.length - 1;
const segmentWidth = 1 / segmentCount;
const segmentIndex = Math.floor(rate / segmentWidth);
const segmentPercent = (rate - segmentIndex * segmentWidth) / segmentWidth;
const color1 = this.gradient[segmentIndex];
const color2 = this.gradient[segmentIndex + 1];
const r = Math.round(color1[0] + (color2[0] - color1[0]) * segmentPercent);
const g = Math.round(color1[1] + (color2[1] - color1[1]) * segmentPercent);
const b = Math.round(color1[2] + (color2[2] - color1[2]) * segmentPercent);
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
}
$(document).ready(function() {
$('body').on('click', '[data-toggle="email-heatmap"]', function(e) {
const emailId = $(this).data('email');
const heatmap = new Heatmap(emailId);
heatmap.init();
e.preventDefault();
});
});
})(window, document, Mautic, mQuery, Math);

View File

@@ -0,0 +1,33 @@
/**
* Used in data-lookup-callback attr of form field in ExampleSendType
* Take a look at https://github.com/twitter/typeahead.js/
*/
Mautic.activateExampleContactLookupField = function(fieldOptions, filterId) {
const lookupElementId = 'example_send_contact';
const action = mQuery('#'+ lookupElementId).attr('data-chosen-lookup');
const options = {
limit: 20,
'searchKey': 'lead.lead',
};
Mautic.activateFieldTypeahead(lookupElementId, filterId, options, action);
mQuery('#'+ lookupElementId).on("change",function(event) {
if (event.target.value === '') {
// Delete selected contact ID from hidden field
mQuery('#example_send_contact_id').val('');
}
});
};
/**
* Used in data-lookup-callback attr of form field in ExampleSendType
*/
Mautic.updateExampleContactLookupListFilter = function(field, item) {
if (item && item.id) {
mQuery('#example_send_contact_id').val(item.id);
mQuery(field).val(item.value);
}
};