diff --git a/RoboFile.php b/RoboFile.php index 9c43104404..b02c7a3b2e 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -65,7 +65,8 @@ class RoboFile extends \Robo\Tasks { 'assets/css/src/admin.styl', 'assets/css/src/newsletter_editor/newsletter_editor.styl', 'assets/css/src/public.styl', - 'assets/css/src/rtl.styl' + 'assets/css/src/rtl.styl', + 'assets/css/src/import.styl' ); $this->_exec(join(' ', array( diff --git a/assets/css/src/import.styl b/assets/css/src/import.styl new file mode 100644 index 0000000000..63c38add18 --- /dev/null +++ b/assets/css/src/import.styl @@ -0,0 +1,69 @@ +.mailpoet_hidden, .mailpoet_validation_error + display none + +.mailpoet_no-search + .select2-search + display none + +.form-table + th + width 300px + +#paste_input + width 100% + +input[type="radio"] + margin-right 0.5em !important + & + span + margin-right 2.5em + +span + &.mailpoet_mailchimp-key-status + &.mailpoet_mailchimp-ok + &:before + content "\2713" + color #0e90d2 + margin-left 15px + &.mailpoet_mailchimp-error + &:before + content "\2717" + color #900 + margin-left 15px + +#subscribers_data + overflow auto + table + width auto + td + padding 0.5em + & > table + & > tbody + & > td + padding 0.5em + & > tr + &:nth-child(odd) + background #f9f9f9 + .mailpoet_header + text-transform uppercase + font-weight 600 + text-decoration underline + +#subscribers_data th:first-child, #subscribers_data td:first-child + width 10em !important + text-align center !important + padding 0 1em 0 1em !important + vertical-align inherit !important + +.mailpoet_data_match + color #0e90d2 + margin-left 0.25em + +.mailpoet_import_error, .mailpoet_validation_error + color #900 + +tr + &.mailpoet_lists + & > td + & > a + margin-left 15px + diff --git a/assets/js/src/import/import.js b/assets/js/src/import/import.js new file mode 100644 index 0000000000..8abf0f5f7b --- /dev/null +++ b/assets/js/src/import/import.js @@ -0,0 +1,78 @@ +define( + [ + 'backbone', + 'underscore', + 'jquery', + 'mailpoet', + 'handlebars', + 'papaparse' + ], + function ( + Backbone, + _, + jQuery, + MailPoet, + Handlebars, + Papa + ) { + jQuery(document).ready(function () { + // configure router + router = new (Backbone.Router.extend({ + routes: { + '': 'home', + 'step_1': 'step_1', + 'step_2': 'step_2', + 'step_3': 'step_3' + }, + home: function () { + this.navigate('step_1', {trigger: true}); + } + })); + + function show_current_step() { + MailPoet.Notice.hide(); + MailPoet.Modal.loading(false); + jQuery('#mailpoet_subscribers_import > div[id^="step_"]').hide(); + jQuery(location.hash).show(); + } + + /* + * STEP 1 (upload or copy/paste) + */ + router.on('route:step_1', function () { + // render process button for each each method + var method_process_template = Handlebars.compile(jQuery('#method_process_template').html()); + jQuery('.mailpoet_method_process').html(method_process_template()); + + // define reusable variables + var current_step = jQuery(location.hash), + select_method = jQuery('#select_method'), + subscribers_paste_input = jQuery('#paste_input'), + subscribers_paste_input_placeholder = subscribers_paste_input.data('placeholder').replace(/\\n/g, '\n'), + subscribers_paste_process = jQuery('#method_paste > div.mailpoet_method_process').find('a.mailpoet_process'), + subscribers_mailchimp_key = jQuery('#mailchimp_key'), + subscribers_mailchimp_key_verify = jQuery('#mailchimp_key_verify'), + subscribers_mailchimp_lists = jQuery('#mailchimp_lists'), + subscribers_mailchimp_process = jQuery('#method_mailchimp > div.mailpoet_method_process').find('a.mailpoet_process'), + subscribers_file_local = jQuery('#file_local'), + subscribers_file_process = jQuery('#method_file > div.mailpoet_method_process').find('a.mailpoet_process'); + + // define method change behavior + select_method.change(function () { + MailPoet.Notice.hide(); + var available_methods = jQuery(':radio[name="select_method"]'), + selected_method = available_methods.index(available_methods.filter(':checked')); + // hide all methods + current_step.find('.inside').children('div[id^="method_"]').hide(); + // show selected method + current_step.find('.inside').children('div[id^="method_"]:eq(' + selected_method + ')').show(); + }); + + // start step 1 + show_current_step(); + }); + + Backbone.history.start(); + }); + } +); \ No newline at end of file diff --git a/assets/js/src/import/import.jsx b/assets/js/src/import/import.jsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/lib/Config/Menu.php b/lib/Config/Menu.php index b66995ed11..63fd1d4b24 100644 --- a/lib/Config/Menu.php +++ b/lib/Config/Menu.php @@ -1,5 +1,6 @@ renderer->render('newsletter/form.html', $data); } - function import() { - echo $this->renderer->render('import.html'); + function import() { + $import = new Import(); + $data = $import->bootstrapImportMenu(); + + echo $this->renderer->render('import.html', $data); } + function formEditor() { $id = (isset($_GET['id']) ? (int)$_GET['id'] : 0); diff --git a/lib/Import/Import.php b/lib/Import/Import.php new file mode 100644 index 0000000000..6fdc467588 --- /dev/null +++ b/lib/Import/Import.php @@ -0,0 +1,103 @@ + __("Email"), + 'subscriber_firstname' => __("First name"), + 'subscriber_lastname' => __("Last name"), + 'subscriber_confirmed_ip' => __("IP address"), + 'subscriber_confirmed_at' => __("Subscription date"), + 'subscriber_state' => __("Status") + ); + } + + function formatSubscriberFields($subscriberFields) { + return array_map(function ($fieldId, $fieldName) { + return array( + 'id' => $fieldId, + 'name' => $fieldName, + 'type' => ($fieldId === 'subscriber_confirmed_at') ? 'date' : null, + 'custom' => false + ); + }, array_keys($subscriberFields), $subscriberFields); + } + + function formatSubscriberCustomFields($subscriberCustomFields) { + return array_map(function ($field) { + return array( + 'id' => $field['id'], + 'name' => $field['name'], + 'label' => $field['name'], + 'type' => $field['type'], + 'custom' => true + ); + }, $subscriberCustomFields); + } + + function formatSelect2Fields($subscriberFields, $subscriberCustomFields) { + $data = array( + array( + 'name' => __("Actions"), + 'children' => array( + array( + 'id' => 'ignore', + 'name' => __("Ignore column..."), + ), + array( + 'id' => 'create', + 'name' => __("Create new column...") + ), + ) + ), + array( + 'name' => __("System columns"), + 'children' => $subscriberFields + ) + ); + + if($subscriberCustomFields) { + array_push($data, array( + 'name' => __("User columns"), + 'children' => $subscriberCustomFields + )); + } + return $data; + } + + function bootstrapImportMenu() { + $data['segments'] = array_map(function ($segment) { + return array( + 'id' => $segment['id'], + 'name' => $segment['name'], + 'text' => $segment['name'] + ); + }, $this->getSegments()); + + $data['subscriberFields'] = $this->formatSubscriberFields( + $this->getSubscriberFields() + ); + + $data['subscriberCustomFields'] = $this->formatSubscriberCustomFields( + $this->getSubscriberCustomFields() + ); + + $data['select2Fields'] = $this->formatSelect2Fields( + $data['subscriberFields'], + $data['subscriberCustomFields'] + ); + return $data; + } +} \ No newline at end of file diff --git a/lib/Util/Helpers.php b/lib/Util/Helpers.php new file mode 100644 index 0000000000..ec783ac027 --- /dev/null +++ b/lib/Util/Helpers.php @@ -0,0 +1,100 @@ + 'dd', + 'D' => 'D', + 'j' => 'd', + 'l' => 'DD', + 'N' => '', + 'S' => '', + 'w' => '', + 'z' => 'o', + // Week + 'W' => '', + // Month + 'F' => 'MM', + 'm' => 'mm', + 'M' => 'M', + 'n' => 'm', + 't' => '', + // Year + 'L' => '', + 'o' => '', + 'Y' => 'yy', + 'y' => 'y', + // Time + 'a' => '', + 'A' => '', + 'B' => '', + 'g' => '', + 'G' => '', + 'h' => '', + 'H' => '', + 'i' => '', + 's' => '', + 'u' => '' + ); + $jqueryui_format = ""; + $escaping = false; + for ($i = 0; $i < strlen($php_format); $i++) { + $char = $php_format[$i]; + if($char === '\\') // PHP date format escaping character + { + $i++; + if($escaping) { + $jqueryui_format .= $php_format[$i]; + } else { + $jqueryui_format .= '\'' . $php_format[$i]; + } + $escaping = true; + } else { + if($escaping) { + $jqueryui_format .= "'"; + $escaping = false; + } + if(isset($SYMBOLS_MATCHING[$char])) { + $jqueryui_format .= $SYMBOLS_MATCHING[$char]; + } else { + $jqueryui_format .= $char; + } + } + } + + return $jqueryui_format; +} + +/* + * Determine maximum post size in bytes + */ +function get_maximum_post_size() { + $maximum_post_size = ini_get('post_max_size'); + $maximum_post_size_bytes = (int) $maximum_post_size; + $unit = strtolower($maximum_post_size[strlen($maximum_post_size) - 1]); + switch ($unit) { + case 'g': + $maximum_post_size_bytes *= 1024; + case 'm': + $maximum_post_size_bytes *= 1024; + case 'k': + $maximum_post_size_bytes *= 1024; + } + + return $maximum_post_size_bytes; +} + +/* + * Flatten multidimensional array + */ +function flatten_array($array) { + return call_user_func_array( + 'array_merge_recursive', array_map('array_values', $array) + ); +} \ No newline at end of file diff --git a/views/import.html b/views/import.html index 2ca4813611..b543a7982b 100644 --- a/views/import.html +++ b/views/import.html @@ -1,5 +1,19 @@ <% extends 'layout.html' %> <% block content %> -
-<% endblock %> +
+

<%= __('Import') %>

+ + <% include 'import/step1.html' %> + + + + +
+ +<%= stylesheet('import.css') %> + +<% endblock %> \ No newline at end of file diff --git a/views/import/step1.html b/views/import/step1.html new file mode 100644 index 0000000000..1438643ce1 --- /dev/null +++ b/views/import/step1.html @@ -0,0 +1,154 @@ +
+
+ + + + + + + + +
+ <%= __('How would you like to import subscribers?') %> + +
+ +
+
+ + +
+ + + + + + + +
+ + + +
+
+ +
+
+ + +
+ + + + + + + +
+ + + +   + <%= __( 'total max upload file size : %s' )|replace({'%s': maximumParseSize}) %> +
+
+ +
+
+ + +
+ + + + + + + + + + + +
+ + + + + +
+ + + +
+
+ +
+
+ + + + +
+
\ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index e05281227f..0cbb5c1928 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -27,7 +27,8 @@ baseConfig = { 'interact$': 'interact.js/interact.js', 'spectrum$': 'spectrum-colorpicker/spectrum.js', 'blob$': 'blob/Blob.js', - 'filesaver$': 'filesaver/FileSaver.js' + 'filesaver$': 'filesaver/FileSaver.js', + 'papaparse': 'papaparse/papaparse.min.js' }, }, node: {