Compare commits

...

395 Commits

Author SHA1 Message Date
6088497433 Bump up release version to 0.0.15 2016-02-12 18:25:12 +02:00
57f0b88299 Merge pull request #347 from mailpoet/sending_frequency
Implements sending frequency
2016-02-12 14:23:22 +02:00
f874ffc19c Merge pull request #346 from mailpoet/rendering_engine_image_update
Updates image rendering & unit test
2016-02-12 12:42:49 +02:00
d11badf3ce - Implements sending frequency
- Updates sending queue worker
2016-02-11 22:46:45 -05:00
3006c982cb - Updates image rendering & unit test 2016-02-11 21:28:43 -05:00
b29e31fdd6 Merge pull request #344 from mailpoet/router_update_sending_queue
Sending queue router update
2016-02-11 11:31:49 -05:00
bc42c8e280 Merge branch 'router_update_sending_queue' of mailpoet:mailpoet/mailpoet into router_update_sending_queue 2016-02-11 11:30:08 -05:00
409697ee64 Sending queue router update
- cleaned up useless code
- bugfixes
- improved code coverage
2016-02-11 11:30:01 -05:00
cfb4265971 Merge pull request #343 from mailpoet/queue_worker_rewrite
Sending queue worker rewrite
2016-02-11 18:16:07 +02:00
13d78aac05 Merge branch 'queue_worker_rewrite' of mailpoet:mailpoet/mailpoet into queue_worker_rewrite 2016-02-11 10:37:26 -05:00
6f176f4e6c - Updates MailChimp unit test 2016-02-11 10:34:24 -05:00
9b584296a5 - Updates queue worker based on code review comments 2016-02-11 10:23:41 -05:00
5ea87c5eed Sending queue router update
- cleaned up useless code
- bugfixes
- improved code coverage
2016-02-11 09:37:53 +01:00
7522084ccb - Rewrites sending queue worker and updates router
- Implements batch sending for queue worker
- Fixes mailer class issue when sender data can be empty
- Updates values for cron execution timeout/limit
2016-02-10 22:34:54 -05:00
214aa60d0e Merge pull request #338 from mailpoet/editor_polishing_2
Change `padded` image attribute to `fullWidth`
2016-02-10 22:34:36 -05:00
7a049ce1b7 - Rewrites sending queue worker and updates router
- Implements batch sending for queue worker
- Fixes mailer class issue when sender data can be empty
- Updates values for cron execution timeout/limit
2016-02-10 22:32:39 -05:00
94d293deb7 Merge pull request #339 from mailpoet/mailchimp_update
Updates MailChimp class and unit test
2016-02-09 18:04:43 +01:00
bd2d38d757 updated MP.Notice in order to handle arrays as error messages 2016-02-09 17:58:40 +01:00
abec524daa Merge pull request #340 from mailpoet/fix_registration_in_comments
Subscribe in comments
2016-02-09 18:41:58 +02:00
cac6beb4ac - Fixes display of error messages 2016-02-09 11:26:00 -05:00
cac995e15b fixed subscribe in comments 2016-02-09 16:55:00 +01:00
b26380fd10 - Updates MailChimp class and unit test 2016-02-09 10:03:29 -05:00
6c6a4070be Remove obsolete attribute from server response 2016-02-09 15:47:56 +02:00
8b001d820b Change padded image attribute to fullWidth 2016-02-09 15:26:06 +02:00
2ae3d8ebdf Merge pull request #337 from mailpoet/fix_listing_pagination
Pagination issues
2016-02-09 12:23:39 +02:00
459ec21f9d fixed pagination issues 2016-02-08 17:11:11 +01:00
9c7790d07e Merge pull request #336 from mailpoet/listing_empty_trash
Listing update + Unit tests
2016-02-08 16:01:42 +02:00
f9c5b99e46 updated Subscriber:: -> self:: in Models\Subscriber 2016-02-08 14:43:59 +01:00
95b0b39366 Fixed bulk_action success messages
- uptaded Subscriber & Segment routers' test
- moved add/remove segments logic to Subscriber::createOrUpdate
- fixed Router\Segments save not returning errors
2016-02-08 13:36:35 +01:00
4b6fa0e760 Added "Subscribers without a segment" filter
- removed useless dependency in filters.jsx
- fixed issue with Router\Subscribers::save() not updating segments
2016-02-08 10:32:06 +01:00
67a3440ced updated router unit tests - refactor + test bulk delete 2016-02-08 10:10:43 +01:00
0de372344a fixed bulkDelete 2016-02-06 15:17:19 +01:00
7a04eeb650 Listing: Empty trash button 2016-02-06 15:15:07 +01:00
8dbfe82922 Bump version up to 0.0.14 2016-02-05 17:34:53 +02:00
7322f2151c Merge pull request #334 from mailpoet/unit_tests_update
Enables conditional bypassing of unit tests
2016-02-05 16:38:31 +02:00
c43d2f240d - Updates MailChimp test and .env.sample 2016-02-05 09:35:32 -05:00
bbcd267b6f - Updates .env.sample with available options for unit tests 2016-02-05 09:00:28 -05:00
bbc4acb2a4 Merge pull request #335 from mailpoet/mailer_class_Fix
Fixes detection of reply_to address
2016-02-05 12:52:39 +02:00
c89cc5a919 Merge pull request #333 from mailpoet/test_email_fix
Test email fix
2016-02-05 12:19:40 +02:00
33075940de - Fixes detection of reply_to address 2016-02-04 19:22:11 -05:00
51c09b8360 Merge pull request #325 from mailpoet/router_unit_tests
Unit tests (Router\NewsletterTemplates & Router\Segments)
2016-02-04 18:59:09 -05:00
2fbf85f371 - Catches exception returned by mailer class when sender is not configured 2016-02-04 18:57:07 -05:00
24cb614adb - Enables conditional bypassing of unit tests 2016-02-04 18:41:18 -05:00
55f851208b Major update of unit tests / updated routers + models + react 2016-02-04 19:04:52 +01:00
990dac7727 Merge pull request #332 from mailpoet/editor_fixes
Editor bug fixes
2016-02-04 09:02:21 -05:00
233020ca20 fix unit tests 2016-02-04 14:38:25 +01:00
0c419cde16 Unit tests (Router\NewsletterTemplates & Router\Segments
- moved json_decode(body) inside NewsletterTemplate->asArray (override)
- updated NewsletterTemplate router accordingly
- finished Segments unit test
2016-02-04 14:36:08 +01:00
35f9530d8e Merge pull request #326 from mailpoet/router_unit_tests_2
More Unit Tests + Initializer fix
2016-02-04 08:24:15 -05:00
992fe2a6e9 Fix preventing dragging by settings/delete block tools 2016-02-04 13:47:46 +02:00
c2cb88f995 Unit tests fixed + models & routers update 2016-02-04 11:41:05 +01:00
ba69d659ab Changed editor to not send 'last_modified', let server pick one 2016-02-03 18:21:30 +02:00
397d988eb1 Allow block dragging only with "Move" tool, but not with others 2016-02-03 18:21:30 +02:00
d85f2341ec Change Posts/ALC to use MailPoet specific image size 2016-02-03 18:21:30 +02:00
2b3c288b5f Merge pull request #330 from mailpoet/template_footer_fix
Removes footer text/link color overrides for coffee shop template
2016-02-03 11:19:43 -05:00
8ec28a23a7 Removes footer text/link color overrides for coffee shop template 2016-02-03 15:04:18 +02:00
12c9623e2f Better Error handling for models
- added (array)getErrors() to models, returns false if no errors
- converted Forms::saveEditor method to use getErrors
- added error handling on the form editor view
2016-02-03 12:23:42 +01:00
8ba9fdccbc fix for initializer - widget is now registered 2016-02-03 10:42:56 +01:00
a2ef62302f More Unit Tests + Initializer fix
- added unit test for Router\Forms
- updated unit test for Model\Segment to reflect changes
2016-02-02 17:22:11 +01:00
24ecc879d3 Merge pull request #319 from mailpoet/php53-fix
Updates code to work with PHP 5.3
2016-02-02 13:25:23 +01:00
a1104a7f90 Merge pull request #320 from mailpoet/sending_queue_worker_fix
Sending queue worker fix
2016-02-01 16:32:42 +02:00
aa959810e9 Merge pull request #322 from mailpoet/code_coverage
Code coverage report
2016-02-01 16:31:53 +02:00
45b7a79277 Merge pull request #321 from mailpoet/router_upgrade_3
Router upgrade 3
2016-02-01 15:41:13 +02:00
41c8c0dae5 Code coverage report 2016-02-01 14:07:54 +01:00
2aeab7aaff Router Upgrade #3 & bugfix on Cron/Supervisor
- ALC
- Cron
- ImportExport
- Mailer
- Newsletters (only get method for consistency with other router get methods)
- Permissions
2016-02-01 13:00:11 +01:00
4fd0c4b484 Router updates + unit tests + React
- added -f flag to run unit test command in order to fail fast
- pass only id to "$endpoint->get($id)" in React forms instead of array
- updated routers according to the ->get($id) change
- refactored a bit the way form creation works
- added unit tests for Segments router
2016-02-01 11:56:21 +01:00
d4623cf763 Router update for Settings and Setup + unit tests 2016-02-01 11:56:21 +01:00
181c4fed08 - Fixes an issues with the sending queue worker throwing and error when
newsletter is not found
2016-01-31 21:35:34 -05:00
7884dd8389 - Updates code to work with PHP 5.3. Closes #307 2016-01-31 14:02:57 -05:00
b577d33414 Merge pull request #318 from mailpoet/minor_cron_update
Cron update
2016-01-29 22:03:55 +02:00
70de0a01bf - Moves cron timeout/execution limit to the central cron helper class 2016-01-29 15:01:10 -05:00
3b7f77d9af Bump up version to 0.0.13 2016-01-29 21:47:56 +02:00
21847ca875 Merge pull request #315 from mailpoet/sending_queue_worker_fix
Sending worker newsletter processing fix
2016-01-29 21:07:17 +02:00
6153316047 Merge pull request #314 from mailpoet/cron_loop_fix
Cron refactoring
2016-01-29 21:06:35 +02:00
32f8f07602 - Refactors and fixes issues identified during code review 2016-01-29 13:30:13 -05:00
70a04d9bf6 Merge pull request #317 from mailpoet/router_upgrade_2
CustomFields (Router update + Form block renaming + Unit tests)
2016-01-29 18:45:29 +02:00
bb1cc997cc CustomFields
- renamed form block type "input" to "text" for consistency with React
- updated CustomFields router to comply with main router
- unit tests for CF router
2016-01-29 17:16:24 +01:00
24f96d9d7d Merge pull request #313 from mailpoet/router_upgrade
Main Router update + Subscribers router update + Unit test
2016-01-29 17:01:21 +02:00
46c7332da2 Merge pull request #316 from mailpoet/sendgrid_mailer_fix
SendGrid mailer update
2016-01-29 12:53:44 +02:00
2f42f643ab - Fixes message body construction 2016-01-28 22:29:47 -05:00
63c87f3746 - Fixes issue with the sending worker failing to process newsletters 2016-01-28 21:40:30 -05:00
d4d575cda4 - Refactors cron supervisor/daemon/router 2016-01-28 21:38:23 -05:00
2cf03ec0a3 - Fixes cron HTTP request loop issue 2016-01-28 12:50:12 -05:00
72ad98a77f unit test for router Subscribers 2016-01-28 18:00:55 +01:00
b5094f568c Merge pull request #312 from mailpoet/mailpoet_bridge_update
MailPoet mailer update
2016-01-28 16:50:47 +02:00
7d224274fc - Rebases master & fixes some code 2016-01-28 09:40:57 -05:00
f3b9f7be92 - Updates MailPoet mailer to support batch sending
- Fixes message encoding issue
2016-01-28 09:40:57 -05:00
6e74f82ace Merge pull request #311 from mailpoet/mailer_tests_update
Disables send method for all mailer tests
2016-01-28 14:20:56 +02:00
831eb6af44 Merge pull request #310 from mailpoet/fix_install
Fix init/hooks so that we properly initialize db/plugin
2016-01-28 13:51:22 +02:00
1e8e5aecee Remove space after if 2016-01-28 13:50:58 +02:00
c0f98c9ba6 - Moves credentials from mailer tests to ENV file
- Disables send method by default unless enabled in ENV file
2016-01-27 18:36:38 -05:00
746c19d6ed - Resolves an issue with cron not starting 2016-01-27 14:15:00 -05:00
894a9e8c90 Router\Router is now taking care of outputting JSON
- converted Subscribers Router to return array instead of wp_send_json()
- added unit test for Subscribers Router
2016-01-27 17:23:30 +01:00
8fea917337 Merge pull request #308 from mailpoet/mailer_updates
Update to mailer methods
2016-01-27 14:13:37 +02:00
c60425afb2 Fix init/hooks so that we properly initialize db/plugin
- Added activation hook in main file (mailpoet.php)
- auto deactivate plugin in case of fatal errors during init
2016-01-27 12:52:40 +01:00
0776e9ad73 - Adds "reply to" option to all mailers
- Replaces WPMail with Swift using local transport (PHP mail)
- Fixes AmazonSES region naming convention
- Updates tests
2016-01-26 19:08:02 -05:00
91981cc324 Merge pull request #306 from mailpoet/mailer_cleanup
Removes Mandrill API mailer method
2016-01-26 18:52:10 +02:00
1906fafacb Merge pull request #301 from mailpoet/shortcodes_implementation
Shortcodes implementation
2016-01-26 18:32:15 +02:00
c11d95b402 - Refactors code 2016-01-26 11:29:56 -05:00
b4c8fe6f45 - Updates newsletter router and sending queue worker to work with the
shortcodes implementation
2016-01-26 10:44:18 -05:00
d0e770e0fc - Removes Mandrill API mailer method 2016-01-26 09:18:48 -05:00
3d6d1a4282 - Disables shortcodes unit test 2016-01-26 09:14:12 -05:00
dc3b47db00 - Implements shortcodes
- Updates newsletter router/sending queue worker to use shortcodes
  replacement class
2016-01-26 09:13:29 -05:00
900d6694e2 Merge pull request #302 from mailpoet/parsley_issue
removed parsley validation on step 3
2016-01-26 15:50:31 +02:00
e8074a61a5 Merge pull request #298 from mailpoet/text_block_rendering_fix
Text block formatting fix
2016-01-26 12:51:07 +02:00
64501a914a removed parsley validation on step 3
- fixed placeholder for select2 instances in settings
- fixed issue in sending queue worker when newsletter does not exist
2016-01-26 11:36:20 +01:00
de70e855ad - Replaces regex with DOM queries 2016-01-25 21:09:16 -05:00
03e3b5a94b - Fixes regex that formats heading tags 2016-01-25 21:08:16 -05:00
3331bed31c Merge pull request #300 from mailpoet/text_rendering
Text rendering
2016-01-25 18:16:16 +02:00
8c5a33a0fe Merge pull request #297 from mailpoet/alc_rendering
Automated latest content rendering
2016-01-25 17:35:51 +02:00
01eb6c7a98 - Refactors ALC 2016-01-25 09:27:51 -05:00
f502e0b677 - Implements text rendering
- Updates tests
- Updates newsletter router and sending queue worker to reflect changes to
  the renderer
2016-01-22 22:07:02 -05:00
a6b64a1c5d - Implements automated latest content rendering 2016-01-22 19:59:50 -05:00
5019131b21 Bump up version to 0.0.12 2016-01-22 16:44:09 +02:00
da483fb88f Merge pull request #296 from mailpoet/form_custom_fields
Form custom fields
2016-01-22 14:56:16 +02:00
788bed4622 moved const definition inside render method 2016-01-22 13:45:52 +01:00
3fbe5423d0 added constant for years range + added comment for month quirk 2016-01-22 13:38:43 +01:00
8357295be2 Merge pull request #295 from mailpoet/import_review_fixes
Various fixes based on Rafael's import review comments
2016-01-22 12:28:52 +02:00
8072b162d4 Unit tests for new methods in model subscriber 2016-01-22 11:28:26 +01:00
3f2f0ec1a9 Merge pull request #294 from mailpoet/mailchimp_import_fix
Fixes MailChimp import error
2016-01-22 12:20:44 +02:00
5a5a777b7d Added check for when the custom field doesn't exist
- suppressed cron supervisor error
2016-01-22 11:02:48 +01:00
6cac7f3652 Saving of date custom fields in React & PHP 2016-01-22 10:45:33 +01:00
6a9313107c - Fixes unit test 2016-01-21 12:21:22 -05:00
72c9d301b7 - Fixes issue with file extension warning
- Removes duplicate notices
- Updates Twig's localize function to escape double quotes
2016-01-21 12:08:51 -05:00
ad925de801 Custom fields (in Form & Edit subscriber) 2016-01-21 17:27:34 +01:00
1da28b7299 - Enforces CSV file extension during import file selection
- Updates "no records found" error message
2016-01-20 15:45:56 -05:00
e837ad7014 - Fixes MailChimp import error 2016-01-20 13:54:20 -05:00
daec56191f Save custom fields on subscribe
- added methods to get/set a specific custom field
- added method to get all custom fields (and assign each custom field to the subscriber's instance)
- fixed zIndex of form editor's toolbar (footer was positioned above, preventing click)
2016-01-19 17:02:05 +01:00
7bd25660df Merge pull request #293 from mailpoet/form_editor
Form editor update
2016-01-19 13:22:21 +02:00
3b9821fbe1 Remove matching form block when custom field is deleted 2016-01-18 17:46:42 +01:00
cabe2d61b7 Form editor update
- when a custom field is updated, the matching form field is now also updated
- ability to toggle "is_required" on First name & Last name
- fixed position of validation errors on segment selection, checkbox and radio
- fixed form subscription not working when using custom fields
- fixed sortable in segment selection after list update (add/remove)
- updated position of messages in form subscription
2016-01-18 17:23:10 +01:00
a6d802e2fa Bump up release version information 2016-01-15 18:16:39 +02:00
1732c4f634 Merge pull request #292 from mailpoet/total_subscriber_shortcode
Shortcodes (Archives & Total subscribers) & MailPoet page (create on install)
2016-01-15 17:07:11 +02:00
bb77134224 Unit tests for Settings getValue/setValue
- fixed typo in Shortcodes
- changed for -> foreach
2016-01-15 15:50:23 +01:00
f1cb64b240 Merge pull request #291 from mailpoet/animations
Editor: Animations
2016-01-15 14:15:49 +01:00
3689545589 Default page on install + Setting::setValue() dot notation + cleanup 2016-01-15 13:37:37 +01:00
9b67c56281 Make "Delete" tool animation less janky 2016-01-15 14:06:44 +02:00
dc38b19667 Change transition timings and easings based on feedback 2016-01-15 12:05:43 +02:00
a574733217 archives page 2016-01-14 20:00:15 +01:00
b90aaa629e Merge pull request #290 from mailpoet/subscribe_on_register
Subscribe on register
2016-01-14 20:21:57 +02:00
8de186c0e6 fixed subscribe on registration not using the proper setting 2016-01-14 17:41:04 +01:00
e3719967f9 renaming + casting 2016-01-14 17:13:02 +01:00
138a631ed7 Fix unit tests + enable signup confirmation by default
- minor cleanup
2016-01-14 15:57:46 +01:00
07b7636a72 Update Setting::getValue() to use dot syntax in Hooks
- fixed styling on welcome page (again)
2016-01-14 15:34:13 +01:00
a63ce3cdac Subscribe on registration 2016-01-14 15:30:22 +01:00
f5c7bb87af Merge pull request #288 from mailpoet/settings_round_1
Subscribe in comments
2016-01-14 16:28:52 +02:00
2c8d925971 fixed fatal error 2016-01-14 15:23:22 +01:00
0c5beb2511 Refactor & Improvements
- fixed position of checkbox in comment form
- refactored Subscriber::subscribe() method
- removed Form\Subscribe class in favor of Subscription\Comment (I'll create a similar class for Registration)
- added labels in Settings > Basics for Subscribe in comments & registration
- added method in Setting model to check whether signup confirmation is enabled
2016-01-14 14:11:25 +01:00
9c0316a87d Merge pull request #289 from mailpoet/newsletter_preview
Hook up sending newsletter previews
2016-01-14 12:48:12 +01:00
46c1b682fa Add option to scroll to notices 2016-01-14 13:39:48 +02:00
7954346a3f Fix left padding for .mailpoet_notice on editor pages 2016-01-14 13:31:40 +02:00
d87ff67f50 Remove whitespace after if and catch keywords 2016-01-13 18:50:52 +02:00
6642bb3bfa Verify preview input data, remove "Sender" inputs 2016-01-13 14:28:43 +02:00
2cb32e7a78 Add a method for sending newsletters via new Mailer class 2016-01-13 13:04:21 +02:00
fcea9adbd9 Unit tests + minor bugfix
- added unit tests for new methods in Models\Subscriber
- removed check for "isNew()". It was an unecessary check. Also isNew() always returns false once the new model is saved.
2016-01-13 11:54:23 +01:00
bbdd0dbb6e Subscribe in comments
- added Subscriber::subscribe($user, $segment_ids)
- refactored Router\Subscribers->subscribe() method to account for new method
- added Form\Subscribe class to handle subscription in comments
- updated Basics settings page (changed "list" to "segment")
2016-01-12 18:46:31 +01:00
1b2cf7bd16 Merge pull request #287 from mailpoet/newsletter_save
JSON encode newsletter body when sending it to server
2016-01-12 12:34:38 +01:00
b7cfa549d5 Change newsletter and template saving to JSON encode body separately 2016-01-11 18:27:30 +02:00
ffc1d0a61c Set MailPoet version to 0.0.10 for release 2016-01-08 19:38:57 +02:00
d1b160def7 Merge pull request #286 from mailpoet/cron_update
Cron update
2016-01-08 19:23:50 +02:00
493fd01754 Merge pull request #285 from mailpoet/import_export_fix
Import export fix
2016-01-08 19:21:56 +02:00
9ced4b1757 Fix export test after temp URL changed 2016-01-08 19:21:32 +02:00
17010e5ba9 Merge pull request #281 from mailpoet/rendering_engine_update
Rendering engine update
2016-01-08 19:00:12 +02:00
42ad7584d4 - Refactors ColumnsHelper class 2016-01-08 11:35:30 -05:00
dbc0f9b238 - Removes header padding 2016-01-08 11:23:02 -05:00
e62e9a5892 - Fixes issue with temp folder
- Updates formatting
2016-01-08 10:55:09 -05:00
bc25fa61b4 - Updates render method
- Removes unused/commented out code
2016-01-08 10:38:46 -05:00
2590967183 - Formats new line identation
- Formats identations in general
2016-01-08 09:00:09 -05:00
86eafd3c17 - Removes tabs 2016-01-08 08:47:41 -05:00
90a6f160c2 - Removes space after function 2016-01-08 07:53:26 -05:00
c774aec6a2 Revert "- Fixes minor issue when daemon has not yet been created"
This reverts commit 8f2fd1d76e.
2016-01-08 07:49:15 -05:00
8f2fd1d76e - Fixes minor issue when daemon has not yet been created 2016-01-08 07:40:32 -05:00
4df11163a1 automatically update 'updated_at' when saving daemon 2016-01-08 12:23:15 +01:00
82a736ffbb Cron update + removing console.log
- use Setting::getValue for getDaemon method
- added "updated_at" property within cron_daemon value (instead of using the setting's column)
- converted line endings to Unix in notice.js and removed console.log
2016-01-08 12:02:11 +01:00
87052986e8 - Rebases master
- Updates template
2016-01-07 23:53:15 -05:00
0c73c0fadc - Resolves issues identified by @rafaehlers during testing 2016-01-07 22:47:59 -05:00
5c7e11076d - WIP on updating import 2016-01-07 18:11:59 -05:00
d1df94c759 - Addresses issues identified during code review 2016-01-07 17:24:46 -05:00
53cc39c6f5 - Removes spacer from social icon elements 2016-01-07 17:24:45 -05:00
4955c72ee1 - Removes transparent background from divider element 2016-01-07 17:24:44 -05:00
16661af8c3 - Updates text alignemnt for buttons 2016-01-07 17:24:44 -05:00
bc80f69e41 - Updates the template 2016-01-07 17:24:43 -05:00
0192934e65 - Removes debug leftovers 2016-01-07 17:24:42 -05:00
2793e74858 - Rewrites the rendering engine
- Updates tests
Closes #280
2016-01-07 17:24:32 -05:00
5996696cc9 Merge pull request #284 from mailpoet/animations
Animations
2016-01-07 17:31:17 +01:00
7f6cf5bbf3 Remove obsolete clear divs 2016-01-07 18:28:37 +02:00
c0ef2254cd Merge pull request #283 from mailpoet/queue_refactor
Queue refactoring
2016-01-07 13:11:47 +01:00
0dbe04c3f8 - Addresses issues identified during code review 2016-01-06 19:19:06 -05:00
ef1805d9b5 Change obsolete "Arial Black" fonts to "Arial", add Velocity to tests 2016-01-06 16:43:39 +02:00
514f539e83 Remove obsolete Velocity dependence due to it being injected elsewhere 2016-01-06 15:58:14 +02:00
50f072705e Fix double animations when ALC block changes, remove console.log lines 2016-01-06 12:48:45 +02:00
f8f7bc3d3d Handle sidebar animations with Velocity, fix delete button transitions 2016-01-06 12:29:32 +02:00
f1bf2bb097 - Refactors Mailer class
- Refactors SendingQueue worker class
- Adds Maier router with a send() method + ability to specify sending method
- Updates tests
- Introduces 'stopping' and 'starting' cron states
- Improves cron control mechanism
Closes #276
2016-01-05 10:34:57 -05:00
bbe2f69a7f Clean up unused and speed up animations, fix sidebar transitions 2016-01-05 17:32:59 +02:00
c844488b0b Switch to VelocityJS for view transitions, slow down some transitions 2016-01-05 15:01:30 +02:00
112fe0cd6e Merge pull request #282 from mailpoet/sticky_kit
Fix sticky-kit dependency usage
2016-01-04 16:25:59 +01:00
c9e6dce785 Rename vendor_static/ to vendor/ 2016-01-04 17:02:46 +02:00
d1c09c015a Remove remote sticky-kit dep, use static local patched package instead 2016-01-04 13:19:40 +02:00
8d61866b77 Merge pull request #278 from mailpoet/drag_and_drop
Editor: Drag&Drop
2015-12-18 14:54:30 +01:00
0831c748b1 Merge pull request #275 from mailpoet/newsletter_template
Newsletter template preview
2015-12-18 14:54:21 +01:00
b2a0bc3860 Merge pull request #273 from mailpoet/review
Global Code Review.
2015-12-18 14:44:53 +01:00
1f99345e7b Remove obsolete code 2015-12-17 18:03:16 +02:00
89782bc94b Refactor editor JS, change blocks to extend base methods 2015-12-17 16:58:02 +02:00
155ff09280 Fix incorrect insertion order for containers 2015-12-17 15:21:56 +02:00
132e6d3342 Rename Wordpress component to Communication component, fix preview JS
syntax
2015-12-17 12:40:26 +02:00
f02699158f Apply newsletter background color to template preview images 2015-12-17 12:40:26 +02:00
be3462925d Change server returned newsletter body to be a json object, not a string 2015-12-17 12:40:26 +02:00
3d82230d10 Merge pull request #274 from mailpoet/acceptance_tests_update
Acceptance test related fixes
2015-12-15 13:21:34 +01:00
c20c46fd86 Removed useless 'use' in Router/Setup 2015-12-15 13:11:07 +01:00
6e63c72aa5 Reinstall feature
- implemented reinstall in Settings > Advanced
- shorten placeholder for Form name input
2015-12-15 13:07:43 +01:00
e059eec5ea Acceptance tests have been removed from the PHP project. 2015-12-14 16:09:20 +01:00
a2d38c9076 Merge pull request #270 from mailpoet/unit_tests_fix
Unit tests + Welcome page
2015-12-14 15:48:52 +01:00
8d507b2ee0 Merge pull request #271 from mailpoet/history_fix
Fix react-router dependency issue
2015-12-14 15:31:02 +01:00
5e2979c283 Changes history version to 1.13.1 2015-12-14 14:29:51 +02:00
84ec0de3cd Unit tests + Welcome page
- fixed unit tests
- commented out failing tests that require changes in the code
- added new welcome page
2015-12-11 17:17:59 +01:00
ee07780833 Version bump: 0.0.8. 2015-12-11 14:42:59 +01:00
ed104156a9 New build script as new plugin, independent from wysija-newsletters. 2015-12-11 14:42:15 +01:00
90f2e9ff9d Merge branch 'fix_history' 2015-12-11 14:13:15 +01:00
98fb7aa65e Fix react history pushState error. 2015-12-11 14:12:35 +01:00
a51eb59cf8 Merge pull request #266 from mailpoet/newsletter_template
Editor: Template related fixes
2015-12-11 13:09:54 +01:00
270023b89c Display error and success messages for template saving and export 2015-12-10 17:13:23 +02:00
7d77e075e9 Make long template name and description fit in the box 2015-12-10 17:13:23 +02:00
9bbe36b3cb Set subject of new newsletters to "Draft newsletter" 2015-12-10 17:13:23 +02:00
7a904ed093 Add SVG icons for standard, welcome and notification email types 2015-12-10 17:13:23 +02:00
e3c065b353 Add Post Notifications Blank Template 2015-12-10 17:13:22 +02:00
4ca2872e0e Add Welcome newsletter template 2015-12-10 17:13:22 +02:00
ce338f7fe2 Add border to template thumbnails 2015-12-10 17:13:22 +02:00
fa28b0a955 Merge pull request #267 from mailpoet/settings_round_1
Settings
2015-12-10 13:04:37 +01:00
a298650187 Settings
- added default from name & address based on wp_user on install
- fixed issue with Setting::setValue (added auto-serialize of value if is_array?)
- removed daily notifications from basics settings
2015-12-10 11:44:44 +01:00
95772ef68a Merge pull request #261 from mailpoet/form_editor_round_1
Form editor round 1
2015-12-09 14:07:33 +01:00
e1c94db516 edit form name follows newsletter's design 2015-12-08 17:10:45 +01:00
7be1a11d1e Form editor
- fixed validations on radio type
- fixed date format for months
- added custom fields storing on subscribe
- fixed date widget (select today's date)
- fixed validation on form widget
2015-12-08 16:55:30 +01:00
c04b95c09a Merge pull request #262 from mailpoet/editor_transitions
Editor: fix transitions and thumbnails
2015-12-07 20:00:07 +01:00
cdfeb8d512 Fix template thumbnails by reverting to earlier html2canvas version 2015-12-07 18:50:50 +02:00
3ef8067968 Remove transition class after it ends for editor content blocks 2015-12-07 18:50:17 +02:00
9fb04bc3c0 first round of fixes #255 2015-12-07 16:54:08 +01:00
25727ea0ba Version bump: 0.0.7 2015-12-07 14:43:16 +01:00
4c8ac369b7 Merge pull request #260 from mailpoet/newsletter_sending
Bugfixes on sending
2015-12-07 14:35:38 +01:00
268dabdc9f Bugfixes on sending
- added checks to prevent adding to the queue useless items
- fixed issue with mta settings (duplicated "host" input / renamed duplicate to "domain" for MailGun)
- fixed namespace issue on cron daemon/supervisor
- fixed typo in migration preventing the newsletter_templates table to be created.
- partially fixed cron.jsx
2015-12-07 13:29:42 +01:00
c0ba218949 Merge branch 'master' of github.com:mailpoet/mailpoet 2015-12-04 21:41:29 +01:00
ee85139089 Merge pull request #254 from mailpoet/queue
Sending Queue: start & track
2015-12-04 21:36:50 +01:00
b4f83fe1bd Merge pull request #257 from mailpoet/image_size
Custom newsletter image size
2015-12-04 21:22:15 +01:00
bd83001ef5 Merge pull request #258 from mailpoet/helpscout
HelpScout integration fixes
2015-12-04 21:20:28 +01:00
fb3a9f485f Merge pull request #259 from mailpoet/custom_fields
Add custom fields to newsletter editor
2015-12-04 21:20:09 +01:00
fd44776ae9 - Fixes SMTP issue 2015-12-04 14:33:43 -05:00
6dbe338b01 - Updates mailers to use HTML and/or TEXT body 2015-12-04 14:06:43 -05:00
1f06a7dd0b fixed naming of sending_queue & added validation of segments on step 3 2015-12-04 19:54:31 +01:00
37c218f782 fixed sending test email text version only 2015-12-04 19:46:44 +01:00
25016f2a8d test sending method works 2015-12-04 19:28:00 +01:00
792744a270 - Implements check for name/email in mailer router 2015-12-04 12:47:14 -05:00
c5fbfca132 Merge branch 'queue' of mailpoet:mailpoet/mailpoet into queue 2015-12-04 12:32:38 -05:00
c2fde308cb - Renames and updates sending queue worker
- Updates mailer router's send() method
2015-12-04 12:31:54 -05:00
533d9b0d38 Change custom field shortcode to follow MP2 format 2015-12-04 17:48:35 +02:00
2035b802e3 Add shortcodes for custom fields to newsletter editor 2015-12-04 17:47:18 +02:00
a5e66ec6a0 implemented sending test email 2015-12-04 16:20:07 +01:00
beb939df9e updated send with tab 2015-12-04 15:31:04 +01:00
44e342c692 Fix z-index, change icon, add instructional message 2015-12-04 15:30:59 +02:00
3a417d460f Add custom MailPoet image size for newsletters 2015-12-04 14:37:05 +02:00
1950d6661f Step 3 sender and reply to per newsletter
- added sender_address/sender_name/reply_to_address/reply_to_name
- added validation on all form fields (except checkbox and radio)
2015-12-04 12:29:11 +01:00
da6e154642 fixed conflicts 2015-12-04 11:25:06 +01:00
acebf669a7 - Updates queue worker to use mailer router for sending
- Updates mailer router to detect method type
- Rebases master
2015-12-03 22:01:33 -05:00
4a2bbe3f88 Sending Progress
- improved progress bar styles (with completed status)
- add pause/resume buttons
- fixed method case in settings.mta for MailPoet & SMTP Providers
- fixed parsley dependency
- added validation on from name & address on step 3
2015-12-03 22:01:19 -05:00
9b011c0281 - Places supervisor/daemon/worker under the new Cron class
- Updates endpoints
- Integrates queue worker with MailPoet mailer
- Fixes script activation check logic
2015-12-03 22:01:18 -05:00
bf58d8a22d Queue
- updated menu icon for our plugin
- added watchCss command to watch only CSS files
- added Status column in Newsletters listing
- added progress bar styles
- fixed issue with JS assets being loaded twice on non MP pages
- changed subscriber_ids to segment_ids in addQueue
2015-12-03 22:01:17 -05:00
72d1eb79a6 fixed too much recursion issue on selection JSX 2015-12-03 22:01:17 -05:00
bb4893c0a0 added missing messages on newsletter form 2015-12-03 22:01:16 -05:00
9929cf0aee - Adds router methods to add/delete/et queue status 2015-12-03 22:01:15 -05:00
83967e84ba - Implements starting/stopping/pausing daemon 2015-12-03 22:01:15 -05:00
9621cb3ca9 Merge pull request #253 from mailpoet/animations
Editor: Animations
2015-12-03 22:48:43 +01:00
a413f666fe Sending Progress
- improved progress bar styles (with completed status)
- add pause/resume buttons
- fixed method case in settings.mta for MailPoet & SMTP Providers
- fixed parsley dependency
- added validation on from name & address on step 3
2015-12-03 18:26:36 +01:00
d1e2c6c074 Add transition for "Delete block" icon 2015-12-03 15:53:56 +02:00
3b6a9f7a6e Add block, tool and widget transition animations 2015-12-03 14:44:32 +02:00
d2e5fb89c2 - Places supervisor/daemon/worker under the new Cron class
- Updates endpoints
- Integrates queue worker with MailPoet mailer
- Fixes script activation check logic
2015-12-02 22:48:15 -05:00
97d1e95037 Add transitions for content block addition and removal 2015-12-02 17:54:06 +02:00
48fbce22e7 Queue
- updated menu icon for our plugin
- added watchCss command to watch only CSS files
- added Status column in Newsletters listing
- added progress bar styles
- fixed issue with JS assets being loaded twice on non MP pages
- changed subscriber_ids to segment_ids in addQueue
2015-12-02 12:25:28 +01:00
916fe76795 Add transitions: sidebar contents, template preview, sidebar 2015-12-01 16:49:50 +02:00
e10310fb5c fixed too much recursion issue on selection JSX 2015-12-01 13:50:35 +01:00
367afcf814 added missing messages on newsletter form 2015-12-01 13:01:06 +01:00
67fa9e0993 - Adds router methods to add/delete/et queue status 2015-11-30 19:09:06 -05:00
d7553a5f27 Merge pull request #251 from mailpoet/newsletter_templates
Editor: Add two sample templates
2015-11-30 21:13:49 +01:00
8c847825fa - Implements starting/stopping/pausing daemon 2015-11-30 13:01:07 -05:00
d21d9b99b0 Add an option for certain templates to be read only 2015-11-30 18:23:43 +02:00
8461c13532 Include thumbnail on saved templates, add another sample template 2015-11-30 18:05:10 +02:00
3b7ffe9ba7 Update Marionette to 2.4.4 from 2.4.3 2015-11-30 13:21:28 +02:00
1724fa22c1 Merge pull request #250 from mailpoet/default_data_on_install
Default segments on install
2015-11-30 12:17:32 +01:00
01089d7a72 Bump version to 0.0.6 2015-11-27 22:00:54 +01:00
717ebfd20c Bump composer lockfile. 2015-11-27 22:00:17 +01:00
daff3d5016 Merge pull request #249 from mailpoet/sticky_kit_fix
Add back sticky-kit dependency
2015-11-27 19:14:03 +01:00
98fb838169 updated default lists' descriptions 2015-11-27 15:56:18 +01:00
f13a4dd4f3 Add back sticky-kit dependency 2015-11-27 16:44:04 +02:00
62a164e4c6 added creation of WP Users & default list on install 2015-11-27 15:27:50 +01:00
04238f3339 Phoenix Vagrant VM has been replaced with a new VM with test data. 2015-11-27 15:16:06 +01:00
bc62474f3c Merge pull request #240 from mailpoet/queue
Queue
2015-11-27 13:49:18 +01:00
98005a2a6f - Rebases master 2015-11-27 07:46:26 -05:00
b7fe8dc6d6 - Adds Daemon status (under Mailpoet->Queue) 2015-11-27 07:40:23 -05:00
436faea591 - Refactors and renames code
- Adds Queue menu option and displays status
2015-11-27 07:40:22 -05:00
4208b148b4 - Implements queue worker class 2015-11-27 07:35:16 -05:00
5ce5f0bf8a - Refactors code 2015-11-27 07:35:16 -05:00
18518de397 - Refactors some code 2015-11-27 07:35:15 -05:00
68f747211a - Adds a helper method to convert cameCase to under_score
- Removes unused method from Queue daemon
2015-11-27 07:35:15 -05:00
ebd26ddd98 - Silences fsockopen errors 2015-11-27 07:35:14 -05:00
924aa0439f - Minor regex changes
- Adds method to Env class that returns plugin activation status
- Prevents Supervisor from running if the plugin isn't activated
2015-11-27 07:35:13 -05:00
3124d6a61b - Fixes issue with ucwords() function on older PHP versions
- Updates Supervisor/Daemon to strip port from site_url() when it's
  running on localhost inside a virtual machine :)
2015-11-27 07:35:13 -05:00
149d031b52 - Adds session support
- Renames Queue to Daemon
- Updates router methods
2015-11-27 07:35:12 -05:00
fa96c4697d - Adds Queue router
- Updates logic for Queue and Supervisor
- #227
2015-11-27 07:35:12 -05:00
d46c9d5412 - Fixes issue with Supervisor when database tables do not exist 2015-11-27 07:35:11 -05:00
9ab8b1f0c5 added loading on wp users sync 2015-11-27 12:57:52 +01:00
9425ac1593 Merge pull request #244 from mailpoet/wp_users_segment
WP Users segment
2015-11-27 11:39:08 +01:00
4e32479609 Merge pull request #246 from mailpoet/import-fixes
Fixes import issues and updates code
2015-11-27 09:30:19 +01:00
7d95b38dc4 - Renames/refactors Import and Export classes/views/JS
- Updates Import and Export to ignore trashed subscribers
- Updates tests
Closes #245
2015-11-26 20:48:19 -05:00
17c83c5bd4 Merge pull request #242 from mailpoet/newsletter_templates
Newsletter template thumbnails
2015-11-26 21:26:23 +01:00
23bb2f111f Add a sample translatable template 2015-11-26 17:21:10 +02:00
4655b2c64c Switch template thumbnails to be generated in jpeg, not png 2015-11-26 17:20:34 +02:00
84fede11b8 removed dynamic lists from import 2015-11-26 16:02:53 +01:00
f37488fc0b Adds a Preview icon for previewing newsletter templates 2015-11-26 13:09:08 +02:00
20e2e03982 Hide non default segments on some pages
- added getPublic() method on Segment model
- filter out dynamic lists from add/move/remove segment in subscribers
2015-11-26 11:32:10 +01:00
a5d96f1534 WP Users list
- refactored and fixed listing issues (related to Segments)
- removed bulk actions from segments
- added synchronize methods for WP users
- Update action in segments only for WP Users list
- added "type" column to segments (default, wp_users, dynamic...)
- added "status" column to subscriber_segment table (useful soon)
2015-11-25 18:31:57 +01:00
a516eb1a95 Add overlay and newsletter thumbnail preview 2015-11-25 16:44:18 +02:00
6dd8270bec WP Users list
- migration for filters & segment_filter tables
- models for new tables
- update of Listing JSX to allow for conditional display of item actions
2015-11-24 17:12:14 +01:00
981cbd579f Fix clickable labels for editor settings views 2015-11-24 16:51:27 +02:00
52a0aae10f Add template thumbnail generation and display 2015-11-24 16:50:57 +02:00
ebca4257a6 Version bump: 0.0.5 2015-11-21 00:39:11 +01:00
c3944095bc Install npm deps in build command. 2015-11-21 00:36:12 +01:00
a1445d1b6a Vagrant Container and new build process. 2015-11-21 00:28:33 +01:00
e62c24879b Merge pull request #239 from mailpoet/revert-238-queue
Revert "Queue"
2015-11-20 23:51:19 +01:00
00f06ea202 Revert "Queue" 2015-11-20 23:51:02 +01:00
32ca24ce38 Merge pull request #238 from mailpoet/queue
Queue
2015-11-20 23:02:30 +01:00
44e3adb422 Merge pull request #235 from mailpoet/alc
Editor: ALC fixes
2015-11-20 23:00:47 +01:00
a1346ebecc Merge pull request #237 from mailpoet/changelog
welcome/update pages display logic
2015-11-20 23:00:30 +01:00
25b51d0446 - Adds queue management and supervisor. Issue #227 2015-11-20 16:20:54 -05:00
556a170903 - Bootstraps queue 2015-11-20 16:20:35 -05:00
7ac386a8e2 fixed welcome/update pages display logic 2015-11-20 13:37:52 +01:00
d91b55ec52 Include static sticky-kit library, patch it, fix sticky editor sidebar 2015-11-20 14:16:51 +02:00
786fbc36a2 Change post types to plural labels, move Posts/ALC UI elements 2015-11-19 19:08:48 +02:00
160e8b7a12 Merge pull request #232 from mailpoet/export-rename
Rename BootstrapMenu.php to BootStrapMenu.php
2015-11-19 15:01:24 +01:00
0b1f85da09 Rename BootstrapMenu.php to BootStrapMenu.php
- Renames BootStrapMenu class to use proper camelCase
2015-11-19 08:57:37 -05:00
fbc6f54ddc Merge pull request #230 from mailpoet/export
Export
2015-11-19 10:35:27 +01:00
a603e97b8c Merge pull request #229 from mailpoet/editor_posts
Editor: Posts widget
2015-11-19 10:31:31 +01:00
0875b627b6 Merge pull request #231 from mailpoet/newsletters_select_all_fix
fixed missing messages in newsletters listing
2015-11-19 10:27:45 +01:00
035784ece0 fixed missing messages in newsletters listing 2015-11-19 10:24:27 +01:00
aa93c7349f - Rebases master
- Adds tests for all Export class methods
Closes #221
2015-11-18 22:14:48 -05:00
82cf4a28fd - Updates export tests 2015-11-18 14:42:28 -05:00
832e5ef342 - Fixed minor import issue wrt to status not being set 2015-11-18 14:42:27 -05:00
e3de3a123a - Corrects exported subscriber count
- Properly exports subscribers not in any list
- Adds test for export class constructor method
Resolves #221
2015-11-18 14:42:27 -05:00
db91590159 Merge pull request #228 from mailpoet/welcome_redirect
Welcome page redirection
2015-11-18 14:33:48 +01:00
28c61fca0b Newsletters listing
- fixed listing actions on newsletters
2015-11-18 14:04:34 +01:00
e62a8c2ec5 Implement Becs' changes to Posts widget and post rendering 2015-11-18 14:44:57 +02:00
fdbd1245e3 Redirect to welcome or update page 2015-11-17 20:11:03 +01:00
0eef46db57 Fix post transformation to take titleIsLink option into account 2015-11-17 16:55:55 +02:00
080ae88a04 Fix Posts block tests 2015-11-17 16:31:57 +02:00
225be9f3cd Starting welcome page redirection 2015-11-17 13:43:25 +01:00
c9a42ebb76 Add auto-refresh of Posts block contents on option change 2015-11-17 14:40:39 +02:00
ae9b3df92d Merge pull request #223 from mailpoet/build
Fix JS lib inclusion in build step
2015-11-16 13:38:33 +01:00
63a08ebb55 Fix inclusion of js/lib/tinymce by disabling removal of node_modules/ 2015-11-16 14:27:29 +02:00
2ef5096fa9 Optimize symlinks when building. 2015-11-13 23:55:24 +01:00
95cfe2d8ec Version bump: 0.0.4. 2015-11-13 19:57:22 +01:00
49676b3fc5 Merge pull request #219 from mailpoet/export
Export
2015-11-13 18:49:32 +01:00
c96ac06423 - Moves ImportExport under Subscribers namespace
- Updates tests
2015-11-13 12:46:54 -05:00
6a3166c311 - Implements export
Closes #210
2015-11-13 12:25:33 -05:00
0017df1c2d - Work-in-progress on the UI 2015-11-13 12:25:32 -05:00
f2a0d4ce96 fixed wrong count value onGetItems in Listing 2015-11-13 12:25:32 -05:00
cb50517cbc toggle Export button depending on subscribers count 2015-11-13 12:25:31 -05:00
0fedd1779f - Moves Import and Export under ImportExport namespace
- Cretes a single BootStrapMenu class for Import and Export
- Updates tests
- Adds 2 new methods to Segments model
2015-11-13 12:25:31 -05:00
1625e1771b - Bootstraps export 2015-11-13 12:25:30 -05:00
bde78b607b Merge pull request #218 from mailpoet/listings_bugfix
Listings bugfixes & Welcome page
2015-11-13 16:01:23 +01:00
f3c58c27be set welcome page as default page 2015-11-13 15:58:36 +01:00
9fec460295 Merge pull request #220 from mailpoet/helpscout_beacon
Adds a HelpScout beacon to admin pages
2015-11-13 15:23:03 +01:00
162859529e reinstated removed tests 2015-11-13 13:56:59 +01:00
3bdaaf8368 Add a HelpScout beacon to admin pages 2015-11-13 14:45:25 +02:00
f6ab0050b2 added welcome page 2015-11-13 12:59:49 +01:00
10a20935c3 cleanup tests 2015-11-12 14:11:27 +01:00
8135b677f7 Merge pull request #216 from mailpoet/editor_layouts
Add layout settings to newsletter editor
2015-11-12 14:04:04 +01:00
90382bc86d Add layout block bg color, remove bg colors of individual columns 2015-11-11 16:55:44 +02:00
47af8939cc Merge pull request #214 from mailpoet/editor_images
Editor small image handling
2015-11-11 09:32:32 +01:00
4a0deb2182 Preserve image width for smaller than column width images 2015-11-10 18:09:36 +02:00
3a206b2c88 Adapt newsletter template selection to API change 2015-11-10 18:08:16 +02:00
70cfcbe7cc Merge pull request #209 from mailpoet/mixpanel
Add MixPanel analytics tracking
2015-11-10 10:41:24 +01:00
6342cb17bd Merge pull request #208 from mailpoet/listing_tests
Listings
2015-11-10 10:39:53 +01:00
64c35b12c8 Merge pull request #191 from mailpoet/import
Import
2015-11-09 23:23:08 +01:00
6a14f97419 Fix select2 results z-index issue for newsletter editor 2015-11-09 18:17:10 +02:00
dfec34eda9 Add Analytics integration with MixPanel 2015-11-09 18:11:06 +02:00
893231e8e5 List selection fix 2015-11-09 14:19:59 +01:00
e9110680ee Listings
- fixed selection field JSX
- fixed bulk actions (added filter function)
- added getPublished/getTrashed static methods on Model
- fixed step 3 of newsletter process
- updated save/get methods of all listing-able models to conform with the new norm
2015-11-09 13:26:33 +01:00
7b54285ca6 - Adds tests for the main Import class
- Updates tests for Env (proper host detection with port)
- Improves import
2015-11-09 00:25:24 -05:00
33ea16eb0f - Cleans up import
- Adds tests for modified models
- Adds tests for import BootStrapMenu and MailChimp classes
2015-11-08 15:57:43 -05:00
b1ae07d38e - Rebased master
- Cleaned up import & moved it under Subscribers menu
2015-11-07 21:16:38 -05:00
3f168d052f - Finishes import migration
- Updates models
- Improves Notice.js
2015-11-07 11:40:42 -05:00
158d26ef86 - Adds import's step 1 method selection logic 2015-11-07 11:31:40 -05:00
ae03ee2c46 - Bootstrapping import menu 2015-11-07 11:25:05 -05:00
ad0adb48e1 - Updates Migrator with new column for Segments
- Updates Segmnets tests
- Updates MailPoet's Notice.js with additional options
- Updates Import's router, WP menu bootstrap logic, client- and
  server-side logic
2015-11-07 11:24:02 -05:00
ff5353c894 - Completes MailChimp import 2015-11-07 11:15:49 -05:00
abb2389378 - Enables MailChimp key verification (import step 1) 2015-11-07 11:15:04 -05:00
3cf50810f9 - Fixed conflic with backbone router in settings and import
- WIP on step 1
2015-11-07 11:09:13 -05:00
20a6e6d6de - Adds import's step 1 method selection logic 2015-11-07 11:09:13 -05:00
0b1fc8f6c3 - Updates webpack config file
- Adds an option to watch/compile just the JS files with webpack
2015-11-07 11:05:48 -05:00
0a771acb02 - Bootstrapping import menu 2015-11-06 21:38:25 -05:00
307 changed files with 22668 additions and 5760 deletions

View File

@ -1,2 +1,16 @@
WP_TEST_PATH="/var/www/wordpress" WP_TEST_PATH="/var/www/wordpress"
WP_TEST_ENABLE_NETWORK_TESTS="true"
WP_TEST_IMPORT_MAILCHIMP_API=""
WP_TEST_IMPORT_MAILCHIMP_LISTS="" // (separated with comma)
WP_TEST_MAILER_ENABLE_SENDING="true"
WP_TEST_MAILER_AMAZON_ACCESS=""
WP_TEST_MAILER_AMAZON_SECRET=""
WP_TEST_MAILER_AMAZON_REGION=""
WP_TEST_MAILER_ELASTICEMAIL_API=""
WP_TEST_MAILER_MAILGUN_API=""
WP_TEST_MAILER_MAILGUN_DOMAIN=""
WP_TEST_MAILER_MAILPOET_API=""
WP_TEST_MAILER_SENDGRID_API=""
WP_TEST_MAILER_SMTP_HOST=""
WP_TEST_MAILER_SMTP_LOGIN=""
WP_TEST_MAILER_SMTP_PASSWORD=""

5
.gitignore vendored
View File

@ -1,7 +1,7 @@
.DS_Store .DS_Store
TODO TODO
composer.phar composer.phar
vendor /vendor
tests/_output/* tests/_output/*
tests/acceptance.suite.yml tests/acceptance.suite.yml
tests/_support/_generated/* tests/_support/_generated/*
@ -15,4 +15,5 @@ temp
wysija-newsletters.zip wysija-newsletters.zip
tests/javascript/testBundles tests/javascript/testBundles
assets/css/*.css assets/css/*.css
assets/js/*.js assets/js/*.js
.vagrant

View File

@ -8,7 +8,6 @@ MailPoet done the right way.
``` ```
php php
nodejs nodejs
phantomjs
wordpress wordpress
``` ```
@ -47,16 +46,6 @@ $ ./do compile:all
$ ./do test:unit $ ./do test:unit
``` ```
- Acceptance tests:
```sh
$ ./do test:acceptance
```
- Run all tests:
```sh
$ ./do test:all
```
- Debug tests: - Debug tests:
```sh ```sh
$ ./do test:debug $ ./do test:debug

View File

@ -47,6 +47,19 @@ class RoboFile extends \Robo\Tasks {
->run(); ->run();
} }
function watchCss() {
$css_files = $this->rsearch('assets/css/src/', array('styl'));
$this->taskWatch()
->monitor($css_files, function() {
$this->compileCss();
})
->run();
}
function watchJs() {
$this->_exec('./node_modules/webpack/bin/webpack.js --watch');
}
function compileAll() { function compileAll() {
$this->compileJs(); $this->compileJs();
$this->compileCss(); $this->compileCss();
@ -61,7 +74,8 @@ class RoboFile extends \Robo\Tasks {
'assets/css/src/admin.styl', 'assets/css/src/admin.styl',
'assets/css/src/newsletter_editor/newsletter_editor.styl', 'assets/css/src/newsletter_editor/newsletter_editor.styl',
'assets/css/src/public.styl', 'assets/css/src/public.styl',
'assets/css/src/rtl.styl' 'assets/css/src/rtl.styl',
'assets/css/src/importExport.styl'
); );
$this->_exec(join(' ', array( $this->_exec(join(' ', array(
@ -94,6 +108,17 @@ class RoboFile extends \Robo\Tasks {
$this->_exec('vendor/bin/codecept run unit -f '.(($file) ? $file : '')); $this->_exec('vendor/bin/codecept run unit -f '.(($file) ? $file : ''));
} }
function testCoverage($file = null) {
$this->loadEnv();
$this->_exec('vendor/bin/codecept build');
$this->_exec(join(' ', array(
'vendor/bin/codecept run',
(($file) ? $file : ''),
'--coverage',
'--coverage-html'
)));
}
function testJavascript() { function testJavascript() {
$this->compileJs(); $this->compileJs();
@ -120,4 +145,4 @@ class RoboFile extends \Robo\Tasks {
$dotenv = new Dotenv\Dotenv(__DIR__); $dotenv = new Dotenv\Dotenv(__DIR__);
$dotenv->load(); $dotenv->load();
} }
} }

View File

@ -5,12 +5,15 @@
@require 'common' @require 'common'
@require 'modal' @require 'modal'
@require 'notice' @require 'notice'
@require 'parsley'
@require 'form_editor' @require 'form_editor'
@require 'listing' @require 'listing'
@require 'box' @require 'box'
@require 'breadcrumb' @require 'breadcrumb'
@require 'form'
@require 'settings' @require 'form'
@require 'parsley'
@require 'form_validation'
@require 'settings'
@require 'progress_bar'

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,8 @@ a:focus
// select 2 // select 2
.select2-container .select2-container
width: 25em !important
// textareas // textareas
textarea.regular-text textarea.regular-text
width: 25em !important width: 25em !important
@ -26,3 +28,25 @@ textarea.regular-text
@media screen and (max-width: 782px) @media screen and (max-width: 782px)
.select2-container .select2-container
width: 100% !important width: 100% !important
// progress bars
progress-border-radius = 5px
progress-background = #efefef
progress-foreground = #69b1e9
progress
background-color: progress-background;
height: 2em
border: 0
width: 100%
progress::-webkit-progress-bar
background-color: progress-background;
progress::-webkit-progress-value
background-color: progress-foreground
border-radius: progress-border-radius
progress::-moz-progress-bar
background-color: progress-foreground
border-radius: progress-border-radius

View File

@ -4,6 +4,9 @@
icons = '../img/form_editor_icons.png' icons = '../img/form_editor_icons.png'
handle_icon = '../img/handle.png' handle_icon = '../img/handle.png'
#mailpoet_form_name
font-size: 23px
#mailpoet_form_history #mailpoet_form_history
display: none display: none
@ -99,6 +102,7 @@ handle_icon = '../img/handle.png'
/* MailPoet Form wrapper */ /* MailPoet Form wrapper */
#mailpoet_form_wrapper #mailpoet_form_wrapper
position: relative position: relative
margin: 20px 0 0 0
/* MailPoet Form container */ /* MailPoet Form container */
#mailpoet_form_container #mailpoet_form_container
@ -121,6 +125,7 @@ handle_icon = '../img/handle.png'
float: none float: none
#mailpoet_form_toolbar #mailpoet_form_toolbar
z-index: 999
position: absolute position: absolute
width: 400px width: 400px

View File

@ -0,0 +1,6 @@
.parsley-errors-list
margin-top: 8px
.parsley-required
.parsley-custom-error-message
color: #b94a48

View File

@ -0,0 +1,78 @@
.mailpoet_hidden, .mailpoet_validation_error
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
#subscribers_data
& > table
& > thead
& > tr
& > th
& > span
width 15em !important
.mailpoet_data_match
color #0e90d2
margin-left 0.25em
.mailpoet_import_error, .mailpoet_validation_error
color #900
tr
&.mailpoet_segments
& > td
& > a
margin-left 15px
span
&.select2-search
&.select2-search--dropdown
display none !important

View File

@ -90,7 +90,7 @@ body.mailpoet_modal_opened
padding: 0 padding: 0
margin: 0 margin: 0
width: 100% width: 100%
transition: margin 0.3s ease-out transition: margin 350ms ease-out
.mailpoet_panel_wrapper .mailpoet_panel_wrapper
background-color: #f1f1f1 background-color: #f1f1f1
@ -159,23 +159,6 @@ body.mailpoet_modal_opened
margin: 0 margin: 0
text-align: right text-align: right
.mailpoet_button
padding: 3px 15px
border: 1px solid #444
font-weight: normal
cursor: pointer
background-color: #222
color: #cfcfcf
font-size: 1em
.mailpoet_button:hover
background-color: #00aacc
color: #fff
.mailpoet_button:active
background-color: #00ccff
color: #fff
@media screen and (max-width: 782px) @media screen and (max-width: 782px)
#mailpoet_modal_overlay.mailpoet_panel_overlay #mailpoet_modal_overlay.mailpoet_panel_overlay
top: 46px top: 46px
@ -217,4 +200,4 @@ body.mailpoet_modal_opened
0% 0%
50% 50%
background-color: #064E6D background-color: #064E6D
100% 100%

View File

@ -7,7 +7,6 @@ $tool-active-secondary-color = #ffffff
$tool-width = 20px $tool-width = 20px
$master-column-tool-width = 24px $master-column-tool-width = 24px
$layer-selector-width = 30px
.mailpoet_tools .mailpoet_tools
position: absolute position: absolute
@ -33,10 +32,35 @@ $layer-selector-width = 30px
width: $master-column-tool-width width: $master-column-tool-width
height: $master-column-tool-width height: $master-column-tool-width
.mailpoet_delete_block_activate
max-width: 100%
max-height: $master-column-tool-width
opacity: 1
display: block
.mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel
max-width: 100%
max-height: 0
opacity: 0
overflow: hidden
display: block
.mailpoet_delete_block_activated .mailpoet_delete_block_activated
width: auto width: auto
height: auto height: auto
.mailpoet_delete_block_activate
overflow: hidden
max-height: 0
opacity: 0
.mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel
max-height: $master-column-tool-width*2
opacity: 1
.mailpoet_tool .mailpoet_tool
display: inline-block display: inline-block
width: $tool-width width: $tool-width
@ -76,28 +100,45 @@ $layer-selector-width = 30px
display: inline-block display: inline-block
padding: 2px padding: 2px
vertical-align: top vertical-align: top
animation-background-color()
.mailpoet_tool .mailpoet_tool
padding: 0 padding: 0
.mailpoet_delete_block_activate
max-width: $tool-width
display: inline-block
opacity: 1
animation-fade-in-and-scale-horizontally()
.mailpoet_delete_block_confirm, .mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel .mailpoet_delete_block_cancel
display: none max-width: 0
opacity: 0
overflow: hidden
display: inline-block
animation-fade-in-and-scale-horizontally()
.mailpoet_delete_block_activated .mailpoet_delete_block_activated
height: auto
width: auto width: auto
border-radius(3px) border-radius(3px)
background-color: $warning-background-color background-color: $warning-background-color
padding: 3px 5px padding: 3px 5px
line-height: 1.2em line-height: 1.2em
height: auto
.mailpoet_delete_block_activate .mailpoet_delete_block_activate
display: none overflow: hidden
max-width: 0
opacity: 0
.mailpoet_delete_block_confirm, .mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel .mailpoet_delete_block_cancel
display: inline-block max-width: 100%
opacity: 1
.mailpoet_delete_block_cancel
margin-left: 3px
.mailpoet_delete_block_confirm .mailpoet_delete_block_confirm
color: $warning-text-color color: $warning-text-color

View File

@ -52,6 +52,7 @@ $draggable-widget-z-index = 2
padding: 0 padding: 0
margin: 0 margin: 0
z-index: $draggable-widget-z-index z-index: $draggable-widget-z-index
animation-fade-in()
.mailpoet_widget_icon .mailpoet_widget_icon
padding: 0 padding: 0

View File

@ -48,7 +48,7 @@
.mailpoet_save_as_template_container, .mailpoet_save_as_template_container,
.mailpoet_export_template_container .mailpoet_export_template_container
border-radius(3px) border-radius(3px)
float: left display: inline-block
clear: both clear: both
margin-top: 5px margin-top: 5px

View File

@ -26,13 +26,9 @@ $widget-icon-width = 30px
border-right: 0 border-right: 0
&.closed .mailpoet_region_content &.closed .mailpoet_region_content
max-height: 0px display: none
overflow: hidden
margin-top: 0
.mailpoet_region_content .mailpoet_region_content
max-height: 2000px
transition: max-height 0.2s ease
padding: 0 20px padding: 0 20px
margin-top: 12px margin-top: 12px

View File

@ -18,3 +18,6 @@
& > .mailpoet_block & > .mailpoet_block
width: 100% width: 100%
.mailpoet_automated_latest_content_display_options
animation-slide-open-downwards()

View File

@ -79,3 +79,4 @@ $three-column-width = ($newsletter-width / 3) - (2 * $column-margin)
box-shadow(inset 1px 2px 1px $primary-inactive-color) box-shadow(inset 1px 2px 1px $primary-inactive-color)
color: #656565 color: #656565
border-radius(3px) border-radius(3px)
animation-background-color()

View File

@ -19,6 +19,10 @@ $divider-hover-border-color = $primary-active-color
width: 100% width: 100%
border: 1px solid transparent border: 1px solid transparent
.mailpoet_active_divider_style
border: 1px solid $active-divider-border-color
background: $active-divider-background-color
.mailpoet_field_divider_style:hover .mailpoet_field_divider_style:hover
border: 1px solid $divider-hover-border-color border: 1px solid $divider-hover-border-color

View File

@ -1,15 +1,19 @@
.mailpoet_image_block .mailpoet_image_block
img
vertical-align: bottom
max-width: 100%
width: auto
height: auto
&.mailpoet_full_image &.mailpoet_full_image
padding-left: 0 padding-left: 0
padding-right: 0 padding-right: 0
margin-bottom: 0 margin-bottom: 0
img
width: 100%
.mailpoet_content a:hover .mailpoet_content a:hover
cursor: all-scroll cursor: all-scroll
img
vertical-align: bottom
max-width: $newsletter-width
width: 100%
height: auto

View File

@ -1,15 +1,12 @@
.mailpoet_posts_block .mailpoet_posts_block
box-shadow(none) padding-left: 0
padding-right: 0
& > .mailpoet_content .mailpoet_posts_block_posts
font-size: 1em overflow: auto
text-align: center
background-color: $primary-active-color & > .mailpoet_block
margin: 20px 0 width: 100%
padding: 15px
box-shadow(inset 1px 2px 1px $primary-inset-shadow-color)
color: $white-color
border-radius(3px)
.mailpoet_post_selection_filter_row .mailpoet_post_selection_filter_row
margin-top: 5px margin-top: 5px
@ -18,7 +15,11 @@
.mailpoet_posts_categories_and_tags .mailpoet_posts_categories_and_tags
width: 100% width: 100%
.mailpoet_settings_posts_show_display_options .mailpoet_settings_posts_display_options
.mailpoet_settings_posts_selection
animation-slide-open-downwards()
.mailpoet_settings_posts_show_display_options,
.mailpoet_settings_posts_show_post_selection .mailpoet_settings_posts_show_post_selection
display: block display: block
margin-top: 10px margin-top: 10px

View File

@ -1,6 +1,6 @@
/* Fix select2 z-index to work with MailPoet.Modal */ /* Fix select2 z-index to work with MailPoet.Modal */
.select2-dropdown .select2-dropdown
z-index: 101000 z-index: 101000 !important
/* Remove input field styles from select2 type input */ /* Remove input field styles from select2 type input */
.select2-container .select2-container
@ -129,3 +129,7 @@ body
#mailpoet_modal_close #mailpoet_modal_close
display: none display: none
.wrap > .mailpoet_notice,
.update-nag
margin-left: 2px + 15px !important

View File

@ -0,0 +1,31 @@
animation-slide-open-downwards()
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
max-height: 2000px
opacity: 1
&.mailpoet_closed
max-height: 0
opacity: 0
overflow-y: hidden
animation-background-color()
transition: background 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
animation-fade-in()
animation-name: fadeIn
animation-duration: 300ms
animation-fill-mode: forwards
animation-timing-function: ease-in
animation-fade-in-and-scale-horizontally()
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
@keyframes fadeIn {
0% {
opacity: 0.3
}
100% {
opacity: 1
}
}

View File

@ -6,6 +6,7 @@
@require 'mixins/border-radius' @require 'mixins/border-radius'
@require 'mixins/box-shadow' @require 'mixins/box-shadow'
@require 'mixins/filter-shadow' @require 'mixins/filter-shadow'
@require 'mixins/transitions'
@require 'variables' @require 'variables'
@require 'common' @require 'common'

View File

@ -0,0 +1,29 @@
.mailpoet_progress
background-color: #efefef
height: 25px
padding: 0
width: 100%
margin: 0
border-radius: 5px
position: relative
.mailpoet_progress_label
position: absolute
width: 100%
text-align: center
display: inline-block
margin: 2px 0 0 0
.mailpoet_progress_bar
position: absolute
display: inline-block
height: 100%
border-radius: 3px
box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset
background-color: #34c2e3
background-image: linear-gradient(top, #34c2e3, darken(#34c2e3, 20%))
.mailpoet_progress_complete
.mailpoet_progress_bar
background-color: #fecf23
background-image: linear-gradient(top, #fecf23, #fd9215)

View File

@ -1,3 +1,4 @@
@import 'nib' @import 'nib'
@require 'parsley' @require 'parsley'
@require 'form_validation'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="47.002px" height="38.003px" viewBox="0 0 47.002 38.003" enable-background="new 0 0 47.002 38.003" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#565656" d="M46.328,36.365c-1.188,1.725-3.553,2.158-5.273,0.962L25.479,26.52
c-1.104-0.763-1.674-2.007-1.631-3.257c-2.006,2.157-4.642,3.606-7.594,4.145c-3.626,0.663-7.288-0.13-10.311-2.227
c-3.024-2.098-5.054-5.253-5.714-8.887c-0.661-3.636,0.127-7.31,2.221-10.344c4.325-6.264,12.927-7.834,19.177-3.5
c5.672,3.938,7.486,11.412,4.537,17.443c1.152-0.486,2.519-0.392,3.627,0.377l15.58,10.808C47.09,32.274,47.52,34.641,46.328,36.365
z M23.235,12.09c-0.459-2.534-1.874-4.734-3.982-6.196C14.897,2.87,8.896,3.963,5.878,8.331c-3.014,4.373-1.922,10.388,2.435,13.408
c4.356,3.025,10.356,1.932,13.374-2.438C23.146,17.187,23.696,14.625,23.235,12.09z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,8 @@
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
mixpanel.init("f683d388fb25fcf331f1b2b5c4449798");
if (typeof mailpoet_analytics_data === 'object') {
mixpanel.track('Wysija Usage', mailpoet_analytics_data || {});
}

106
assets/js/src/cron.jsx Normal file
View File

@ -0,0 +1,106 @@
define(
[
'react',
'react-dom',
'mailpoet'
],
function(
React,
ReactDOM,
MailPoet
) {
var CronControl = React.createClass({
getInitialState: function() {
return {
status: 'loading'
};
},
getCronData: function() {
MailPoet.Ajax.post({
endpoint: 'cron',
action: 'getStatus'
})
.done(function(response) {
jQuery('.button-primary')
.removeClass('disabled');
if(response.status !== undefined) {
this.setState(response);
} else {
this.replaceState();
}
}.bind(this));
},
componentDidMount: function() {
if(this.isMounted()) {
this.getCronData();
setInterval(this.getCronData, 5000);
}
},
controlCron: function(action) {
if(jQuery('.button-primary').hasClass('disabled')) {
return;
}
jQuery('.button-primary')
.addClass('disabled');
MailPoet.Ajax.post({
endpoint: 'cron',
action: action,
})
.done(function(response) {
if(!response.result) {
MailPoet.Notice.error(MailPoetI18n.daemonControlError);
}
}.bind(this));
},
render: function() {
if(this.state.status === 'loading') {
return(<div>Loading daemon status...</div>);
}
switch(this.state.status) {
case 'started':
return(
<div>
Cron daemon is running.
<br/>
<br/>
It was started
<strong> {this.state.timeSinceStart} </strong> and last executed
<strong> {this.state.timeSinceUpdate} </strong> for a total of
<strong> {this.state.counter} </strong> times (once every 30 seconds, unless it was interrupted and restarted).
<br />
<br />
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>Stop</a>
</div>
);
break;
case 'starting':
case 'stopping':
return(
<div>
Daemon is {this.state.status}
</div>
);
break;
case 'stopped':
return(
<div>
Daemon is {this.state.status}
<br />
<br />
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>Start</a>
</div>
);
break;
}
}
});
const container = document.getElementById('cron_container');
if(container) {
ReactDOM.render(
<CronControl />,
container
);
}
});

View File

@ -1,31 +1,31 @@
define([ define([
'react', 'react'
'react-checkbox-group'
], ],
function( function(
React, React
CheckboxGroup
) { ) {
var FormFieldCheckbox = React.createClass({ const FormFieldCheckbox = React.createClass({
onValueChange: function(e) {
e.target.value = this.refs.checkbox.checked ? '1' : '';
return this.props.onValueChange(e);
},
render: function() { render: function() {
var selected_values = this.props.item[this.props.field.name] || ''; const isChecked = !!(this.props.item[this.props.field.name]);
if(
selected_values !== undefined
&& selected_values.constructor !== Array
) {
selected_values = selected_values.split(';').map(function(value) {
return value.trim();
});
}
var count = Object.keys(this.props.field.values).length;
var options = Object.keys(this.props.field.values).map( const options = Object.keys(this.props.field.values).map(
function(value, index) { function(value, index) {
return ( return (
<p key={ 'checkbox-' + index }> <p key={ 'checkbox-' + index }>
<label> <label>
<input type="checkbox" value={ value } /> <input
&nbsp;{ this.props.field.values[value] } ref="checkbox"
type="checkbox"
value="1"
checked={ isChecked }
onChange={ this.onValueChange }
name={ this.props.field.name }
/>
{ this.props.field.values[value] }
</label> </label>
</p> </p>
); );
@ -33,30 +33,10 @@ function(
); );
return ( return (
<CheckboxGroup <div>
name={ this.props.field.name }
value={ selected_values }
ref={ this.props.field.name }
onChange={ this.handleValueChange }>
{ options } { options }
</CheckboxGroup> </div>
); );
},
handleValueChange: function() {
var field = this.props.field.name;
var group = this.refs[field];
var selected_values = [];
if(group !== undefined) {
selected_values = group.getCheckedValues();
}
return this.props.onValueChange({
target: {
name: field,
value: selected_values.join(';')
}
});
} }
}); });

View File

@ -0,0 +1,196 @@
define([
'react',
'moment',
], function(
React,
Moment
) {
class FormFieldDateYear extends React.Component {
render() {
const yearsRange = 100;
const years = [];
const currentYear = Moment().year();
for (let i = currentYear; i >= currentYear - yearsRange; i--) {
years.push((
<option
key={ i }
value={ i }
>{ i }</option>
));
}
return (
<select
name={ this.props.name + '[year]' }
value={ this.props.year }
onChange={ this.props.onValueChange }
>
{ years }
</select>
);
}
}
class FormFieldDateMonth extends React.Component {
render() {
const months = [];
for (let i = 1; i <= 12; i++) {
months.push((
<option
key={ i }
value={ i }
>{ this.props.monthNames[i - 1] }</option>
));
}
return (
<select
name={ this.props.name + '[month]' }
value={ this.props.month }
onChange={ this.props.onValueChange }
>
{ months }
</select>
);
}
}
class FormFieldDateDay extends React.Component {
render() {
const days = [];
for (let i = 1; i <= 31; i++) {
days.push((
<option
key={ i }
value={ i }
>{ i }</option>
));
}
return (
<select
name={ this.props.name + '[day]' }
value={ this.props.day }
onChange={ this.props.onValueChange }
>
{ days }
</select>
);
}
}
class FormFieldDate extends React.Component {
constructor(props) {
super(props);
this.state = {
year: Moment().year(),
month: 1,
day: 1
}
}
componentDidMount() {
}
componentDidUpdate(prevProps, prevState) {
if (
(this.props.item !== undefined && prevProps.item !== undefined)
&& (this.props.item.id !== prevProps.item.id)
) {
this.extractTimeStamp();
}
}
extractTimeStamp() {
const timeStamp = parseInt(this.props.item[this.props.field.name], 10);
this.setState({
year: Moment.unix(timeStamp).year(),
// Moment returns the month as [0..11]
// We increment it to match PHP's mktime() which expects [1..12]
month: Moment.unix(timeStamp).month() + 1,
day: Moment.unix(timeStamp).date()
});
}
updateTimeStamp(field) {
let newTimeStamp = Moment(
`${this.state.month}/${this.state.day}/${this.state.year}`,
'M/D/YYYY'
).valueOf();
if (!isNaN(newTimeStamp) && parseInt(newTimeStamp, 10) > 0) {
// convert milliseconds to seconds
newTimeStamp /= 1000;
return this.props.onValueChange({
target: {
name: field,
value: newTimeStamp
}
});
}
}
onValueChange(e) {
// extract property from name
const matches = e.target.name.match(/(.*?)\[(.*?)\]/);
let field = null;
let property = null;
if (matches !== null && matches.length === 3) {
field = matches[1];
property = matches[2];
let value = parseInt(e.target.value, 10);
this.setState({
[`${property}`]: value
}, () => {
this.updateTimeStamp(field);
});
}
}
render() {
const monthNames = window.mailpoet_month_names || [];
const dateType = this.props.field.params.date_type;
const dateSelects = dateType.split('_');
const fields = dateSelects.map(type => {
switch(type) {
case 'year':
return (<FormFieldDateYear
onValueChange={ this.onValueChange.bind(this) }
ref={ 'year' }
key={ 'year' }
name={ this.props.field.name }
year={ this.state.year }
/>);
break;
case 'month':
return (<FormFieldDateMonth
onValueChange={ this.onValueChange.bind(this) }
ref={ 'month' }
key={ 'month' }
name={ this.props.field.name }
month={ this.state.month }
monthNames={ monthNames }
/>);
break;
case 'day':
return (<FormFieldDateDay
onValueChange={ this.onValueChange.bind(this) }
ref={ 'day' }
key={ 'day' }
name={ this.props.field.name }
day={ this.state.day }
/>);
break;
}
});
return (
<div>
{fields}
</div>
);
}
};
return FormFieldDate;
});

View File

@ -5,7 +5,8 @@ define([
'form/fields/select.jsx', 'form/fields/select.jsx',
'form/fields/radio.jsx', 'form/fields/radio.jsx',
'form/fields/checkbox.jsx', 'form/fields/checkbox.jsx',
'form/fields/selection.jsx' 'form/fields/selection.jsx',
'form/fields/date.jsx',
], ],
function( function(
React, React,
@ -14,7 +15,8 @@ function(
FormFieldSelect, FormFieldSelect,
FormFieldRadio, FormFieldRadio,
FormFieldCheckbox, FormFieldCheckbox,
FormFieldSelection FormFieldSelection,
FormFieldDate
) { ) {
var FormField = React.createClass({ var FormField = React.createClass({
renderField: function(data, inline = false) { renderField: function(data, inline = false) {
@ -55,6 +57,10 @@ function(
case 'selection': case 'selection':
field = (<FormFieldSelection {...data} />); field = (<FormFieldSelection {...data} />);
break; break;
case 'date':
field = (<FormFieldDate {...data} />);
break;
} }
if(inline === true) { if(inline === true) {
@ -66,10 +72,10 @@ function(
); );
} else { } else {
return ( return (
<p key={ 'field-' + (data.index || 0) }> <div key={ 'field-' + (data.index || 0) }>
{ field } { field }
{ description } { description }
</p> </div>
); );
} }
}, },

View File

@ -7,7 +7,6 @@ function(
var FormFieldRadio = React.createClass({ var FormFieldRadio = React.createClass({
render: function() { render: function() {
var selected_value = this.props.item[this.props.field.name]; var selected_value = this.props.item[this.props.field.name];
var count = Object.keys(this.props.field.values).length;
var options = Object.keys(this.props.field.values).map( var options = Object.keys(this.props.field.values).map(
function(value, index) { function(value, index) {
@ -20,7 +19,7 @@ function(
value={ value } value={ value }
onChange={ this.props.onValueChange } onChange={ this.props.onValueChange }
name={ this.props.field.name } /> name={ this.props.field.name } />
&nbsp;{ this.props.field.values[value] } { this.props.field.values[value] }
</label> </label>
</p> </p>
); );

View File

@ -23,7 +23,9 @@ function(
name={ this.props.field.name } name={ this.props.field.name }
id={ 'field_'+this.props.field.name } id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] } value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange }> onChange={ this.props.onValueChange }
{...this.props.field.validation}
>
{options} {options}
</select> </select>
); );

View File

@ -20,8 +20,15 @@ function(
this.loadCachedItems(); this.loadCachedItems();
this.setupSelect2(); this.setupSelect2();
}, },
componentDidUpdate: function() { componentDidUpdate: function(prevProps, prevState) {
this.setupSelect2(); if(
(this.props.item !== undefined && prevProps.item !== undefined)
&& (this.props.item.id !== prevProps.item.id)
) {
jQuery('#'+this.refs.select.id)
.val(this.props.item[this.props.field.name])
.trigger('change');
}
}, },
setupSelect2: function() { setupSelect2: function() {
if( if(
@ -43,7 +50,19 @@ function(
} }
}); });
var hasRemoved = false;
select2.on('select2:unselecting', function(e) {
hasRemoved = true;
});
select2.on('select2:opening', function(e) {
if(hasRemoved === true) {
hasRemoved = false;
e.preventDefault();
}
});
select2.on('change', this.handleChange); select2.on('change', this.handleChange);
select2.select2( select2.select2(
'val', 'val',
this.props.item[this.props.field.name] this.props.item[this.props.field.name]
@ -54,6 +73,11 @@ function(
loadCachedItems: function() { loadCachedItems: function() {
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') { if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
var items = window['mailpoet_'+this.props.field.endpoint]; var items = window['mailpoet_'+this.props.field.endpoint];
if(this.props.field['filter'] !== undefined) {
items = items.filter(this.props.field.filter);
}
this.setState({ this.setState({
items: items items: items
}); });
@ -62,7 +86,7 @@ function(
handleChange: function(e) { handleChange: function(e) {
if(this.props.onValueChange !== undefined) { if(this.props.onValueChange !== undefined) {
if(this.props.field.multiple) { if(this.props.field.multiple) {
value = jQuery('#'+this.refs.select.id).select2('val'); value = jQuery('#'+this.refs.select.id).val();
} else { } else {
value = e.target.value; value = e.target.value;
} }
@ -73,40 +97,35 @@ function(
} }
}); });
} }
return true;
}, },
render: function() { render: function() {
if(this.state.items.length === 0) { var options = this.state.items.map(function(item, index) {
return false;
} else {
var options = this.state.items.map(function(item, index) {
return (
<option
key={ item.id }
value={ item.id }
>
{ item.name }
</option>
);
});
var default_value = (
(this.props.item !== undefined && this.props.field.name !== undefined)
? this.props.item[this.props.field.name]
: null
);
return ( return (
<select <option
id={ this.props.field.id || this.props.field.name } key={ item.id }
ref="select" value={ item.id }
placeholder={ this.props.field.placeholder } >
multiple={ this.props.field.multiple } { item.name }
onChange={ this.handleChange } </option>
defaultValue={ default_value }
>{ options }</select>
); );
} });
var default_value = (
(this.props.item !== undefined && this.props.field.name !== undefined)
? this.props.item[this.props.field.name]
: null
);
return (
<select
id={ this.props.field.id || this.props.field.name }
ref="select"
data-placeholder={ this.props.field.placeholder }
multiple={ this.props.field.multiple }
defaultValue={ default_value }
{...this.props.field.validation}
>{ options }</select>
);
} }
}); });

View File

@ -6,6 +6,8 @@ function(
) { ) {
var FormFieldText = React.createClass({ var FormFieldText = React.createClass({
render: function() { render: function() {
var value = this.props.item[this.props.field.name];
if(!value) { value = null; }
return ( return (
<input <input
type="text" type="text"
@ -17,10 +19,12 @@ function(
} }
name={ this.props.field.name } name={ this.props.field.name }
id={ 'field_'+this.props.field.name } id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] } value={ value }
placeholder={ this.props.field.placeholder } placeholder={ this.props.field.placeholder }
defaultValue={ this.props.field.defaultValue } defaultValue={ this.props.field.defaultValue }
onChange={ this.props.onValueChange } /> onChange={ this.props.onValueChange }
{...this.props.field.validation}
/>
); );
} }
}); });

View File

@ -15,7 +15,9 @@ function(
value={ this.props.item[this.props.field.name] } value={ this.props.item[this.props.field.name] }
placeholder={ this.props.field.placeholder } placeholder={ this.props.field.placeholder }
defaultValue={ this.props.field.defaultValue } defaultValue={ this.props.field.defaultValue }
onChange={ this.props.onValueChange } /> onChange={ this.props.onValueChange }
{...this.props.field.validation}
/>
); );
} }
}); });

View File

@ -48,7 +48,7 @@ define(
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: this.props.endpoint, endpoint: this.props.endpoint,
action: 'get', action: 'get',
data: { id: id } data: id
}).done(function(response) { }).done(function(response) {
if(response === false) { if(response === false) {
this.setState({ this.setState({
@ -68,12 +68,25 @@ define(
handleSubmit: function(e) { handleSubmit: function(e) {
e.preventDefault(); e.preventDefault();
// handle validation
if(this.props.isValid !== undefined) {
if(this.props.isValid() === false) {
return;
}
}
this.setState({ loading: true }); this.setState({ loading: true });
// only get values from displayed fields // only get values from displayed fields
item = {}; var item = {};
this.props.fields.map(function(field) { this.props.fields.map(function(field) {
item[field.name] = this.state.item[field.name]; if(field['fields'] !== undefined) {
field.fields.map(function(subfield) {
item[subfield.name] = this.state.item[subfield.name];
}.bind(this));
} else {
item[field.name] = this.state.item[field.name];
}
}.bind(this)); }.bind(this));
// set id if specified // set id if specified
@ -88,23 +101,23 @@ define(
}).done(function(response) { }).done(function(response) {
this.setState({ loading: false }); this.setState({ loading: false });
if(response === true) { if(response.result === true) {
if(this.props.onSuccess !== undefined) { if(this.props.onSuccess !== undefined) {
this.props.onSuccess() this.props.onSuccess();
} else { } else {
this.history.pushState(null, '/') this.history.pushState(null, '/')
} }
if(this.props.params.id !== undefined) { if(this.props.params.id !== undefined) {
this.props.messages['updated'](); this.props.messages.onUpdate();
} else { } else {
this.props.messages['created'](); this.props.messages.onCreate();
} }
} else { } else {
if(response === false) { if(response.result === false) {
// unknown error occurred if(response.errors.length > 0) {
} else { this.setState({ errors: response.errors });
this.setState({ errors: response }); }
} }
} }
}.bind(this)); }.bind(this));
@ -121,13 +134,15 @@ define(
return true; return true;
}, },
render: function() { render: function() {
var errors = this.state.errors.map(function(error, index) { if(this.state.errors !== undefined) {
return ( var errors = this.state.errors.map(function(error, index) {
<p key={ 'error-'+index } className="mailpoet_error"> return (
{ error } <p key={ 'error-'+index } className="mailpoet_error">
</p> { error }
); </p>
}); );
});
}
var formClasses = classNames( var formClasses = classNames(
'mailpoet_form', 'mailpoet_form',

View File

@ -231,7 +231,7 @@ var WysijaHistory = {
/* MailPoet Form */ /* MailPoet Form */
var WysijaForm = { var WysijaForm = {
version: '0.6', version: '0.7',
options: { options: {
container: 'mailpoet_form_container', container: 'mailpoet_form_container',
editor: 'mailpoet_form_editor', editor: 'mailpoet_form_editor',
@ -317,6 +317,7 @@ var WysijaForm = {
save: function() { save: function() {
var position = 1, var position = 1,
data = { data = {
'name': $F('mailpoet_form_name'),
'settings': $('mailpoet_form_settings').serialize(true), 'settings': $('mailpoet_form_settings').serialize(true),
'body': [], 'body': [],
'styles': (MailPoet.CodeEditor !== undefined) ? MailPoet.CodeEditor.getValue() : null 'styles': (MailPoet.CodeEditor !== undefined) ? MailPoet.CodeEditor.getValue() : null
@ -616,6 +617,28 @@ var WysijaForm = {
// this is a url, so do not encode the protocol // this is a url, so do not encode the protocol
return encodeURI(str).replace(/[!'()*]/g, escape); return encodeURI(str).replace(/[!'()*]/g, escape);
} }
},
updateBlock: function(field) {
var hasUpdated = false;
WysijaForm.getBlocks().each(function(b) {
if(b.block.getData().id === field.id) {
hasUpdated = true;
b.block.redraw(field);
}
});
return hasUpdated;
},
removeBlock: function(field, callback) {
var hasRemoved = false;
WysijaForm.getBlocks().each(function(b) {
if(b.block.getData().id === field.id) {
hasRemoved = true;
b.block.removeBlock(callback);
}
});
return hasRemoved;
} }
}; };
@ -824,10 +847,6 @@ WysijaForm.Block = Class.create({
Effect.Fade(this.element.identify(), { Effect.Fade(this.element.identify(), {
duration: 0.2, duration: 0.2,
afterFinish: function(effect) { afterFinish: function(effect) {
if(effect.element.next('.mailpoet_form_block') !== undefined && callback !== false) {
// show controls of next block to allow mass delete
WysijaForm.get(effect.element.next('.mailpoet_form_block')).block.showControls();
}
// remove placeholder // remove placeholder
if(effect.element.previous('.block_placeholder') !== undefined) { if(effect.element.previous('.block_placeholder') !== undefined) {
effect.element.previous('.block_placeholder').remove(); effect.element.previous('.block_placeholder').remove();

View File

@ -1,57 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, History } from 'react-router'
import MailPoet from 'mailpoet'
import Form from 'form/form.jsx'
const fields = [
{
name: 'name',
label: 'Name',
type: 'text'
},
{
name: 'segments',
label: 'Lists',
type: 'selection',
endpoint: 'segments',
multiple: true
}
]
const messages = {
updated: function() {
MailPoet.Notice.success('Form successfully updated!');
},
created: function() {
MailPoet.Notice.success('Form successfully added!');
}
}
const FormForm = React.createClass({
mixins: [
History
],
render() {
return (
<div>
<h2 className="title">
Form <a
href="javascript:;"
className="add-new-h2"
onClick={ this.history.goBack }
>Back to list</a>
</h2>
<Form
endpoint="forms"
fields={ fields }
params={ this.props.params }
messages={ messages }
onSuccess={ this.history.goBack } />
</div>
);
}
});
module.exports = FormForm

View File

@ -2,7 +2,6 @@ import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { Router, Route, IndexRoute } from 'react-router' import { Router, Route, IndexRoute } from 'react-router'
import FormList from 'forms/list.jsx' import FormList from 'forms/list.jsx'
import FormForm from 'forms/form.jsx'
import createHashHistory from 'history/lib/createHashHistory' import createHashHistory from 'history/lib/createHashHistory'
let history = createHashHistory({ queryKey: false }) let history = createHashHistory({ queryKey: false })
@ -20,8 +19,6 @@ if(container) {
<Router history={ history }> <Router history={ history }>
<Route path="/" component={ App }> <Route path="/" component={ App }>
<IndexRoute component={ FormList } /> <IndexRoute component={ FormList } />
<Route path="new" component={ FormForm } />
<Route path="edit/:id" component={ FormForm } />
<Route path="*" component={ FormList } /> <Route path="*" component={ FormList } />
</Route> </Route>
</Router> </Router>

View File

@ -25,58 +25,49 @@ const columns = [
const messages = { const messages = {
onTrash: function(response) { onTrash: function(response) {
if(response) { var count = ~~response;
let message = null; var message = null;
if(~~response === 1) {
message = (
'1 form was moved to the trash.'
);
} else if(~~response > 1) {
message = (
'%$1d forms were moved to the trash.'
).replace('%$1d', ~~response);
}
if(message !== null) { if(count === 1) {
MailPoet.Notice.success(message); message = (
} '1 form was moved to the trash.'
);
} else {
message = (
'%$1d forms were moved to the trash.'
).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
}, },
onDelete: function(response) { onDelete: function(response) {
if(response) { var count = ~~response;
let message = null; var message = null;
if(~~response === 1) {
message = (
'1 form was permanently deleted.'
);
} else if(~~response > 1) {
message = (
'%$1d forms were permanently deleted.'
).replace('%$1d', ~~response);
}
if(message !== null) { if(count === 1) {
MailPoet.Notice.success(message); message = (
} '1 form was permanently deleted.'
);
} else {
message = (
'%$1d forms were permanently deleted.'
).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
}, },
onRestore: function(response) { onRestore: function(response) {
if(response) { var count = ~~response;
let message = null; var message = null;
if(~~response === 1) {
message = (
'1 form has been restored from the trash.'
);
} else if(~~response > 1) {
message = (
'%$1d forms have been restored from the trash.'
).replace('%$1d', ~~response);
}
if(message !== null) { if(count === 1) {
MailPoet.Notice.success(message); message = (
} '1 form has been restored from the trash.'
);
} else {
message = (
'%$1d forms have been restored from the trash.'
).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
} }
}; };
@ -105,6 +96,9 @@ const item_actions = [
refresh(); refresh();
}); });
} }
},
{
name: 'trash'
} }
]; ];
@ -122,8 +116,8 @@ const FormList = React.createClass({
endpoint: 'forms', endpoint: 'forms',
action: 'create' action: 'create'
}).done(function(response) { }).done(function(response) {
if(response !== false) { if(response.result && response.form_id) {
window.location = response; window.location = mailpoet_form_edit_url + response.form_id;
} }
}); });
}, },

View File

@ -0,0 +1,3 @@
define([], function() {
!function(e,o,n){window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={},t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!1,baseUrl:""},contact:{enabled:!0,formId:"e5c408c7-895e-11e5-9e75-0a7d6919297d"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r)}(document,window.HSCW||{},window.HS||{});
});

View File

@ -15,16 +15,16 @@ function(
this.setState({ this.setState({
action: e.target.value, action: e.target.value,
extra: false extra: false
}); }, function() {
var action = this.getSelectedAction();
var action = this.getSelectedAction(); // action on select callback
if(action !== null && action['onSelect'] !== undefined) {
// action on select callback this.setState({
if(action !== null && action['onSelect'] !== undefined) { extra: action.onSelect(e)
this.setState({ });
extra: action.onSelect(e) }
}); }.bind(this));
}
}, },
handleApplyAction: function(e) { handleApplyAction: function(e) {
e.preventDefault(); e.preventDefault();
@ -85,7 +85,12 @@ function(
Select bulk action Select bulk action
</label> </label>
<select ref="action" value={ this.state.action } onChange={this.handleChangeAction}> <select
name="bulk_actions"
ref="action"
value={ this.state.action }
onChange={this.handleChangeAction}
>
<option value="">Bulk Actions</option> <option value="">Bulk Actions</option>
{ this.props.bulk_actions.map(function(action, index) { { this.props.bulk_actions.map(function(action, index) {
return ( return (

View File

@ -14,6 +14,9 @@ function(
}) })
return this.props.onSelectFilter(filters); return this.props.onSelectFilter(filters);
}, },
handleEmptyTrash: function() {
return this.props.onEmptyTrash();
},
getAvailableFilters: function() { getAvailableFilters: function() {
let filters = this.props.filters; let filters = this.props.filters;
@ -34,7 +37,7 @@ function(
const available_filters = this.getAvailableFilters() const available_filters = this.getAvailableFilters()
.map(function(filter, i) { .map(function(filter, i) {
let default_value = false; let default_value = false;
if(selected_filters[filter] !== undefined && selected_filters[filter]) { if (selected_filters[filter] !== undefined && selected_filters[filter]) {
default_value = selected_filters[filter] default_value = selected_filters[filter]
} else { } else {
jQuery(`select[name="${filter}"]`).val(''); jQuery(`select[name="${filter}"]`).val('');
@ -60,9 +63,10 @@ function(
let button = false; let button = false;
if(available_filters.length > 0) { if (available_filters.length > 0) {
button = ( button = (
<input <input
id="post-query-submit"
onClick={ this.handleFilterAction } onClick={ this.handleFilterAction }
type="submit" type="submit"
defaultValue="Filter" defaultValue="Filter"
@ -70,10 +74,23 @@ function(
); );
} }
let empty_trash = false;
if (this.props.group === 'trash') {
empty_trash = (
<input
onClick={ this.handleEmptyTrash }
type="submit"
value="Empty Trash"
className="button"
/>
);
}
return ( return (
<div className="alignleft actions actions"> <div className="alignleft actions actions">
{ available_filters } { available_filters }
{ button } { button }
{ empty_trash }
</div> </div>
); );
} }

View File

@ -11,7 +11,7 @@ define(['react', 'classnames'], function(React, classNames) {
column.is_primary = (index === 0); column.is_primary = (index === 0);
column.sorted = (this.props.sort_by === column.name) column.sorted = (this.props.sort_by === column.name)
? this.props.sort_order ? this.props.sort_order
: 'asc'; : 'desc';
return ( return (
<ListingColumn <ListingColumn
onSort={this.props.onSort} onSort={this.props.onSort}
@ -32,6 +32,7 @@ define(['react', 'classnames'], function(React, classNames) {
</label> </label>
<input <input
type="checkbox" type="checkbox"
name="select_all"
ref="toggle" ref="toggle"
checked={ this.props.selection } checked={ this.props.selection }
onChange={ this.handleSelectItems } /> onChange={ this.handleSelectItems } />

View File

@ -79,27 +79,48 @@ define(
if(custom_actions.length > 0) { if(custom_actions.length > 0) {
item_actions = custom_actions.map(function(action, index) { item_actions = custom_actions.map(function(action, index) {
if(action.refresh) { if(action.display !== undefined) {
if(action.display(this.props.item) === false) {
return;
}
}
if(action.name === 'trash') {
return (
<span key={ 'action-'+index } className="trash">
{(index > 0) ? ' | ' : ''}
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
null,
this.props.item.id
) }>
Trash
</a>
</span>
);
} else if(action.refresh) {
return ( return (
<span <span
onClick={ this.props.onRefreshItems } onClick={ this.props.onRefreshItems }
key={ 'action-'+index } className={ action.name }> key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
{ action.link(this.props.item) } { action.link(this.props.item) }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span> </span>
); );
} else if(action.link) { } else if(action.link) {
return ( return (
<span <span
key={ 'action-'+index } className={ action.name }> key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
{ action.link(this.props.item) } { action.link(this.props.item) }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span> </span>
); );
} else { } else {
return ( return (
<span <span
key={ 'action-'+index } className={ action.name }> key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
<a href="javascript:;" onClick={ <a href="javascript:;" onClick={
(action.onClick !== undefined) (action.onClick !== undefined)
? action.onClick.bind(null, ? action.onClick.bind(null,
@ -108,7 +129,6 @@ define(
) )
: false : false
}>{ action.label }</a> }>{ action.label }</a>
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span> </span>
); );
} }
@ -158,17 +178,6 @@ define(
<div> <div>
<div className="row-actions"> <div className="row-actions">
{ item_actions } { item_actions }
{ ' | ' }
<span className="trash">
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
null,
this.props.item.id
) }>
Trash
</a>
</span>
</div> </div>
<button <button
onClick={ this.handleToggleItem.bind(null, this.props.item.id) } onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
@ -388,6 +397,12 @@ define(
if(this.isMounted()) { if(this.isMounted()) {
const params = this.props.params || {} const params = this.props.params || {}
this.initWithParams(params) this.initWithParams(params)
if(this.props.auto_refresh) {
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
this.getItems();
}.bind(this));
}
} }
}, },
componentWillReceiveProps: function(nextProps) { componentWillReceiveProps: function(nextProps) {
@ -419,7 +434,13 @@ define(
groups: response.groups || [], groups: response.groups || [],
count: response.count || 0, count: response.count || 0,
loading: false loading: false
}); }, function() {
if(this.props['onGetItems'] !== undefined) {
this.props.onGetItems(
~~(this.state.groups[0]['count'])
);
}
}.bind(this));
}.bind(this)); }.bind(this));
} }
}, },
@ -483,10 +504,21 @@ define(
this.getItems(); this.getItems();
}.bind(this)); }.bind(this));
}, },
handleEmptyTrash: function() {
this.handleBulkAction('all', {
action: 'delete',
group: 'trash'
}, function(response) {
MailPoet.Notice.success(
MailPoetI18n.permanentlyDeleted.replace('%d', response)
);
});
},
handleBulkAction: function(selected_ids, params, callback) { handleBulkAction: function(selected_ids, params, callback) {
if( if(
this.state.selection === false this.state.selection === false
&& this.state.selected_ids.length === 0 && this.state.selected_ids.length === 0
&& selected_ids !== 'all'
) { ) {
return; return;
} }
@ -499,8 +531,10 @@ define(
limit: 0, limit: 0,
filter: this.state.filter, filter: this.state.filter,
group: this.state.group, group: this.state.group,
search: this.state.search, search: this.state.search
selection: selected_ids }
if(selected_ids !== 'all') {
data.listing.selection = selected_ids;
} }
MailPoet.Ajax.post({ MailPoet.Ajax.post({
@ -631,7 +665,7 @@ define(
// bulk actions // bulk actions
var bulk_actions = this.props.bulk_actions || []; var bulk_actions = this.props.bulk_actions || [];
if(this.state.group === 'trash') { if(this.state.group === 'trash' && bulk_actions.length > 0) {
bulk_actions = [ bulk_actions = [
{ {
name: 'restore', name: 'restore',
@ -694,7 +728,10 @@ define(
<ListingFilters <ListingFilters
filters={ this.state.filters } filters={ this.state.filters }
filter={ this.state.filter } filter={ this.state.filter }
onSelectFilter={ this.handleFilter } /> group={ this.state.group }
onSelectFilter={ this.handleFilter }
onEmptyTrash={ this.handleEmptyTrash }
/>
<ListingPages <ListingPages
count={ this.state.count } count={ this.state.count }
page={ this.state.page } page={ this.state.page }

View File

@ -7,7 +7,11 @@ define(['react', 'classnames'], function(React, classNames) {
} }
}, },
setPage: function(page) { setPage: function(page) {
this.props.onSetPage(page); this.setState({
page: null
}, function () {
this.props.onSetPage(this.constrainPage(page));
}.bind(this));
}, },
setFirstPage: function() { setFirstPage: function() {
this.setPage(1); this.setPage(1);
@ -16,10 +20,14 @@ define(['react', 'classnames'], function(React, classNames) {
this.setPage(this.getLastPage()); this.setPage(this.getLastPage());
}, },
setPreviousPage: function() { setPreviousPage: function() {
this.setPage(this.constrainPage(this.props.page - 1)); this.setPage(this.constrainPage(
parseInt(this.props.page, 10) - 1)
);
}, },
setNextPage: function() { setNextPage: function() {
this.setPage(this.constrainPage(this.props.page + 1)); this.setPage(this.constrainPage(
parseInt(this.props.page, 10) + 1)
);
}, },
constrainPage: function(page) { constrainPage: function(page) {
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage()); return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
@ -27,14 +35,16 @@ define(['react', 'classnames'], function(React, classNames) {
handleSetManualPage: function(e) { handleSetManualPage: function(e) {
if(e.which === 13) { if(e.which === 13) {
this.setPage(this.state.page); this.setPage(this.state.page);
this.setState({ page: null });
} }
}, },
handleChangeManualPage: function(e) { handleChangeManualPage: function(e) {
this.setState({ this.setState({
page: this.constrainPage(e.target.value) page: e.target.value
}); });
}, },
handleBlurManualPage: function(e) {
this.setPage(e.target.value);
},
getLastPage: function() { getLastPage: function() {
return Math.ceil(this.props.count / this.props.limit); return Math.ceil(this.props.count / this.props.limit);
}, },
@ -101,6 +111,11 @@ define(['react', 'classnames'], function(React, classNames) {
); );
} }
let pageValue = this.props.page;
if(this.state.page !== null) {
pageValue = this.state.page;
}
pagination = ( pagination = (
<span className="pagination-links"> <span className="pagination-links">
{firstPage} {firstPage}
@ -115,10 +130,11 @@ define(['react', 'classnames'], function(React, classNames) {
type="text" type="text"
onChange={ this.handleChangeManualPage } onChange={ this.handleChangeManualPage }
onKeyUp={ this.handleSetManualPage } onKeyUp={ this.handleSetManualPage }
onBlur={ this.handleBlurManualPage }
aria-describedby="table-paging" aria-describedby="table-paging"
size="1" size="1"
ref="page" ref="page"
value={ this.state.page || this.props.page } value={ pageValue }
name="paged" name="paged"
id="current-page-selector" id="current-page-selector"
className="current-page" /> className="current-page" />

View File

@ -47,7 +47,6 @@ define([
autoScroll: true, autoScroll: true,
onstart: function(event) { onstart: function(event) {
console.log('Drag start', event, this);
if (that.options.cloneOriginal === true) { if (that.options.cloneOriginal === true) {
// Use substitution instead of a clone // Use substitution instead of a clone

View File

@ -11,10 +11,10 @@ define([
'newsletter_editor/blocks/base', 'newsletter_editor/blocks/base',
'newsletter_editor/blocks/button', 'newsletter_editor/blocks/button',
'newsletter_editor/blocks/divider', 'newsletter_editor/blocks/divider',
'newsletter_editor/components/wordpress', 'newsletter_editor/components/communication',
'underscore', 'underscore',
'jquery' 'jquery'
], function(App, BaseBlock, ButtonBlock, DividerBlock, WordpressComponent, _, jQuery) { ], function(App, BaseBlock, ButtonBlock, DividerBlock, CommunicationComponent, _, jQuery) {
"use strict"; "use strict";
@ -35,7 +35,7 @@ define([
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock', titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right' titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true titleIsLink: false, // false|true
imagePadded: true, // true|false imageFullWidth: false, // true|false
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none' //imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText' showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:', authorPrecededBy: 'Author:',
@ -63,15 +63,15 @@ define([
initialize: function() { initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments); base.BlockView.prototype.initialize.apply(this, arguments);
this.fetchPosts(); this.fetchPosts();
this.on('change:amount change:contentType change:terms change:inclusionType change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:sortBy change:showDivider', this._scheduleFetchPosts, this); this.on('change:amount change:contentType change:terms change:inclusionType change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imageFullWidth change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:sortBy change:showDivider', this._scheduleFetchPosts, this);
this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts); this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts);
this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts); this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts);
}, },
fetchPosts: function() { fetchPosts: function() {
var that = this; var that = this;
WordpressComponent.getTransformedPosts(this.toJSON()).done(function(content) { CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
console.log('ALC fetched', arguments);
that.get('_container').get('blocks').reset(content, {parse: true}); that.get('_container').get('blocks').reset(content, {parse: true});
that.trigger('postsChanged');
}).fail(function(error) { }).fail(function(error) {
console.log('ALC fetchPosts error', arguments); console.log('ALC fetchPosts error', arguments);
}); });
@ -100,6 +100,11 @@ define([
toolsRegion: '.mailpoet_tools', toolsRegion: '.mailpoet_tools',
postsRegion: '.mailpoet_automated_latest_content_block_posts', postsRegion: '.mailpoet_automated_latest_content_block_posts',
}, },
modelEvents: _.extend(
_.omit(base.BlockView.prototype.modelEvents, 'change'),
{
'postsChanged': 'render',
}),
events: _.extend(base.BlockView.prototype.events, { events: _.extend(base.BlockView.prototype.events, {
'click .mailpoet_automated_latest_content_block_overlay': 'showSettings', 'click .mailpoet_automated_latest_content_block_overlay': 'showSettings',
}), }),
@ -139,7 +144,7 @@ define([
"change .mailpoet_automated_latest_content_include_or_exclude": _.partial(this.changeField, "inclusionType"), "change .mailpoet_automated_latest_content_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_automated_latest_content_title_position": _.partial(this.changeField, "titlePosition"), "change .mailpoet_automated_latest_content_title_position": _.partial(this.changeField, "titlePosition"),
"change .mailpoet_automated_latest_content_title_alignment": _.partial(this.changeField, "titleAlignment"), "change .mailpoet_automated_latest_content_title_alignment": _.partial(this.changeField, "titleAlignment"),
"change .mailpoet_automated_latest_content_image_padded": _.partial(this.changeBoolField, "imagePadded"), "change .mailpoet_automated_latest_content_image_full_width": _.partial(this.changeBoolField, "imageFullWidth"),
"change .mailpoet_automated_latest_content_show_author": _.partial(this.changeField, "showAuthor"), "change .mailpoet_automated_latest_content_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"), "keyup .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_automated_latest_content_show_categories": _.partial(this.changeField, "showCategories"), "change .mailpoet_automated_latest_content_show_categories": _.partial(this.changeField, "showCategories"),
@ -161,7 +166,7 @@ define([
var that = this; var that = this;
// Dynamically update available post types // Dynamically update available post types
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this)); CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({ this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({
multiple: true, multiple: true,
@ -174,10 +179,10 @@ define([
}, },
transport: function(options, success, failure) { transport: function(options, success, failure) {
var taxonomies, var taxonomies,
promise = WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) { promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
taxonomies = tax; taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched // Fetch available terms based on the list of taxonomies already fetched
var promise = WordpressComponent.getTerms({ var promise = CommunicationComponent.getTerms({
search: options.data.term, search: options.data.term,
taxonomies: _.keys(taxonomies) taxonomies: _.keys(taxonomies)
}).then(function(terms) { }).then(function(terms) {
@ -226,11 +231,11 @@ define([
toggleDisplayOptions: function(event) { toggleDisplayOptions: function(event) {
var el = this.$('.mailpoet_automated_latest_content_display_options'), var el = this.$('.mailpoet_automated_latest_content_display_options'),
showControl = this.$('.mailpoet_automated_latest_content_show_display_options'); showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
if (el.hasClass('mailpoet_hidden')) { if (el.hasClass('mailpoet_closed')) {
el.removeClass('mailpoet_hidden'); el.removeClass('mailpoet_closed');
showControl.addClass('mailpoet_hidden'); showControl.addClass('mailpoet_hidden');
} else { } else {
el.addClass('mailpoet_hidden'); el.addClass('mailpoet_closed');
showControl.removeClass('mailpoet_hidden'); showControl.removeClass('mailpoet_hidden');
} }
}, },
@ -306,7 +311,7 @@ define([
_.each(postTypes, function(type) { _.each(postTypes, function(type) {
select.append(jQuery('<option>', { select.append(jQuery('<option>', {
value: type.name, value: type.name,
text: type.labels.singular_name, text: type.label,
})); }));
}); });
select.val(selectedValue); select.val(selectedValue);

View File

@ -48,6 +48,7 @@ define([
}, },
modelEvents: { modelEvents: {
'change': 'render', 'change': 'render',
'delete': 'deleteBlock',
}, },
events: { events: {
"mouseenter": "showTools", "mouseenter": "showTools",
@ -88,7 +89,9 @@ define([
this.$el.addClass('mailpoet_editor_view_' + this.cid); this.$el.addClass('mailpoet_editor_view_' + this.cid);
}, },
initialize: function() { initialize: function() {
this.on('showSettings', this.showSettings); this.on('showSettings', this.showSettings, this);
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
}, },
showTools: function(_event) { showTools: function(_event) {
if (!this.showingToolsDisabled) { if (!this.showingToolsDisabled) {
@ -114,12 +117,49 @@ define([
* Defines drop behavior of BlockView instance * Defines drop behavior of BlockView instance
*/ */
getDropFunc: function() { getDropFunc: function() {
var that = this;
return function() { return function() {
var newModel = that.model.clone(); return this.model.clone();
//that.model.destroy(); }.bind(this);
return newModel; },
}; showBlock: function() {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function() {
this.transitionOut().then(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('slideDown', 'fadeIn', 'easeOut');
},
transitionOut: function() {
return this._transition('slideUp', 'fadeOut', 'easeIn');
},
_transition: function(slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function() {
promise.resolve();
}.bind(this),
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false, // Do not enqueue, trigger animation in parallel
}
);
return promise;
}, },
}); });
@ -168,24 +208,22 @@ define([
}, },
deleteBlock: function(event) { deleteBlock: function(event) {
event.preventDefault(); event.preventDefault();
this.model.destroy(); this.model.trigger('delete');
return false; return false;
} },
}); });
Module.BlockSettingsView = Marionette.LayoutView.extend({ Module.BlockSettingsView = Marionette.LayoutView.extend({
className: 'mailpoet_editor_settings', className: 'mailpoet_editor_settings',
initialize: function() { initialize: function() {
var that = this;
MailPoet.Modal.panel({ MailPoet.Modal.panel({
element: this.$el, element: this.$el,
template: '', template: '',
position: 'right', position: 'right',
width: App.getConfig().get('sidepanelWidth'), width: App.getConfig().get('sidepanelWidth'),
onCancel: function() { onCancel: function() {
that.destroy(); this.destroy();
}, }.bind(this),
}); });
}, },
close: function(event) { close: function(event) {
@ -203,6 +241,9 @@ define([
changeBoolField: function(field, event) { changeBoolField: function(field, event) {
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false); this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
}, },
changeBoolCheckboxField: function(field, event) {
this.model.set(field, (!!jQuery(event.target).prop('checked')));
},
changeColorField: function(field, event) { changeColorField: function(field, event) {
var value = jQuery(event.target).val(); var value = jQuery(event.target).val();
if (value === '') { if (value === '') {

View File

@ -42,16 +42,12 @@ define([
Module.ButtonBlockView = base.BlockView.extend({ Module.ButtonBlockView = base.BlockView.extend({
className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block", className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block",
getTemplate: function() { return templates.buttonBlock; }, getTemplate: function() { return templates.buttonBlock; },
modelEvents: {
'change': 'render',
},
onDragSubstituteBy: function() { return Module.ButtonWidgetView; }, onDragSubstituteBy: function() { return Module.ButtonWidgetView; },
initialize: function() { initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments); base.BlockView.prototype.initialize.apply(this, arguments);
var that = this;
// Listen for attempts to change all dividers in one go // Listen for attempts to change all dividers in one go
this._replaceButtonStylesHandler = function(data) { that.model.set(data); }; this._replaceButtonStylesHandler = function(data) { this.model.set(data); }.bind(this);
App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler); App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler);
}, },
onRender: function() { onRender: function() {

View File

@ -75,7 +75,8 @@ define([
getEmptyView: function() { return Module.ContainerBlockEmptyView; }, getEmptyView: function() { return Module.ContainerBlockEmptyView; },
emptyViewOptions: function() { return { renderOptions: this.renderOptions }; }, emptyViewOptions: function() { return { renderOptions: this.renderOptions }; },
modelEvents: { modelEvents: {
'change': 'render' 'change': 'render',
'delete': 'deleteBlock',
}, },
events: { events: {
"mouseenter": "showTools", "mouseenter": "showTools",
@ -136,6 +137,8 @@ define([
}, },
initialize: function(options) { initialize: function(options) {
this.renderOptions = _.defaults(options.renderOptions || {}, {}); this.renderOptions = _.defaults(options.renderOptions || {}, {});
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
}, },
// Determines which view type should be used for a child // Determines which view type should be used for a child
getChildView: function(model) { getChildView: function(model) {
@ -162,10 +165,10 @@ define([
this.toolsView = new Module.ContainerBlockToolsView({ this.toolsView = new Module.ContainerBlockToolsView({
model: this.model, model: this.model,
tools: { tools: {
settings: this.renderOptions.depth > 1, settings: this.renderOptions.depth === 1,
delete: this.renderOptions.depth === 1, delete: this.renderOptions.depth === 1,
move: this.renderOptions.depth === 1, move: this.renderOptions.depth === 1,
layerSelector: this.renderOptions.depth === 1, layerSelector: false,
}, },
}); });
this.toolsRegion.show(this.toolsView); this.toolsRegion.show(this.toolsView);
@ -229,12 +232,49 @@ define([
_.extend(this, this._buildRegions(this.regions)); _.extend(this, this._buildRegions(this.regions));
}, },
getDropFunc: function() { getDropFunc: function() {
var that = this;
return function() { return function() {
var newModel = that.model.clone(); return this.model.clone();
that.model.destroy(); }.bind(this);
return newModel; },
}; showBlock: function() {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function() {
this.transitionOut().done(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('slideDown', 'fadeIn', 'easeIn');
},
transitionOut: function() {
return this._transition('slideUp', 'fadeOut', 'easeOut');
},
_transition: function(slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function() {
promise.resolve();
}.bind(this),
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false, // Do not enqueue, trigger animation in parallel
}
);
return promise;
}, },
}); });
@ -265,6 +305,41 @@ define([
behaviors: { behaviors: {
ColorPickerBehavior: {}, ColorPickerBehavior: {},
}, },
regions: {
columnsSettingsRegion: '.mailpoet_container_columns_settings',
},
initialize: function() {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
this._columnsSettingsView = new (Module.ContainerBlockColumnsSettingsView)({
collection: this.model.get('blocks'),
});
},
onRender: function() {
this.columnsSettingsRegion.show(this._columnsSettingsView);
},
});
Module.ContainerBlockColumnsSettingsView = Marionette.CollectionView.extend({
getChildView: function() { return Module.ContainerBlockColumnSettingsView; },
childViewOptions: function(model, index) {
return {
columnIndex: index,
};
},
});
Module.ContainerBlockColumnSettingsView = Marionette.ItemView.extend({
getTemplate: function() { return templates.containerBlockColumnSettings; },
initialize: function(options) {
this.columnNumber = (options.columnIndex || 0) + 1;
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
columnNumber: this.columnNumber,
};
},
}); });
Module.OneColumnContainerWidgetView = base.WidgetView.extend({ Module.OneColumnContainerWidgetView = base.WidgetView.extend({

View File

@ -57,11 +57,9 @@ define([
this.listenTo(this.model, 'change:styles.block.padding', this.changePadding); this.listenTo(this.model, 'change:styles.block.padding', this.changePadding);
}, },
templateHelpers: function() { templateHelpers: function() {
return { return _.extend({
model: this.model.toJSON(),
viewCid: this.cid,
totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px', totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px',
}; }, base.BlockView.prototype.templateHelpers.apply(this));
}, },
onRender: function() { onRender: function() {
this.toolsView = new Module.DividerBlockToolsView({ model: this.model }); this.toolsView = new Module.DividerBlockToolsView({ model: this.model });

View File

@ -39,9 +39,9 @@ define([
Module.FooterBlockView = base.BlockView.extend({ Module.FooterBlockView = base.BlockView.extend({
className: "mailpoet_block mailpoet_footer_block mailpoet_droppable_block", className: "mailpoet_block mailpoet_footer_block mailpoet_droppable_block",
getTemplate: function() { return templates.footerBlock; }, getTemplate: function() { return templates.footerBlock; },
modelEvents: { modelEvents: _.extend({
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render', 'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
}, }, base.BlockView.prototype.modelEvents),
onDragSubstituteBy: function() { return Module.FooterWidgetView; }, onDragSubstituteBy: function() { return Module.FooterWidgetView; },
onRender: function() { onRender: function() {
this.toolsView = new Module.FooterBlockToolsView({ model: this.model }); this.toolsView = new Module.FooterBlockToolsView({ model: this.model });

View File

@ -39,9 +39,9 @@ define([
Module.HeaderBlockView = base.BlockView.extend({ Module.HeaderBlockView = base.BlockView.extend({
className: "mailpoet_block mailpoet_header_block mailpoet_droppable_block", className: "mailpoet_block mailpoet_header_block mailpoet_droppable_block",
getTemplate: function() { return templates.headerBlock; }, getTemplate: function() { return templates.headerBlock; },
modelEvents: { modelEvents: _.extend({
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render', 'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
}, }, base.BlockView.prototype.modelEvents),
onDragSubstituteBy: function() { return Module.HeaderWidgetView; }, onDragSubstituteBy: function() { return Module.HeaderWidgetView; },
onRender: function() { onRender: function() {
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model }); this.toolsView = new Module.HeaderBlockToolsView({ model: this.model });

View File

@ -20,7 +20,7 @@ define([
link: 'http://example.org', link: 'http://example.org',
src: 'no-image.png', src: 'no-image.png',
alt: 'An image of...', alt: 'An image of...',
padded: true, // true | false - Padded or full width fullWidth: true, // true | false
width: '64px', width: '64px',
height: '64px', height: '64px',
styles: { styles: {
@ -37,20 +37,18 @@ define([
getTemplate: function() { return templates.imageBlock; }, getTemplate: function() { return templates.imageBlock; },
onDragSubstituteBy: function() { return Module.ImageWidgetView; }, onDragSubstituteBy: function() { return Module.ImageWidgetView; },
templateHelpers: function() { templateHelpers: function() {
return { return _.extend({
model: this.model.toJSON(),
viewCid: this.cid,
imageMissingSrc: App.getConfig().get('urls.imageMissing'), imageMissingSrc: App.getConfig().get('urls.imageMissing'),
}; }, base.BlockView.prototype.templateHelpers.apply(this));
}, },
onRender: function() { onRender: function() {
this.toolsView = new Module.ImageBlockToolsView({ model: this.model }); this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView); this.toolsRegion.show(this.toolsView);
if (this.model.get('padded')) { if (this.model.get('fullWidth')) {
this.$el.removeClass('mailpoet_full_image');
} else {
this.$el.addClass('mailpoet_full_image'); this.$el.addClass('mailpoet_full_image');
} else {
this.$el.removeClass('mailpoet_full_image');
} }
}, },
}); });
@ -66,7 +64,7 @@ define([
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"), "keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"), "keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"), "keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
"change .mailpoet_field_image_padded": _.partial(this.changeBoolField, "padded"), "change .mailpoet_field_image_full_width": _.partial(this.changeBoolCheckboxField, "fullWidth"),
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"), "change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
"click .mailpoet_field_image_select_another_image": "showMediaManager", "click .mailpoet_field_image_select_another_image": "showMediaManager",
"click .mailpoet_done_editing": "close", "click .mailpoet_done_editing": "close",

View File

@ -18,12 +18,12 @@ define([
'jquery', 'jquery',
'mailpoet', 'mailpoet',
'newsletter_editor/App', 'newsletter_editor/App',
'newsletter_editor/components/wordpress', 'newsletter_editor/components/communication',
'newsletter_editor/blocks/base', 'newsletter_editor/blocks/base',
'newsletter_editor/blocks/button', 'newsletter_editor/blocks/button',
'newsletter_editor/blocks/divider', 'newsletter_editor/blocks/divider',
'select2' 'select2'
], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, WordpressComponent, BaseBlock, ButtonBlock, DividerBlock) { ], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, CommunicationComponent, BaseBlock, ButtonBlock, DividerBlock) {
"use strict"; "use strict";
@ -31,7 +31,7 @@ define([
base = BaseBlock; base = BaseBlock;
Module.PostsBlockModel = base.BlockModel.extend({ Module.PostsBlockModel = base.BlockModel.extend({
stale: ['_selectedPosts', '_availablePosts'], stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
defaults: function() { defaults: function() {
return this._getDefaults({ return this._getDefaults({
type: 'posts', type: 'posts',
@ -46,7 +46,7 @@ define([
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock', titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right' titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true titleIsLink: false, // false|true
imagePadded: true, // true|false imageFullWidth: false, // true|false
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none' //imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText' showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:', authorPrecededBy: 'Author:',
@ -63,6 +63,7 @@ define([
divider: {}, divider: {},
_selectedPosts: [], _selectedPosts: [],
_availablePosts: [], _availablePosts: [],
_transformedPosts: new (App.getBlockTypeModel('container'))(),
}, App.getConfig().get('blockDefaults.posts')); }, App.getConfig().get('blockDefaults.posts'));
}, },
relations: function() { relations: function() {
@ -71,21 +72,31 @@ define([
divider: App.getBlockTypeModel('divider'), divider: App.getBlockTypeModel('divider'),
_selectedPosts: Backbone.Collection, _selectedPosts: Backbone.Collection,
_availablePosts: Backbone.Collection, _availablePosts: Backbone.Collection,
_transformedPosts: App.getBlockTypeModel('container'),
}; };
}, },
initialize: function() { initialize: function() {
var that = this; var that = this,
POST_REFRESH_DELAY_MS = 500,
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
// Attach Radio.Requests API primarily for highlighting // Attach Radio.Requests API primarily for highlighting
_.extend(this, Radio.Requests); _.extend(this, Radio.Requests);
this.fetchAvailablePosts(); this.fetchAvailablePosts();
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', this._scheduleFetchAvailablePosts, this); this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', refreshAvailablePosts);
this.listenTo(this.get('_selectedPosts'), 'add remove reset', refreshTransformedPosts);
this.on('change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imageFullWidth change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:showDivider', refreshTransformedPosts);
this.listenTo(this.get('readMoreButton'), 'change', refreshTransformedPosts);
this.listenTo(this.get('divider'), 'change', refreshTransformedPosts);
this.on('insertSelectedPosts', this._insertSelectedPosts, this); this.on('insertSelectedPosts', this._insertSelectedPosts, this);
}, },
fetchAvailablePosts: function() { fetchAvailablePosts: function() {
var that = this; var that = this;
WordpressComponent.getPosts(this.toJSON()).done(function(posts) { CommunicationComponent.getPosts(this.toJSON()).done(function(posts) {
console.log('Posts fetched', arguments);
that.get('_availablePosts').reset(posts); that.get('_availablePosts').reset(posts);
that.get('_selectedPosts').reset(); // Empty out the collection that.get('_selectedPosts').reset(); // Empty out the collection
that.trigger('change:_availablePosts'); that.trigger('change:_availablePosts');
@ -93,20 +104,22 @@ define([
console.log('Posts fetchPosts error', arguments); console.log('Posts fetchPosts error', arguments);
}); });
}, },
/** _refreshTransformedPosts: function() {
* Batch more changes during a specific time, instead of fetching var that = this,
* ALC posts on each model change data = this.toJSON();
*/
_scheduleFetchAvailablePosts: function() { data.posts = this.get('_selectedPosts').pluck('ID');
var timeout = 500,
that = this; if (data.posts.length === 0) {
if (this._fetchPostsTimer !== undefined) { this.get('_transformedPosts.blocks').reset();
clearTimeout(this._fetchPostsTimer); return;
} }
this._fetchPostsTimer = setTimeout(function() {
that.fetchAvailablePosts(); CommunicationComponent.getTransformedPosts(data).done(function(posts) {
that._fetchPostsTimer = undefined; that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
}, timeout); }).fail(function() {
console.log('Posts _refreshTransformedPosts error', arguments);
});
}, },
_insertSelectedPosts: function() { _insertSelectedPosts: function() {
var that = this, var that = this,
@ -118,8 +131,7 @@ define([
if (data.posts.length === 0) return; if (data.posts.length === 0) return;
WordpressComponent.getTransformedPosts(data).done(function(posts) { CommunicationComponent.getTransformedPosts(data).done(function(posts) {
console.log('Available posts fetched', arguments);
collection.add(posts, { at: index }); collection.add(posts, { at: index });
}).fail(function() { }).fail(function() {
console.log('Posts fetchPosts error', arguments); console.log('Posts fetchPosts error', arguments);
@ -130,10 +142,14 @@ define([
Module.PostsBlockView = base.BlockView.extend({ Module.PostsBlockView = base.BlockView.extend({
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block", className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
getTemplate: function() { return templates.postsBlock; }, getTemplate: function() { return templates.postsBlock; },
modelEvents: {}, modelEvents: {}, // Forcefully disable all events
regions: _.extend({
postsRegion: '.mailpoet_posts_block_posts',
}, base.BlockView.prototype.regions),
onDragSubstituteBy: function() { return Module.PostsWidgetView; }, onDragSubstituteBy: function() { return Module.PostsWidgetView; },
initialize: function() { initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments); base.BlockView.prototype.initialize.apply(this, arguments);
this.toolsView = new Module.PostsBlockToolsView({ model: this.model }); this.toolsView = new Module.PostsBlockToolsView({ model: this.model });
this.model.reply('blockView', this.notifyAboutSelf, this); this.model.reply('blockView', this.notifyAboutSelf, this);
}, },
@ -142,6 +158,13 @@ define([
this.toolsRegion.show(this.toolsView); this.toolsRegion.show(this.toolsView);
} }
this.trigger('showSettings'); this.trigger('showSettings');
var ContainerView = App.getBlockTypeView('container'),
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
};
this.postsRegion.show(new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
}, },
notifyAboutSelf: function() { notifyAboutSelf: function() {
return this; return this;
@ -197,8 +220,8 @@ define([
}, },
switchToDisplayOptions: function() { switchToDisplayOptions: function() {
// Switch content view // Switch content view
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_closed');
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_closed');
// Switch controls // Switch controls
this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden');
@ -206,8 +229,8 @@ define([
}, },
switchToPostSelection: function() { switchToPostSelection: function() {
// Switch content view // Switch content view
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_closed');
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_closed');
// Switch controls // Switch controls
this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden');
@ -216,6 +239,7 @@ define([
insertPosts: function() { insertPosts: function() {
this.model.trigger('insertSelectedPosts'); this.model.trigger('insertSelectedPosts');
this.model.destroy(); this.model.destroy();
this.close();
}, },
}); });
@ -245,7 +269,7 @@ define([
var that = this; var that = this;
// Dynamically update available post types // Dynamically update available post types
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this)); CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
this.$('.mailpoet_posts_categories_and_tags').select2({ this.$('.mailpoet_posts_categories_and_tags').select2({
multiple: true, multiple: true,
@ -258,10 +282,10 @@ define([
}, },
transport: function(options, success, failure) { transport: function(options, success, failure) {
var taxonomies, var taxonomies,
promise = WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) { promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
taxonomies = tax; taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched // Fetch available terms based on the list of taxonomies already fetched
var promise = WordpressComponent.getTerms({ var promise = CommunicationComponent.getTerms({
search: options.data.term, search: options.data.term,
taxonomies: _.keys(taxonomies) taxonomies: _.keys(taxonomies)
}).then(function(terms) { }).then(function(terms) {
@ -307,11 +331,6 @@ define([
}, },
}).trigger( 'change' ); }).trigger( 'change' );
}, },
onBeforeDestroy: function() {
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
// Force close select2 if it hasn't closed yet
this.$('.mailpoet_posts_categories_and_tags').select2('close');
},
changeField: function(field, event) { changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val()); this.model.set(field, jQuery(event.target).val());
}, },
@ -323,7 +342,7 @@ define([
_.each(postTypes, function(type) { _.each(postTypes, function(type) {
select.append(jQuery('<option>', { select.append(jQuery('<option>', {
value: type.name, value: type.name,
text: type.labels.singular_name, text: type.label,
})); }));
}); });
select.val(selectedValue); select.val(selectedValue);
@ -377,7 +396,7 @@ define([
"change .mailpoet_posts_include_or_exclude": _.partial(this.changeField, "inclusionType"), "change .mailpoet_posts_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_posts_title_position": _.partial(this.changeField, "titlePosition"), "change .mailpoet_posts_title_position": _.partial(this.changeField, "titlePosition"),
"change .mailpoet_posts_title_alignment": _.partial(this.changeField, "titleAlignment"), "change .mailpoet_posts_title_alignment": _.partial(this.changeField, "titleAlignment"),
"change .mailpoet_posts_image_padded": _.partial(this.changeBoolField, "imagePadded"), "change .mailpoet_posts_image_full_width": _.partial(this.changeBoolField, "imageFullWidth"),
"change .mailpoet_posts_show_author": _.partial(this.changeField, "showAuthor"), "change .mailpoet_posts_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"), "keyup .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_posts_show_categories": _.partial(this.changeField, "showCategories"), "change .mailpoet_posts_show_categories": _.partial(this.changeField, "showCategories"),

View File

@ -103,7 +103,8 @@ define([
getTemplate: function() { return templates.socialBlock; }, getTemplate: function() { return templates.socialBlock; },
childViewContainer: '.mailpoet_social', childViewContainer: '.mailpoet_social',
modelEvents: { modelEvents: {
'change': 'render' 'change': 'render',
'delete': 'deleteBlock',
}, },
events: { events: {
"mouseover": "showTools", "mouseover": "showTools",
@ -145,6 +146,10 @@ define([
arguments[0].collection = arguments[0].model.get('icons'); arguments[0].collection = arguments[0].model.get('icons');
Marionette.CompositeView.apply(this, arguments); Marionette.CompositeView.apply(this, arguments);
}, },
initialize: function() {
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
// Determines which view type should be used for a child // Determines which view type should be used for a child
childView: SocialIconView, childView: SocialIconView,
templateHelpers: function() { templateHelpers: function() {
@ -170,12 +175,9 @@ define([
_event.stopPropagation(); _event.stopPropagation();
}, },
getDropFunc: function() { getDropFunc: function() {
var that = this;
return function() { return function() {
var newModel = that.model.clone(); return this.model.clone();
//that.model.destroy(); }.bind(this);
return newModel;
};
}, },
_buildRegions: function(regions) { _buildRegions: function(regions) {
var that = this; var that = this;
@ -194,6 +196,46 @@ define([
this.regionManager.destroy(); this.regionManager.destroy();
_.extend(this, this._buildRegions(this.regions)); _.extend(this, this._buildRegions(this.regions));
}, },
showBlock: function() {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function() {
this.transitionOut().done(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('slideDown', 'fadeIn', 'easeIn');
},
transitionOut: function() {
return this._transition('slideUp', 'fadeOut', 'easeOut');
},
_transition: function(slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function() {
promise.resolve();
}.bind(this),
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false, // Do not enqueue, trigger animation in parallel
}
);
return promise;
},
}); });
Module.SocialBlockToolsView = base.BlockToolsView.extend({ Module.SocialBlockToolsView = base.BlockToolsView.extend({

View File

@ -26,6 +26,8 @@ define([
getTemplate: function() { return templates.textBlock; }, getTemplate: function() { return templates.textBlock; },
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing
initialize: function(options) { initialize: function(options) {
base.BlockView.prototype.initialize.apply(this, arguments);
this.renderOptions = _.defaults(options.renderOptions || {}, { this.renderOptions = _.defaults(options.renderOptions || {}, {
disableTextEditor: false, disableTextEditor: false,
}); });

View File

@ -9,7 +9,7 @@ define([
Module._query = function(args) { Module._query = function(args) {
return MailPoet.Ajax.post({ return MailPoet.Ajax.post({
endpoint: 'wordpress', endpoint: 'automatedLatestContent',
action: args.action, action: args.action,
data: args.options || {}, data: args.options || {},
}); });
@ -63,16 +63,18 @@ define([
}; };
Module.saveNewsletter = function(options) { Module.saveNewsletter = function(options) {
return Module._query({ return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'save', action: 'save',
options: options, data: options || {},
}); });
}; };
Module.previewNewsletter = function(options) { Module.previewNewsletter = function(options) {
return Module._query({ return MailPoet.Ajax.post({
action: 'preview', endpoint: 'newsletters',
options: options, action: 'sendPreview',
data: options || {},
}); });
}; };

View File

@ -11,7 +11,7 @@ define([
// Does not hold newsletter content nor newsletter styles, those are // Does not hold newsletter content nor newsletter styles, those are
// handled by other components. // handled by other components.
Module.NewsletterModel = SuperModel.extend({ Module.NewsletterModel = SuperModel.extend({
stale: ['body'], stale: ['body', 'created_at', 'deleted_at', 'updated_at'],
initialize: function(options) { initialize: function(options) {
this.on('change', function() { this.on('change', function() {
App.getChannel().trigger('autoSave'); App.getChannel().trigger('autoSave');
@ -44,10 +44,10 @@ define([
}; };
Module.getBody = function() { Module.getBody = function() {
return JSON.stringify({ return {
content: App._contentContainer.toJSON(), content: App._contentContainer.toJSON(),
globalStyles: App.getGlobalStyles().toJSON(), globalStyles: App.getGlobalStyles().toJSON(),
}); };
}; };
Module.toJSON = function() { Module.toJSON = function() {
@ -73,8 +73,7 @@ define([
}); });
App.on('start', function(options) { App.on('start', function(options) {
// TODO: Other newsletter information will be needed as well. var body = options.newsletter.body;
var body = JSON.parse(options.newsletter.body);
App._contentContainer = new (App.getBlockTypeModel('container'))(body.content, {parse: true}); App._contentContainer = new (App.getBlockTypeModel('container'))(body.content, {parse: true});
App._contentContainerView = new (App.getBlockTypeView('container'))({ App._contentContainerView = new (App.getBlockTypeView('container'))({
model: App._contentContainer, model: App._contentContainer,

View File

@ -1,13 +1,26 @@
define([ define([
'newsletter_editor/App', 'newsletter_editor/App',
'newsletter_editor/components/communication',
'mailpoet', 'mailpoet',
'notice', 'notice',
'backbone', 'backbone',
'backbone.marionette', 'backbone.marionette',
'jquery', 'jquery',
'blob', 'blob',
'filesaver' 'filesaver',
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver) { 'html2canvas'
], function(
App,
CommunicationComponent,
MailPoet,
Notice,
Backbone,
Marionette,
jQuery,
Blob,
FileSaver,
html2canvas
) {
"use strict"; "use strict";
@ -16,26 +29,33 @@ define([
// Save editor contents to server // Save editor contents to server
Module.save = function() { Module.save = function() {
App.getChannel().trigger('beforeEditorSave');
var json = App.toJSON(); var json = App.toJSON();
// Stringify to enable transmission of primitive non-string value types
if (!_.isUndefined(json.body)) {
json.body = JSON.stringify(json.body);
}
App.getChannel().trigger('beforeEditorSave', json);
// save newsletter // save newsletter
MailPoet.Ajax.post({ CommunicationComponent.saveNewsletter(json).done(function(response) {
endpoint: 'newsletters',
action: 'save',
data: json,
}).done(function(response) {
if(response.success !== undefined && response.success === true) { if(response.success !== undefined && response.success === true) {
// TODO: Handle translations // TODO: Handle translations
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>"); //MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
} else if(response.error !== undefined) { } else if(response.error !== undefined) {
if(response.error.length === 0) { if(response.error.length === 0) {
// TODO: Handle translations // TODO: Handle translations
MailPoet.Notice.error("<?php _e('An unknown error occurred, please check your settings.'); ?>"); MailPoet.Notice.error(
"An unknown error occurred, please check your settings.",
{
scroll: true,
}
);
} else { } else {
$(response.error).each(function(i, error) { $(response.error).each(function(i, error) {
MailPoet.Notice.error(error); MailPoet.Notice.error(error, { scroll: true });
}); });
} }
} }
@ -46,26 +66,52 @@ define([
}); });
}; };
Module.getThumbnail = function(element, options) {
return html2canvas(element, options || {});
};
Module.saveTemplate = function(options) { Module.saveTemplate = function(options) {
return MailPoet.Ajax.post({ var that = this,
endpoint: 'newsletterTemplates', promise = jQuery.Deferred();
action: 'save',
data: _.extend(options || {}, { promise.then(function(thumbnail) {
body: App.getBody(), var data = _.extend(options || {}, {
}), thumbnail: thumbnail.toDataURL('image/jpeg'),
body: JSON.stringify(App.getBody()),
});
return MailPoet.Ajax.post({
endpoint: 'newsletterTemplates',
action: 'save',
data: data,
});
}); });
Module.getThumbnail(
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
).then(function(thumbnail) {
promise.resolve(thumbnail);
});
return promise;
}; };
Module.exportTemplate = function(options) { Module.exportTemplate = function(options) {
var data = _.extend(options || {}, { var that = this;
body: App.getBody(), return Module.getThumbnail(
}); jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
var blob = new Blob( ).then(function(thumbnail) {
[JSON.stringify(data)], var data = _.extend(options || {}, {
{ type: 'application/json;charset=utf-8' } thumbnail: thumbnail.toDataURL('image/jpeg'),
); body: App.getBody(),
});
var blob = new Blob(
[JSON.stringify(data)],
{ type: 'application/json;charset=utf-8' }
);
FileSaver.saveAs(blob, 'template.json'); FileSaver.saveAs(blob, 'template.json');
});
}; };
Module.SaveView = Marionette.LayoutView.extend({ Module.SaveView = Marionette.LayoutView.extend({
@ -119,20 +165,52 @@ define([
}, },
saveAsTemplate: function() { saveAsTemplate: function() {
var templateName = this.$('.mailpoet_save_as_template_name').val(), var templateName = this.$('.mailpoet_save_as_template_name').val(),
templateDescription = this.$('.mailpoet_save_as_template_description').val(); templateDescription = this.$('.mailpoet_save_as_template_description').val(),
that = this;
console.log('Saving template with ', templateName, templateDescription); if (templateName === '') {
Module.saveTemplate({ MailPoet.Notice.error(
name: templateName, App.getConfig().get('translations.templateNameMissing'),
description: templateDescription, {
}).done(function() { positionAfter: that.$el,
console.log('Template saved', arguments); scroll: true,
}).fail(function() { }
// TODO: Handle error messages );
console.log('Template save failed', arguments); } else if (templateDescription === '') {
}); MailPoet.Notice.error(
App.getConfig().get('translations.templateDescriptionMissing'),
{
positionAfter: that.$el,
scroll: true,
}
);
} else {
console.log('Saving template with ', templateName, templateDescription);
Module.saveTemplate({
name: templateName,
description: templateDescription,
}).done(function() {
console.log('Template saved', arguments);
MailPoet.Notice.success(
App.getConfig().get('translations.templateSaved'),
{
positionAfter: that.$el,
scroll: true,
}
);
}).fail(function() {
console.log('Template save failed', arguments);
MailPoet.Notice.error(
App.getConfig().get('translations.templateSaveFailed'),
{
positionAfter: that.$el,
scroll: true,
}
);
});
this.hideOptionContents();
}
this.hideOptionContents();
}, },
toggleExportTemplate: function() { toggleExportTemplate: function() {
this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden'); this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden');
@ -143,12 +221,25 @@ define([
}, },
exportTemplate: function() { exportTemplate: function() {
var templateName = this.$('.mailpoet_export_template_name').val(), var templateName = this.$('.mailpoet_export_template_name').val(),
templateDescription = this.$('.mailpoet_export_template_description').val(); templateDescription = this.$('.mailpoet_export_template_description').val(),
that = this;
if (templateName === '') { if (templateName === '') {
MailPoet.Notice.error(App.getConfig().get('translations.templateNameMissing')); MailPoet.Notice.error(
App.getConfig().get('translations.templateNameMissing'),
{
positionAfter: that.$el,
scroll: true,
}
);
} else if (templateDescription === '') { } else if (templateDescription === '') {
MailPoet.Notice.error(App.getConfig().get('translations.templateDescriptionMissing')); MailPoet.Notice.error(
App.getConfig().get('translations.templateDescriptionMissing'),
{
positionAfter: that.$el,
scroll: true,
}
);
} else { } else {
console.log('Exporting template with ', templateName, templateDescription); console.log('Exporting template with ', templateName, templateDescription);
Module.exportTemplate({ Module.exportTemplate({

View File

@ -1,12 +1,13 @@
define([ define([
'newsletter_editor/App', 'newsletter_editor/App',
'newsletter_editor/components/communication',
'backbone', 'backbone',
'backbone.marionette', 'backbone.marionette',
'backbone.supermodel', 'backbone.supermodel',
'underscore', 'underscore',
'jquery', 'jquery',
'sticky-kit' 'sticky-kit'
], function(App, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) { ], function(App, CommunicationComponent, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
"use strict"; "use strict";
@ -50,8 +51,33 @@ define([
}, },
events: { events: {
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) { 'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) {
this.$el.find('.mailpoet_sidebar_region').addClass('closed'); var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
this.$el.find(event.target).parent().parent().removeClass('closed'); $targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
if ($openRegion.get(0) === $targetRegion.get(0)) {
return;
}
$openRegion.find('.mailpoet_region_content').velocity(
'slideUp',
{
duration: 250,
easing: "easeOut",
complete: function() {
$openRegion.addClass('closed');
}.bind(this)
}
);
$targetRegion.find('.mailpoet_region_content').velocity(
'slideDown',
{
duration: 250,
easing: "easeIn",
complete: function() {
$targetRegion.removeClass('closed');
},
}
);
}, },
}, },
initialize: function(options) { initialize: function(options) {
@ -90,7 +116,6 @@ define([
}); });
}, },
onDomRefresh: function() { onDomRefresh: function() {
var that = this;
this.$el.parent().stick_in_parent({ this.$el.parent().stick_in_parent({
offset_top: 32, offset_top: 32,
}); });
@ -169,10 +194,8 @@ define([
}, },
initialize: function(options) { initialize: function(options) {
this.availableStyles = options.availableStyles; this.availableStyles = options.availableStyles;
var that = this;
}, },
onRender: function() { onRender: function() {
var that = this;
this.$('.mailpoet_color').spectrum({ this.$('.mailpoet_color').spectrum({
clickoutFiresChange: true, clickoutFiresChange: true,
showInput: true, showInput: true,
@ -202,6 +225,11 @@ define([
showPreview: function() { showPreview: function() {
var json = App.toJSON(); var json = App.toJSON();
// Stringify to enable transmission of primitive non-string value types
if (!_.isUndefined(json.body)) {
json.body = JSON.stringify(json.body);
}
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: 'newsletters', endpoint: 'newsletters',
action: 'render', action: 'render',
@ -219,26 +247,29 @@ define([
console.log('trying to send a preview'); console.log('trying to send a preview');
// get form data // get form data
var data = { var data = {
from_name: this.$('#mailpoet_preview_from_name').val(), subscriber: this.$('#mailpoet_preview_to_email').val(),
from_email: this.$('#mailpoet_preview_from_email').val(), id: App.getNewsletter().get('id'),
to_email: this.$('#mailpoet_preview_to_email').val(),
newsletter: App.newsletterId,
}; };
// send test email // send test email
MailPoet.Modal.loading(true); MailPoet.Modal.loading(true);
// TODO: Migrate logic to new AJAX format CommunicationComponent.previewNewsletter(data).done(function(response) {
Wordpress.previewNewsletter(data).done(function(response) { if(response.result !== undefined && response.result === true) {
if(response.success !== undefined && response.success === true) { MailPoet.Notice.success(App.getConfig().get('translations.newsletterPreviewSent'), { scroll: true });
MailPoet.Notice.success(App.getConfig().get('translations.testEmailSent')); } else {
} else if(response.error !== undefined) { if (_.isArray(response.errors)) {
if(response.error.length === 0) { response.errors.map(function(error) {
MailPoet.Notice.error(App.getConfig().get('translations.unknownErrorOccurred')); MailPoet.Notice.error(error, { scroll: true });
} else {
$(response.error).each(function(i, error) {
MailPoet.Notice.error(error);
}); });
} else {
MailPoet.Notice.error(
App.getConfig().get('translations.newsletterPreviewFailedToSend'),
{
scroll: true,
static: true,
}
);
} }
} }
MailPoet.Modal.loading(false); MailPoet.Modal.loading(false);

View File

@ -17,7 +17,7 @@ define([
}, },
h1: { h1: {
fontColor: '#111111', fontColor: '#111111',
fontFamily: 'Arial Black', fontFamily: 'Arial',
fontSize: '40px' fontSize: '40px'
}, },
h2: { h2: {
@ -72,7 +72,7 @@ define([
App.getAvailableStyles = Module.getAvailableStyles; App.getAvailableStyles = Module.getAvailableStyles;
var body = JSON.parse(options.newsletter.body); var body = options.newsletter.body;
this.setGlobalStyles(body.globalStyles); this.setGlobalStyles(body.globalStyles);
}); });

View File

@ -21,6 +21,10 @@ define(
label: 'Subject', label: 'Subject',
sortable: true sortable: true
}, },
{
name: 'status',
label: 'Status'
},
{ {
name: 'segments', name: 'segments',
label: 'Lists' label: 'Lists'
@ -39,58 +43,49 @@ define(
var messages = { var messages = {
onTrash: function(response) { onTrash: function(response) {
var count = ~~response.newsletters; var count = ~~response;
var message = null; var message = null;
if(count === 1 || response === true) { if(count === 1) {
message = ( message = (
'1 newsletter was moved to the trash.' '1 newsletter was moved to the trash.'
); );
} else if(count > 1) { } else {
message = ( message = (
'%$1d newsletters were moved to the trash.' '%$1d newsletters were moved to the trash.'
).replace('%$1d', count); ).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
if(message !== null) {
MailPoet.Notice.success(message);
}
}, },
onDelete: function(response) { onDelete: function(response) {
var count = ~~response.newsletters; var count = ~~response;
var message = null; var message = null;
if(count === 1 || response === true) { if(count === 1) {
message = ( message = (
'1 newsletter was permanently deleted.' '1 newsletter was permanently deleted.'
); );
} else if(count > 1) { } else {
message = ( message = (
'%$1d newsletters were permanently deleted.' '%$1d newsletters were permanently deleted.'
).replace('%$1d', count); ).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
if(message !== null) {
MailPoet.Notice.success(message);
}
}, },
onRestore: function(response) { onRestore: function(response) {
var count = ~~response.newsletters; var count = ~~response;
var message = null; var message = null;
if(count === 1 || response === true) { if(count === 1) {
message = ( message = (
'1 newsletter has been restored from the trash.' '1 newsletter has been restored from the trash.'
); );
} else if(count > 1) { } else {
message = ( message = (
'%$1d newsletters have been restored from the trash.' '%$1d newsletters have been restored from the trash.'
).replace('%$1d', count); ).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
if(message !== null) {
MailPoet.Notice.success(message);
}
} }
}; };
@ -112,12 +107,101 @@ define(
</a> </a>
); );
} }
},
{
name: 'trash'
} }
]; ];
var NewsletterList = React.createClass({ var NewsletterList = React.createClass({
renderItem: function(newsletter, actions) { pauseSending: function(item) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'pause',
data: item.id
}).done(function() {
jQuery('#resume_'+item.id).show();
jQuery('#pause_'+item.id).hide();
});
},
resumeSending: function(item) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'resume',
data: item.id
}).done(function() {
jQuery('#pause_'+item.id).show();
jQuery('#resume_'+item.id).hide();
});
},
renderStatus: function(item) {
if(item.queue === null) {
return (
<span>Not sent yet.</span>
);
} else {
var progressClasses = classNames(
'mailpoet_progress',
{ 'mailpoet_progress_complete': item.queue.status === 'completed'}
);
// calculate percentage done
var percentage = Math.round(
(item.queue.count_processed * 100) / (item.queue.count_total)
);
var label = false;
if(item.queue.status === 'completed') {
label = (
<span>
Sent to {
item.queue.count_processed - item.queue.count_failed
} out of { item.queue.count_total }.
</span>
);
} else {
label = (
<span>
{ item.queue.count_processed } / { item.queue.count_total }
&nbsp;&nbsp;
<a
id={ 'resume_'+item.id }
className="button"
style={{ display: (item.queue.status === 'paused') ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.resumeSending.bind(null, item) }
>Resume</a>
<a
id={ 'pause_'+item.id }
className="button mailpoet_pause"
style={{ display: (item.queue.status === null) ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.pauseSending.bind(null, item) }
>Pause</a>
</span>
);
}
return (
<div>
<div className={ progressClasses }>
<span
className="mailpoet_progress_bar"
style={ { width: percentage + "%"} }
></span>
<span className="mailpoet_progress_label">
{ percentage + "%" }
</span>
</div>
<p style={{ textAlign:'center' }}>
{ label }
</p>
</div>
);
}
},
renderItem: function(newsletter, actions) {
var rowClasses = classNames( var rowClasses = classNames(
'manage-column', 'manage-column',
'column-primary', 'column-primary',
@ -138,6 +222,9 @@ define(
</strong> </strong>
{ actions } { actions }
</td> </td>
<td className="column" data-colname="Lists">
{ this.renderStatus(newsletter) }
</td>
<td className="column" data-colname="Lists"> <td className="column" data-colname="Lists">
{ segments } { segments }
</td> </td>
@ -164,7 +251,8 @@ define(
columns={columns} columns={columns}
bulk_actions={ bulk_actions } bulk_actions={ bulk_actions }
item_actions={ item_actions } item_actions={ item_actions }
messages={ messages } /> messages={ messages }
auto_refresh={ true } />
</div> </div>
); );
} }

View File

@ -16,7 +16,7 @@ define(
Breadcrumb Breadcrumb
) { ) {
var settings = window.mailpoet_settings || {}; var settings = window.mailpoet_settings || {};
var fields = [ var fields = [
{ {
@ -24,17 +24,26 @@ define(
label: 'Subject line', label: 'Subject line',
tip: "Be creative! It's the first thing your subscribers see."+ tip: "Be creative! It's the first thing your subscribers see."+
"Tempt them to open your email.", "Tempt them to open your email.",
type: 'text' type: 'text',
validation: {
'data-parsley-required': true
}
}, },
{ {
name: 'segments', name: 'segments',
label: 'Lists', label: 'Segments',
tip: "The subscriber list that will be used for this campaign.", tip: "The subscriber segment that will be used for this campaign.",
type: 'selection', type: 'selection',
placeholder: "Select a list", placeholder: "Select a segment",
id: "mailpoet_segments", id: "mailpoet_segments",
endpoint: "segments", endpoint: "segments",
multiple: true multiple: true,
filter: function(segment) {
return !!(!segment.deleted_at);
},
validation: {
'data-parsley-required': true
}
}, },
{ {
name: 'sender', name: 'sender',
@ -42,17 +51,24 @@ define(
tip: "Name & email of yourself or your company.", tip: "Name & email of yourself or your company.",
fields: [ fields: [
{ {
name: 'from_name', name: 'sender_name',
type: 'text', type: 'text',
placeholder: 'John Doe', placeholder: 'John Doe',
defaultValue: settings.from_name defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
validation: {
'data-parsley-required': true
}
}, },
{ {
name: 'from_email', name: 'sender_address',
type: 'text', type: 'text',
placeholder: 'john.doe@email.com', placeholder: 'john.doe@email.com',
defaultValue: settings.from_address defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
}, validation: {
'data-parsley-required': true,
'data-parsley-type': 'email'
}
}
] ]
}, },
{ {
@ -65,20 +81,25 @@ define(
{ {
name: 'reply_to_name', name: 'reply_to_name',
type: 'text', type: 'text',
placeholder: 'John Doe' placeholder: 'John Doe',
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
}, },
{ {
name: 'reply_to_email', name: 'reply_to_address',
type: 'text', type: 'text',
placeholder: 'john.doe@email.com' placeholder: 'john.doe@email.com',
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
}, },
] ]
} }
]; ];
var messages = { var messages = {
updated: function() { onUpdate: function() {
MailPoet.Notice.success('The newsletter has been updated!'); MailPoet.Notice.success('Newsletter successfully updated!');
},
onCreate: function() {
MailPoet.Notice.success('Newsletter successfully added!');
} }
}; };
@ -87,34 +108,42 @@ define(
Router.History Router.History
], ],
handleSend: function() { handleSend: function() {
MailPoet.Ajax.post({ if(jQuery('#mailpoet_newsletter').parsley().validate() === true) {
endpoint: 'newsletters', MailPoet.Ajax.post({
action: 'send', endpoint: 'sendingQueue',
data: { action: 'add',
id: this.props.params.id, data: {
newsletter: jQuery('#mailpoet_newsletter').serializeObject(), newsletter_id: this.props.params.id,
segments: jQuery('#mailpoet_segments').val() segments: jQuery('#mailpoet_segments').val(),
} sender: {
}).done(function(response) { 'name': jQuery('#mailpoet_newsletter [name="sender_name"]').val(),
if(response === true) { 'address': jQuery('#mailpoet_newsletter [name="sender_address"]').val()
this.history.pushState(null, '/'); },
reply_to: {
MailPoet.Notice.success( 'name': jQuery('#mailpoet_newsletter [name="reply_to_name"]').val(),
'The newsletter has been sent!' 'address': jQuery('#mailpoet_newsletter [name="reply_to_address"]').val()
); }
} else { }
if(response.errors) { }).done(function(response) {
MailPoet.Notice.error( if(response.result === true) {
response.errors.join("<br />") this.history.pushState(null, '/');
MailPoet.Notice.success(
'The newsletter is being sent...'
); );
} else { } else {
MailPoet.Notice.error( if(response.errors) {
'An error occurred while trying to send. '+ MailPoet.Notice.error(
'<a href="?page=mailpoet-settings">Check your settings.</a>' response.errors.join("<br />")
); );
} else {
MailPoet.Notice.error(
'An error occurred while trying to send. '+
'<a href="?page=mailpoet-settings">Check your settings.</a>'
);
}
} }
} }.bind(this));
}.bind(this)); }
}, },
render: function() { render: function() {
return ( return (
@ -128,8 +157,8 @@ define(
endpoint="newsletters" endpoint="newsletters"
fields={ fields } fields={ fields }
params={ this.props.params } params={ this.props.params }
messages={ messages }> messages={ messages }
>
<p className="submit"> <p className="submit">
<input <input
className="button button-primary" className="button button-primary"

View File

@ -18,12 +18,18 @@ define(
var ImportTemplate = React.createClass({ var ImportTemplate = React.createClass({
saveTemplate: function(template) { saveTemplate: function(template) {
// Stringify to enable transmission of primitive non-string value types
if (!_.isUndefined(template.body)) {
template.body = JSON.stringify(template.body);
}
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: 'newsletterTemplates', endpoint: 'newsletterTemplates',
action: 'save', action: 'save',
data: template data: template
}).done(function(response) { }).done(function(response) {
if(response === true) { if(response.result === true) {
this.props.onImport(template); this.props.onImport(template);
} else { } else {
response.map(function(error) { response.map(function(error) {
@ -99,7 +105,7 @@ define(
"MailPoet's Guide", "MailPoet's Guide",
description: description:
"This is the standard template that comes with MailPoet.", "This is the standard template that comes with MailPoet.",
readonly: true readonly: "1"
} }
] ]
} }
@ -111,19 +117,26 @@ define(
}.bind(this)); }.bind(this));
}, },
handleSelectTemplate: function(template) { handleSelectTemplate: function(template) {
var body = template.body;
// Stringify to enable transmission of primitive non-string value types
if (!_.isUndefined(body)) {
body = JSON.stringify(body);
}
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: 'newsletters', endpoint: 'newsletters',
action: 'save', action: 'save',
data: { data: {
id: this.props.params.id, id: this.props.params.id,
body: template.body body: body
} }
}).done(function(response) { }).done(function(response) {
if(response === true) { if(response.result === true) {
// TODO: Move this URL elsewhere // TODO: Move this URL elsewhere
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id; window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id;
} else { } else {
response.map(function(error) { response.errors.map(function(error) {
MailPoet.Notice.error(error); MailPoet.Notice.error(error);
}); });
} }
@ -150,6 +163,13 @@ define(
this.setState({ loading: false }); this.setState({ loading: false });
} }
}, },
handleShowTemplate: function(template) {
MailPoet.Modal.popup({
title: template.name,
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
data: template,
});
},
handleTemplateImport: function() { handleTemplateImport: function() {
this.getTemplates(); this.getTemplates();
}, },
@ -164,11 +184,22 @@ define(
Delete Delete
</a> </a>
</div> </div>
); ), thumbnail = '';
if (typeof template.thumbnail === 'string'
&& template.thumbnail.length > 0) {
thumbnail = (
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
<img src={ template.thumbnail } />
<div className="mailpoet_overlay"></div>
</a>
);
}
return ( return (
<li key={ 'template-'+index }> <li key={ 'template-'+index }>
<div className="mailpoet_thumbnail"> <div className="mailpoet_thumbnail">
{ thumbnail }
</div> </div>
<div className="mailpoet_description"> <div className="mailpoet_description">
@ -192,7 +223,7 @@ define(
Preview Preview
</a> </a>
</div> </div>
{ (template.readonly) ? false : deleteLink } { (template.readonly === "1") ? false : deleteLink }
</li> </li>
); );
}.bind(this)); }.bind(this));

View File

@ -26,14 +26,17 @@ define(
action: 'create', action: 'create',
data: { data: {
type: type, type: type,
subject: 'Draft newsletter',
} }
}).done(function(response) { }).done(function(response) {
if(response.id !== undefined) { if(response.result && response.newsletter.id) {
this.history.pushState(null, `/template/${response.id}`); this.history.pushState(null, `/template/${response.newsletter.id}`);
} else { } else {
response.map(function(error) { if(response.errors.length > 0) {
MailPoet.Notice.error(error); response.errors.map(function(error) {
}); MailPoet.Notice.error(error);
});
}
} }
}.bind(this)); }.bind(this));
}, },

View File

@ -138,12 +138,14 @@ define(
options: this.state, options: this.state,
}, },
}).done(function(response) { }).done(function(response) {
if(response.id !== undefined) { if(response.result && response.newsletter.id) {
this.showTemplateSelection(response.id); this.showTemplateSelection(response.newsletter.id);
} else { } else {
response.map(function(error) { if(response.errors.length > 0) {
MailPoet.Notice.error(error); response.errors.map(function(error) {
}); MailPoet.Notice.error(error);
});
}
} }
}.bind(this)); }.bind(this));
}, },

View File

@ -32,12 +32,15 @@ define(
type: 'standard', type: 'standard',
} }
}).done(function(response) { }).done(function(response) {
if(response.id !== undefined) { console.log(response);
this.showTemplateSelection(response.id); if(response.result && response.newsletter.id) {
this.showTemplateSelection(response.newsletter.id);
} else { } else {
response.map(function(error) { if(response.errors.length > 0) {
MailPoet.Notice.error(error); response.errors.map(function(error) {
}); MailPoet.Notice.error(error);
});
}
} }
}.bind(this)); }.bind(this));
}, },

View File

@ -111,12 +111,14 @@ define(
options: this.state, options: this.state,
}, },
}).done(function(response) { }).done(function(response) {
if(response.id !== undefined) { if(response.result && response.newsletter.id) {
this.showTemplateSelection(response.id); this.showTemplateSelection(response.newsletter.id);
} else { } else {
response.map(function(error) { if(response.errors.length > 0) {
MailPoet.Notice.error(error); response.errors.map(function(error) {
}); MailPoet.Notice.error(error);
});
}
} }
}.bind(this)); }.bind(this));
}, },

View File

@ -1,191 +1,219 @@
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) { define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
"use strict"; "use strict";
/*================================================================================================== /*==================================================================================================
MailPoet Notice: MailPoet Notice:
description: Handles notices description: Handles notices
version: 0.2 version: 0.2
author: Jonathan Labreuille author: Jonathan Labreuille
company: Wysija company: Wysija
dependencies: jQuery dependencies: jQuery
Usage: Usage:
// success message (static: false) // success message (static: false)
MailPoet.Notice.success('Yatta!'); MailPoet.Notice.success('Yatta!');
// error message (static: false) // error message (static: false)
MailPoet.Notice.error('Boo!'); MailPoet.Notice.error('Boo!');
// system message (static: true) // system message (static: true)
MailPoet.Notice.system('You need to updated ASAP!'); MailPoet.Notice.system('You need to updated ASAP!');
Examples: Examples:
MailPoet.Notice.success('- success #1 -'); MailPoet.Notice.success('- success #1 -');
setTimeout(function() { setTimeout(function() {
MailPoet.Notice.success('- success #2 -'); MailPoet.Notice.success('- success #2 -');
setTimeout(function() { setTimeout(function() {
MailPoet.Notice.error('- error -'); MailPoet.Notice.error('- error -');
setTimeout(function() { setTimeout(function() {
MailPoet.Notice.system('- system -'); MailPoet.Notice.system('- system -');
setTimeout(function() { setTimeout(function() {
MailPoet.Notice.hide(); MailPoet.Notice.hide();
}, 2500); }, 2500);
}, 300); }, 300);
}, 400); }, 400);
}, 500); }, 500);
==================================================================================================*/ ==================================================================================================*/
MailPoet.Notice = { MailPoet.Notice = {
version: 0.2, version: 0.2,
// default options // default options
defaults: { defaults: {
type: 'success', type: 'success',
message: '', message: '',
static: false, static: false,
scroll: false, hideClose: false,
timeout: 2000, id: null,
onOpen: null, positionAfter: false,
onClose: null scroll: false,
}, timeout: 2000,
options: {}, onOpen: null,
init: function(options) { onClose: null
// set options },
this.options = jQuery.extend({}, this.defaults, options); options: {},
init: function(options) {
// clone element // set options
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone(); this.options = jQuery.extend({}, this.defaults, options);
// remove id from clone // clone element
this.element.removeAttr('id'); this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
// insert notice after its parent // add data-id to the element
jQuery('#mailpoet_notice_'+this.options.type).after(this.element); if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
// setup onClose callback // remove id from clone
var onClose = null; this.element.removeAttr('id');
if(this.options.onClose !== null) {
onClose = this.options.onClose; // insert notice after its parent
} var positionAfter;
if (typeof this.options.positionAfter === 'object') {
// listen to remove event positionAfter = this.options.positionAfter;
var element = this.element; } else if (typeof this.options.positionAfter === 'string') {
jQuery(this.element).on('close', function() { positionAfter = jQuery(this.options.positionAfter);
jQuery(this).fadeOut(200, function() { } else {
// on close callback positionAfter = jQuery('#mailpoet_notice_'+this.options.type);
if(onClose !== null) { }
onClose(); positionAfter.after(this.element);
}
// remove notice // setup onClose callback
jQuery(this).remove(); var onClose = null;
}); if(this.options.onClose !== null) {
}.bind(this.element)); onClose = this.options.onClose;
}
// listen to message event
jQuery(this.element).on('message', function(e, message) { // listen to remove event
MailPoet.Notice.setMessage(message); jQuery(this.element).on('close', function() {
}.bind(this.element)); jQuery(this).fadeOut(200, function() {
// on close callback
return this; if(onClose !== null) {
}, onClose();
isHTML: function(str) { }
var a = document.createElement('div'); // remove notice
a.innerHTML = str; jQuery(this).remove();
for(var c = a.childNodes, i = c.length; i--;) { });
if(c[i].nodeType == 1) return true; }.bind(this.element));
}
return false; // listen to message event
}, jQuery(this.element).on('message', function(e, message) {
setMessage: function(message) { MailPoet.Notice.setMessage(message);
// if it's not an html message, let's sugar coat the message with a fancy <p> }.bind(this.element));
if(this.isHTML(message) === false) {
message = '<p>'+message+'</p>'; return this;
} },
// set message isHTML: function(str) {
return this.element.html(message); var a = document.createElement('div');
}, a.innerHTML = str;
show: function(options) { for(var c = a.childNodes, i = c.length; i--;) {
// initialize if(c[i].nodeType == 1) return true;
this.init(options); }
return false;
// show notice },
this.showNotice(); setMessage: function(message) {
// if it's not an html message, let's sugar coat the message with a fancy <p>
// return this; if(this.isHTML(message) === false) {
}, message = '<p>'+message+'</p>';
showNotice: function() { }
// set message // set message
this.setMessage(this.options.message); return this.element.html(message);
},
// position notice show: function(options) {
this.element.insertAfter(jQuery('h2.title')); // initialize
this.init(options);
// set class name
switch(this.options.type) { // show notice
case 'success': this.showNotice();
this.element.addClass('updated');
break; // return this;
case 'system': },
this.element.addClass('update-nag'); showNotice: function() {
break; // set message
case 'error': this.setMessage(this.options.message);
this.element.addClass('error');
break; // position notice
} this.element.insertAfter(jQuery('h2.title'));
// make the notice appear // set class name
this.element.fadeIn(200); switch(this.options.type) {
case 'success':
// if scroll option is enabled, scroll to the notice this.element.addClass('updated');
if(this.options.scroll === true) { break;
this.element.get(0).scrollIntoView(false); case 'system':
} this.element.addClass('update-nag');
break;
// if the notice is not static, it has to disappear after a timeout case 'error':
if(this.options.static === false) { this.element.addClass('error');
this.element.delay(this.options.timeout).trigger('close'); break;
} else { }
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
this.element.find('.mailpoet_notice_close').on('click', function() { // make the notice appear
jQuery(this).trigger('close'); this.element.fadeIn(200);
});
} // if scroll option is enabled, scroll to the notice
if(this.options.scroll === true) {
// call onOpen callback this.element.get(0).scrollIntoView(false);
if(this.options.onOpen !== null) { }
this.options.onOpen(this.element);
} // if the notice is not static, it has to disappear after a timeout
}, if(this.options.static === false) {
hide: function(all) { this.element.delay(this.options.timeout).trigger('close');
if(all !== undefined && all === true) { } else if (this.options.hideClose === false) {
jQuery('.mailpoet_notice:not([id])').trigger('close'); this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
} else { this.element.find('.mailpoet_notice_close').on('click', function() {
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])') jQuery(this).trigger('close');
.trigger('close'); });
} }
},
error: function(message, options) { // call onOpen callback
this.show(jQuery.extend({}, { if(this.options.onOpen !== null) {
type: 'error', this.options.onOpen(this.element);
message: '<p>'+message+'</p>' }
}, options)); },
}, hide: function(all) {
success: function(message, options) { if(all !== undefined && all === true) {
this.show(jQuery.extend({}, { jQuery('.mailpoet_notice:not([id])').trigger('close');
type: 'success', } else if (all !== undefined && jQuery.isArray(all)) {
message: '<p>'+message+'</p>' for (var id in all) {
}, options)); jQuery('[data-id="notice_' + all[id] + '"]')
}, .trigger('close');
system: function(message, options) { }
this.show(jQuery.extend({}, { } if (all !== undefined) {
type: 'system', jQuery('[data-id="notice_' + all + '"]')
static: true, .trigger('close');
message: message } else {
}, options)); jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
} .trigger('close');
}; }
}); },
error: function(message, options) {
this.show(jQuery.extend({}, {
type: 'error',
message: '<p>'+this.formatMessage(message)+'</p>'
}, options));
},
success: function(message, options) {
this.show(jQuery.extend({}, {
type: 'success',
message: '<p>'+this.formatMessage(message)+'</p>'
}, options));
},
system: function(message, options) {
this.show(jQuery.extend({}, {
type: 'system',
static: true,
message: '<p>'+this.formatMessage(message)+'</p>'
}, options));
},
formatMessage: function(message) {
if(Array.isArray(message)) {
return message.join('<br />');
} else {
return message;
}
}
};
});

View File

@ -26,10 +26,10 @@ define(
]; ];
var messages = { var messages = {
updated: function() { onUpdate: function() {
MailPoet.Notice.success('Segment successfully updated!'); MailPoet.Notice.success('Segment successfully updated!');
}, },
created: function() { onCreate: function() {
MailPoet.Notice.success('Segment successfully added!'); MailPoet.Notice.success('Segment successfully added!');
} }
}; };
@ -42,11 +42,7 @@ define(
return ( return (
<div> <div>
<h2 className="title"> <h2 className="title">
Segment <a Segment
href="javascript:;"
className="add-new-h2"
onClick={ this.history.goBack }
>Back to list</a>
</h2> </h2>
<Form <Form
@ -54,7 +50,7 @@ define(
fields={ fields } fields={ fields }
params={ this.props.params } params={ this.props.params }
messages={ messages } messages={ messages }
onSuccess={ this.history.goBack } /> />
</div> </div>
); );
} }

View File

@ -42,58 +42,49 @@ var columns = [
const messages = { const messages = {
onTrash: function(response) { onTrash: function(response) {
if(response) { var count = ~~response;
let message = null; var message = null;
if(~~response === 1) {
message = (
'1 segment was moved to the trash.'
);
} else if(~~response > 1) {
message = (
'%$1d segments were moved to the trash.'
).replace('%$1d', ~~response);
}
if(message !== null) { if(count === 1) {
MailPoet.Notice.success(message); message = (
} '1 segment was moved to the trash.'
);
} else {
message = (
'%$1d segments were moved to the trash.'
).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
}, },
onDelete: function(response) { onDelete: function(response) {
if(response) { var count = ~~response;
let message = null; var message = null;
if(~~response === 1) {
message = (
'1 segment was permanently deleted.'
);
} else if(~~response > 1) {
message = (
'%$1d segments were permanently deleted.'
).replace('%$1d', ~~response);
}
if(message !== null) { if(count === 1) {
MailPoet.Notice.success(message); message = (
} '1 segment was permanently deleted.'
);
} else {
message = (
'%$1d segments were permanently deleted.'
).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
}, },
onRestore: function(response) { onRestore: function(response) {
if(response) { var count = ~~response;
let message = null; var message = null;
if(~~response === 1) {
message = (
'1 segment has been restored from the trash.'
);
} else if(~~response > 1) {
message = (
'%$1d segments have been restored from the trash.'
).replace('%$1d', ~~response);
}
if(message !== null) { if(count === 1) {
MailPoet.Notice.success(message); message = (
} '1 segment has been restored from the trash.'
);
} else {
message = (
'%$1d segments have been restored from the trash.'
).replace('%$1d', count);
} }
MailPoet.Notice.success(message);
} }
}; };
@ -121,6 +112,32 @@ const item_actions = [
); );
refresh(); refresh();
}); });
},
display: function(segment) {
return (segment.type !== 'wp_users');
}
},
{
name: 'synchronize_segment',
label: 'Update',
className: 'update',
onClick: function(item, refresh) {
MailPoet.Modal.loading(true);
MailPoet.Ajax.post({
endpoint: 'segments',
action: 'synchronize'
}).done(function(response) {
MailPoet.Modal.loading(false);
if(response === true) {
MailPoet.Notice.success(
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
);
refresh();
}
});
},
display: function(segment) {
return (segment.type === 'wp_users');
} }
}, },
{ {
@ -130,15 +147,16 @@ const item_actions = [
<a href={ item.subscribers_url }>View subscribers</a> <a href={ item.subscribers_url }>View subscribers</a>
); );
} }
},
{
name: 'trash',
display: function(segment) {
return (segment.type !== 'wp_users');
}
} }
]; ];
const bulk_actions = [ const bulk_actions = [
{
name: 'trash',
label: 'Trash',
onSuccess: messages.onTrash
}
]; ];
const SegmentList = React.createClass({ const SegmentList = React.createClass({
@ -148,7 +166,6 @@ const SegmentList = React.createClass({
'column-primary', 'column-primary',
'has-row-actions' 'has-row-actions'
); );
return ( return (
<div> <div>
<td className={ rowClasses }> <td className={ rowClasses }>

View File

@ -15,10 +15,10 @@ define(
MailPoet.Router = new (Backbone.Router.extend({ MailPoet.Router = new (Backbone.Router.extend({
routes: { routes: {
'mta(/:method)': 'sendingMethod', 'mta(/:group)': 'sendingMethodGroup',
'(:tab)': 'tabs', '(:tab)': 'tabs',
}, },
sendingMethod: function(method) { sendingMethodGroup: function(group) {
// display mta tab // display mta tab
this.tabs('mta'); this.tabs('mta');
@ -30,13 +30,13 @@ define(
// hide "save settings" button // hide "save settings" button
jQuery('.mailpoet_settings_submit').hide(); jQuery('.mailpoet_settings_submit').hide();
if(method === null) { if(group === null) {
// show sending methods // show sending methods
jQuery('.mailpoet_sending_methods').fadeIn(); jQuery('.mailpoet_sending_methods').fadeIn();
} else { } else {
// hide DKIM option when using MailPoet's API // hide DKIM option when using MailPoet's API
jQuery('#mailpoet_mta_dkim')[ jQuery('#mailpoet_mta_dkim')[
(method === 'mailpoet') (group === 'mailpoet')
? 'hide' ? 'hide'
: 'show' : 'show'
](); ]();
@ -45,7 +45,7 @@ define(
jQuery('.mailpoet_sending_methods').hide(); jQuery('.mailpoet_sending_methods').hide();
// display selected sending method's settings // display selected sending method's settings
jQuery('.mailpoet_sending_method[data-method="'+ method +'"]').show(); jQuery('.mailpoet_sending_method[data-group="'+ group +'"]').show();
jQuery('#mailpoet_sending_method_setup').fadeIn(); jQuery('#mailpoet_sending_method_setup').fadeIn();
} }
}, },
@ -73,7 +73,7 @@ define(
})); }));
jQuery(document).ready(function() { jQuery(document).ready(function() {
Backbone.history.start(); if (!Backbone.History.started) Backbone.history.start();
}); });
} }
); );

View File

@ -37,14 +37,43 @@ define(
'subscribed': 'Subscribed', 'subscribed': 'Subscribed',
'unsubscribed': 'Unsubscribed' 'unsubscribed': 'Unsubscribed'
} }
},
{
name: 'segments',
label: 'Lists',
type: 'selection',
placeholder: "Select a list",
endpoint: "segments",
multiple: true,
filter: function(segment) {
return !!(!segment.deleted_at);
}
} }
]; ];
var custom_fields = window.mailpoet_custom_fields || [];
custom_fields.map(custom_field => {
let field = {
name: 'cf_' + custom_field.id,
label: custom_field.name,
type: custom_field.type
};
if(custom_field.params) {
field.params = custom_field.params;
}
if(custom_field.params.values) {
field.values = custom_field.params.values;
}
fields.push(field);
});
var messages = { var messages = {
updated: function() { onUpdate: function() {
MailPoet.Notice.success('Subscriber successfully updated!'); MailPoet.Notice.success('Subscriber successfully updated!');
}, },
created: function() { onCreate: function() {
MailPoet.Notice.success('Subscriber successfully added!'); MailPoet.Notice.success('Subscriber successfully added!');
} }
}; };
@ -59,11 +88,7 @@ define(
return ( return (
<div> <div>
<h2 className="title"> <h2 className="title">
Subscriber <a Subscriber
href="javascript:;"
className="add-new-h2"
onClick={ this.history.goBack }
>Back to list</a>
</h2> </h2>
<Form <Form
@ -71,7 +96,7 @@ define(
fields={ fields } fields={ fields }
params={ this.props.params } params={ this.props.params }
messages={ messages } messages={ messages }
onSuccess={ this.history.goBack } /> />
</div> </div>
); );
} }

View File

@ -0,0 +1,170 @@
define(
[
'underscore',
'jquery',
'mailpoet',
'handlebars',
'select2'
],
function (
_,
jQuery,
MailPoet,
Handlebars
) {
if (!jQuery("#mailpoet_subscribers_export").length) {
return;
}
jQuery(document).ready(function () {
if (!exportData.segments) {
return;
}
var subscribers_export_template =
Handlebars.compile(jQuery('#mailpoet_subscribers_export_template').html());
//render template
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribers_export_template(exportData));
// define reusable variables
var segmentsContainerElement = jQuery("#export_lists"),
subscriberFieldsContainerElement = jQuery("#export_columns"),
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]'),
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]'),
nextStepButton = jQuery("a.mailpoet_export_process"),
renderSegmentsAndFields = function (container, data) {
if (container.data('select2')) {
container
.html('')
.select2('destroy');
}
container
.select2({
data: data,
width: '20em',
templateResult: function (item) {
return (item.subscriberCount > 0)
? item.name + ' (' + item.subscriberCount + ')'
: item.name;
},
templateSelection: function (item) {
return (item.subscriberCount > 0)
? item.name + ' (' + item.subscriberCount + ')'
: item.name;
}
})
.on('select2:selecting', function (selectEvent) {
var selectElement = this,
selectedOptionId = selectEvent.params.args.data.id,
fieldsToExclude = [
'select',
'deselect'
];
if (_.contains(fieldsToExclude, selectedOptionId)) {
selectEvent.preventDefault();
if (selectedOptionId === 'deselect') {
jQuery(selectElement).val('').trigger('change');
} else {
var allOptions = [];
_.each(container.find('option'), function (field) {
if (!_.contains(fieldsToExclude, field.value)) {
allOptions.push(field.value);
}
});
jQuery(selectElement).val(allOptions).trigger('change');
}
jQuery(selectElement).select2('close');
}
})
.on('change', function () {
if ((exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
||
(!exportData.segments && subscriberFieldsContainerElement.select2('data').length)
) {
toggleNextStepButton('on');
}
else {
toggleNextStepButton('off');
}
if (segmentsContainerElement.select2('data').length > 1 && exportData.groupBySegmentOption) {
jQuery('.mailpoet_group_by_list').show();
}
else if (exportData.groupBySegmentOption) {
jQuery('.mailpoet_group_by_list').hide();
}
});
};
renderSegmentsAndFields(subscriberFieldsContainerElement, subscriberFieldsSelect2);
renderSegmentsAndFields(segmentsContainerElement, segments);
subscriberFieldsContainerElement.select2('val', [
'status',
'email',
'first_name',
'last_name'
]);
exportConfirmedOptionElement.change(function () {
var selectedSegments = segmentsContainerElement.val();
if (this.value == 1) {
exportData.exportConfirmedOption = true;
renderSegmentsAndFields(segmentsContainerElement, segmentsWithConfirmedSubscribers);
}
else {
exportData.exportConfirmedOption = false;
renderSegmentsAndFields(segmentsContainerElement, segments);
}
segmentsContainerElement.select2('val', selectedSegments);
});
function toggleNextStepButton(condition) {
var disabled = 'button-disabled';
if (condition === 'on') {
nextStepButton.removeClass(disabled);
}
else {
nextStepButton.addClass(disabled);
}
}
nextStepButton.click(function () {
if (jQuery(this).hasClass('button-disabled')) {
return;
}
MailPoet.Modal.loading(true);
MailPoet.Ajax
.post({
endpoint: 'ImportExport',
action: 'processExport',
data: JSON.stringify({
'export_confirmed_option': exportData.exportConfirmedOption,
'export_format_option': jQuery(':radio[name="option_format"]:checked').val(),
'group_by_segment_option': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
'segments': (exportData.segments) ? segmentsContainerElement.val() : false,
'subscriber_fields': subscriberFieldsContainerElement.val()
})
})
.done(function (response) {
MailPoet.Modal.loading(false);
if (response.result === false) {
MailPoet.Notice.error(response.error);
} else {
resultMessage = MailPoetI18n.exportMessage
.replace('%1$s', '<strong>' + response.data.totalExported + '</strong>')
.replace('[link]', '<a href="' + response.data.exportFileURL + '" target="_blank" >')
.replace('[/link]', '</a>');
jQuery('#export_result_notice > ul > li').html(resultMessage);
jQuery('#export_result_notice').show();
window.location.href = response.data.exportFileURL;
}
})
.error(function (error) {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
);
});
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -101,7 +101,12 @@ const bulk_actions = [
onSelect: function() { onSelect: function() {
let field = { let field = {
id: 'move_to_segment', id: 'move_to_segment',
endpoint: 'segments' endpoint: 'segments',
filter: function(segment) {
return !!(
!segment.deleted_at && segment.type === 'default'
);
}
}; };
return ( return (
@ -127,7 +132,12 @@ const bulk_actions = [
onSelect: function() { onSelect: function() {
let field = { let field = {
id: 'add_to_segment', id: 'add_to_segment',
endpoint: 'segments' endpoint: 'segments',
filter: function(segment) {
return !!(
!segment.deleted_at && segment.type === 'default'
);
}
}; };
return ( return (
@ -153,7 +163,12 @@ const bulk_actions = [
onSelect: function() { onSelect: function() {
let field = { let field = {
id: 'remove_from_segment', id: 'remove_from_segment',
endpoint: 'segments' endpoint: 'segments',
filter: function(segment) {
return !!(
segment.type === 'default'
);
}
}; };
return ( return (
@ -200,6 +215,21 @@ const bulk_actions = [
} }
]; ];
const item_actions = [
{
name: 'edit',
label: 'Edit',
link: function(item) {
return (
<Link to={ `/edit/${item.id}` }>Edit</Link>
);
}
},
{
name: 'trash'
}
];
const SubscriberList = React.createClass({ const SubscriberList = React.createClass({
renderItem: function(subscriber, actions) { renderItem: function(subscriber, actions) {
let row_classes = classNames( let row_classes = classNames(
@ -270,11 +300,16 @@ const SubscriberList = React.createClass({
</div> </div>
); );
}, },
onGetItems: function(count) {
jQuery('#mailpoet_export_button')[(count > 0) ? 'show' : 'hide']();
},
render: function() { render: function() {
return ( return (
<div> <div>
<h2 className="title"> <h2 className="title">
Subscribers <Link className="add-new-h2" to="/new">New</Link> Subscribers <Link className="add-new-h2" to="/new">New</Link>
<a className="add-new-h2" href="?page=mailpoet-import#step1">Import</a>
<a id="mailpoet_export_button" className="add-new-h2" href="?page=mailpoet-export">Export</a>
</h2> </h2>
<Listing <Listing
@ -284,7 +319,9 @@ const SubscriberList = React.createClass({
onRenderItem={ this.renderItem } onRenderItem={ this.renderItem }
columns={ columns } columns={ columns }
bulk_actions={ bulk_actions } bulk_actions={ bulk_actions }
item_actions={ item_actions }
messages={ messages } messages={ messages }
onGetItems={ this.onGetItems }
/> />
</div> </div>
) )

View File

@ -0,0 +1,264 @@
// Generated by CoffeeScript 1.9.2
/**
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
*/
(function() {
var $, win;
$ = this.jQuery || window.jQuery;
win = $(window);
$.fn.stick_in_parent = function(opts) {
var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
if (opts == null) {
opts = {};
}
sticky_class = opts.sticky_class, inner_scrolling = opts.inner_scrolling, recalc_every = opts.recalc_every, parent_selector = opts.parent, offset_top = opts.offset_top, manual_spacer = opts.spacer, enable_bottoming = opts.bottoming;
if (offset_top == null) {
offset_top = 0;
}
if (parent_selector == null) {
parent_selector = void 0;
}
if (inner_scrolling == null) {
inner_scrolling = true;
}
if (sticky_class == null) {
sticky_class = "is_stuck";
}
doc = $(document);
if (enable_bottoming == null) {
enable_bottoming = true;
}
outer_width = function(el) {
var _el, computed, w;
if (window.getComputedStyle) {
_el = el[0];
computed = window.getComputedStyle(el[0]);
w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
if (computed.getPropertyValue("box-sizing") !== "border-box") {
w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
}
return w;
} else {
return el.outerWidth(true);
}
};
fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
if (elm.data("sticky_kit")) {
return;
}
elm.data("sticky_kit", true);
last_scroll_height = doc.height();
parent = elm.parent();
if (parent_selector != null) {
parent = parent.closest(parent_selector);
}
if (!parent.length) {
throw "failed to find stick parent";
}
fixed = false;
bottomed = false;
spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
if (spacer) {
spacer.css('position', elm.css('position'));
}
recalc = function() {
var border_top, padding_top, restore;
if (detached) {
return;
}
last_scroll_height = doc.height();
border_top = parseInt(parent.css("border-top-width"), 10);
padding_top = parseInt(parent.css("padding-top"), 10);
padding_bottom = parseInt(parent.css("padding-bottom"), 10);
parent_top = parent.offset().top + border_top + padding_top;
parent_height = parent.height();
if (fixed) {
fixed = false;
bottomed = false;
if (manual_spacer == null) {
elm.insertAfter(spacer);
spacer.detach();
}
elm.css({
position: "",
top: "",
width: "",
bottom: ""
}).removeClass(sticky_class);
restore = true;
}
top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
height = elm.outerHeight(true);
el_float = elm.css("float");
if (spacer) {
spacer.css({
width: outer_width(elm),
height: height,
display: elm.css("display"),
"vertical-align": elm.css("vertical-align"),
"float": el_float
});
}
if (restore) {
return tick();
}
};
recalc();
last_pos = void 0;
offset = offset_top;
recalc_counter = recalc_every;
tick = function() {
var css, delta, recalced, scroll, will_bottom, win_height;
if (detached) {
return;
}
recalced = false;
if (recalc_counter != null) {
recalc_counter -= 1;
if (recalc_counter <= 0) {
recalc_counter = recalc_every;
recalc();
recalced = true;
}
}
if (!recalced && doc.height() !== last_scroll_height) {
recalc();
recalced = true;
}
scroll = win.scrollTop();
if (last_pos != null) {
delta = scroll - last_pos;
}
last_pos = scroll;
if (fixed) {
if (enable_bottoming) {
will_bottom = scroll + height + offset > parent_height + parent_top;
if (bottomed && !will_bottom) {
bottomed = false;
elm.css({
position: "fixed",
bottom: "",
top: offset
}).trigger("sticky_kit:unbottom");
}
}
if (scroll < top) {
fixed = false;
offset = offset_top;
if (manual_spacer == null) {
if (el_float === "left" || el_float === "right") {
elm.insertAfter(spacer);
}
spacer.detach();
}
css = {
position: "",
width: "",
top: ""
};
elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
}
if (inner_scrolling) {
win_height = win.height();
if (height + offset_top > win_height) {
if (!bottomed) {
offset -= delta;
offset = Math.max(win_height - height, offset);
offset = Math.min(offset_top, offset);
if (fixed) {
elm.css({
top: offset + "px"
});
}
}
}
}
} else {
if (scroll > top) {
fixed = true;
css = {
position: "fixed",
top: offset
};
css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
elm.css(css).addClass(sticky_class);
if (manual_spacer == null) {
elm.after(spacer);
if (el_float === "left" || el_float === "right") {
spacer.append(elm);
}
}
elm.trigger("sticky_kit:stick");
}
}
if (fixed && enable_bottoming) {
if (will_bottom == null) {
will_bottom = scroll + height + offset > parent_height + parent_top;
}
if (!bottomed && will_bottom) {
bottomed = true;
if (parent.css("position") === "static") {
parent.css({
position: "relative"
});
}
return elm.css({
position: "absolute",
bottom: padding_bottom,
top: "auto"
}).trigger("sticky_kit:bottom");
}
}
};
recalc_and_tick = function() {
recalc();
return tick();
};
detach = function() {
detached = true;
win.off("touchmove", tick);
win.off("scroll", tick);
win.off("resize", recalc_and_tick);
$(document.body).off("sticky_kit:recalc", recalc_and_tick);
elm.off("sticky_kit:detach", detach);
elm.removeData("sticky_kit");
elm.css({
position: "",
bottom: "",
top: "",
width: ""
});
parent.position("position", "");
if (fixed) {
if (manual_spacer == null) {
if (el_float === "left" || el_float === "right") {
elm.insertAfter(spacer);
}
spacer.remove();
}
return elm.removeClass(sticky_class);
}
};
win.on("touchmove", tick);
win.on("scroll", tick);
win.on("resize", recalc_and_tick);
$(document.body).on("sticky_kit:recalc", recalc_and_tick);
elm.on("sticky_kit:detach", detach);
return setTimeout(tick, 0);
};
for (i = 0, len = this.length; i < len; i++) {
elm = this[i];
fn($(elm));
}
return this;
};
}).call(this);

42
build
View File

@ -1,42 +0,0 @@
#!/bin/sh
# Remove previous build.
rm wysija-newsletters.zip;
# Create temp dir.
mkdir wysija-newsletters;
# Production assets.
./do compile:all;
# Production libraries.
rm -rf vendor;
rm composer.lock;
./composer.phar install --no-dev;
# Copy release folders.
cp -rf lang wysija-newsletters;
cp -rfL assets wysija-newsletters;
cp -rf lib wysija-newsletters;
cp -rf vendor wysija-newsletters;
cp -rf views wysija-newsletters;
rm -rf wysija-newsletters/assets/css/src;
rm -rf wysija-newsletters/assets/js/src;
# Copy release files.
cp LICENSE wysija-newsletters;
cp index.php wysija-newsletters;
cp mailpoet.php wysija-newsletters;
cp readme.txt wysija-newsletters;
cp uninstall.php wysija-newsletters;
# Zip final release.
zip -r wysija-newsletters.zip wysija-newsletters;
# Remove temp dir.
rm -rf wysija-newsletters;
# Reinstall dev dependencies.
rm composer.lock;
./composer.phar install;
./do install;

42
build.sh Executable file
View File

@ -0,0 +1,42 @@
#!/bin/sh
plugin_name='mailpoet'
# Remove previous build.
rm $plugin_name.zip
# Create temp dir.
mkdir $plugin_name
# Production assets.
rm -rf node_modules
npm install
./do compile:all
# Production libraries.
./composer.phar install --no-dev
# Copy release folders.
cp -Rf lang $plugin_name
cp -RfL assets $plugin_name
cp -Rf lib $plugin_name
cp -Rf vendor $plugin_name
cp -Rf views $plugin_name
rm -Rf $plugin_name/assets/css/src
rm -Rf $plugin_name/assets/js/src
# Copy release files.
cp LICENSE $plugin_name
cp index.php $plugin_name
cp $plugin_name.php $plugin_name
cp readme.txt $plugin_name
cp uninstall.php $plugin_name
# Zip final release.
zip -r $plugin_name.zip $plugin_name
# Remove temp dir.
rm -rf $plugin_name
# Reinstall dev dependencies.
./composer.phar install

View File

@ -19,3 +19,12 @@ modules:
user: '' user: ''
password: '' password: ''
dump: tests/_data/dump.sql dump: tests/_data/dump.sql
coverage:
enabled: true
whitelist:
include:
- lib/*
exclude:
blacklist:
include:
exclude:

View File

@ -8,7 +8,10 @@
"tburry/pquery": "*", "tburry/pquery": "*",
"j4mie/paris": "1.5.4", "j4mie/paris": "1.5.4",
"swiftmailer/swiftmailer": "^5.4", "swiftmailer/swiftmailer": "^5.4",
"phpseclib/phpseclib": "*" "phpseclib/phpseclib": "*",
"mtdowling/cron-expression": "^1.0",
"nesbot/carbon": "^1.21",
"soundasleep/html2text": "^0.3.0"
}, },
"require-dev": { "require-dev": {
"codeception/codeception": "*", "codeception/codeception": "*",

954
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,25 @@
<?php
namespace MailPoet\Analytics;
class Reporter {
private $fields = array(
'Plugin Version' => 'pluginVersion',
);
function __construct() {}
function getData() {
$_this = $this;
$analytics_data = array_map(function($func) use ($_this) {
return $_this->$func();
}, $this->fields);
return $analytics_data;
}
private function pluginVersion() {
return MAILPOET_VERSION;
}
}

View File

@ -9,13 +9,6 @@ class Activator {
function __construct() { function __construct() {
} }
function init() {
register_activation_hook(
Env::$file,
array($this, 'activate')
);
}
function activate() { function activate() {
$migrator = new Migrator(); $migrator = new Migrator();
$migrator->up(); $migrator->up();
@ -23,4 +16,9 @@ class Activator {
$populator = new Populator(); $populator = new Populator();
$populator->up(); $populator->up();
} }
function deactivate() {
$migrator = new Migrator();
$migrator->down();
}
} }

33
lib/Config/Analytics.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace MailPoet\Config;
use \MailPoet\Analytics\Reporter;
use \MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class Analytics {
function __construct() {
}
function init() {
// review: this creates a fatal error when mailpoet tables are dropped.
//add_action('admin_enqueue_scripts', array($this, 'setupAdminDependencies'));
}
function setupAdminDependencies() {
if(Setting::getValue('send_analytics_now', false)) {
$analytics = new Reporter();
wp_enqueue_script(
'analytics',
Env::$assets_url . '/js/lib/analytics.js',
array(),
Env::$version
);
wp_localize_script(
'analytics',
'mailpoet_analytics_data',
$analytics->getData()
);
}
}
}

61
lib/Config/Changelog.php Normal file
View File

@ -0,0 +1,61 @@
<?php
namespace MailPoet\Config;
use \MailPoet\Models\Setting;
class Changelog {
function init() {
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);
// don't run any check when it's an ajax request
if($doing_ajax) {
return;
}
// don't run any check when we're not on our pages
if(
!(isset($_GET['page']))
or
(isset($_GET['page']) && strpos($_GET['page'], 'mailpoet') !== 0)
) {
return;
}
add_action(
'admin_init',
array($this, 'check')
);
}
function check() {
$version = Setting::getValue('version', null);
$redirect_url = null;
if($version === null) {
// new install
$redirect_url = admin_url('admin.php?page=mailpoet-welcome');
} else if($version !== Env::$version) {
// update
$redirect_url = admin_url('admin.php?page=mailpoet-update');
}
if($redirect_url !== null) {
// save version number
Setting::setValue('version', Env::$version);
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
if($redirect_url !== $current_url) {
wp_safe_redirect(
add_query_arg(
array(
'mailpoet_redirect' => urlencode($current_url)
),
$redirect_url
)
);
exit;
}
}
}
}

View File

@ -4,53 +4,73 @@ namespace MailPoet\Config;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
class Env { class Env {
public static $version; static $version;
public static $plugin_name; static $plugin_name;
public static $file; static $plugin_path;
public static $path; static $file;
public static $views_path; static $path;
public static $assets_path; static $views_path;
public static $assets_url; static $assets_path;
public static $languages_path; static $assets_url;
public static $lib_path; static $temp_path;
public static $plugin_prefix; static $temp_URL;
public static $db_prefix; static $languages_path;
public static $db_source_name; static $lib_path;
public static $db_host; static $plugin_prefix;
public static $db_name; static $db_prefix;
public static $db_username; static $db_source_name;
public static $db_password; static $db_host;
public static $db_charset; static $db_socket;
static $db_port;
static $db_name;
static $db_username;
static $db_password;
static $db_charset;
public static function init($file, $version) { static function init($file, $version) {
global $wpdb; global $wpdb;
self::$version = $version; self::$version = $version;
self::$plugin_name = 'mailpoet';
self::$file = $file; self::$file = $file;
self::$path = dirname(self::$file); self::$path = dirname(self::$file);
self::$plugin_name = 'mailpoet';
self::$views_path = self::$path . '/views'; self::$views_path = self::$path . '/views';
self::$assets_path = self::$path . '/assets'; self::$assets_path = self::$path . '/assets';
self::$assets_url = plugins_url('/assets', $file); self::$assets_url = plugins_url('/assets', $file);
$wp_upload_dir = wp_upload_dir();
self::$temp_path = $wp_upload_dir['path'];
self::$temp_URL = $wp_upload_dir['url'];
self::$languages_path = self::$path . '/lang'; self::$languages_path = self::$path . '/lang';
self::$lib_path = self::$path . '/lib'; self::$lib_path = self::$path . '/lib';
self::$plugin_prefix = 'mailpoet_'; self::$plugin_prefix = 'mailpoet_';
self::$db_prefix = $wpdb->prefix . self::$plugin_prefix; self::$db_prefix = $wpdb->prefix . self::$plugin_prefix;
self::$db_source_name = self::dbSourceName();
self::$db_host = DB_HOST; self::$db_host = DB_HOST;
self::$db_port = 3306;
self::$db_socket = false;
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
list(self::$db_host, self::$db_port) = explode(':', DB_HOST);
} else {
if(preg_match('/:/', DB_HOST)) {
self::$db_socket = true;
}
}
self::$db_name = DB_NAME; self::$db_name = DB_NAME;
self::$db_username = DB_USER; self::$db_username = DB_USER;
self::$db_password = DB_PASSWORD; self::$db_password = DB_PASSWORD;
self::$db_charset = $wpdb->get_charset_collate(); self::$db_charset = $wpdb->get_charset_collate();
self::$db_source_name = self::dbSourceName(self::$db_host, self::$db_socket, self::$db_port);
} }
private static function dbSourceName() { private static function dbSourceName($host, $socket, $port) {
$source_name = array( $source_name = array(
'mysql:host=', (!$socket) ? 'mysql:host=' : 'mysql:unix_socket=',
DB_HOST, $host,
';',
'port=',
$port,
';', ';',
'dbname=', 'dbname=',
DB_NAME DB_NAME
); );
return implode('', $source_name); return implode('', $source_name);
} }
} }

113
lib/Config/Hooks.php Normal file
View File

@ -0,0 +1,113 @@
<?php
namespace MailPoet\Config;
use \MailPoet\Models\Setting;
class Hooks {
function __construct() {
}
function init() {
// Subscribe in comments
if((bool)Setting::getValue('subscribe.on_comment.enabled')) {
if(is_user_logged_in()) {
add_action(
'comment_form_field_comment',
'\MailPoet\Subscription\Comment::extendLoggedInForm'
);
} else {
add_action(
'comment_form_after_fields',
'\MailPoet\Subscription\Comment::extendLoggedOutForm'
);
}
add_action(
'comment_post',
'\MailPoet\Subscription\Comment::onSubmit',
60,
2
);
add_action(
'wp_set_comment_status',
'\MailPoet\Subscription\Comment::onStatusUpdate',
60,
2
);
}
// Subscribe in registration form
if((bool)Setting::getValue('subscribe.on_register.enabled')) {
if(is_multisite()) {
add_action(
'signup_extra_fields',
'\MailPoet\Subscription\Registration::extendForm'
);
add_action(
'wpmu_validate_user_signup',
'\MailPoet\Subscription\Registration::onMultiSiteRegister',
60,
1
);
} else {
add_action(
'register_form',
'\MailPoet\Subscription\Registration::extendForm'
);
add_action(
'register_post',
'\MailPoet\Subscription\Registration::onRegister',
60,
3
);
}
}
// WP Users synchronization
add_action(
'user_register',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'added_existing_user',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'profile_update',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'delete_user',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
// multisite
add_action(
'deleted_user',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'remove_user_from_blog',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_filter(
'image_size_names_choose',
array(
$this,
'appendImageSizes'
)
);
}
function appendImageSizes($sizes) {
return array_merge($sizes, array(
'mailpoet_newsletter_max' => __('MailPoet Newsletter'),
));
}
}

View File

@ -2,10 +2,15 @@
namespace MailPoet\Config; namespace MailPoet\Config;
use MailPoet\Models; use MailPoet\Models;
use MailPoet\Cron\Supervisor;
use MailPoet\Router; use MailPoet\Router;
use MailPoet\Models\Setting;
use MailPoet\Settings\Pages;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
class Initializer { class Initializer {
function __construct($params = array( function __construct($params = array(
'file' => '', 'file' => '',
@ -16,13 +21,34 @@ class Initializer {
function init() { function init() {
$this->setupDB(); $this->setupDB();
$this->setupActivator();
$this->setupRenderer(); register_activation_hook(Env::$file, array($this, 'runMigrator'));
$this->setupLocalizer(); register_activation_hook(Env::$file, array($this, 'runPopulator'));
$this->setupMenu();
$this->setupRouter(); add_action('plugins_loaded', array($this, 'setup'));
$this->setupWidget(); add_action('widgets_init', array($this, 'setupWidget'));
$this->setupPermissions(); }
function setup() {
try {
$this->setupRenderer();
$this->setupLocalizer();
$this->setupMenu();
$this->setupRouter();
$this->setupPermissions();
$this->setupPublicAPI();
$this->setupAnalytics();
$this->setupChangelog();
$this->runQueueSupervisor();
$this->setupShortcodes();
$this->setupHooks();
$this->setupPages();
$this->setupImages();
} catch(\Exception $e) {
// if anything goes wrong during init
// automatically deactivate the plugin
deactivate_plugins(Env::$file);
}
} }
function setupDB() { function setupDB() {
@ -31,7 +57,8 @@ class Initializer {
\ORM::configure('password', Env::$db_password); \ORM::configure('password', Env::$db_password);
\ORM::configure('logging', WP_DEBUG); \ORM::configure('logging', WP_DEBUG);
\ORM::configure('driver_options', array( \ORM::configure('driver_options', array(
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET TIME_ZONE = "+00:00"'
)); ));
$subscribers = Env::$db_prefix . 'subscribers'; $subscribers = Env::$db_prefix . 'subscribers';
@ -46,6 +73,8 @@ class Initializer {
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field'; $subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields'; $newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
$newsletter_option = Env::$db_prefix . 'newsletter_option'; $newsletter_option = Env::$db_prefix . 'newsletter_option';
$sending_queues = Env::$db_prefix . 'sending_queues';
$newsletter_statistics = Env::$db_prefix . 'newsletter_statistics';
define('MP_SUBSCRIBERS_TABLE', $subscribers); define('MP_SUBSCRIBERS_TABLE', $subscribers);
define('MP_SETTINGS_TABLE', $settings); define('MP_SETTINGS_TABLE', $settings);
@ -59,11 +88,18 @@ class Initializer {
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field); define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields); define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option); define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
define('MP_SENDING_QUEUES_TABLE', $sending_queues);
define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics);
} }
function setupActivator() { function runMigrator() {
$activator = new Activator(); $migrator = new Migrator();
$activator->init(); $migrator->up();
}
function runPopulator() {
$populator = new Populator();
$populator->up();
} }
function setupRenderer() { function setupRenderer() {
@ -77,10 +113,7 @@ class Initializer {
} }
function setupMenu() { function setupMenu() {
$menu = new Menu( $menu = new Menu($this->renderer, Env::$assets_url);
$this->renderer,
Env::$assets_url
);
$menu->init(); $menu->init();
} }
@ -94,8 +127,51 @@ class Initializer {
$widget->init(); $widget->init();
} }
function setupAnalytics() {
$widget = new Analytics();
$widget->init();
}
function setupPermissions() { function setupPermissions() {
$permissions = new Permissions(); $permissions = new Permissions();
$permissions->init(); $permissions->init();
} }
}
function setupChangelog() {
$changelog = new Changelog();
$changelog->init();
}
function setupPages() {
$pages = new Pages();
$pages->init();
}
function setupShortcodes() {
$shortcodes = new Shortcodes();
$shortcodes->init();
}
function setupHooks() {
$hooks = new Hooks();
$hooks->init();
}
function setupPublicAPI() {
$publicAPI = new PublicAPI();
$publicAPI->init();
}
function runQueueSupervisor() {
if(php_sapi_name() === 'cli') return;
try {
$supervisor = new Supervisor();
$supervisor->checkDaemon();
} catch(\Exception $e) {
}
}
function setupImages() {
add_image_size('mailpoet_newsletter_max', 1320);
}
}

Some files were not shown because too many files have changed in this diff Show More