Compare commits

...

751 Commits

Author SHA1 Message Date
e4213437e9 Bump up release version to 0.0.35 2016-07-08 14:57:30 +03:00
386bdceed3 Merge pull request #543 from mailpoet/api_refactor
API refactor
2016-07-08 13:55:01 +03:00
87eda71931 Merge pull request #544 from mailpoet/issue_434
Settings Page
2016-07-08 13:28:06 +03:00
cef9f1dcf8 - Updates constant names 2016-07-07 14:32:05 -04:00
c78b2088eb - Updates the check for invalid API endpoint 2016-07-07 14:20:27 -04:00
150364de3a - Fixes API endpoint naming convention
- Generates/saves cron daemon token as soon as its executed
2016-07-07 14:00:07 -04:00
1cd9f3eb67 - Removes counter from cron daemon
- Invokes token regeneration/comparion at a later stage
2016-07-07 10:24:24 -04:00
89253125af - Fixes a typo 2016-07-07 10:00:12 -04:00
e7ee356f90 cleanup permissions related classes 2016-07-07 15:49:03 +02:00
a88017400b Remove email validation as it was not working properly
- using the MailPoet sending method forces signup confirmation
- save settings when activating a sending method (works when pressing enter in an input when setting up method)
2016-07-07 15:36:59 +02:00
f557881462 - Updates code based on review comments 2016-07-07 09:01:59 -04:00
8dba4727c4 - Updates open/click link generation logic to utilize API's buildRequest
method
2016-07-06 22:57:39 -04:00
8ec094089f - Removes TODO notice 2016-07-06 20:18:02 -04:00
3c353e715b - Fixes view in browser API URL 2016-07-06 19:56:26 -04:00
ab33a9c352 - Updates cron API URL
- Removes cron daemon counter
- Generates/saves cron daemon token as soon as its executed
2016-07-06 19:48:16 -04:00
406b509ac4 Remove saving of roles & permissions when saving settings
- make sure we try to activate the sending method instead of saving settings when pressing enter in an input
- added Default sender row with global from/reply_to
- hide notification emails setting
- removed notification from/reply to email (for the time being, we will reintroduce it if need be later on)
2016-07-06 17:29:24 +02:00
bd814baf28 - Fixes data not being passed to API buildRequest method 2016-07-06 10:02:47 -04:00
2db681d908 - Adds and centralizes API data encoding/decoding method 2016-07-06 09:22:34 -04:00
37e3af584e added parsley validation on settings form - need to fix permissions 2016-07-06 14:12:30 +02:00
5fc863bd82 Settings page update (issue #434) 2016-07-06 14:10:46 +02:00
76649f9590 removed debug mode and roles and permissions from advanced tab 2016-07-06 13:56:11 +02:00
cb2faec8b2 - Refactors API
- Updates existing classes to use the refactored API methods
2016-07-05 20:17:25 -04:00
e8604284fe Merge pull request #540 from mailpoet/issue_431
Bulk actions messages + remaining UI items from issue 431
2016-07-05 17:59:15 +03:00
d2ccdef6c7 better alternative to remove duplicate MailPoet submenu 2016-07-05 16:55:03 +02:00
38199dc96f - Adds validation for API data 2016-07-05 10:20:30 -04:00
630b219e96 Merge pull request #539 from mailpoet/twig_caching
Fix Twig cache regeneration
2016-07-05 09:57:02 -04:00
64155bc121 Merge pull request #541 from mailpoet/populator_settings
Do not reset plugin settings on plugin reactivation
2016-07-05 14:42:28 +02:00
7fb45a15ee Fix code style errors 2016-07-05 15:28:38 +03:00
ed5294477f Fix Populator to not overwrite existing settings 2016-07-05 15:23:11 +03:00
d152b073a6 fixed onSuccess on bulk actions and locale formatted numbers in success messages 2016-07-05 13:58:12 +02:00
f8efb3934b remove 'MailPoet' submenu and make newsletters the default page 2016-07-05 13:16:14 +02:00
5a21d3fdc8 added missing 'row-title' class on listings 2016-07-05 11:44:49 +02:00
710ede15ce sending method daily emails frequency in locale 2016-07-05 10:49:12 +02:00
150286ab6b Enable regenerating templates that have changed 2016-07-04 18:05:50 +03:00
9e758e8a33 Bump up release version to 0.0.34 2016-07-01 16:57:48 +03:00
059165e5d2 Merge pull request #536 from mailpoet/manage_subscriptions
Manage subscriptions
2016-07-01 16:17:50 +03:00
fe154d9251 fixed code sniffer reported errors 2016-07-01 14:17:39 +02:00
a8ffbc2d0e handle empty/unchecked/checked checkboxes properly in both react and forms 2016-07-01 14:14:18 +02:00
9de3a245b0 fixed both radio & checkbox fields so that it selects the proper value 2016-07-01 14:14:18 +02:00
5eef709af5 Uniform date display format for Manage Subscriptions & Subscriber new/edit
- use isWPUser instead of wp_user !== null
2016-07-01 14:14:18 +02:00
7b0c130d0a updated unit test for custom fields of date type 2016-07-01 14:14:18 +02:00
7f265675b0 changed the way custom field date type is handled (react + form + db) 2016-07-01 14:14:18 +02:00
ba15db9829 fixed value loading for textarea 2016-07-01 14:14:18 +02:00
d15473a8e4 disabled first/last name inputs for WP User on manage subscription page 2016-07-01 14:14:18 +02:00
d9f93dc6e7 Merge pull request #537 from mailpoet/qa
QA Tools and improvements
2016-06-30 18:17:05 +02:00
634c5b699d Remove leftover merge conflict, fix empty ALC block message #505 2016-06-30 19:12:26 +03:00
23e8ce38dd Merge remote-tracking branch 'origin/qa' into qa
Conflicts:
	lib/Config/Initializer.php
	lib/Cron/Workers/SendingQueue/SendingQueue.php
	lib/Models/SendingQueue.php
	lib/Router/Router.php
2016-06-30 19:01:44 +03:00
c62ae2ce80 Add PHP CodeSniffer option to reduce severity, fixed syntax error 2016-06-30 18:52:07 +03:00
d0813bb4e2 Fix class and method names to use camel case 2016-06-30 18:52:07 +03:00
0ac701eb20 Change line endings from DOS CRLF to Unix LF 2016-06-30 18:52:07 +03:00
607395be6f Fix spacing around commas 2016-06-30 18:52:07 +03:00
55d48df8a4 Fix indentation issues 2016-06-30 18:50:48 +03:00
e0282ae45b Fix empty catch statement error 2016-06-30 18:50:48 +03:00
235fdea00f Remove commented out code, raise code similarity trigger treshold 2016-06-30 18:50:48 +03:00
b8c6d54f48 Fix "Closing brace must be on a line by itself" code sniffer errors 2016-06-30 18:50:48 +03:00
67661e3aad Remove useless constructors 2016-06-30 18:50:48 +03:00
c03facdc45 Add space after comma in function call parameters 2016-06-30 18:49:50 +03:00
9ddc1ef555 Remove statements that cannot be executed 2016-06-30 18:49:50 +03:00
9cfc2fd940 Remove an unnecessary return statement 2016-06-30 18:49:50 +03:00
24e108bce7 Remove spaces after type casts 2016-06-30 18:49:50 +03:00
48f0c03425 Fix spacing between control structure and opening parenthesis 2016-06-30 18:46:33 +03:00
0bfbe6dc79 Change TRUE, FALSE, NULL capitalization to lowercase 2016-06-30 18:46:33 +03:00
ad0a9838bc Disable line width limits 2016-06-30 18:46:33 +03:00
81ec293e54 Remove rule that prevents statements with only comments in body 2016-06-30 18:46:32 +03:00
b8b3d76a1d Remove PHPMD and PHPCPD tools we don't use 2016-06-30 18:46:32 +03:00
805e641d40 Add PHP lint and PHP code sniffer 2016-06-30 18:46:32 +03:00
18326f9df1 Merge pull request #535 from mailpoet/sending_queue_refactor
Fixes error that resulted in additional newsletter to be sent
2016-06-30 16:39:45 +02:00
46dda84012 - Moves queue subscriber handling logic to the queu model 2016-06-30 10:23:06 -04:00
9979261cb6 fixed a few more warnings 2016-06-30 15:42:58 +02:00
e8887e2aa5 Add PHP CodeSniffer option to reduce severity, fixed syntax error 2016-06-30 15:24:50 +03:00
4a91fae984 Fix class and method names to use camel case 2016-06-30 15:13:48 +03:00
0fe975f614 - Declares array 2016-06-30 07:52:16 -04:00
c7fd7b8a32 Change line endings from DOS CRLF to Unix LF 2016-06-30 14:39:28 +03:00
b7e3c3ae81 Fix spacing around commas 2016-06-30 14:03:07 +03:00
b2c3206185 - Fixes error that resulted in additional newsletter to be sent 2016-06-30 06:42:56 -04:00
b7d8d482fe Fix indentation issues 2016-06-30 13:29:23 +03:00
8a9d14319b Fix empty catch statement error 2016-06-30 12:40:22 +03:00
c396254e64 Remove commented out code, raise code similarity trigger treshold 2016-06-29 21:19:24 +03:00
e4dbeca664 Fix "Closing brace must be on a line by itself" code sniffer errors 2016-06-29 20:48:14 +03:00
168540d6d2 Remove useless constructors 2016-06-29 20:42:03 +03:00
c62cd6c023 Add space after comma in function call parameters 2016-06-29 19:26:07 +03:00
033e0581f1 Remove statements that cannot be executed 2016-06-29 19:20:50 +03:00
9f978d3362 Remove an unnecessary return statement 2016-06-29 19:09:07 +03:00
841340a42d Remove spaces after type casts 2016-06-29 19:04:23 +03:00
9595e9629f Fix spacing between control structure and opening parenthesis 2016-06-29 18:54:01 +03:00
56ba543f8d Change TRUE, FALSE, NULL capitalization to lowercase 2016-06-29 18:38:38 +03:00
1cead6c6cd Disable line width limits 2016-06-29 17:57:38 +03:00
f5f7ce4c42 Remove rule that prevents statements with only comments in body 2016-06-29 17:15:00 +03:00
b13075b8f2 Remove PHPMD and PHPCPD tools we don't use 2016-06-29 16:49:22 +03:00
c4db9e3227 Add PHP lint and PHP code sniffer 2016-06-29 16:19:50 +03:00
f47bfb5439 Merge pull request #532 from mailpoet/newsletter_creation
Newsletter creation: Step 1 and 3 changes
2016-06-28 15:25:14 +02:00
cc4639cb23 Set default values when immediately sending scheduled newsletter 2016-06-28 15:57:02 +03:00
69094f57fd Fix typos 2016-06-28 15:01:31 +03:00
ffe7b80888 Simplify variable declarations 2016-06-28 14:12:09 +03:00
fc846b808e Remove obsolete debugging statement 2016-06-28 14:12:09 +03:00
1cbf6b67b2 Remove console.log statements 2016-06-28 14:12:09 +03:00
286c02bdd9 Fix standard newsletter scheduling to always include scheduleAt 2016-06-28 14:12:08 +03:00
5f1d76225b - Add sorting of segment names in Welcome newsletter segment selector;
- Add an option to FormFieldSelect to allow sorting options;
- Change "Send" button label for scheduled newsletters;
- Disable "Send" button for sending/already sent newsletters.
2016-06-28 14:12:08 +03:00
c05ea1b968 Change "Go back to editor" to save form fields first 2016-06-28 14:12:08 +03:00
2d45ab2e88 Add WP user segment selection to Notification and Standard newsletters 2016-06-28 14:12:08 +03:00
ca9b1e25a7 Change notification newsletter time to be displayed in WP format 2016-06-28 14:12:08 +03:00
2927875e16 Regenerate thumbnails of default newsletter templates 2016-06-28 14:12:07 +03:00
486a97fa30 Vertically center template thumbs and don't enforce min-height for them 2016-06-28 14:12:07 +03:00
c22d434dff Merge pull request #531 from mailpoet/unit_test_catchup
Unit test update
2016-06-28 12:22:50 +03:00
306cdeb68f Models unit tests update 2016-06-27 13:53:56 +02:00
7ee83dad06 Merge pull request #527 from mailpoet/sending_queue_refactor
Sending queue refactor
2016-06-23 18:16:21 +03:00
d414313749 - Fixes const definition for PHP 5.5 2016-06-22 13:35:48 -04:00
66d329f630 - Configures mailer inside the mailer task class 2016-06-22 11:21:11 -04:00
f524ffcb28 - Updates mailer task to store mailer instance 2016-06-22 11:15:40 -04:00
264b7e180b listing handler and bulk actions tests completed 2016-06-22 13:47:54 +02:00
88dc7f4199 removing DKIM and useless classes 2016-06-22 13:47:54 +02:00
9652f75028 Merge pull request #530 from mailpoet/fix_safari_es6_bug
removed ES6 syntax from non converted JS file - fixes #529 (Safari bug)
2016-06-22 14:29:57 +03:00
36c32db2d1 Merge pull request #528 from mailpoet/listing_sorting
Listing sorting + bugfixes
2016-06-22 14:24:05 +03:00
fd12bd557e fixed 'Setup' link in homepage 2016-06-22 12:21:26 +02:00
7bd8ed4639 Use promises for handleBulkAction
- fixed filters not being updated when going back/forward
- improved redirection to "all" group after emptying the trash (former way became buggy)
- fixed error thrown by "onGetItems" -> this logic has to go at some point
- Newsletters listing are sorted by "updated_at" desc
- Subscribers are sorted by "created_at" desc (Subscribed on)
2016-06-21 22:36:13 +02:00
ca0e511efd removed ES6 syntax from non converted JS file - fixes #529 (Safari bug) 2016-06-21 16:47:25 +02:00
e5f3fabcda - Moves mailer logic into Mailer Task class 2016-06-21 10:14:19 -04:00
efc9bac760 - Updates unit tests 2016-06-20 23:36:30 -04:00
ce6327c3d5 - Re-adds the old multidimensional array flatten method 2016-06-20 23:35:47 -04:00
f32d6bb331 - Joins bulk and individual processing into one method
- Refactors code as per code review comments
2016-06-20 23:12:32 -04:00
e807aad814 - Updates array flatten function for multidimensional arrays
- Removes custom array unique method for multidimensional arrays
2016-06-20 11:50:54 -04:00
b87754ca30 Listing setParam only needs to be run when url history is specified
- added missing code to deleteManySubscriptions() so that it doesn't remove from all segments
2016-06-20 17:28:19 +02:00
22dfb372ec - Updates bulk insert logic 2016-06-20 10:34:41 -04:00
674bbd728e updated Subscriber unit test to use model constants - no fix here 2016-06-20 16:30:34 +02:00
68c09b8678 Sorting for all listings & bugfixes for all listings except Newsletters
- newsletters listing now uses hash history
- newsletters are sorted by Subject (a->z)
- segments are sorted by Name (a->z)
- re-added WordPress Users list as a segment you can send a newsletter to
- added explicit error messages when an auto newsletter isn't fully configured
- added missing strings for "selectAll" in Segments listing
- fixed filters() in Subscribers listing (wrong count as it was not taking groups/filters/search into account)
2016-06-20 16:23:27 +02:00
c83ab0886f - Rebases master 2016-06-19 22:10:18 -04:00
999a0b3ede - Refactors sending queue worker by breaking it into smaller tasks
- Adds arrayUnique method to Helpers for multidimensional arrays
2016-06-17 14:52:56 -04:00
6daecd6466 - Fixes URL extraction (undefined index notice)
- Updates link replacement in text body
- Updates links saving logic
2016-06-17 14:52:33 -04:00
7af2775972 Allowed ability to set default sort_by/order on listings
- improved performance of listings (less refresh of items)
- fixed sorting issue where the order would not be reversed
2016-06-17 17:27:40 +02:00
4bb1acf493 Bump up release version to 0.0.33 2016-06-17 17:14:19 +03:00
c8cd3d3eb5 Merge pull request #526 from mailpoet/copy_review
copy_review
2016-06-17 16:30:35 +03:00
2360c4d6e4 Fix periodicity strings 2016-06-17 16:28:40 +03:00
36e9168eef Escape quotes where needed 2016-06-17 16:06:03 +03:00
fb79d189d7 Edits June 17 2016 2016-06-17 15:09:25 +03:00
12330d6d34 Copy edits 6/15/2016 2016-06-17 15:09:25 +03:00
5efbcfd9c1 Update June 13 2016-06-17 15:09:24 +03:00
75240fc2e1 Merge pull request #508 from mailpoet/newsletters_listing
Newsletters multi-listing
2016-06-17 15:01:30 +03:00
b6fabcc739 removing some leftover trailing commas. 2016-06-17 13:16:20 +02:00
269ddae93a Refactored scheduling options for React (semi-converted to ES6 too)
- fixed issue with Pausing sending (missing self::)
2016-06-17 13:05:46 +02:00
90c3f0e4e4 only update status to Sent for Standard newsletters 2016-06-16 20:31:47 +02:00
dd8c54aae3 removed useless newsletters/list.jsx
- removed constant from Scheduler since it's defined on the SendingQueue model
2016-06-16 20:08:42 +02:00
aa3a46b941 Status update of newsletters completed
- duplicate newsletter now includes options as well
- fixed NaN issue in statistics when newsletter is being sent
- use constant for scheduled (and put it as the sendingQueue Model level)
2016-06-16 20:01:53 +02:00
744455f0df removed useless methods 2016-06-16 12:21:54 +02:00
9aa25446d1 fixed unit tests 2016-06-15 16:33:48 +02:00
6199caea29 - Notification settings column
- added "width" option to listing headers/columns
2016-06-15 16:33:48 +02:00
d6a68dd4d0 settings column done for welcome emails + WordPress capitalization fix 2016-06-15 16:33:48 +02:00
ee6e261c42 Conditional display of statistics column (for standard)
- improved duplicate action (for standard)
- moved STATUS_COMPLETED constant from worker to SendingQueue model where it belongs
2016-06-15 16:26:42 +02:00
cabfd8a946 better with the proper type 2016-06-15 16:26:42 +02:00
cf712636ed progress on notification type listing + NL model improvements 2016-06-15 16:26:42 +02:00
873c3d15a0 Fixed Setting::getValue issue where defaults were not returned for single keys
- updated static strings with constants
2016-06-15 16:26:42 +02:00
bc1bd3bad1 commenting on a react quirk 2016-06-15 16:26:42 +02:00
9f971632c9 update status in welcome listing 2016-06-15 16:26:42 +02:00
91bc0505ac Welcome emails progress 2016-06-15 16:26:42 +02:00
90c94624cc added preview link for standard newsletters 2016-06-15 16:26:42 +02:00
cd412894c6 Refactored filtering (groups / status / type)
- standard listing close to completion (missing item actions)
- enabled tracking by default on install
2016-06-15 16:26:42 +02:00
ecf15d53d9 Newsletters listing
- added stylesheet for newsletters listing
- added "status" database column on Newsletters for grouping in listings
- added duplicate link to standard newsletters
2016-06-15 16:15:02 +02:00
a593347336 call groups/filters only if Model has defined those methods 2016-06-15 16:15:02 +02:00
22566869cb tab system for newsletters listing 2016-06-15 16:14:06 +02:00
c959e7ec96 fixed total count and filtering + basic tab implementation in React 2016-06-15 16:14:06 +02:00
86a2846215 Tab system for listings 2016-06-15 16:14:06 +02:00
3b97a26a8a Newsletters multi-listing 2016-06-15 16:14:06 +02:00
dc6c973574 Merge pull request #523 from mailpoet/editor_ui
Email Editor: round 2 fixes
2016-06-14 15:26:22 +02:00
2a3a561464 updated shortcode (user -> subscriber) in FrankRoast template 2016-06-14 15:25:23 +02:00
e5f45fb7ad Merge pull request #524 from mailpoet/link_processing_fix
Prevents URLs in link titles from being processed when tracking is enabled
2016-06-14 15:21:18 +02:00
f22cadd319 - Declares hash length as constant
- Introduces check for nonexistent values/updates loop condition
2016-06-14 09:02:08 -04:00
5ea25ec697 Fix the way we query for WP subscriber in email preview 2016-06-14 15:43:13 +03:00
c0a250fc0f Turn sidebar/sidepanel text size into a variable 2016-06-14 15:26:00 +03:00
e69aa792c4 - Prevents URLs in link titles from being processed when tracking is enabled. Closes #519 2016-06-13 21:13:23 -04:00
3b4ac4d2d2 Change newsletter preview to use current user as subscriber 2016-06-13 15:02:20 +03:00
781973777e Vertically and horizontally center block deletion confirmation dialog 2016-06-13 14:13:47 +03:00
8698d2c6ba Change placeholder text of preheader input 2016-06-13 13:06:59 +03:00
47c15eca83 Change sidebar and sidepanel text font size to 13px 2016-06-13 13:06:59 +03:00
64f4bed080 Bump up release version to 0.0.32 2016-06-10 17:25:38 +03:00
fe47ba8a38 Merge pull request #522 from mailpoet/tests_fix
Fix PHP unit tests
2016-06-10 10:12:53 -04:00
eb02adc7ba Exclude lib/Util/Helpers.php class from unit test coverage calculations 2016-06-10 16:54:11 +03:00
f257b503e9 - Fix unit tests to account for translation changes;
- Exclude 3rd party utility libraries from coverage calculations;
2016-06-10 15:06:44 +03:00
bfdabe3554 Merge pull request #521 from mailpoet/copy_review
Copy review
2016-06-10 12:36:47 +03:00
77dd71935a Update - June 10 2016 2016-06-10 11:13:40 +02:00
4b418f041b Merge pull request #518 from mailpoet/alc_posts_ui
ALC & Posts widgets UI fixes
2016-06-09 16:19:57 +02:00
c8a0e006a0 Fix Select2 placeholder in Posts settings 2016-06-09 15:42:01 +03:00
359119d896 Disable dragging with right click, fixes #517 2016-06-09 13:34:26 +03:00
1a3c767601 - Fix double HR tag issue on ALC/Posts block settings;
- Change "Drop content here" message to a custom one for Posts/ALC blocks
2016-06-09 13:34:26 +03:00
6a97e82d42 Fix select2 placeholder text not appearing for Posts widget 2016-06-09 13:34:26 +03:00
3edfd32879 - Add highlighting of blocks that are being edited;
- Refactor block settings views;
- Change Posts widget to display 8 posts in settings;
- Move ALC/Posts category selector label to Select2 placeholder.
2016-06-09 13:34:26 +03:00
33bdde1156 Merge pull request #516 from mailpoet/unit_tests
Adds unit test for open/unsubscribe statistics
2016-06-09 12:54:16 +03:00
710cab64c3 - Fixes error due to: "the blacklist functionality has been removed from
PHPUnit 5, please remove blacklist section from configuration"
2016-06-08 21:26:23 -04:00
ed707b1738 - Adds unit test for unsubscribe statistics 2016-06-08 21:25:40 -04:00
398903e8b8 - Adds unit test for open statistics 2016-06-08 12:38:52 -04:00
d590f5ea98 Merge pull request #512 from mailpoet/preview_link_refactoring
Extracts browser preview URl logic into a separate class
2016-06-08 17:12:10 +02:00
d6cbe5aac8 - Fixes incorrect shortcode name
- Updates unit test
2016-06-08 11:09:33 -04:00
08e6430c7d June 8 2016 Copy review 2016-06-08 17:02:50 +02:00
945fe66bbb Merge pull request #514 from mailpoet/model_cleanup
Removes unused method from the base model
2016-06-08 15:13:51 +02:00
8e3eb2b795 Merge pull request #515 from mailpoet/unit_tests
Adds unit test for click statistics
2016-06-08 16:02:42 +03:00
52fbc0ee8a Merge pull request #513 from mailpoet/renderer_fix
Rendering fix
2016-06-08 12:56:22 +03:00
a355228b93 Test Commit
This is an initial test commit for the copy review.
2016-06-08 11:31:04 +02:00
2cb0b3b071 - Adds unit test for click statistics 2016-06-07 21:57:04 -04:00
bc1fb235d3 - Removes unused method from the base model. Closes #511 2016-06-07 18:47:00 -04:00
713dda913e - Fixes rendering issue where DOMDocument throws a notice on unescaped
html entity
2016-06-07 12:27:40 -04:00
d182638971 - Updates references to the new view in browser URL class
- Removes unnecessary rtrim condition in URL generation
2016-06-07 10:53:01 -04:00
a5c620acf3 - Updates the way the view in browser URL is constructed 2016-06-07 10:41:19 -04:00
c176ad1d16 - Updates based on code review comments 2016-06-07 10:14:37 -04:00
14c2b4d90f - Changes location for the main view in browser class
- Updates code formattign for case statements
2016-06-07 09:28:29 -04:00
03eb4ad0fc - Changes location for the view in browser URL class 2016-06-07 09:16:48 -04:00
ba9cd15651 - Extracts view in browser URl logic into a separate class 2016-06-07 09:08:01 -04:00
329ec63dfd Bump up release version to 0.0.31 2016-06-03 18:29:40 +03:00
4925c7868e Merge pull request #510 from mailpoet/twig_deprecation
Twig deprecation notice (latest version)
2016-06-03 18:10:53 +03:00
13d28d0aa7 implemented interface in our Twig extension to comply with latest Twig standards 2016-06-03 15:07:30 +02:00
c7d3c79fe3 Merge pull request #509 from mailpoet/unit_tests
- Increases Mailer unit test coverage to 100%
2016-06-02 19:38:43 +03:00
1e9da724ea - Updates exception test logic 2016-06-02 12:33:58 -04:00
645d4e15ab - Updates unit test 2016-06-02 11:03:16 -04:00
cad5b242b2 - Increases Mailer unit test coverage to 100% 2016-06-02 10:11:36 -04:00
99a81042c1 Merge pull request #507 from mailpoet/custom_shortcodes
Implements shortcodes for custom fields
2016-06-01 17:09:38 +03:00
61987a204e - Fixes custom field shortcode matching logic 2016-06-01 09:59:45 -04:00
a208104fc8 - Fixes naming convention 2016-06-01 09:40:59 -04:00
00ccc8adf4 Merge pull request #506 from mailpoet/alc_update
Multiple ALC block support for newsletter editor
2016-06-01 15:15:26 +02:00
df0ed9ce53 Rename mailpoet_custom_fields symlink to mailpoet_shortcodes 2016-06-01 16:00:05 +03:00
26d9b915a2 - Adds unit test for shortcodes helper class 2016-05-31 21:30:38 -04:00
16cb91990b - Updates unit test 2016-05-31 20:08:45 -04:00
9642d3e672 - Renames all references of "custom fields" to "shortcodes" 2016-05-31 11:25:16 -04:00
aed60e6905 - Updates menu/editor view to work with the refactored shortcodes logic 2016-05-31 11:04:10 -04:00
da7615ba4c - Removes redundant shortcode description
- Implements shortcode processing for custom fields
2016-05-31 11:03:04 -04:00
3eb6a21980 - Centralizes a list of all shortcodes
- Returns all shortcodes with custom fields
2016-05-31 11:02:08 -04:00
b4e371302c Fix PHP coding style based on feedback 2016-05-31 17:50:38 +03:00
e6724b1d4a Change unsubscribe verifier to check for "Unsubscribe" shortcode
presence
2016-05-31 16:29:10 +03:00
2b6e87c3a7 Force TinyMCE to use absolute URLs 2016-05-31 16:12:33 +03:00
b01ee80ec2 Update Backbone, Marionette, Backbone Radio, TinyMCE dependency versions 2016-05-31 15:14:36 +03:00
5d48ecac80 Add a method to bulk update ALC blocks in newsletter editor 2016-05-31 13:53:45 +03:00
ebdb826011 Bump up release version to 0.0.30 2016-05-27 18:31:35 +03:00
9dc725e34d Merge pull request #488 from mailpoet/wp_users
Wp users
2016-05-27 18:26:45 +03:00
f47c331a5b updated db schema and fixed unit test missing Segment cleanup after 2016-05-27 15:38:24 +02:00
b45c70f32b removed status from subscribeManyToSegments() query 2016-05-27 14:18:02 +02:00
cf33d6f066 removed extra spaces 2016-05-27 14:15:46 +02:00
8292e9a744 Revert batch processing on bulk actions - too buggy
- minor fixes and cleanup
2016-05-27 14:15:46 +02:00
3c46a5b434 Optimized Bulk actions
- Updated SQL schema for every created_at column so that it has a default value
- Updated unit tests based on recent changes (new methods in SubscriberSegment model)
- Added check for HelpScout initialization code so that it doesn't throw errors
2016-05-27 14:15:46 +02:00
4a4c4e093a Added unit tests for the WP segment
- moved WP segment creation to the Segment model
2016-05-27 14:14:35 +02:00
4fa8a650b8 Added unit tests for SubscriberSegment / Subscriber models 2016-05-27 14:14:35 +02:00
da755b7902 Renamed method names for better clarity + refactoring
- renamed getWPUsers() to getWPSegment()
- renamed SubscriberSegment methods
2016-05-27 14:14:35 +02:00
ceebb18bdf minor spacing fix 2016-05-27 14:14:35 +02:00
d10a29598d prevent deletion of WP Users segment in Segments listing 2016-05-27 14:14:35 +02:00
8c56c8da5e Fixed bulk actions (return false if no items were selected)
- added missing check for WPUsers segment in case it does not exist
2016-05-27 14:14:35 +02:00
c4ddb38d18 Prevent WP users from being trashed/deleted
- return actual rowCount of affected rows for bulk actions (based on PDO last statement)
- prevent removal of WP Users segment relationship with subscribers.
2016-05-27 14:14:35 +02:00
15a21e5745 fix segments loaded on subscribers page + removed counts for bulk actions' segments 2016-05-27 14:14:35 +02:00
7df1a856ea Merge pull request #501 from mailpoet/import_fix
Import fix
2016-05-27 14:19:32 +03:00
69381205a2 - Updates unit test 2016-05-27 07:16:11 -04:00
9e0d8056b3 - Changes success/error notices font size to 13px in import and export 2016-05-27 07:16:11 -04:00
4b85c57436 - Updates Export notification class
- Updates Export "back to subscribers" button language
2016-05-27 07:16:11 -04:00
377498be1d - Removes validation of MailChimp API key
- Refactors import class
- Creates new method in Newsletter model to select welcome notifications
  for specific segments
- Updates Step 2 (error) and Step 3 (success) notices
- Gives MenuBootstrap class a comprehensible name
2016-05-27 07:16:11 -04:00
142421ad48 - Updates unit test 2016-05-27 07:16:11 -04:00
768115b794 - Disables "next step" button on import's step 2 when no segments are
selected
2016-05-27 07:16:11 -04:00
8b9d76db8a - Displays notice on step 3 of import when subscribers are added to a
segment with welcome notification enabled
2016-05-27 07:16:11 -04:00
f17c78fda2 - Updates Segment model to return segments even when there are no
subscribers
2016-05-27 07:16:11 -04:00
3d45a8b7d4 - Updates subscriber/subscriber_segment status using const values 2016-05-27 07:14:34 -04:00
3888241cbd - Simplified date matching logic by using Moment.js 2016-05-27 07:14:34 -04:00
603b6749de - Styles the import results notice to look like WP's "update" 2016-05-27 07:14:34 -04:00
22918ecfd1 - Updates the wording of the "back to list" button 2016-05-27 07:14:34 -04:00
70ded73b51 - Updates the look of the MailChimp API key "verify" button 2016-05-27 07:14:34 -04:00
da147047ec - Updates import email regex to use standard HTML5 regex
- Improves email detection/filtering logic
2016-05-27 07:14:34 -04:00
0e24174373 Merge pull request #486 from mailpoet/export_fix
Fixes segment subscriber count when status is "unsubscribed"
2016-05-26 11:40:38 +02:00
bc9b4eeb19 - Update Segment model/test to use const values for subscriber status 2016-05-25 17:31:38 -04:00
c6b13c5175 Merge pull request #498 from mailpoet/rendering_fix
Fixes a couple of rendering issues
2016-05-25 14:23:15 +03:00
f754b1d1b2 - Applies text alignment to ALC block
- Prevents duplicate column content
2016-05-24 15:41:26 -04:00
bd5300d69a Merge pull request #495 from mailpoet/standard_newsletter_fix
Fix scheduling immediate standard newsletters
2016-05-24 12:02:01 -04:00
9996f3ef41 Change Scheduler to use Newsletter object, not array 2016-05-24 17:57:34 +03:00
0f95d7bc8a Use Scheduler to schedule next post notification sending timestamp 2016-05-24 17:08:34 +03:00
14098643ae Fix scheduling immediate standard newsletters 2016-05-24 16:04:42 +03:00
7c2d5a45c5 - Updates unit test 2016-05-20 12:12:29 -04:00
d25070829d Bump up release version to 0.0.29 2016-05-20 18:49:10 +03:00
9d5902e179 - Fixes segment subscriber count when status is "unsubscribed" 2016-05-20 11:35:58 -04:00
d194502b27 Merge pull request #485 from mailpoet/standard_scheduling_fix
Prevent user from manually editing Standard newsletter scheduling date
2016-05-20 15:09:04 +02:00
2cef99de2b Merge pull request #484 from mailpoet/re_adding_localestring
Re-adding toLocaleString on Segments listing
2016-05-20 08:50:02 -04:00
05d3756a1c Change readOnly field to boolean type 2016-05-20 15:27:46 +03:00
b3d9fc54fe Prevent user from manually editing standard scheduling date 2016-05-20 14:19:38 +03:00
20841eb5e8 Re-adding toLocaleString on Segments listing 2016-05-20 09:25:40 +02:00
9e23326d45 Merge pull request #483 from mailpoet/react_forms
Forms / WP Sync
2016-05-19 10:20:47 -04:00
f81d639b19 Merge pull request #482 from mailpoet/export_fix
Export fix
2016-05-19 15:49:12 +02:00
ab3e272020 extra space 2016-05-19 15:40:14 +02:00
4c265d1339 convert some React fields to ES6
- renamed empty label to placeholder
2016-05-19 15:19:31 +02:00
da08de0e74 - Fixes unit test
- Enables check for ZIP extension
2016-05-19 09:10:26 -04:00
6074aa927b Fix WP Sync
- added missing translations in listings (responsive view)
2016-05-19 14:32:40 +02:00
046127eeba fixed react forms (new bug discovered on new forms with default values not saved) 2016-05-19 14:30:33 +02:00
0c8a8c6854 Merge pull request #473 from mailpoet/standard_scheduling
Standard newsletter scheduling
2016-05-19 13:56:44 +03:00
acfcfdd730 - Refactors scheduler worker and updates logic responsible for scheduling
a new post notification queue
2016-05-18 15:40:41 -04:00
8f935df12a Merge branch 'standard_scheduling' of mailpoet:mailpoet/mailpoet into standard_scheduling 2016-05-18 14:37:11 -04:00
2784bb7282 - Adds queue rescheduling for post notifications 2016-05-18 14:33:55 -04:00
08a8ca4969 Change segments handling for notification newsletters 2016-05-18 21:27:58 +03:00
5a03eb9a17 Fix newsletter calls to use array access and not object access 2016-05-18 19:03:06 +03:00
91bb215e4d Change Date picker to use Wordpress date format 2016-05-18 18:38:59 +03:00
0b3a388a78 - Remove space after if;
- Change Scheduler::standard function name to a more descriptive one
2016-05-18 18:38:58 +03:00
66a93768e1 Remove unnecessary function calls 2016-05-18 18:14:54 +03:00
61df4899cd - Refactor WP DateTime helper
- Add a unit test helper to stub out Wordpress functions
2016-05-18 18:14:54 +03:00
f8b1e153be Make date picker translatable 2016-05-18 18:14:53 +03:00
f322433875 Add standard newsletter scheduling backend 2016-05-18 18:14:53 +03:00
be155c38bc Fix datepicker onChange handler 2016-05-18 17:51:39 +03:00
13ee338fb0 Add jQuery UI datepicker with Melon skin 2016-05-18 17:51:39 +03:00
f8628c1f4e - Add input validation
- Unmount datepicker on destruction
- Change prop propagation pipeline
2016-05-18 17:51:39 +03:00
f7c70be5eb Add standard newsletter scheduling UI 2016-05-18 17:51:39 +03:00
65df28d52e Merge pull request #478 from mailpoet/post_notification_update
Post notification update
2016-05-18 16:10:23 +02:00
3e658033da - Updates conditional check 2016-05-18 10:00:38 -04:00
31e082eb2b - Removes space between IF and statement 2016-05-18 10:00:38 -04:00
bf1ab3a593 - Removes space between (int) and variable 2016-05-18 10:00:38 -04:00
8540c51679 - Changes newsletter object to array in SendingQueue router
- Schedules post notification right after its creation
2016-05-18 10:00:09 -04:00
d2a6b6bd4e - Updates based on code review comments 2016-05-18 10:00:09 -04:00
06417c1e88 - Fix ALC post amount handling of boundary values;
- Change magic number to named constant
2016-05-18 10:00:09 -04:00
be238f4c67 - Fixes unit test
- Corrects method name in scheduler
2016-05-18 10:00:09 -04:00
343da0fdcc - Saves sent posts during rendering by sending queue worker
- Prevents empty notification emails from being sent
- Hooks to WP's post update and rewrite post notification logic
- Prevents scheduling multiple queues of the same newsletter
- Fixes issue with segments not updating when scheduling a newsletter
- Removes depreciated hash field & associated logic
2016-05-18 10:00:09 -04:00
dbb3c96300 Modify ALC to skip already sent posts 2016-05-18 10:00:09 -04:00
c8f7bea419 Let wordpress exclude already used posts in ALC selection 2016-05-18 10:00:09 -04:00
396ab50fa0 - Implements exclusion of duplicate posts from ALC 2016-05-18 10:00:09 -04:00
84e9ecef29 Merge pull request #477 from mailpoet/php7_mysql_fix
Fix for PHP7 & Mysql 5.7
2016-05-18 15:42:36 +02:00
57472d1a2e - Removes comment for an explicitly invoked condition 2016-05-18 09:39:29 -04:00
cb4f055263 - Updates export SQL to always use GROUP BY and aggregate columns 2016-05-17 11:07:12 -04:00
3f1bdd2c59 - Removed debug function
- Corrected comment
2016-05-17 10:01:06 -04:00
a6802a1925 - Fixes GROUP BY when using non-aggregate columns 2016-05-17 09:54:58 -04:00
a02b2d3aa0 - Updates migrator/schema to work with MySQL 5.7
- Fixes unit tests
- Fixes export's SQL query to work with strict ONLY_FULL_GROUP_BY option
2016-05-16 22:26:21 -04:00
607a151c23 Fix for PHP7 & Mysql 5.7
- added default values to not null columns
- fixed passing by reference issue on MailChimp->getDataCenter
- fixed a couple unit tests
2016-05-16 14:50:43 +02:00
c8748e5e6e Merge pull request #475 from mailpoet/uniform_listings
bulk actions / pagination and customizable limit per page for all
2016-05-16 13:37:14 +03:00
6e45a8e788 all listings have bulk actions / pagination and customizable limit per page 2016-05-13 16:53:48 +02:00
395e95bc54 Bump up release version to 0.0.28 2016-05-13 16:18:01 +03:00
a8309b436d Merge pull request #474 from mailpoet/UI_updates
Ui updates
2016-05-13 16:14:23 +03:00
77fe385645 - Disables input fields if subscriber is a WP user. #421 (5)
- Removes 'unconfirmed' status if subscriber is a WP user. #421 (9)
- Displays notice if subscriber is a WP user. #421 (6)
2016-05-12 19:38:34 -04:00
290f749220 - Renames "Update" link to "Force Sync" #421 (12)
- Adds new link "Read more" #421 (13)
2016-05-12 11:48:33 -04:00
8cb5b3729d Merge pull request #472 from mailpoet/UI_updates
Expands MailPoet admin menu for sub-subpages
2016-05-12 17:40:04 +03:00
ce4a3196ee - Highlight main MailPoet menu
- Expands menu for newsletter editor
2016-05-12 10:37:56 -04:00
40ea5a0b84 - Improves menu expansion code
::
2016-05-12 09:59:05 -04:00
af95380a62 - Expands MailPoet admin menu for sub-subpages. #431 2016-05-12 09:10:35 -04:00
483eaffc0e Merge pull request #471 from mailpoet/import_update
Import udate
2016-05-12 12:40:23 +03:00
7e86ee3266 Merge pull request #470 from mailpoet/UI_updates
Various UI fixes
2016-05-12 12:15:52 +03:00
65630e6726 - Formats numbers in export 2016-05-11 15:47:47 -04:00
23682011af - Prevents WP user's first/last name to be updated during import
- Resets "next step" button state when going to step 3 of import
- Closes #469
2016-05-11 14:44:29 -04:00
00eaa768a6 - Updates Segment model to return subscriber count only if subscriber's
status is "subscribed"
2016-05-11 13:06:26 -04:00
bc92d9a61e - Updates all datetime references to use WordPress format
- Removes depreciated datetime conversion method from Helpers
- Fixes translation & error display issue in import
Closes #432
2016-05-11 10:56:34 -04:00
a1d8dec047 - Properly formats subscriber count during import 2016-05-11 10:18:59 -04:00
ff030068b0 Merge pull request #468 from mailpoet/UI_updates
Various UI fixes
2016-05-11 16:39:28 +03:00
79e37018c9 - Fixes unit test
- Updates Settings page heading to h1
2016-05-11 08:56:37 -04:00
7ae8248339 Merge branch 'UI_updates' of mailpoet:mailpoet/mailpoet into UI_updates 2016-05-11 08:52:38 -04:00
30720975ab - Disables "confirm unconfirmed" batch action as per #432 2016-05-11 08:52:13 -04:00
b2359258d4 Update newsletter sending process to format numbers >1000 2016-05-11 11:47:23 +03:00
1bd7639cc2 - Formats all numbers >1000 to use comma
- Removes subscriber count from segments if its === 0
Fixes #431
2016-05-10 20:09:02 -04:00
2dab89135f - Sets MailPoet notice timeout to 5sec
- Removes custom timeout from import
2016-05-10 18:59:31 -04:00
c3368d69fd - Updates Segment model to return ASC sorted results
- Updates Subscriber listing to display segmnets with count
2016-05-10 18:37:37 -04:00
751d8e7852 - Adds missing page titles 2016-05-10 13:01:40 -04:00
8a4dec08e1 - Converts page title headings to H1
- Adds "back to list" to subpages
2016-05-10 10:13:57 -04:00
8b9f5ad5b1 Merge pull request #467 from mailpoet/cron_fix
- Corrects the daemon stop status
2016-05-10 15:51:53 +03:00
fc597a53bb - Corrects the daemon stop status 2016-05-09 20:28:02 -04:00
2f25dc6a20 Bump up release version to 0.0.27 2016-05-06 14:46:12 +03:00
fc38ee2f08 Merge pull request #466 from mailpoet/rendering_engine_update
Fixes empty paragraph spacing
2016-05-05 13:04:26 +03:00
33bebc6629 - Replaces empty paragraphs with single space according to specific
condition
2016-05-04 19:57:10 -04:00
14a8e02f99 Merge pull request #465 from mailpoet/browser_preview
Newsletter preview
2016-05-04 17:58:41 +03:00
0bf6c87ec7 - Fixes shortcode category name for view in browser url
- Updates shortcode regex
2016-05-04 10:45:30 -04:00
422fba2835 - Updates based on code review comments 2016-05-03 18:41:57 -04:00
f36dbb78e6 - Updates unit test 2016-05-02 13:44:36 -04:00
3213dd0d08 - Removes unused exception handlers 2016-05-02 13:19:10 -04:00
3f2199fd63 - Updates link tracking to use the same Public API data format as other
endpoints
2016-05-02 13:15:48 -04:00
a4477a9bd6 - Updates CONST values 2016-05-02 12:27:00 -04:00
52790d7bd6 - Updates code comment 2016-05-02 12:24:56 -04:00
0b9812210f - Updates CONST values 2016-05-02 11:30:37 -04:00
756dbb4641 - Updates daemon status to CONSTs
- Uncomments temporary commented out code
- Removes unnecessary exception handles in Public API
2016-05-02 10:32:34 -04:00
b38742ddc0 - Changes divider to CONST 2016-05-02 10:02:22 -04:00
49e38549ec - Fixes URLs not being properly generated when tracking is enabled 2016-05-02 10:00:35 -04:00
afcb0a0d7f - Upperases all constants 2016-05-02 09:47:54 -04:00
d18f0e50b5 - Removes debug backtrace
- Removes uncommented queue save
2016-05-02 09:23:37 -04:00
6868a07ead - Moves link saving logic into Links class 2016-05-01 21:55:24 -04:00
cca76d0d97 - Adds custom shortcode processing logic
- Updates shortcode categories code
- Completely rewrites shortcodes unit tess
2016-05-01 14:07:06 -04:00
70fa77d333 - Creates temporary folder if it doesn't exist 2016-04-30 22:38:55 -04:00
412201d965 - Updates unit test 2016-04-30 22:38:39 -04:00
045a92c7d6 - Standardizes variable names 2016-04-30 22:21:03 -04:00
2ba2e3eca5 - Refactors sending queue worker 2016-04-30 22:19:59 -04:00
90c294f60e - Updates editor/router to use the new browser preview class 2016-04-30 22:19:59 -04:00
57b953dd14 - Rewrites shortcode processing class to work with other changes 2016-04-30 22:19:59 -04:00
0f81a8db60 - Updates method to utilize constant value 2016-04-30 22:19:59 -04:00
2d6971f8df - Refactors link tracking class 2016-04-30 22:19:59 -04:00
0abe8b5371 - Implements view in browser 2016-04-30 22:19:59 -04:00
5bad682879 - Merges all shortcode links under one category
- Adds filter hooks for custom links
- Renames old shortcodes in the editor and elsewhere
2016-04-30 22:19:59 -04:00
fa9d32c230 Bump up release version to 0.0.26 2016-04-29 21:09:41 +03:00
1b3ceca7b2 Merge pull request #464 from mailpoet/undo_go_back
remove attempt at 'goback' after editing an entity
2016-04-29 21:07:10 +03:00
9be326b45d remove attempt at 'goback' after editing an entity 2016-04-29 18:35:52 +02:00
2d2e1298c4 Merge pull request #462 from mailpoet/segments_listing
React components update + bugfixes
2016-04-29 14:18:05 +03:00
5dc3a4386e Merge pull request #463 from mailpoet/queue_processed_date_fix
Updates queue processed date to account for time offset
2016-04-29 12:16:51 +03:00
b617dde266 - Updates queue processed date to account for time offset 2016-04-28 12:42:16 -04:00
962e91f9dc fixed merge fuck 2016-04-28 17:15:07 +02:00
4047b41a7f Updated all React components to their latest version
- updated code due to deprecated warnings (mostly router stuff & input default value)
- set default sender based on settings when creating new newsletter
- fixed erroneous UTC offset when displaying dates (PHP takes care of it)
2016-04-28 17:02:24 +02:00
59199140bf Merge pull request #448 from mailpoet/twig_template_cache
modified temp path/url for cache/export folders
2016-04-28 10:18:53 -04:00
1079c0beae Merge pull request #458 from mailpoet/click_rate_update
fixes click rate
2016-04-28 17:01:05 +03:00
1694baab7d Merge branch 'click_rate_update' of mailpoet:mailpoet/mailpoet into click_rate_update 2016-04-28 09:37:04 -04:00
335ac9c778 - Updates statistics to display only 1 click event from a given subscriber
Fixes #417 (7)
2016-04-28 09:34:59 -04:00
65efd234f9 - Fixes duplicate status for different newsletters
- Updates statistics not to display NaN values
2016-04-28 09:25:09 -04:00
f726d943db Merge pull request #460 from mailpoet/text_rendering_fix
Text rendering fix
2016-04-28 12:55:08 +03:00
2d1e950097 Merge pull request #459 from mailpoet/scheduled_status
Updates newsletter listing status
2016-04-28 12:52:46 +03:00
ca2f16970b - Fixes and issue with lists not retaining links and other HTML content 2016-04-27 21:22:39 -04:00
4a123f8fe9 - Displays proper status for scheduled newsletters 2016-04-27 20:59:04 -04:00
7b224328e1 - Updates statistics to display only 1 click event from a given subscriber
Fixes #417 (7)
2016-04-27 20:55:19 -04:00
699dfa19f0 - Removes leftover console command 2016-04-27 20:14:06 -04:00
06d56fe19d - Adds "scheduled for" status for notifications
- Adds post notification/welcome activation message
- Implements schedule update when editing newsletter
Implements #405 (3 & 7)
2016-04-27 20:08:45 -04:00
1b7ac62b5c Merge pull request #453 from mailpoet/queue_statistics
Send statistics
2016-04-27 21:28:42 +03:00
ae358ce13e - Updates statistics SQL query
- Fixes tests + expands coverage
- Removes leftover console command
2016-04-27 21:26:47 +03:00
18f35b5e91 - Displays statistics in newsletter listing 2016-04-27 21:26:47 +03:00
67ca305b7f - Adds withStatistics method to the Newsletter model
- Updates Newsletter router to return statistics
- Returns only completed/running queues
2016-04-27 21:26:47 +03:00
cb0908fc70 Merge pull request #454 from mailpoet/post_title_shortcode_fix
Updates newsletter post title shortcode
2016-04-27 21:24:34 +03:00
42df59076d Merge pull request #457 from mailpoet/forms_listing
Forms listing
2016-04-27 15:15:53 +03:00
ff8be3bdc6 Merge pull request #456 from mailpoet/manage_subscription_custom_pages
don't enforce our content on custom subscription pages
2016-04-27 15:03:27 +03:00
f7b6dcf409 - Remove bulk actions from forms
- form name is now a link to the edit form page
- added "row-title" class so that the name is "bigger"
2016-04-27 13:31:30 +02:00
940f3848dd Merge pull request #452 from mailpoet/form_stats
Form subscription stats
2016-04-27 14:14:42 +03:00
e847ad2df2 don't enforce our content on custom subscription pages 2016-04-27 11:13:36 +02:00
2459a103fd - Ignores sticky posts when fetching latest post title
- Updates unit test
2016-04-26 19:53:23 -04:00
0dd3f2178e updated form stats to record individual subscription 2016-04-26 18:12:05 +02:00
de873eca71 Unit test for StatisticsForms model
- improved incrementation of subscriptions count so that we don't need to fetch the record after an update
2016-04-26 15:38:42 +02:00
ef461da77f Statistics for Form Subscriptions
- added statistics_forms table
- added corresponding model to record stats
- record stats whenever someone subscribes via a form
2016-04-26 15:16:37 +02:00
caf6dcddfa Merge pull request #451 from mailpoet/react_select_fix
Subscribers bulk actions with an extra select
2016-04-26 14:00:32 +03:00
9684c88651 refactor 2016-04-26 12:43:48 +02:00
256bca8ed9 Fix selection.jsx by adding a check before trying to destroy the select2 instance 2016-04-26 12:19:39 +02:00
8d56e8582f Merge pull request #450 from mailpoet/unsubscribe_tracking
Implements tracking of unsubscribe events
2016-04-25 21:32:10 +03:00
56959f2f49 Terminate execution after redirecting a click 2016-04-25 20:33:17 +03:00
bbc7de6898 - Removes commit leftovers 2016-04-25 12:02:56 -04:00
0df246da15 Merge pull request #447 from mailpoet/tracking_fix
Fixes click tracking that would end request when calling open tracking
2016-04-25 18:37:15 +03:00
757e18355d Merge pull request #446 from mailpoet/welcome_email_fix
welcome email fix
2016-04-25 18:36:57 +03:00
4b29b04bd1 - Implements tracking of unsubscribe events 2016-04-25 11:06:01 -04:00
6cb94bc413 - Updates open rate tracking class constructor 2016-04-25 09:43:39 -04:00
f624c891ab - Capitalizes CONST values 2016-04-25 09:42:02 -04:00
b83abf0ac5 modified temp path/url 2016-04-25 14:26:27 +02:00
ef1b0036e5 - Fixes click tracking that would end request when calling open tracking 2016-04-22 20:30:16 -04:00
5efa9f65c6 - Fixes an issue with welcome email not being sent to WP user with any
role
2016-04-22 18:51:51 -04:00
a8e3dd424e Bump up release version to 0.0.25 2016-04-22 23:25:28 +03:00
26628ba156 Merge pull request #445 from mailpoet/sending_fix
Fixes check for changed newsletter content
2016-04-22 23:22:30 +03:00
78cabde9e1 Select an existing segment as initial value for Welcome segment select 2016-04-22 16:08:45 -04:00
1ec0372c2d - Fixes check for changed newsletter content 2016-04-22 16:08:45 -04:00
31e2d5e771 Merge pull request #444 from mailpoet/fix_router
fixed Router when data represents a single variable (not an array)
2016-04-22 18:05:38 +03:00
d2dbf86a9c fixed Router when data represents a single variable (not an array) 2016-04-22 16:59:28 +02:00
1c39d39078 Merge pull request #442 from mailpoet/sending_subscriber_recalculation
Dynamically updates queue's subscriber count
2016-04-22 17:03:09 +03:00
6d0795abba Merge pull request #438 from mailpoet/non_ajax_subscription
Non ajax subscription
2016-04-22 09:51:47 -04:00
5f45c6cc74 - Extracts subscriber recalculation into a spearate method
- Updates subscriber recalculation logic
2016-04-22 09:47:53 -04:00
d4d806e247 Merge pull request #443 from mailpoet/editor_rendering
Editor rendering update
2016-04-22 15:38:54 +02:00
758a545eb6 Merge pull request #441 from mailpoet/shortcodes_fix
Updates the newsletter shortcode
2016-04-22 13:45:15 +03:00
578088d2e5 Add fallback fonts for all editor fonts 2016-04-22 12:38:59 +03:00
e17dba2b07 Add blockquote bottom margin 2016-04-22 12:30:57 +03:00
7e5cf533f0 Fixes class name 2016-04-22 12:30:57 +03:00
f7c656aed5 Fixes blockquote content duplication 2016-04-22 12:30:57 +03:00
2e42305710 Updates line breaks in footer/header to match the editor 2016-04-22 12:30:57 +03:00
b3e310652e Remove bottom margin from paragraphs 2016-04-22 12:30:57 +03:00
6737158130 - Change paragraph bottom margin to equal line-height
- Remove horizontal blockquote margin to match Renderer;
- Removed bottom padding from last text elements in column;
- Removed bottom padding from full width images;
2016-04-22 12:30:57 +03:00
906558a772 Apply paragraph styles to LI tags 2016-04-22 12:30:57 +03:00
d559483c7b - Add bottom margin to headings
- Fix top and bottom margin for UL tags
- Add bottom margin to LI tags
2016-04-22 12:30:57 +03:00
bec3e02285 Remove vertical button padding, following renderer 2016-04-22 12:30:57 +03:00
9d6d72dd8c Use fallback font stack for Lucida font 2016-04-22 12:30:57 +03:00
3b76f838d1 Merge pull request #440 from mailpoet/sending_fix
Adds condition that prevents duplicate and/or stuck post notifications
2016-04-22 11:32:14 +03:00
909ad86e33 - Recalculates the number of processed/to process subscribers if the
original count does not the current count from the database
2016-04-21 21:36:37 -04:00
dd8f58e35e - Fixes incorrect newsletter issue number display
- Updates newsletter 'last post title' logic
- Updates shortcode extraction regex to allow limiting extraction of only
  specific categories
- Updates unit tests
- Closes #380
2016-04-21 20:04:02 -04:00
4b2061fcfa - Adds condition that prevents duplicate and/or stuck post notifications 2016-04-21 18:20:13 -04:00
8e6ca502b3 Merge pull request #439 from mailpoet/sending_fix
Sending fix
2016-04-21 19:05:49 +03:00
ef85834db5 - Removes debugging function 2016-04-21 11:32:29 -04:00
2bdcd0eb42 - Prevents duplicate newsletters from being sent 2016-04-21 10:45:00 -04:00
3b90be4122 - Fixes scheduling of notifications 2016-04-21 10:44:43 -04:00
375e70d84b success messages in both context when subscribing to a form 2016-04-21 12:39:14 +02:00
42d586610e form subscription only using Router now 2016-04-20 16:59:18 +02:00
e4a5438512 Merge pull request #435 from mailpoet/open_tracking
Implements open tracking
2016-04-20 14:32:20 +03:00
11b22fa63a - Enables newsletter open tracking when links are clicked 2016-04-19 21:01:27 -04:00
45b933d635 - Implements open tracking 2016-04-19 21:01:27 -04:00
15cf087d40 - Implements post-processing filter during rendering 2016-04-19 21:01:27 -04:00
ed09c3e5d4 Merge pull request #427 from mailpoet/batch_sending_fix
Adds missing rendered subject to the queue during bulk processing
2016-04-18 18:27:24 +03:00
285a556f21 Merge pull request #426 from mailpoet/subscriber_selection_update
Updates subscriber selection logic
2016-04-18 17:41:03 +03:00
dc1ef2af47 - Removes check for signup confirmation when selecting subscribers 2016-04-18 17:33:08 +03:00
6f17f0d2d1 Merge branch 'subscriber_selection_update' 2016-04-18 17:28:53 +03:00
91076580ef - Enables check for subscribers who has "subscribed" status during
newsletter sending
- Enforces "signup_confirmation" option when selecting subscribers for
  newsletter sending
- Updates unit tests
2016-04-18 17:28:37 +03:00
d4abaa7150 Merge branch 'link_tracking_update' 2016-04-18 16:57:56 +03:00
d2bc0fd24a - Removes check for signup confirmation when selecting subscribers 2016-04-18 08:45:38 -04:00
1f26079b5f - Removes unused properties from Links class and converts methods to
static
2016-04-16 16:48:35 -04:00
87a1211a55 - Adds missing rendered subject to the queue during bulk processing 2016-04-15 20:06:35 -04:00
0ca0a7d029 - Enables check for subscribers who has "subscribed" status during
newsletter sending
- Enforces "signup_confirmation" option when selecting subscribers for
  newsletter sending
- Updates unit tests
2016-04-15 20:02:44 -04:00
cb3d49f200 Bump up release version to 0.0.24 2016-04-15 21:27:07 +03:00
c0da428c27 Merge pull request #425 from mailpoet/subscription_pages
Subscription pages
2016-04-15 19:07:20 +02:00
435cd9b777 fixed unsubscribe action + updated public api decodeData to prevent warning 2016-04-15 18:59:20 +02:00
bfde34eb8d - Rebases master
- Updates PubliAPI's logic dealing with request data
2016-04-15 12:10:56 -04:00
a4c1b24c35 fixed unit tests 2016-04-15 11:56:33 -04:00
2cbd2d54f3 Subscription pages 2016-04-15 11:56:33 -04:00
378f6d803a Merge pull request #422 from mailpoet/link_tracking
Implements links tracking
2016-04-15 18:47:14 +03:00
af4d29ebe6 - Updates codes based on Taut's comments 2016-04-15 11:36:57 -04:00
599661e028 - Updates code based on Taut's comments 2016-04-14 20:35:56 -04:00
84294b7ee6 - Adds subsription tracking option to Settings->Advanced
- Updates sending queue worker to not replace links if tracking is
  disabled
Closes #417
2016-04-14 19:37:27 -04:00
a3e6eb5bba - Implementsi tracking clicks & redirecting URLs 2016-04-14 19:37:27 -04:00
67359980e9 - Renames statistics table to the new "statistics_entity" format
- Adds new clicks statistics model
2016-04-14 19:37:27 -04:00
f1b955d74a - Implements link extraction, processing, replacement and saving 2016-04-14 19:37:27 -04:00
809be415c5 Merge pull request #423 from mailpoet/text_block_fix
Fixes 'Can't use method return value in write context' error
2016-04-14 12:32:31 +03:00
457a4d1bba - Fixes 'Can't use method return value in write context' error 2016-04-13 22:46:59 -04:00
1e16912763 Merge pull request #420 from mailpoet/sending_queue_router_fix
Prevents newsletters from being queued when mailer is not configured
2016-04-12 16:02:18 +03:00
e2c9971c99 - Prevents newsletters from being queued when mailer is not configured
Closes #342
2016-04-12 08:58:21 -04:00
c1d31ca400 Merge pull request #414 from mailpoet/sending_last_step
Sending options in last newsletter creation step
2016-04-12 13:14:34 +02:00
a011c3aade Fix standard newsletters to not send when segments are missing 2016-04-12 14:11:26 +03:00
e35e97cdbf Fix Select2 integration in Form component 2016-04-12 14:04:01 +03:00
49a59d35a1 Add number of subscribers to segment selection 2016-04-12 14:04:01 +03:00
9a46640c15 Allow selecting only published segments, fix "Save" translation 2016-04-12 14:04:01 +03:00
678a0b3835 Add form loading state, remove errors and params 2016-04-12 14:04:01 +03:00
0c008325c4 - Fix unmounting select2 component
- Fix subjects of welcome and notification newsletters
- Remove debugging statements
2016-04-12 14:04:01 +03:00
ad5441487b Show different fields based on newsletter type, fix saving across
multiple endpoints
2016-04-12 14:04:01 +03:00
104620a40a Add react component support to Forms, make newsletter scheduling
components reusable
2016-04-12 14:04:01 +03:00
c1a3ba67f5 Merge pull request #418 from mailpoet/fix_initializer
fix cron issue
2016-04-11 09:41:08 -04:00
c42bbf3dc4 fix cron issue 2016-04-11 15:35:47 +02:00
7d34274fbf Merge pull request #416 from mailpoet/rendering_engine_update
Sets text-align to left when it's not center|justify|right
2016-04-11 13:51:42 +03:00
f930b3303b - Sets text-align to left when it's not center|justify|right 2016-04-08 20:37:00 -04:00
a25ea3ddf6 Bump up release version to 0.0.23 2016-04-09 02:08:05 +03:00
021349caee Merge pull request #415 from mailpoet/public_api_update
Updates query parameters for public API
2016-04-09 02:01:20 +03:00
89f2958d23 - Updates cron to use new public API query parameters 2016-04-08 18:53:57 -04:00
8c8435766e - Updates query parameters for public API 2016-04-08 18:40:45 -04:00
40dd1bbb3b Merge pull request #413 from mailpoet/rendering_engine_update
Rendering engine update
2016-04-08 22:27:48 +03:00
ba40437eb9 - Enables dynamic line-height based on font-size 2016-04-08 15:19:14 -04:00
813db1ae33 - Sets LI bottom margin to 10px 2016-04-08 08:45:09 -04:00
9588397e4e - Moves all styles logic to the StylesHelper class
- Sets margin-bottom on h1-4 to be 0.3*font-size
2016-04-07 19:39:09 -04:00
ecf83ca419 - Disables color compression when tidying CSS 2016-04-07 19:39:09 -04:00
9cc494f0fa - Removes image height HTML attribute and sets "height:auto" style 2016-04-07 19:39:09 -04:00
dd4b7e4d1c - Adds lines heights to LIs 2016-04-07 19:39:09 -04:00
738b2f6c17 - Adds dynamic line breaks to paragraphs when the elment is followed by
list
2016-04-07 19:39:09 -04:00
33289342d3 - Implements dynamic margins on headings 2016-04-07 19:39:09 -04:00
a00f1efcfe - Updates unit test due to button style change 2016-04-07 19:38:15 -04:00
b89897e6d4 - Adds line breaks after headings IF they are followed by paragraph
- Adds line breaks after blocksquotes
- Updates margin on lists
- Adds line breaks after paragraphs IF they are not the last element
2016-04-07 19:38:15 -04:00
c539837896 - Makes button's stroke width = border width 2016-04-07 19:38:15 -04:00
27edf5f71d - Updates list styles 2016-04-07 19:38:15 -04:00
32cc5644f9 - Removes height:auto from images 2016-04-07 19:38:15 -04:00
8a664aa7f1 - Set left text alignment on all elements when alignment is otherwise
absent
2016-04-07 19:38:15 -04:00
7e5e8a4282 - Updates button padding 2016-04-07 19:38:15 -04:00
70d5d609e2 Merge pull request #412 from mailpoet/scheduled_sending
Fixes an issue with welcome emails not being sent to confirmed subscribers
2016-04-07 12:09:30 +03:00
19160c99e1 Merge pull request #411 from mailpoet/send_with_review
Send with review
2016-04-06 18:23:50 +03:00
99b2a7457e Merge pull request #410 from mailpoet/newsletter_send_preview_fix
Fixes an issue with shortcodes not rendering when sending newsletter
2016-04-06 18:05:39 +03:00
945d7edc70 Send with review
- updated MailPoet logo
- added SPF
- hide Dkim for beta
- added warning in case the number of emails/sec is too high
2016-04-06 16:57:10 +02:00
6a97badfed - Fixes an issue with welcome emails not being seint to confirmed
subscribers
2016-04-05 14:34:22 -04:00
5ec8e4ed52 - Fixes an issue with shortcodes not rendering when sending newsletter
preview
2016-04-05 09:54:04 -04:00
1569b5f80a Merge pull request #409 from mailpoet/scheduled_sending
Fixed an issue with newsletter schedule not being saved
2016-04-05 12:10:34 +03:00
e62ecc5036 - Fixes duplicate detection check 2016-04-04 21:09:15 -04:00
b0150e184b - Fixed an issue with newsletter schedule not being saved 2016-04-04 19:41:18 -04:00
8b96854f39 Bump up release version to 0.0.22 2016-04-01 14:57:11 +03:00
28d8600078 Merge pull request #408 from mailpoet/rendering_engine_update
Rendering engine update
2016-04-01 12:37:11 +03:00
4f30158722 - Updates button height calculation
- Removes background color when it's set to "transparent"
- Removes second line break from paragraphs
- Introduces other changes based on Becks's testing
- Updates unit tests
2016-03-31 19:19:58 -04:00
4486f9c5b0 Merge pull request #407 from mailpoet/scheduled_sending
Prevents welcome emails to be sent to unconfirmed subsribers
2016-03-31 14:53:41 +03:00
8515dcf29b Merge pull request #406 from mailpoet/rendering_engine_update
Rendering engine update
2016-03-31 14:05:18 +03:00
d16eb87782 - Prevents welcome emails to be sent to unconfirmed subscribers
- Closes #384
2016-03-30 20:59:55 -04:00
3c77e5d25e - Removes dynamic line-height based on column width
- Updates logic behind removing last element bottom padding
- Properly sets font size for H1-4 tags, header, footer and text
2016-03-30 20:09:06 -04:00
aa1a2a0da9 - Updates button padding 2016-03-30 20:09:06 -04:00
7c9029b227 Bump up release version to 0.0.21 2016-03-25 13:10:37 +02:00
1823bf606a Merge pull request #403 from mailpoet/signup_confirmation_review
Signup confirmation review
2016-03-24 14:43:36 -04:00
525b7fdd65 Merge pull request #402 from mailpoet/scheduled_sending
Scheduled sending
2016-03-24 16:10:56 +02:00
11031d2b56 Remove unused cron translations, fix start and stop button translations 2016-03-24 16:10:39 +02:00
4d45635d03 - Implements "in any role" for WP user welcome e-mails 2016-03-24 10:02:04 -04:00
b81764402b - Updates hooks used to schedule welcome email
- Simplifies cron status message
- Implements newsletter change detection between scheduled queues
- Implements detection for subscribers who unsubscribed from list
- Implements detection for WP users who changed roles
- Updates scheduler worker logic
- Various fixes
2016-03-24 10:02:04 -04:00
947e1150d8 - Removes cron worker hooks as the execution timer can't be persisted
between them
2016-03-24 10:00:52 -04:00
97f0e512af populate from/reply_to for signup confirmation
- added loading screen to reinstall process
2016-03-24 14:22:37 +01:00
f15374de43 don't replace title on custom mailpoet pages 2016-03-24 14:22:37 +01:00
f082c065d1 Confirmation email + Subscription pages
- form as an iframe: increased marginY
- fixed issue with page titles (old themes using wp_title hook)
- redirect on reinstall instead of showing a message (to avoid being able to re-save old settings)
2016-03-24 14:22:37 +01:00
72a9951125 Merge pull request #400 from mailpoet/i18n
Javascript translations
2016-03-24 13:11:51 +01:00
a82d9a63d4 Add missing unsubscribedOn translation on subscribers form 2016-03-24 14:09:35 +02:00
a7e9979781 Merge pull request #398 from mailpoet/rendering_engine_update
Updates rendering engine based on Becs's comments
2016-03-24 13:18:06 +02:00
cebd1ee7ae Merge pull request #399 from mailpoet/editor_rendering
Adapts editor rendering to Becs' feedback
2016-03-23 18:21:00 -04:00
5faa467306 - Updates merge issue with template
- Updates button width/height based on border radius
2016-03-23 18:18:21 -04:00
53eb9cd2ae Translate JS code in listings and forms 2016-03-23 18:59:01 +02:00
7f6eed6d66 Translate cron, forms, newsletters, segments and subscribers 2016-03-23 14:52:06 +02:00
74f3fa65cd Add translatiosn for newsletter creation and listings 2016-03-23 14:52:06 +02:00
8723aa4e4e Remove obsolete test file 2016-03-23 14:52:06 +02:00
ccab8b4cf3 Add MailPoet.I18n for basic translation handling, removed MailPoetI18n 2016-03-23 14:52:06 +02:00
4c4a4ab31d Reduce max button width to 288px, max button border to 10px 2016-03-23 14:09:19 +02:00
45df02b0ec Merge pull request #395 from mailpoet/many_improvements
Many improvements
2016-03-23 13:38:40 +02:00
dd7067e590 Merge pull request #397 from mailpoet/scheduled_sending
Updates scheduling logic to work with the new normalized DB time
2016-03-23 11:51:58 +02:00
1219b5ba49 Merge pull request #396 from mailpoet/timezone_fix
Normalizes time difference between WP and database
2016-03-23 11:45:42 +02:00
01496ac813 Merge branch 'scheduled_sending' of mailpoet:mailpoet/mailpoet into scheduled_sending 2016-03-22 20:55:42 -04:00
3b3ccc18ce - Updates scheduling logic to work with the new normalized DB time 2016-03-22 20:43:16 -04:00
3dce951e66 - Updates rendering engine based on Becs's comments
- Prevents removing of side padding
- Enables font size on output
- Sets line-height in footer
- Adds image height:auto
- Adds mobile styles for buttons
- Updates style sheet
2016-03-22 20:21:39 -04:00
db1dc172aa - Updates scheduling logic to work with the new normalized DB time 2016-03-22 18:26:37 -04:00
26c5cc1e43 - Normalizes time difference between WP and database 2016-03-22 13:05:41 -04:00
f91bfbf473 handle form as iframe 2016-03-22 17:25:25 +01:00
3dae0ef13f increased page input in listings 2016-03-22 17:25:25 +01:00
3281ac390e Improved segment selection errors in form editor
- improved error display to make it more obvious (added border on select2 on error)
2016-03-22 17:25:25 +01:00
d731a6b432 toggle segment selection validation on form editor 2016-03-22 17:25:25 +01:00
bb869e8ae8 Fixed setWindowTitle for WP version < 4.4
- fixed variable name Env::temp_url (instead of temp_URL)
- updated cache folder to be in the temp folder (uploads) instead of views (within plugin)
2016-03-22 17:25:25 +01:00
7331e5cabd Merge pull request #392 from mailpoet/shortcodes_update
Updates shortcodes logic
2016-03-21 17:58:38 +02:00
ba05ca35af - Implements shortcodes rendering in subject line 2016-03-21 11:45:08 -04:00
91e7bf6336 Merge branch 'shortcodes_update' of mailpoet:mailpoet/mailpoet into shortcodes_update 2016-03-21 10:12:10 -04:00
ff58067d55 Change array_column to Helpers::arrayColumn method 2016-03-21 16:07:20 +02:00
2ba6bb339e Fix newsletter options format for newsletters 2016-03-21 15:50:48 +02:00
a47afdd313 - Fixes queue worker issue 2016-03-21 09:09:43 -04:00
608b559ee1 - Minor adjustment to the shortcodes logic 2016-03-21 09:09:43 -04:00
181ed45d0b - Updates shortcodes logic
- Implements [newsletter:total] and [newsletter:number] shortcodes
- Implements shortcode replacement in subject line
- Updates unit tests
Issue #380
2016-03-21 09:09:43 -04:00
29fac8d052 Merge pull request #394 from mailpoet/scheduled_sending
Updates scheduled sending
2016-03-21 15:06:30 +02:00
a3d7d53eea - Fixes queue worker issue 2016-03-21 08:58:51 -04:00
3f6caf5fa4 - Implements scheduler worker for welcome and post notifications
- Updates sending queue worker to save rendered newsletter body
- Updates sending queue router to schedule post notification newsletters
2016-03-20 22:01:01 -04:00
42c4139ba5 - Minor adjustment to the shortcodes logic 2016-03-20 11:37:59 -04:00
ad31b143d2 - Updates scheduler worker to process queued newsletters 2016-03-19 12:29:10 -04:00
6ec15bec22 - Updates shortcodes logic
- Implements [newsletter:total] and [newsletter:number] shortcodes
- Implements shortcode replacement in subject line
- Updates unit tests
Issue #380
2016-03-19 11:19:22 -04:00
71d8fb0d93 Bump up version to 0.0.20 2016-03-18 19:40:11 +02:00
2f293da7a3 Merge pull request #391 from mailpoet/scheduled_sending
Scheduled sending
2016-03-18 18:49:47 +02:00
17b56f0160 - Fixes scheduling issues
- Fixes unit test
- Updates as per code review comments
2016-03-18 12:11:38 -04:00
bb9fce7f82 - Implements post notification scheduling 2016-03-18 11:15:31 -04:00
ad4f1f8326 Merge pull request #390 from mailpoet/newsletter_footer_links
Newsletter footer links
2016-03-18 16:10:17 +02:00
8ece62c9a6 fix tests 2016-03-18 14:58:33 +01:00
a9b9e9c631 Updated tests in order to fix WP related issues 2016-03-18 14:30:59 +01:00
6e289b6a8f - Implements welcome e-mail scheduling 2016-03-17 11:22:29 -04:00
20ced8b099 replace mailpoet_title 'hack' by checking the post type 2016-03-17 15:48:41 +01:00
f38b632707 properly handle custom subscriptions pages 2016-03-17 15:48:41 +01:00
8ce0595342 turned static values into constants 2016-03-17 15:48:06 +01:00
f11de2f1ad Updated shortcodes for unsubscribe/manage/browser links
- fixed all issues in #387 except the custom mailpoet pages
2016-03-17 15:48:06 +01:00
e28451d410 removed edit page in settings + manage subscription update 2016-03-17 15:45:05 +01:00
72882aaf2b fixed shortcodes replacement for "global:" in newsletters
- extracted "get subscription pages urls" from models\Subscriber
- added unit tests for subscription\url class (not working because of WP/Codeception issue)
2016-03-17 15:45:05 +01:00
fdf9dd0fa3 Merge pull request #386 from mailpoet/editor_refactor
Editor refactor
2016-03-17 15:19:04 +01:00
97dd0abea2 fixed makepot task
- added proper grunt module (grunt-cli)
- updated translations extract code to remove warnings when no translations are found
2016-03-17 15:08:38 +01:00
a099174226 Revert "Extract text labels from React code for translation in twig views"
This reverts commit 9aef6850c2.

Conflicts:
	views/newsletters.html
2016-03-17 15:24:07 +02:00
a054acc6e6 Merge pull request #389 from mailpoet/import_xss_update
Updates import to santize user input
2016-03-17 12:02:58 +02:00
74254d7e2a - Updates import to santize user input 2016-03-15 13:06:21 -04:00
1795964c69 - Updates composer dependencies 2016-03-15 12:10:09 -04:00
0118b2472a Fix loading of makepot task for pot translation file generation 2016-03-10 19:24:35 +02:00
5fe03f0dee Add event selection explanation to welcome email type 2016-03-10 17:49:06 +02:00
9aef6850c2 Extract text labels from React code for translation in twig views 2016-03-10 17:08:40 +02:00
f688a69f8b Fix translations to be injected at config time 2016-03-07 17:52:14 +02:00
b2682fa0b7 Remove console.log statements from newsletter editor 2016-03-07 16:57:20 +02:00
18b15c5440 Restructure JS loading to not duplicate JS asset loading 2016-03-07 16:15:26 +02:00
c3416977bb Replace editor translations with Twig localize function 2016-03-07 15:42:12 +02:00
88a00bc38c Rename editor form.html to editor.html 2016-03-07 14:42:12 +02:00
a1441dfde6 Bump up version to 0.0.19 2016-03-04 18:43:22 +02:00
8e7336d352 Merge pull request #373 from mailpoet/editor_rendering
Editor rendering (Part 2)
2016-03-04 11:11:42 -05:00
e0e2933cdf Merge pull request #379 from mailpoet/scheduled_sending
Fix mistyped method name
2016-03-04 11:06:40 -05:00
c93ec629ea Fix mistyped method name 2016-03-04 18:03:02 +02:00
d613df6558 Merge pull request #378 from mailpoet/scheduled_sending
Prepares codebase for future scheduled sending
2016-03-04 17:58:59 +02:00
01c9096543 - Rewrites hooks to not use closures 2016-03-04 10:47:55 -05:00
f120e839dd Merge pull request #375 from mailpoet/export_large_dataset_fix
Export update
2016-03-04 14:12:20 +02:00
f0ab592c04 Merge pull request #376 from mailpoet/rendering_serverside_update
Removes padding from the last column element
2016-03-04 13:30:31 +02:00
1a269d28b3 Merge pull request #374 from mailpoet/edit_subscription
edit subscription form + save
2016-03-04 12:54:56 +02:00
8d4a666bf0 added logged out handling of subscriber save request 2016-03-04 11:52:06 +01:00
4a96e483a6 edit profile rendering (missing segments list) + fallback for Url::redirectBack() 2016-03-04 11:20:17 +01:00
263a66407f - Removes mailpoet_padded class from the last element in a column 2016-03-03 19:13:50 -05:00
c4b728f4e1 - Updates Daemon to execute workers via WP's action hook
- Bootstraps Scheduler class
2016-03-03 15:42:10 -05:00
9970ad7fb6 Merge pull request #369 from mailpoet/manage_subscription
Manage subscription (part 2)
2016-03-03 15:33:56 -05:00
eb380499d9 - Rewrites export to support large datasets
- Updates unit tests
- Fixes an issue with export UI not displaying subscribers without segment
2016-03-03 14:33:10 -05:00
4b528549f5 edit subscription form + save 2016-03-03 15:57:42 +01:00
a903017bc2 Update "Please include unsubscribe link" message 2016-03-03 15:48:59 +02:00
afd25e9174 Remove old and unused editor template 2016-03-03 15:39:46 +02:00
b1bbf1b3bc Add default email for newsletter preview, disallow empty email 2016-03-03 12:59:49 +02:00
9e84e8df93 Show loading icon when loading newsletter preview 2016-03-02 16:57:04 +02:00
df775b5a07 Set font-style for paragraph and headings 2016-03-02 16:57:04 +02:00
c9c22b9b52 Switch 'Comic Sans' to 'Comic Sans MS' 2016-03-02 16:57:04 +02:00
93d20688ea Prevent heading line height and font weight from being overridden 2016-03-02 16:57:04 +02:00
9ebcddfa2a Update button defaults 2016-03-02 16:57:04 +02:00
85995bc8a6 Merge pull request #369 from mailpoet/manage_subscription
Manage subscription (part 2)
2016-03-02 16:42:12 +02:00
a8f2959bc6 added Subscriber::generateToken() 2016-03-02 15:07:37 +01:00
36242bd580 Fixed sending confirmation email to new subscribers (first time)
- fixed newsletters listing issue with queue
2016-03-02 13:11:06 +01:00
1e7dbc8449 refactor pages 2016-03-01 18:27:31 +01:00
12c159c627 bugfix + refactoring 2016-03-01 16:23:15 +01:00
82ed7e51c5 Replaced "contains" by "indexOf" (chrome issue)
- added public ajax routing (not checking permissions)
- exception handling in form subscription
2016-03-01 13:18:36 +01:00
0b3aa0d12a Merge pull request #370 from mailpoet/import_batch_update
Import batch size increase
2016-03-01 12:10:46 +02:00
4d788f69aa - Updates batch size to 2000 2016-02-29 11:41:17 -05:00
c721843c12 extracted subscription pages code from router 2016-02-29 13:34:17 +01:00
d2be407ccb rendering of edit subscription form 2016-02-29 13:34:17 +01:00
e6337216cf improved demo view of subscription pages 2016-02-29 13:34:17 +01:00
f1c396f0b0 basic implementation of confirm/edit/unsubscribe pages 2016-02-29 13:34:17 +01:00
3622bc9fcb fixed sending issue of confirmation email 2016-02-29 13:34:17 +01:00
14fe333678 Send confirmation email + page 2016-02-29 13:34:17 +01:00
cf6466197a Fixed Subscribers' bulk actions when filtering by a segment
- filter by segment is now affected by the selected group (all, trash,...)
- updated relationship methods between subscribers & segments (to account for subsegment status)
2016-02-29 13:34:17 +01:00
f56bee76f2 MailPoet.Date to handle localized dates and times 2016-02-29 13:34:17 +01:00
2ec6bc8c99 Update release version to 0.0.18 2016-02-26 16:57:42 +02:00
4aeccb1961 Merge pull request #368 from mailpoet/rendering_serverside_update
Rendering serverside update
2016-02-26 16:18:11 +02:00
bd593b1ad4 - Updates button/spacer rendering and unit tests 2016-02-26 09:05:43 -05:00
bb7812bd5d Merge pull request #366 from mailpoet/newsletter_rendering
Newsletter rendering
2016-02-25 21:14:16 -05:00
73ed070a34 - Formats code 2016-02-25 11:32:56 -05:00
9e81c48bf8 - Adds new 'fontWeight' property
- Limits max button width to column width
- Adds bold option to buttons
2016-02-25 11:32:09 -05:00
f9028d28c0 - Adds background color to spacer 2016-02-25 11:31:38 -05:00
da32b243ea Change header, footer and text padding based on Becs' feedback 2016-02-25 16:29:55 +02:00
5092c3d328 Reduce ALC post refresh timeout to 0.5s instead of 2s 2016-02-25 16:29:55 +02:00
06ad4488bf Homogenize ALC and Posts output, wrap Read More text in a paragraph tag 2016-02-25 16:29:55 +02:00
a856800e6d Set line height multiplier to golden ratio for editor text blocks 2016-02-25 16:28:32 +02:00
dfc680f3a1 Do not rerender header and footer blocks on content change 2016-02-25 16:28:32 +02:00
a9baecc504 Specify encoding for newsletter previews in browser to fix entity issue 2016-02-25 16:28:32 +02:00
11e15659ac Merge pull request #365 from mailpoet/export_fix
Export update
2016-02-25 15:51:52 +02:00
ebe440a272 Remove debugging statement 2016-02-25 15:51:25 +02:00
853794d459 Merge pull request #364 from mailpoet/phpmail_fix
Fixes localhost sending
2016-02-25 15:20:30 +02:00
cfea13bf81 Merge pull request #363 from mailpoet/alc_posts_polishing
Alc posts polishing
2016-02-25 14:27:44 +02:00
7f291d80b9 - Updates UI based on @rafaehlers review comments
- Closes #323
2016-02-24 21:54:33 -05:00
9840b55de6 - Adds unit test for image link 2016-02-24 19:16:48 -05:00
ca7322933f - Fixes issue with incorrect transport being used for localhost sending 2016-02-24 11:56:29 -05:00
81569e5b81 Fix PHP code style 2016-02-23 15:37:00 +02:00
1942972282 Change title position to featured image position 2016-02-23 15:26:12 +02:00
a23aac370c Add optional links to image rendering 2016-02-23 13:07:52 +02:00
99d6f74d1b Change inline form fields to not be inline 2016-02-23 13:07:52 +02:00
a883e1176c Merge pull request #359 from mailpoet/import_batch_processing
Import update
2016-02-23 13:04:48 +02:00
24b98a1154 Move jquery.asyncqueue.js to assets/js/src/vendor 2016-02-23 12:49:11 +02:00
8dbb6ab79f - Updates based on code review comments 2016-02-22 11:54:31 -05:00
3e7d1690bd Merge pull request #360 from mailpoet/manage_subscription
Manage subscriptions (part 1)
2016-02-22 17:05:24 +02:00
07d533a810 Manage subscriptions
- make use of the SubscriberSegment::status column to keep track of unsubscriptions
- unsubscribed segments now appear grayed out in the Subscribers listing
- added unsubscribed_at after segment names when editing a subscriber
- added date() method for Twig (uses WP's date format / date_offset)
- fixed typo in Form iframe export
- fixed unit test for Newsletters
- updated selection component (JSX) to allow more customization
2016-02-22 11:35:34 +01:00
499936e3ab - Removes file size limit in import
- Implements chunked import processing
- Updates tests/migrator/Subscriber model
2016-02-20 18:55:34 -05:00
580ac989aa Bump up version to 0.0.17 2016-02-19 15:35:27 +02:00
acf300160d Merge pull request #356 from mailpoet/page_reviews
Page reviews
2016-02-18 15:34:10 +02:00
983df4ee13 Merge pull request #355 from mailpoet/editor_polishing_3
Editor polishing 3
2016-02-18 07:45:20 -05:00
03c782d4ab Fixed issue #305
- added validation on add/edit Custom Field for multiple values (select/radio)
- disabled Remove link when only one value remains
- Added confirmation message on Reinstall
- Added missing period in Import
2016-02-18 13:14:57 +01:00
0daf7e12c1 remove logging function (polluting unit tests) & bugfix on model subscriber addToSegments 2016-02-18 13:13:19 +01:00
6a2e18a0e1 fix segments being reset on Subscriber::createOrUpdate() 2016-02-18 13:13:19 +01:00
316d5ab183 fixed unit tests 2016-02-18 13:13:19 +01:00
eb6bba5961 Merge pull request #351 from mailpoet/import_language_update
Updates error messages displayed during import
2016-02-18 09:55:59 +01:00
b5864adf06 - Fixes writable path check 2016-02-17 11:47:20 -05:00
9bce50a633 Fix "Full width" image option 2016-02-17 15:10:51 +02:00
365a53cf27 Merge pull request #353 from mailpoet/subscribers_page_review
Subscribers page review
2016-02-17 14:28:36 +02:00
31a4575d43 replaced closure by callbacks 2016-02-17 13:02:35 +01:00
1ae584c4e7 Restyle ALC Post number/type selector, limit to 2 character input 2016-02-17 13:29:16 +02:00
aac2cd6eb8 Add button "Bold" text option, fix unit tests 2016-02-17 12:25:03 +02:00
9874e1c371 Remove 1px left black border from newsletter thumbnails 2016-02-17 12:25:03 +02:00
16b1c0dc41 Convert mailpoet buttons to WP buttons in newsletter editor 2016-02-17 12:25:03 +02:00
9223fbf478 Display loading animation when listing newsletter templates 2016-02-17 12:25:03 +02:00
eb27de36f4 Select active block format and allow custom colors in TinyMCe 2016-02-17 12:25:03 +02:00
636fa38ab6 - Enables check for writable export file 2016-02-16 17:35:20 -05:00
9b1503dc7a Fixed reported issues + refactoring
- refactored Config/Hooks to make it more readable
- added hook to save limit per page
- added default limit per page as a constant in Listing/Handler
2016-02-16 16:33:20 +01:00
2ac3b00af6 Merge pull request #354 from mailpoet/parsley_firefox_fix
fixed parsley issue with firefox
2016-02-16 12:32:29 +02:00
5fcdbfe826 fixed parsley issue with firefox 2016-02-15 21:19:21 +01:00
67036ddb61 cleanup and bugfix on bulk actions 2016-02-15 15:50:47 +01:00
6c0f6a07cd removed useless constant 2016-02-15 15:40:21 +01:00
8139a7dd0a Subscribers page review
- added screen option to set number of items per page
- improved bulk actions in order to handle large sets
2016-02-15 15:40:21 +01:00
97adfc14c0 Bump up version to 0.0.16 2016-02-15 14:50:13 +02:00
4ed703a351 Merge pull request #352 from mailpoet/exception_fix
Fixes remaining exception namescape issues
2016-02-15 13:39:18 +02:00
2aee853406 - Fixes remaining exception namescape issues 2016-02-13 21:39:55 -05:00
854736fac7 - Updates error messages 2016-02-12 14:12:14 -05:00
841c69af59 Merge pull request #348 from mailpoet/page_reviews
Page reviews
2016-02-12 13:36:47 -05:00
56b4688f93 updated unit tests for segments 2016-02-12 19:29:30 +01:00
e60bc7c387 handle duplicates in model 2016-02-12 19:24:04 +01:00
6094a83f4b Merge pull request #350 from mailpoet/rendering_engine_image_update
Updates logic behind image dimensions based on column width
2016-02-12 19:14:49 +02:00
91ddb98f56 Merge pull request #349 from mailpoet/exception_namescape_fix
Fixes namespace issue when catching exceptions
2016-02-12 19:11:25 +02:00
27d5972306 - Updates logic behind image dimensions based on column width 2016-02-12 12:05:27 -05:00
6088497433 Bump up release version to 0.0.15 2016-02-12 18:25:12 +02:00
0d894a6fef - Fixes namespace issue when catching exceptions 2016-02-12 11:20:43 -05:00
57f0b88299 Merge pull request #347 from mailpoet/sending_frequency
Implements sending frequency
2016-02-12 14:23:22 +02:00
5121dbe0c8 Form Editor Round 3
- added prefix to form styles so that it does not conflict when multiple forms are on the same page
- added validation on form save for segments
2016-02-12 12:45:07 +01: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
e928a5c2bc Segments page review
- remove edit link for WordPress users list
- hide trashed segments from Import
- fixed display issue in listing's item actions
2016-02-12 11:30:08 +01: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
418 changed files with 24369 additions and 17914 deletions

View File

@ -89,14 +89,14 @@ class RoboFile extends \Robo\Tasks {
}
function makepot() {
$this->_exec('grunt makepot'.
$this->_exec('./node_modules/.bin/grunt makepot'.
' --gruntfile '.__DIR__.'/tasks/makepot/makepot.js'.
' --base_path '.__DIR__
);
}
function pushpot() {
$this->_exec('grunt pushpot'.
$this->_exec('./node_modules/.bin/grunt pushpot'.
' --gruntfile '.__DIR__.'/tasks/makepot/makepot.js'.
' --base_path '.__DIR__
);
@ -108,11 +108,12 @@ class RoboFile extends \Robo\Tasks {
$this->_exec('vendor/bin/codecept run unit -f '.(($file) ? $file : ''));
}
function testCoverage() {
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'
)));
@ -122,7 +123,7 @@ class RoboFile extends \Robo\Tasks {
$this->compileJs();
$this->_exec(join(' ', array(
'./node_modules/mocha/bin/mocha',
'./node_modules/.bin/mocha',
'-r tests/javascript/mochaTestHelper.js',
'tests/javascript/testBundles/**/*.js'
)));
@ -140,8 +141,28 @@ class RoboFile extends \Robo\Tasks {
$this->_exec('vendor/bin/codecept run -g failed');
}
function qaLint() {
$this->_exec('./tasks/php_lint.sh lib/ tests/');
}
function qaCodeSniffer($severity='errors') {
if ($severity === 'all') {
$severityFlag = '-w';
} else {
$severityFlag = '-n';
}
$this->_exec(
'./vendor/bin/phpcs '.
'--standard=./tasks/code_sniffer/MailPoet '.
'--ignore=./lib/Util/Sudzy/*,./lib/Util/CSS.php,./lib/Util/XLSXWriter.php,'.
'./lib/Config/PopulatorData/Templates/* '.
'lib/ '.
$severityFlag
);
}
protected function loadEnv() {
$dotenv = new Dotenv\Dotenv(__DIR__);
$dotenv->load();
}
}
}

View File

@ -1,6 +1,7 @@
@import 'nib'
@require 'select2/dist/css/select2.css'
@require 'datepicker/datepicker'
@require 'common'
@require 'modal'
@ -8,6 +9,8 @@
@require 'form_editor'
@require 'listing'
@require 'listing/newsletters'
@require 'box'
@require 'breadcrumb'
@ -17,3 +20,5 @@
@require 'settings'
@require 'progress_bar'
@require 'subscribers'

View File

@ -27,13 +27,12 @@
img
min-width: 150px
min-height: 150px
height: auto
width: 110%
position: relative
top: 0
top: 50%
left: 50%
transform: translate(-50%, 0%)
transform: translate(-50%, -50%)
.mailpoet_overlay
position: absolute

View File

@ -0,0 +1,2 @@
@require 'jquery-ui-1.10.1.css'
@require 'melon.datepicker.css'

View File

@ -0,0 +1,649 @@
/*! jQuery UI - v1.10.1 - 2013-03-10
* http://jqueryui.com
* Includes: jquery.ui.core.css, jquery.ui.datepicker.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
* Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT */
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
display: none;
}
.ui-helper-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
border-collapse: collapse;
}
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-clearfix {
min-height: 0; /* support: IE7 */
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0);
}
.ui-front {
z-index: 100;
}
/* Interaction Cues
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-datepicker {
width: 17em;
padding: .2em .2em 0;
display: none;
}
.ui-datepicker .ui-datepicker-header {
position: relative;
padding: .2em 0;
}
.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
position: absolute;
top: 2px;
width: 1.8em;
height: 1.8em;
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
top: 1px;
}
.ui-datepicker .ui-datepicker-prev {
left: 2px;
}
.ui-datepicker .ui-datepicker-next {
right: 2px;
}
.ui-datepicker .ui-datepicker-prev-hover {
left: 1px;
}
.ui-datepicker .ui-datepicker-next-hover {
right: 1px;
}
.ui-datepicker .ui-datepicker-prev span,
.ui-datepicker .ui-datepicker-next span {
display: block;
position: absolute;
left: 50%;
margin-left: -8px;
top: 50%;
margin-top: -8px;
}
.ui-datepicker .ui-datepicker-title {
margin: 0 2.3em;
line-height: 1.8em;
text-align: center;
}
.ui-datepicker .ui-datepicker-title select {
font-size: 1em;
margin: 1px 0;
}
.ui-datepicker select.ui-datepicker-month-year {
width: 100%;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 49%;
}
.ui-datepicker table {
width: 100%;
font-size: .9em;
border-collapse: collapse;
margin: 0 0 .4em;
}
.ui-datepicker th {
padding: .7em .3em;
text-align: center;
font-weight: bold;
border: 0;
}
.ui-datepicker td {
border: 0;
padding: 1px;
}
.ui-datepicker td span,
.ui-datepicker td a {
display: block;
padding: .2em;
text-align: right;
text-decoration: none;
}
.ui-datepicker .ui-datepicker-buttonpane {
background-image: none;
margin: .7em 0 0 0;
padding: 0 .2em;
border-left: 0;
border-right: 0;
border-bottom: 0;
}
.ui-datepicker .ui-datepicker-buttonpane button {
float: right;
margin: .5em .2em .4em;
cursor: pointer;
padding: .2em .6em .3em .6em;
width: auto;
overflow: visible;
}
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
float: left;
}
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi {
width: auto;
}
.ui-datepicker-multi .ui-datepicker-group {
float: left;
}
.ui-datepicker-multi .ui-datepicker-group table {
width: 95%;
margin: 0 auto .4em;
}
.ui-datepicker-multi-2 .ui-datepicker-group {
width: 50%;
}
.ui-datepicker-multi-3 .ui-datepicker-group {
width: 33.3%;
}
.ui-datepicker-multi-4 .ui-datepicker-group {
width: 25%;
}
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
border-left-width: 0;
}
.ui-datepicker-multi .ui-datepicker-buttonpane {
clear: left;
}
.ui-datepicker-row-break {
clear: both;
width: 100%;
font-size: 0;
}
/* RTL support */
.ui-datepicker-rtl {
direction: rtl;
}
.ui-datepicker-rtl .ui-datepicker-prev {
right: 2px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next {
left: 2px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-prev:hover {
right: 1px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next:hover {
left: 1px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane {
clear: right;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
float: left;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
.ui-datepicker-rtl .ui-datepicker-group {
float: right;
}
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
border-right-width: 0;
border-left-width: 1px;
}
/* Component containers
----------------------------------*/
.ui-widget {
font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
font-size: 1.1em;
}
.ui-widget .ui-widget {
font-size: 1em;
}
.ui-widget input,
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
font-size: 1em;
}
.ui-widget-content {
border: 1px solid #dddddd;
background: #eeeeee url(../img/datepicker/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x;
color: #333333;
}
.ui-widget-content a {
color: #333333;
}
.ui-widget-header {
border: 1px solid #e78f08;
background: #f6a828 url(../img/datepicker/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x;
color: #ffffff;
font-weight: bold;
}
.ui-widget-header a {
color: #ffffff;
}
/* Interaction states
----------------------------------*/
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
border: 1px solid #cccccc;
background: #f6f6f6 url(../img/datepicker/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x;
font-weight: bold;
color: #1c94c4;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited {
color: #1c94c4;
text-decoration: none;
}
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus {
border: 1px solid #fbcb09;
background: #fdf5ce url(../img/datepicker/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x;
font-weight: bold;
color: #c77405;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
.ui-state-hover a:visited {
color: #c77405;
text-decoration: none;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active {
border: 1px solid #fbd850;
background: #ffffff url(../img/datepicker/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
font-weight: bold;
color: #eb8f00;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
color: #eb8f00;
text-decoration: none;
}
/* Interaction Cues
----------------------------------*/
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #fed22f;
background: #ffe45c url(../img/datepicker/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x;
color: #363636;
}
.ui-state-highlight a,
.ui-widget-content .ui-state-highlight a,
.ui-widget-header .ui-state-highlight a {
color: #363636;
}
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #cd0a0a;
background: #b81900 url(../img/datepicker/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat;
color: #ffffff;
}
.ui-state-error a,
.ui-widget-content .ui-state-error a,
.ui-widget-header .ui-state-error a {
color: #ffffff;
}
.ui-state-error-text,
.ui-widget-content .ui-state-error-text,
.ui-widget-header .ui-state-error-text {
color: #ffffff;
}
.ui-priority-primary,
.ui-widget-content .ui-priority-primary,
.ui-widget-header .ui-priority-primary {
font-weight: bold;
}
.ui-priority-secondary,
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
filter:Alpha(Opacity=70);
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
filter:Alpha(Opacity=35);
background-image: none;
}
.ui-state-disabled .ui-icon {
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
width: 16px;
height: 16px;
background-position: 16px 16px;
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url(../img/datepicker/ui-icons_222222_256x240.png);
}
.ui-widget-header .ui-icon {
background-image: url(../img/datepicker/ui-icons_ffffff_256x240.png);
}
.ui-state-default .ui-icon {
background-image: url(../img/datepicker/ui-icons_ef8c08_256x240.png);
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon {
background-image: url(../img/datepicker/ui-icons_ef8c08_256x240.png);
}
.ui-state-active .ui-icon {
background-image: url(../img/datepicker/ui-icons_ef8c08_256x240.png);
}
.ui-state-highlight .ui-icon {
background-image: url(../img/datepicker/ui-icons_228ef1_256x240.png);
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url(../img/datepicker/ui-icons_ffd27a_256x240.png);
}
/* positioning */
.ui-icon-carat-1-n { background-position: 0 0; }
.ui-icon-carat-1-ne { background-position: -16px 0; }
.ui-icon-carat-1-e { background-position: -32px 0; }
.ui-icon-carat-1-se { background-position: -48px 0; }
.ui-icon-carat-1-s { background-position: -64px 0; }
.ui-icon-carat-1-sw { background-position: -80px 0; }
.ui-icon-carat-1-w { background-position: -96px 0; }
.ui-icon-carat-1-nw { background-position: -112px 0; }
.ui-icon-carat-2-n-s { background-position: -128px 0; }
.ui-icon-carat-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -64px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -64px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-on { background-position: -96px -144px; }
.ui-icon-radio-off { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
border-top-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
border-top-right-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
border-bottom-left-radius: 4px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
border-bottom-right-radius: 4px;
}
/* Overlays */
.ui-widget-overlay {
background: #666666 url(../img/datepicker/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat;
opacity: .5;
filter: Alpha(Opacity=50);
}
.ui-widget-shadow {
margin: -5px 0 0 -5px;
padding: 5px;
background: #000000 url(../img/datepicker/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x;
opacity: .2;
filter: Alpha(Opacity=20);
border-radius: 5px;
}

View File

@ -0,0 +1,115 @@
/**
* Melon skin from: https://github.com/rtsinani/jquery-datepicker-skins
*/
.wp-admin {
font-size: 90%;
}
.wp-admin .ui-widget {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
background: #2e3641;
border: none;
border-radius: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
}
.wp-admin .ui-datepicker {
padding: 0;
}
.wp-admin .ui-datepicker-header {
border: none;
background: transparent;
font-weight: normal;
font-size: 15px;
}
.wp-admin .ui-datepicker-header .ui-state-hover {
background: transparent;
border-color: transparent;
cursor: pointer;
border-radius: 0;
-webkit-border-radius: 0;
-moz-border-radius: 0;
}
.wp-admin .ui-datepicker .ui-datepicker-title {
margin-top: .4em;
margin-bottom: .3em;
color: #e9f0f4;
}
.wp-admin .ui-datepicker .ui-datepicker-prev-hover,
.wp-admin .ui-datepicker .ui-datepicker-next-hover,
.wp-admin .ui-datepicker .ui-datepicker-next,
.wp-admin .ui-datepicker .ui-datepicker-prev {
top: .9em;
border:none;
}
.wp-admin .ui-datepicker .ui-datepicker-prev-hover {
left: 2px;
}
.wp-admin .ui-datepicker .ui-datepicker-next-hover {
right: 2px;
}
.wp-admin .ui-datepicker .ui-datepicker-next span,
.wp-admin .ui-datepicker .ui-datepicker-prev span {
background-image: url(../img/datepicker/ui-icons_ffffff_256x240.png);
background-position: -32px 0;
margin-top: 0;
top: 0;
font-weight: normal;
}
.wp-admin .ui-datepicker .ui-datepicker-prev span {
background-position: -96px 0;
}
.wp-admin .ui-datepicker table {
margin: 0;
}
.wp-admin .ui-datepicker th {
padding: 1em 0;
color: #ccc;
font-size: 13px;
font-weight: normal;
border: none;
border-top: 1px solid #3a414d;
}
.wp-admin .ui-datepicker td {
background: #f97e76;
border: none;
padding: 0;
}
.wp-admin td .ui-state-default {
background: transparent;
border: none;
text-align: center;
padding: .5em;
margin: 0;
font-weight: normal;
color: #efefef;
font-size: 16px;
}
.wp-admin .ui-state-disabled {
opacity: 1;
}
.wp-admin .ui-state-disabled .ui-state-default {
color: #fba49e;
}
.wp-admin td .ui-state-active,
.wp-admin td .ui-state-hover {
background: #2e3641;
}

View File

@ -1,2 +1,5 @@
.mailpoet_form
margin: 0 0 20px 0
.mailpoet_form td
vertical-align: top !important
vertical-align: top !important

View File

@ -563,3 +563,6 @@ handle_icon = '../img/handle.png'
.CodeMirror
border: 1px solid #eee
/* Settings */
#mailpoet_form_segments.parsley-error + span .select2-selection
border: 1px solid #b94a48

View File

@ -0,0 +1,3 @@
#newsletters_container
h2.nav-tab-wrapper
margin-bottom: 1rem

View File

@ -46,6 +46,7 @@ $master-column-tool-width = 24px
opacity: 0
overflow: hidden
display: block
margin: 0
.mailpoet_delete_block_activated
width: auto
@ -125,7 +126,6 @@ $master-column-tool-width = 24px
border-radius(3px)
background-color: $warning-background-color
padding: 3px 5px
line-height: 1.2em
.mailpoet_delete_block_activate
overflow: hidden

View File

@ -4,7 +4,6 @@
.mailpoet_form_field_title
clear: both
font-size: 1.1em
margin-bottom: 5px
.mailpoet_form_field_title_small
@ -37,3 +36,6 @@
input[type=text]
vertical-align: middle
.mailpoet_form_field_block
display: block

View File

@ -38,9 +38,7 @@
content: '\f142'
.mailpoet_save_show_options_icon
width: auto
height: auto
line-height: auto
vertical-align: middle
&::before
content: '\f140'

View File

@ -17,6 +17,7 @@ $widget-icon-width = 30px
border-left: $content-border-color
border-bottom: $content-border-color
color: $sidebar-text-color
font-size: $sidebar-text-size
.mailpoet_sidebar_region
margin-bottom: 0

View File

@ -3,6 +3,7 @@ $sidepanel-active-heading-color = $primary-active-color
/* Sidepanel */
.mailpoet_editor_settings
color: $sidebar-text-color
font-size: $sidebar-text-size
p
font-size: 1em
@ -18,7 +19,6 @@ $sidepanel-active-heading-color = $primary-active-color
.mailpoet_sidepanel_field_title
clear: both
font-size: 1.1em
margin-bottom: 5px
.mailpoet_sidepanel_field_title_small

View File

@ -21,3 +21,9 @@
.mailpoet_automated_latest_content_display_options
animation-slide-open-downwards()
.mailpoet_automated_latest_content_show_amount
width: 25px
.mailpoet_automated_latest_content_content_type
width: 180px

View File

@ -1,4 +1,5 @@
$block-hover-highlight-color = $primary-active-color
$block-text-line-height = $text-line-height
.mailpoet_block
box-sizing: border-box
@ -22,6 +23,7 @@ $block-hover-highlight-color = $primary-active-color
border: 1px solid $transparent-color
&:hover > .mailpoet_block_highlight
&.mailpoet_highlight > .mailpoet_block_highlight
border: 1px dashed $block-hover-highlight-color
@ -30,3 +32,34 @@ $block-hover-highlight-color = $primary-active-color
.mailpoet_content
position: relative
line-height: $block-text-line-height
p, h1, h2, h3, h4, h5, h6
line-height: $block-text-line-height
padding: 0
margin: 0
font-style: normal
font-weight: normal
h1, h2, h3, h4, h5, h6
margin-bottom: 0.3em
p
margin-top: 0
margin-bottom: 0
font-weight: normal
ul
padding: 0
margin-top: 10px
margin-bottom: 10px
li
margin-top: 0
font-weight: normal
margin-bottom: 10px
blockquote
margin: 0 0 $block-text-line-height
padding-left: 10px
border-left: 2px #565656 solid

View File

@ -1,9 +1,4 @@
$button-vertical-padding = 13px
.mailpoet_button_block
padding-top: $button-vertical-padding
padding-bottom: $button-vertical-padding
overflow: hidden
.mailpoet_editor_button

View File

@ -17,7 +17,7 @@ $three-column-width = ($newsletter-width / 3) - (2 * $column-margin)
padding-left: 0
padding-right: 0
&:hover
&:hover > .mailpoet_block_highlight
border: 0
.mailpoet_container_vertical > *

View File

@ -1,6 +1,10 @@
.mailpoet_footer_block
padding-left: 0
padding-right: 0
margin-bottom: 0
.mailpoet_content
padding: 5px 20px
padding: 10px 20px
& > *:last-child
margin-bottom: 0

View File

@ -1,6 +1,10 @@
.mailpoet_header_block
padding-left: 0
padding-right: 0
margin-bottom: 0
.mailpoet_content
padding: 5px 20px
padding: 10px 20px
& > *:last-child
margin-bottom: 0

View File

@ -11,9 +11,6 @@
padding-right: 0
margin-bottom: 0
img
width: 100%
.mailpoet_content a:hover
cursor: all-scroll

View File

@ -1,4 +1,4 @@
$social-block-vertical-padding = 11px
$social-block-vertical-padding = 0px
$social-icon-width = 32px
$active-social-icon-set-border-color = #adadad

View File

@ -1,17 +1,13 @@
$text-vertical-padding = 3px
.mailpoet_text_block
padding-left: 0
padding-right: 0
& > .mailpoet_content
overflow: hidden
padding-top: 13px
padding-bottom: 13px
padding-top: 0
padding-bottom: 0px
padding-left: 20px
padding-right: 20px
blockquote
margin: 1em
padding-left: 1em
border-left: 2px #565656 solid
& > *:last-child
margin-bottom: 0

View File

@ -31,7 +31,7 @@ div.mce-toolbar-grp.mce-container
box-shadow(0px 0px 3px 1px rgba(0, 0, 0, 0.05))
.mce-window
/* Fix TinyMCE mailpoet_custom_fields window lack of hiding overflow */
/* Fix TinyMCE mailpoet_shortcodes window lack of hiding overflow */
div.mce-container-body.mce-abs-layout
overflow: hidden
@ -40,8 +40,8 @@ div.mce-toolbar-grp.mce-container
width: -webkit-calc( 100% - 36px )
width: calc( 100% - 36px )
/* TinyMCE mailpoet_custom_fields toolbar icon */
.mce-i-mailpoet_custom_fields:before
/* TinyMCE mailpoet_shortcodes toolbar icon */
.mce-i-mailpoet_shortcodes:before
font: 400 20px/1 dashicons!important
content: "\f307"
@ -84,7 +84,7 @@ position: relative
body
overflow-x: auto
/* Hide the "Details" section of Wordpress Media manager */
/* Hide the "Details" section of WordPress Media manager */
.media-sidebar
display: none
@ -98,7 +98,7 @@ body
.attachments-browser .uploader-inline
right: 0
/* Remove max width from date selector in Wordpress Media Manager */
/* Remove max width from date selector in WordPress Media Manager */
#media-attachment-date-filters
max-width: calc(100% - 12px)
@ -133,3 +133,17 @@ body
.wrap > .mailpoet_notice,
.update-nag
margin-left: 2px + 15px !important
/* Make a button group */
.mailpoet_button_group
.button:first-child
border-right: 0
border-top-right-radius: 0
border-bottom-right-radius: 0
.button:last-child
border-left: 0
border-top-left-radius: 0
border-bottom-left-radius: 0

View File

@ -24,3 +24,6 @@ $error-text-color = #d54e21
// Dimensions
$newsletter-width = 660px
$text-line-height = 1.6em
$sidebar-text-size = 13px

View File

@ -18,6 +18,7 @@ textarea.parsley-error
list-style-type none
font-size 0.9em
line-height 0.9em
color #B94A48
opacity 0
transition all .3s ease-in
-o-transition all .3s ease-in

View File

@ -2,3 +2,13 @@
@require 'parsley'
@require 'form_validation'
/* labels */
.mailpoet_text_label
.mailpoet_textarea_label
.mailpoet_select_label
.mailpoet_radio_label
.mailpoet_checkbox_label
.mailpoet_list_label
.mailpoet_date_label
display:block

View File

@ -1,14 +1,10 @@
#mailpoet_settings
// common
.mailpoet_panel
display: none
display none
.form-table th
width:20em
// advanced
#mailpoet_role_permissions
margin-top: 20px;
width 20em
// sending methods
.mailpoet_sending_methods
@ -28,8 +24,7 @@
line-height 54px
font-size 1.5em
.mailpoet_description
line-height 1.5em
font-size 1.1em
font-size 14px
.mailpoet_status
background-color #2f2f2f
color #fff
@ -60,11 +55,11 @@
// responsive
@media screen and (max-width: 782px)
.form-table th
width: auto
width auto
.mailpoet_sending_methods
li
float none
width: auto
margin-right: 0
width auto
margin-right 0

View File

@ -0,0 +1,3 @@
#subscribers_container
.mailpoet_segments_unsubscribed
color: lighten(#555, 33)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1 +0,0 @@
../src/newsletter_editor/tinymce/mailpoet_custom_fields

View File

@ -0,0 +1 @@
../src/newsletter_editor/tinymce/mailpoet_shortcodes

View File

@ -48,28 +48,22 @@ define(
})
.done(function(response) {
if(!response.result) {
MailPoet.Notice.error(MailPoetI18n.daemonControlError);
MailPoet.Notice.error(MailPoet.I18n.t('daemonControlError'));
}
}.bind(this));
},
render: function() {
if(this.state.status === 'loading') {
return(<div>Loading daemon status...</div>);
return(<div>{MailPoet.I18n.t('loadingDaemonStatus')}</div>);
}
switch(this.state.status) {
case 'started':
return(
<div>
Cron daemon is running.
{MailPoet.I18n.t('cronDaemonIsRunning')}
<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>
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>{MailPoet.I18n.t('stop')}</a>
</div>
);
break;
@ -77,17 +71,17 @@ define(
case 'stopping':
return(
<div>
Daemon is {this.state.status}
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
</div>
);
break;
case 'stopped':
return(
<div>
Daemon is {this.state.status}
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
<br />
<br />
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>Start</a>
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>{MailPoet.I18n.t('start')}</a>
</div>
);
break;
@ -103,4 +97,4 @@ define(
container
);
}
});
});

146
assets/js/src/date.js Normal file
View File

@ -0,0 +1,146 @@
define('date',
[
'mailpoet',
'jquery',
'moment'
], function(
MailPoet,
jQuery,
Moment
) {
'use strict';
MailPoet.Date = {
version: 0.1,
options: {},
defaults: {
offset: 0,
format: 'F, d Y H:i:s'
},
init: function(options) {
options = options || {};
// set UTC offset
if (
options.offset === undefined
&& window.mailpoet_date_offset !== undefined
) {
options.offset = window.mailpoet_date_offset;
}
// set date format
if (
options.format === undefined
&& window.mailpoet_date_format !== undefined
) {
options.format = window.mailpoet_date_format;
}
// merge options
this.options = jQuery.extend({}, this.defaults, options);
return this;
},
format: function(date, options) {
options = options || {};
this.init(options);
var date = Moment(date, this.convertFormat(options.parseFormat));
if (options.offset === 0) date = date.utc();
return date.format(this.convertFormat(this.options.format));
},
toDate: function(date, options) {
options = options || {};
this.init(options);
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
},
short: function(date) {
return this.format(date, {
format: 'F, j Y'
});
},
full: function(date) {
return this.format(date, {
format: 'F, j Y H:i:s'
});
},
time: function(date) {
return this.format(date, {
format: 'H:i:s'
});
},
convertFormat: function(format) {
var format_mappings = {
date: {
D: 'ddd',
l: 'dddd',
d: 'DD',
j: 'D',
z: 'DDDD',
N: 'E',
S: '',
M: 'MMM',
F: 'MMMM',
m: 'MM',
n: '',
t: '',
y: 'YY',
Y: 'YYYY',
H: 'HH',
h: 'hh',
g: 'h',
A: 'A',
i: 'mm',
s: 'ss',
T: 'z',
O: 'ZZ',
w: 'd',
W: 'WW'
},
strftime: {
a: 'ddd',
A: 'dddd',
b: 'MMM',
B: 'MMMM',
d: 'DD',
e: 'D',
F: 'YYYY-MM-DD',
H: 'HH',
I: 'hh',
j: 'DDDD',
k: 'H',
l: 'h',
m: 'MM',
M: 'mm',
p: 'A',
S: 'ss',
u: 'E',
w: 'd',
W: 'WW',
y: 'YY',
Y: 'YYYY',
z: 'ZZ',
Z: 'z'
}
};
if (!format || format.length <= 0) return format;
var replacements = format_mappings['date'];
var outputFormat = '';
Object.keys(replacements).forEach(function(key) {
if (format.indexOf(key) !== -1) {
format = format.replace(key, '%'+key);
}
});
outputFormat = format;
Object.keys(replacements).forEach(function(key) {
if (outputFormat.indexOf('%'+key) !== -1) {
outputFormat = outputFormat.replace('%'+key, replacements[key]);
}
});
return outputFormat;
}
};
});

View File

@ -6,14 +6,19 @@ function(
) {
const FormFieldCheckbox = React.createClass({
onValueChange: function(e) {
e.target.value = this.refs.checkbox.checked ? '1' : '';
e.target.value = this.refs.checkbox.checked ? '1' : '0';
return this.props.onValueChange(e);
},
render: function() {
const isChecked = !!(this.props.item[this.props.field.name]);
if (this.props.field.values === undefined) {
return false;
}
// isChecked will be true only if the value is "1"
// it will be false in case value is "0" or empty
const isChecked = !!(~~(this.props.item[this.props.field.name]));
const options = Object.keys(this.props.field.values).map(
function(value, index) {
(value, index) => {
return (
<p key={ 'checkbox-' + index }>
<label>
@ -29,7 +34,7 @@ function(
</label>
</p>
);
}.bind(this)
}
);
return (

View File

@ -9,6 +9,13 @@ define([
render() {
const yearsRange = 100;
const years = [];
if (this.props.placeholder !== undefined) {
years.push((
<option value="" key={ 0 }>{ this.props.placeholder }</option>
));
}
const currentYear = Moment().year();
for (let i = currentYear; i >= currentYear - yearsRange; i--) {
years.push((
@ -20,7 +27,7 @@ define([
}
return (
<select
name={ this.props.name + '[year]' }
name={ `${this.props.name}[year]` }
value={ this.props.year }
onChange={ this.props.onValueChange }
>
@ -33,6 +40,13 @@ define([
class FormFieldDateMonth extends React.Component {
render() {
const months = [];
if (this.props.placeholder !== undefined) {
months.push((
<option value="" key={ 0 }>{ this.props.placeholder }</option>
));
}
for (let i = 1; i <= 12; i++) {
months.push((
<option
@ -43,7 +57,7 @@ define([
}
return (
<select
name={ this.props.name + '[month]' }
name={ `${this.props.name}[month]` }
value={ this.props.month }
onChange={ this.props.onValueChange }
>
@ -56,6 +70,13 @@ define([
class FormFieldDateDay extends React.Component {
render() {
const days = [];
if (this.props.placeholder !== undefined) {
days.push((
<option value="" key={ 0 }>{ this.props.placeholder }</option>
));
}
for (let i = 1; i <= 31; i++) {
days.push((
<option
@ -67,7 +88,7 @@ define([
return (
<select
name={ this.props.name + '[day]' }
name={ `${this.props.name}[day]` }
value={ this.props.day }
onChange={ this.props.onValueChange }
>
@ -81,47 +102,99 @@ define([
constructor(props) {
super(props);
this.state = {
year: Moment().year(),
month: 1,
day: 1
year: '',
month: '',
day: ''
}
}
componentDidMount() {
this.extractDateParts();
}
componentDidUpdate(prevProps, prevState) {
if (
(this.props.item !== undefined && prevProps.item !== undefined)
&& (this.props.item.id !== prevProps.item.id)
) {
this.extractTimeStamp();
this.extractDateParts();
}
}
extractTimeStamp() {
const timeStamp = parseInt(this.props.item[this.props.field.name], 10);
extractDateParts() {
const value = (this.props.item[this.props.field.name] !== undefined)
? this.props.item[this.props.field.name].trim()
: '';
if(value === '') {
return;
}
const dateType = this.props.field.params.date_type;
const dateParts = value.split('-');
let year = '';
let month = '';
let day = '';
switch(dateType) {
case 'year_month_day':
year = ~~(dateParts[0]);
month = ~~(dateParts[1]);
day = ~~(dateParts[2]);
break;
case 'year_month':
year = ~~(dateParts[0]);
month = ~~(dateParts[1]);
break;
case 'month':
month = ~~(dateParts[0]);
break;
case 'year':
year = ~~(dateParts[0]);
break;
}
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()
year: year,
month: month,
day: day
});
}
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
}
});
formatValue() {
const dateType = this.props.field.params.date_type;
let value;
switch(dateType) {
case 'year_month_day':
value = {
'year': this.state.year,
'month': this.state.month,
'day': this.state.day
};
break;
case 'year_month':
value = {
'year': this.state.year,
'month': this.state.month
};
break;
case 'month':
value = {
'month': this.state.month
};
break;
case 'year':
value = {
'year': this.state.year
};
break;
}
return value;
}
onValueChange(e) {
// extract property from name
@ -133,35 +206,40 @@ define([
field = matches[1];
property = matches[2];
let value = parseInt(e.target.value, 10);
let value = ~~(e.target.value);
this.setState({
[`${property}`]: value
}, () => {
this.updateTimeStamp(field);
this.props.onValueChange({
target: {
name: field,
value: this.formatValue()
}
});
});
}
}
render() {
const monthNames = window.mailpoet_month_names || [];
const dateFormats = window.mailpoet_date_formats || {};
const dateType = this.props.field.params.date_type;
const dateSelects = dateType.split('_');
const dateSelects = dateFormats[dateType][0].split('/');
const fields = dateSelects.map(type => {
switch(type) {
case 'year':
case 'yyyy':
return (<FormFieldDateYear
onValueChange={ this.onValueChange.bind(this) }
ref={ 'year' }
key={ 'year' }
name={ this.props.field.name }
year={ this.state.year }
placeholder={ this.props.field.year_placeholder }
/>);
break;
case 'month':
case 'mm':
return (<FormFieldDateMonth
onValueChange={ this.onValueChange.bind(this) }
ref={ 'month' }
@ -169,16 +247,18 @@ define([
name={ this.props.field.name }
month={ this.state.month }
monthNames={ monthNames }
placeholder={ this.props.field.month_placeholder }
/>);
break;
case 'day':
case 'dd':
return (<FormFieldDateDay
onValueChange={ this.onValueChange.bind(this) }
ref={ 'day' }
key={ 'day' }
name={ this.props.field.name }
day={ this.state.day }
placeholder={ this.props.field.day_placeholder }
/>);
break;
}

View File

@ -61,6 +61,10 @@ function(
case 'date':
field = (<FormFieldDate {...data} />);
break;
case 'reactComponent':
field = (<data.field.component {...data} />);
break;
}
if(inline === true) {
@ -121,4 +125,4 @@ function(
});
return FormField;
});
});

View File

@ -4,12 +4,15 @@ define([
function(
React
) {
var FormFieldRadio = React.createClass({
const FormFieldRadio = React.createClass({
render: function() {
var selected_value = this.props.item[this.props.field.name];
if (this.props.field.values === undefined) {
return false;
}
var options = Object.keys(this.props.field.values).map(
function(value, index) {
const selected_value = this.props.item[this.props.field.name];
const options = Object.keys(this.props.field.values).map(
(value, index) => {
return (
<p key={ 'radio-' + index }>
<label>
@ -23,7 +26,7 @@ function(
</label>
</p>
);
}.bind(this)
}
);
return (

View File

@ -1,36 +1,76 @@
define([
'react'
],
function(
React
) {
var FormFieldSelect = React.createClass({
render: function() {
var options =
Object.keys(this.props.field.values).map(function(value, index) {
return (
<option
key={ 'option-' + index }
value={ value }>
{ this.props.field.values[value] }
</option>
);
}.bind(this)
);
import React from 'react'
import _ from 'underscore'
return (
<select
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange }
{...this.props.field.validation}
>
{options}
</select>
const FormFieldSelect = React.createClass({
render() {
if (this.props.field.values === undefined) {
return false;
}
let filter = false;
let placeholder = false;
let sortBy = false;
if (this.props.field.placeholder !== undefined) {
placeholder = (
<option value="">{ this.props.field.placeholder }</option>
);
}
});
return FormFieldSelect;
});
if (this.props.field['filter'] !== undefined) {
filter = this.props.field.filter;
}
if (_.isFunction(this.props.field.sortBy)) {
sortBy = this.props.field.sortBy;
}
let keys;
if (sortBy) {
// Extract keys from sorted [key, value] select value pairs, sorted by
// provided sorting order.
keys =
_.map(
_.sortBy(
_.pairs(this.props.field.values),
(item) => sortBy(item[0], item[1])
),
(item) => item[0]
);
} else {
keys = Object.keys(this.props.field.values)
}
const options = keys.map(
(value, index) => {
if (filter !== false && filter(this.props.item, value) === false) {
return;
}
return (
<option
key={ 'option-' + index }
value={ value }>
{ this.props.field.values[value] }
</option>
);
}
);
return (
<select
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange }
{...this.props.field.validation}
>
{placeholder}
{options}
</select>
);
}
});
module.exports = FormFieldSelect;

View File

@ -13,12 +13,22 @@ function(
getInitialState: function() {
return {
items: [],
initialized: false
}
select2: false
};
},
componentWillMount: function() {
this.loadCachedItems();
},
allowMultipleValues: function() {
return (this.props.field.multiple === true);
},
isSelect2Initialized: function() {
return (this.state.select2 === true);
},
componentDidMount: function() {
this.loadCachedItems();
this.setupSelect2();
if(this.allowMultipleValues()) {
this.setupSelect2();
}
},
componentDidUpdate: function(prevProps, prevState) {
if(
@ -26,16 +36,22 @@ function(
&& (this.props.item.id !== prevProps.item.id)
) {
jQuery('#'+this.refs.select.id)
.val(this.props.item[this.props.field.name])
.val(this.getSelectedValues())
.trigger('change');
}
},
componentWillUnmount: function() {
if(this.allowMultipleValues()) {
this.destroySelect2();
}
},
destroySelect2: function() {
if(this.isSelect2Initialized()) {
jQuery('#'+this.refs.select.id).select2('destroy');
}
},
setupSelect2: function() {
if(
!this.props.field.multiple
|| this.state.initialized === true
|| this.refs.select === undefined
) {
if(this.isSelect2Initialized()) {
return;
}
@ -45,7 +61,11 @@ function(
if(item.element && item.element.selected) {
return null;
} else {
return item.text;
if(item.title) {
return item.title;
} else {
return item.text;
}
}
}
});
@ -63,17 +83,22 @@ function(
select2.on('change', this.handleChange);
select2.select2(
'val',
this.props.item[this.props.field.name]
);
this.setState({ initialized: true });
this.setState({ select2: true });
},
getSelectedValues: function() {
if(this.props.field['selected'] !== undefined) {
return this.props.field['selected'](this.props.item);
} else if(this.props.item !== undefined && this.props.field.name !== undefined) {
return this.props.item[this.props.field.name];
} else {
return null;
}
},
loadCachedItems: function() {
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
var items = window['mailpoet_'+this.props.field.endpoint];
if(this.props.field['filter'] !== undefined) {
items = items.filter(this.props.field.filter);
}
@ -98,31 +123,48 @@ function(
});
}
},
getLabel: function(item) {
if(this.props.field['getLabel'] !== undefined) {
return this.props.field.getLabel(item, this.props.item);
}
return item.name;
},
getSearchLabel: function(item) {
if(this.props.field['getSearchLabel'] !== undefined) {
return this.props.field.getSearchLabel(item, this.props.item);
}
return null;
},
getValue: function(item) {
if(this.props.field['getValue'] !== undefined) {
return this.props.field.getValue(item, this.props.item);
}
return item.id;
},
render: function() {
var options = this.state.items.map(function(item, index) {
const options = this.state.items.map((item, index) => {
let label = this.getLabel(item);
let searchLabel = this.getSearchLabel(item);
let value = this.getValue(item);
return (
<option
key={ item.id }
value={ item.id }
key={ 'option-'+index }
value={ value }
title={ searchLabel }
>
{ item.name }
{ label }
</option>
);
});
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 }
defaultValue={ this.getSelectedValues() }
{...this.props.field.validation}
>{ options }</select>
);
@ -130,4 +172,4 @@ function(
});
return Selection;
});
});

View File

@ -1,33 +1,35 @@
define([
'react'
],
function(
React
) {
var FormFieldText = React.createClass({
render: function() {
var value = this.props.item[this.props.field.name];
if(!value) { value = null; }
return (
<input
type="text"
className={ (this.props.field.size) ? '' : 'regular-text' }
size={
(this.props.field.size !== 'auto' && this.props.field.size > 0)
? this.props.field.size
: false
}
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ value }
placeholder={ this.props.field.placeholder }
defaultValue={ this.props.field.defaultValue }
onChange={ this.props.onValueChange }
{...this.props.field.validation}
/>
);
}
});
import React from 'react'
return FormFieldText;
});
const FormFieldText = React.createClass({
render() {
let value = this.props.item[this.props.field.name];
if (value === undefined) {
value = this.props.field.defaultValue || '';
}
return (
<input
type="text"
disabled={
(this.props.field['disabled'] !== undefined)
? this.props.field.disabled(this.props.item)
: false
}
className={ (this.props.field.size) ? '' : 'regular-text' }
size={
(this.props.field.size !== 'auto' && this.props.field.size > 0)
? this.props.field.size
: false
}
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ value }
placeholder={ this.props.field.placeholder }
onChange={ this.props.onValueChange }
{...this.props.field.validation}
/>
);
}
});
module.exports = FormFieldText;

View File

@ -13,10 +13,16 @@ define(
Router,
FormField
) {
var Form = React.createClass({
mixins: [
Router.History
],
contextTypes: {
router: React.PropTypes.object.isRequired
},
getDefaultProps: function() {
return {
params: {},
};
},
getInitialState: function() {
return {
loading: false,
@ -24,10 +30,20 @@ define(
item: {}
};
},
getValues: function() {
return this.props.item ? this.props.item : this.state.item;
},
getErrors: function() {
return this.props.errors ? this.props.errors : this.state.errors;
},
componentDidMount: function() {
if(this.props.params.id !== undefined) {
if(this.isMounted()) {
if(this.isMounted()) {
if(this.props.params.id !== undefined) {
this.loadItem(this.props.params.id);
} else {
this.setState({
item: jQuery('.mailpoet_form').serializeObject()
});
}
}
},
@ -37,7 +53,9 @@ define(
loading: false,
item: {}
});
this.refs.form.reset();
if (props.item === undefined) {
this.refs.form.reset();
}
} else {
this.loadItem(props.params.id);
}
@ -55,7 +73,7 @@ define(
loading: false,
item: {}
}, function() {
this.history.pushState(null, '/new');
this.context.router.push('/new');
}.bind(this));
} else {
this.setState({
@ -88,7 +106,6 @@ define(
item[field.name] = this.state.item[field.name];
}
}.bind(this));
// set id if specified
if(this.props.params.id !== undefined) {
item.id = this.props.params.id;
@ -105,7 +122,7 @@ define(
if(this.props.onSuccess !== undefined) {
this.props.onSuccess();
} else {
this.history.pushState(null, '/')
this.context.router.push('/');
}
if(this.props.params.id !== undefined) {
@ -123,19 +140,23 @@ define(
}.bind(this));
},
handleValueChange: function(e) {
var item = this.state.item,
field = e.target.name;
if (this.props.onChange) {
return this.props.onChange(e);
} else {
var item = this.state.item,
field = e.target.name;
item[field] = e.target.value;
item[field] = e.target.value;
this.setState({
item: item
});
return true;
this.setState({
item: item
});
return true;
}
},
render: function() {
if(this.state.errors !== undefined) {
var errors = this.state.errors.map(function(error, index) {
if(this.getErrors() !== undefined) {
var errors = this.getErrors().map(function(error, index) {
return (
<p key={ 'error-'+index } className="mailpoet_error">
{ error }
@ -146,14 +167,25 @@ define(
var formClasses = classNames(
'mailpoet_form',
{ 'mailpoet_form_loading': this.state.loading }
{ 'mailpoet_form_loading': this.state.loading || this.props.loading }
);
var beforeFormContent = false;
var afterFormContent = false;
if (this.props.beforeFormContent !== undefined) {
beforeFormContent = this.props.beforeFormContent(this.getValues());
}
if (this.props.afterFormContent !== undefined) {
afterFormContent = this.props.afterFormContent(this.getValues());
}
var fields = this.props.fields.map(function(field, i) {
return (
<FormField
field={ field }
item={ this.state.item }
item={ this.getValues() }
onValueChange={ this.handleValueChange }
key={ 'field-'+i } />
);
@ -167,36 +199,40 @@ define(
<input
className="button button-primary"
type="submit"
value="Save"
value={MailPoet.I18n.t('save')}
disabled={this.state.loading} />
);
}
return (
<form
id={ this.props.id }
ref="form"
className={ formClasses }
onSubmit={
(this.props.onSubmit !== undefined)
? this.props.onSubmit
: this.handleSubmit
}
>
{ errors }
<div>
{ beforeFormContent }
<form
id={ this.props.id }
ref="form"
className={ formClasses }
onSubmit={
(this.props.onSubmit !== undefined)
? this.props.onSubmit
: this.handleSubmit
}
>
{ errors }
<table className="form-table">
<tbody>
{fields}
</tbody>
</table>
<table className="form-table">
<tbody>
{fields}
</tbody>
</table>
{ actions }
</form>
{ actions }
</form>
{ afterFormContent }
</div>
);
}
});
return Form;
}
);
);

View File

@ -402,11 +402,30 @@ var WysijaForm = {
}
});
// hide list selection if a list widget has been dragged into the editor
$('mailpoet_settings_segment_selection')[
(($$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]').length > 0) === true)
? 'hide' : 'show'
]();
var hasSegmentSelection = WysijaForm.hasSegmentSelection();
if(hasSegmentSelection) {
$('mailpoet_form_segments').writeAttribute('required', false).disable();
$('mailpoet_settings_segment_selection').hide();
} else {
$('mailpoet_form_segments').writeAttribute('required', true).enable();
$('mailpoet_settings_segment_selection').show();
}
},
hasSegmentSelection: function() {
return ($$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]').length > 0);
},
isSegmentSelectionValid: function() {
var segment_selection = $$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]')[0];
if(segment_selection !== undefined) {
var block = WysijaForm.get(segment_selection).block.getData();
return (
(block.params.values !== undefined)
&&
(block.params.values.length > 0)
);
}
return false;
},
setBlockPositions: function(event, target) {
// release dragging lock

View File

@ -1,10 +1,10 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, Route, IndexRoute } from 'react-router'
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
import FormList from 'forms/list.jsx'
import createHashHistory from 'history/lib/createHashHistory'
let history = createHashHistory({ queryKey: false })
const history = useRouterHistory(createHashHistory)({ queryKey: false });
const App = React.createClass({
render() {
@ -12,7 +12,7 @@ const App = React.createClass({
}
});
let container = document.getElementById('forms_container');
const container = document.getElementById('forms_container');
if(container) {
ReactDOM.render((

View File

@ -8,100 +8,100 @@ import MailPoet from 'mailpoet'
const columns = [
{
name: 'name',
label: 'Name',
label: MailPoet.I18n.t('formName'),
sortable: true
},
{
name: 'segments',
label: 'Lists',
sortable: false
label: MailPoet.I18n.t('segments')
},
{
name: 'created_at',
label: 'Created on',
label: MailPoet.I18n.t('createdOn'),
sortable: true
}
];
const messages = {
onTrash: function(response) {
if(response) {
let 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);
}
var count = ~~response;
var message = null;
if(message !== null) {
MailPoet.Notice.success(message);
}
if(count === 1) {
message = (
MailPoet.I18n.t('oneFormTrashed')
);
} else {
message = (
MailPoet.I18n.t('multipleFormsTrashed')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onDelete: function(response) {
if(response) {
let message = null;
if(~~response === 1) {
message = (
'1 form was permanently deleted.'
);
} else if(~~response > 1) {
message = (
'%$1d forms were permanently deleted.'
).replace('%$1d', ~~response);
}
var count = ~~response;
var message = null;
if(message !== null) {
MailPoet.Notice.success(message);
}
if(count === 1) {
message = (
MailPoet.I18n.t('oneFormDeleted')
);
} else {
message = (
MailPoet.I18n.t('multipleFormsDeleted')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onRestore: function(response) {
if(response) {
let 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);
}
var count = ~~response;
var message = null;
if(message !== null) {
MailPoet.Notice.success(message);
}
if(count === 1) {
message = (
MailPoet.I18n.t('oneFormRestored')
);
} else {
message = (
MailPoet.I18n.t('multipleFormsRestored')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
}
};
const bulk_actions = [
{
name: 'trash',
label: MailPoet.I18n.t('trash'),
onSuccess: messages.onTrash
}
];
const item_actions = [
{
name: 'edit',
label: 'Edit',
label: MailPoet.I18n.t('edit'),
link: function(item) {
return (
<a href={ `admin.php?page=mailpoet-form-editor&id=${item.id}` }>Edit</a>
<a href={ `admin.php?page=mailpoet-form-editor&id=${item.id}` }>{MailPoet.I18n.t('edit')}</a>
);
}
},
{
name: 'duplicate_form',
label: 'Duplicate',
name: 'duplicate',
label: MailPoet.I18n.t('duplicate'),
onClick: function(item, refresh) {
return MailPoet.Ajax.post({
endpoint: 'forms',
action: 'duplicate',
data: item.id
}).done(function(response) {
MailPoet.Notice.success(
('Form "%$1s" has been duplicated.').replace('%$1s', response.name)
);
if (response !== false && response['name'] !== undefined) {
MailPoet.Notice.success(
(MailPoet.I18n.t('formDuplicated')).replace('%$1s', response.name)
);
}
refresh();
});
}
@ -111,14 +111,6 @@ const item_actions = [
}
];
const bulk_actions = [
{
name: 'trash',
label: 'Trash',
onSuccess: messages.onTrash
}
];
const FormList = React.createClass({
createForm() {
MailPoet.Ajax.post({
@ -147,15 +139,18 @@ const FormList = React.createClass({
<div>
<td className={ row_classes }>
<strong>
<a>{ form.name }</a>
<a
className="row-title"
href={ `admin.php?page=mailpoet-form-editor&id=${form.id}` }
>{ form.name }</a>
</strong>
{ actions }
</td>
<td className="column-format" data-colname="Lists">
<td className="column-format" data-colname={MailPoet.I18n.t('segments')}>
{ segments }
</td>
<td className="column-date" data-colname="Created on">
<abbr>{ form.created_at }</abbr>
<td className="column-date" data-colname={MailPoet.I18n.t('createdOn')}>
<abbr>{ MailPoet.Date.format(form.created_at) }</abbr>
</td>
</div>
);
@ -163,20 +158,20 @@ const FormList = React.createClass({
render() {
return (
<div>
<h2 className="title">
Forms <a
className="add-new-h2"
<h1 className="title">
{MailPoet.I18n.t('pageTitle')} <a
className="page-title-action"
href="javascript:;"
onClick={ this.createForm }
>New</a>
</h2>
>{MailPoet.I18n.t('new')}</a>
</h1>
<Listing
limit={ mailpoet_listing_per_page }
location={ this.props.location }
params={ this.props.params }
messages={ messages }
search={ false }
limit={ 1000 }
endpoint="forms"
onRenderItem={ this.renderItem }
columns={ columns }
@ -188,4 +183,4 @@ const FormList = React.createClass({
}
});
module.exports = FormList;
module.exports = FormList;

View File

@ -140,5 +140,20 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
return parseInt(string, 10);
});
Handlebars.registerHelper('fontWithFallback', function(font) {
switch(font) {
case 'Arial': return new Handlebars.SafeString("Arial, 'Helvetica Neue', Helvetica, sans-serif");
case 'Comic Sans MS': return new Handlebars.SafeString("'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif");
case 'Courier New': return new Handlebars.SafeString("'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace");
case 'Georgia': return new Handlebars.SafeString("Georgia, Times, 'Times New Roman', serif");
case 'Lucida': return new Handlebars.SafeString("'Lucida Sans Unicode', 'Lucida Grande', sans-serif");
case 'Tahoma': return new Handlebars.SafeString("Tahoma, Verdana, Segoe, sans-serif");
case 'Times New Roman': return new Handlebars.SafeString("'Times New Roman', Times, Baskerville, Georgia, serif");
case 'Trebuchet MS': return new Handlebars.SafeString("'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif");
case 'Verdana': return new Handlebars.SafeString("Verdana, Geneva, sans-serif");
default: return font;
}
});
window.Handlebars = Handlebars;
});

25
assets/js/src/i18n.js Normal file
View File

@ -0,0 +1,25 @@
define('i18n',
[
'mailpoet',
'underscore',
], function(
MailPoet,
_
) {
'use strict';
var translations = {};
MailPoet.I18n = {
add: function(key, value) {
translations[key] = value;
},
t: function(key) {
return translations[key] || 'TRANSLATION "%$1s" NOT FOUND'.replace("%$1s", key);
},
all: function() {
return translations;
}
};
});

23
assets/js/src/iframe.js Normal file
View File

@ -0,0 +1,23 @@
define('iframe', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
'use strict';
MailPoet.Iframe = {
marginY: 20,
autoSize: function(iframe) {
if(!iframe) return;
this.setSize(
iframe,
iframe.contentWindow.document.body.scrollHeight
);
},
setSize: function(iframe, i) {
if(!iframe) return;
iframe.style.height = (
parseInt(i) + this.marginY
) + "px";
}
};
return MailPoet;
});

View File

@ -1,8 +1,10 @@
define([
'react'
'react',
'mailpoet'
],
function(
React
React,
MailPoet
) {
var ListingBulkActions = React.createClass({
getInitialState: function() {
@ -45,13 +47,13 @@ function(
data.action = this.state.action;
var callback = function() {};
var onSuccess = function() {};
if(action['onSuccess'] !== undefined) {
callback = action.onSuccess;
onSuccess = action.onSuccess;
}
if(data.action) {
this.props.onBulkAction(selected_ids, data, callback);
this.props.onBulkAction(selected_ids, data).then(onSuccess);
}
this.setState({
@ -82,7 +84,7 @@ function(
<label
className="screen-reader-text"
htmlFor="bulk-action-selector-top">
Select bulk action
{MailPoet.I18n.t('selectBulkAction')}
</label>
<select
@ -91,7 +93,7 @@ function(
value={ this.state.action }
onChange={this.handleChangeAction}
>
<option value="">Bulk Actions</option>
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
{ this.props.bulk_actions.map(function(action, index) {
return (
<option
@ -104,7 +106,7 @@ function(
<input
onClick={ this.handleApplyAction }
type="submit"
defaultValue="Apply"
defaultValue={MailPoet.I18n.t('apply')}
className="button action" />
{ this.state.extra }
@ -114,4 +116,4 @@ function(
});
return ListingBulkActions;
});
});

View File

@ -1,22 +1,26 @@
define([
'react',
'jquery'
'jquery',
'mailpoet'
],
function(
React,
jQuery
jQuery,
MailPoet
) {
var ListingFilters = React.createClass({
handleFilterAction: function() {
let filters = {}
let filters = {};
this.getAvailableFilters().map((filter, i) => {
filters[this.refs['filter-'+i].name] = this.refs['filter-'+i].value
})
});
return this.props.onSelectFilter(filters);
},
handleEmptyTrash: function() {
return this.props.onEmptyTrash();
},
getAvailableFilters: function() {
let filters = this.props.filters;
return Object.keys(filters).filter(function(filter) {
return !(
filters[filter].length === 0
@ -25,26 +29,29 @@ function(
&& !filters[filter][0].value
)
);
})
});
},
componentDidUpdate: function() {
const selected_filters = this.props.filter;
const available_filters = this.getAvailableFilters().map(
function(filter, i) {
if (selected_filters[filter] !== undefined && selected_filters[filter]) {
jQuery(this.refs['filter-'+i])
.val(selected_filters[filter])
.trigger('change');
}
}.bind(this)
);
},
render: function() {
const filters = this.props.filters;
const selected_filters = this.props.filter;
const available_filters = this.getAvailableFilters()
.map(function(filter, i) {
let default_value = false;
if(selected_filters[filter] !== undefined && selected_filters[filter]) {
default_value = selected_filters[filter]
} else {
jQuery(`select[name="${filter}"]`).val('');
}
return (
<select
ref={ `filter-${i}` }
key={ `filter-${i}` }
name={ filter }
defaultValue={ default_value }
>
{ filters[filter].map(function(option, j) {
return (
@ -58,22 +65,36 @@ function(
);
}.bind(this));
let button = false;
let button;
if(available_filters.length > 0) {
if (available_filters.length > 0) {
button = (
<input
id="post-query-submit"
onClick={ this.handleFilterAction }
type="submit"
defaultValue="Filter"
defaultValue={MailPoet.I18n.t('filter')}
className="button" />
);
}
let empty_trash;
if (this.props.group === 'trash') {
empty_trash = (
<input
onClick={ this.handleEmptyTrash }
type="submit"
value={MailPoet.I18n.t('emptyTrash')}
className="button"
/>
);
}
return (
<div className="alignleft actions actions">
{ available_filters }
{ button }
{ empty_trash }
</div>
);
}

View File

@ -21,7 +21,7 @@ define(['react', 'classnames'], function(React, classNames) {
href="javascript:;"
className={classes}
onClick={this.handleSelect.bind(this, group.name)} >
{group.label} <span className="count">({ group.count })</span>
{group.label} <span className="count">({ group.count.toLocaleString() })</span>
</a>
</li>
);

View File

@ -1,90 +1,91 @@
define(['react', 'classnames'], function(React, classNames) {
var ListingHeader = React.createClass({
handleSelectItems: function() {
return this.props.onSelectItems(
this.refs.toggle.checked
);
},
render: function() {
var columns = this.props.columns.map(function(column, index) {
column.is_primary = (index === 0);
column.sorted = (this.props.sort_by === column.name)
? this.props.sort_order
: 'desc';
return (
<ListingColumn
onSort={this.props.onSort}
sort_by={this.props.sort_by}
key={ 'column-' + index }
column={column} />
);
}.bind(this));
var checkbox = false;
if(this.props.is_selectable === true) {
checkbox = (
<th
className="manage-column column-cb check-column">
<label className="screen-reader-text">
{ 'Select All' }
</label>
<input
type="checkbox"
name="select_all"
ref="toggle"
checked={ this.props.selection }
onChange={ this.handleSelectItems } />
</th>
);
}
import MailPoet from 'mailpoet'
import React from 'react'
import classNames from 'classnames'
const ListingHeader = React.createClass({
handleSelectItems: function() {
return this.props.onSelectItems(
this.refs.toggle.checked
);
},
render: function() {
const columns = this.props.columns.map(function(column, index) {
column.is_primary = (index === 0);
column.sorted = (this.props.sort_by === column.name)
? this.props.sort_order
: 'desc';
return (
<tr>
{checkbox}
{columns}
</tr>
<ListingColumn
onSort={this.props.onSort}
sort_by={this.props.sort_by}
key={ 'column-' + index }
column={column} />
);
}
});
}.bind(this));
var ListingColumn = React.createClass({
handleSort: function() {
var sort_by = this.props.column.name,
sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
this.props.onSort(sort_by, sort_order);
},
render: function() {
var classes = classNames(
'manage-column',
{ 'column-primary': this.props.column.is_primary },
{ 'sortable': this.props.column.sortable },
this.props.column.sorted,
{ 'sorted': (this.props.sort_by === this.props.column.name) }
);
var label;
let checkbox;
if(this.props.column.sortable === true) {
label = (
<a onClick={this.handleSort}>
<span>{ this.props.column.label }</span>
<span className="sorting-indicator"></span>
</a>
);
} else {
label = this.props.column.label;
}
return (
if(this.props.is_selectable === true) {
checkbox = (
<th
className={ classes }
id={this.props.column.name }
scope="col">
{label}
className="manage-column column-cb check-column">
<label className="screen-reader-text">
{MailPoet.I18n.t('selectAll')}
</label>
<input
type="checkbox"
name="select_all"
ref="toggle"
checked={ this.props.selection }
onChange={ this.handleSelectItems } />
</th>
);
}
});
return ListingHeader;
});
return (
<tr>
{checkbox}
{columns}
</tr>
);
}
});
const ListingColumn = React.createClass({
handleSort: function() {
const sort_by = this.props.column.name;
const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
this.props.onSort(sort_by, sort_order);
},
render: function() {
const classes = classNames(
'manage-column',
{ 'column-primary': this.props.column.is_primary },
{ 'sortable': this.props.column.sortable },
this.props.column.sorted,
{ 'sorted': (this.props.sort_by === this.props.column.name) }
);
let label;
if(this.props.column.sortable === true) {
label = (
<a onClick={ this.handleSort }>
<span>{ this.props.column.label }</span>
<span className="sorting-indicator"></span>
</a>
);
} else {
label = this.props.column.label;
}
return (
<th
className={ classes }
id={this.props.column.name }
scope="col"
width={ this.props.column.width || null }
>{label}</th>
);
}
});
module.exports = ListingHeader;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,12 @@
define(['react', 'classnames'], function(React, classNames) {
define([
'react',
'classnames',
'mailpoet'
], function(
React,
classNames,
MailPoet
) {
var ListingPages = React.createClass({
getInitialState: function() {
@ -7,7 +15,11 @@ define(['react', 'classnames'], function(React, classNames) {
}
},
setPage: function(page) {
this.props.onSetPage(page);
this.setState({
page: null
}, function () {
this.props.onSetPage(this.constrainPage(page));
}.bind(this));
},
setFirstPage: function() {
this.setPage(1);
@ -16,10 +28,14 @@ define(['react', 'classnames'], function(React, classNames) {
this.setPage(this.getLastPage());
},
setPreviousPage: function() {
this.setPage(this.constrainPage(this.props.page - 1));
this.setPage(this.constrainPage(
parseInt(this.props.page, 10) - 1)
);
},
setNextPage: function() {
this.setPage(this.constrainPage(this.props.page + 1));
this.setPage(this.constrainPage(
parseInt(this.props.page, 10) + 1)
);
},
constrainPage: function(page) {
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
@ -27,14 +43,16 @@ define(['react', 'classnames'], function(React, classNames) {
handleSetManualPage: function(e) {
if(e.which === 13) {
this.setPage(this.state.page);
this.setState({ page: null });
}
},
handleChangeManualPage: function(e) {
this.setState({
page: this.constrainPage(e.target.value)
page: e.target.value
});
},
handleBlurManualPage: function(e) {
this.setPage(e.target.value);
},
getLastPage: function() {
return Math.ceil(this.props.count / this.props.limit);
},
@ -62,7 +80,7 @@ define(['react', 'classnames'], function(React, classNames) {
<a href="javascript:;"
onClick={ this.setPreviousPage }
className="prev-page">
<span className="screen-reader-text">Previous page</span>
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
<span aria-hidden="true"></span>
</a>
);
@ -73,7 +91,7 @@ define(['react', 'classnames'], function(React, classNames) {
<a href="javascript:;"
onClick={ this.setFirstPage }
className="first-page">
<span className="screen-reader-text">First page</span>
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
<span aria-hidden="true">«</span>
</a>
);
@ -84,7 +102,7 @@ define(['react', 'classnames'], function(React, classNames) {
<a href="javascript:;"
onClick={ this.setNextPage }
className="next-page">
<span className="screen-reader-text">Next page</span>
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
<span aria-hidden="true"></span>
</a>
);
@ -95,12 +113,17 @@ define(['react', 'classnames'], function(React, classNames) {
<a href="javascript:;"
onClick={ this.setLastPage }
className="last-page">
<span className="screen-reader-text">Last page</span>
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
<span aria-hidden="true">»</span>
</a>
);
}
let pageValue = this.props.page;
if(this.state.page !== null) {
pageValue = this.state.page;
}
pagination = (
<span className="pagination-links">
{firstPage}
@ -110,21 +133,22 @@ define(['react', 'classnames'], function(React, classNames) {
<span className="paging-input">
<label
className="screen-reader-text"
htmlFor="current-page-selector">Current Page</label>
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
<input
type="text"
onChange={ this.handleChangeManualPage }
onKeyUp={ this.handleSetManualPage }
onBlur={ this.handleBlurManualPage }
aria-describedby="table-paging"
size="1"
size="2"
ref="page"
value={ this.state.page || this.props.page }
value={ pageValue }
name="paged"
id="current-page-selector"
className="current-page" />
&nbsp;of&nbsp;
&nbsp;{MailPoet.I18n.t('pageOutOf')}&nbsp;
<span className="total-pages">
{Math.ceil(this.props.count / this.props.limit)}
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
</span>
</span>
&nbsp;
@ -142,7 +166,9 @@ define(['react', 'classnames'], function(React, classNames) {
return (
<div className={ classes }>
<span className="displaying-num">{ this.props.count } items</span>
<span className="displaying-num">{
MailPoet.I18n.t('numberOfItems').replace('%$1d', this.props.count.toLocaleString())
}</span>
{ pagination }
</div>
);
@ -151,4 +177,4 @@ define(['react', 'classnames'], function(React, classNames) {
});
return ListingPages;
});
});

View File

@ -1,4 +1,10 @@
define(['react'], function(React) {
define([
'mailpoet',
'react'
], function(
MailPoet,
React
) {
var ListingSearch = React.createClass({
handleSearch: function(e) {
@ -18,7 +24,7 @@ define(['react'], function(React) {
<form name="search" onSubmit={this.handleSearch}>
<p className="search-box">
<label htmlFor="search_input" className="screen-reader-text">
Search
{MailPoet.I18n.t('searchLabel')}
</label>
<input
type="search"
@ -28,7 +34,7 @@ define(['react'], function(React) {
defaultValue={this.props.search} />
<input
type="submit"
defaultValue={MailPoetI18n.searchLabel}
defaultValue={MailPoet.I18n.t('searchLabel')}
className="button" />
</p>
</form>

View File

@ -112,7 +112,16 @@ define([
}
}
},
}).preventDefault('auto');
})
.preventDefault('auto')
.actionChecker(function (pointer, event, action) {
// Disable dragging with right click
if (event.button !== 0) {
return null;
}
return action;
});
if (this.options.drop !== undefined) {
interactable.getDropModel = this.options.drop;

View File

@ -0,0 +1,23 @@
/**
* Highlight Editing Behavior
*
* Highlights a block that is being edited
*/
define([
'backbone.marionette',
'newsletter_editor/behaviors/BehaviorsLookup',
], function(Marionette, BehaviorsLookup) {
BehaviorsLookup.HighlightEditingBehavior = Marionette.Behavior.extend({
modelEvents: {
'startEditing': 'enableHighlight',
'stopEditing': 'disableHighlight',
},
enableHighlight: function() {
this.$el.addClass('mailpoet_highlight');
},
disableHighlight: function() {
this.$el.removeClass('mailpoet_highlight');
},
});
});

View File

@ -12,15 +12,57 @@ define([
'newsletter_editor/blocks/button',
'newsletter_editor/blocks/divider',
'newsletter_editor/components/communication',
'mailpoet',
'backbone.supermodel',
'underscore',
'jquery'
], function(App, BaseBlock, ButtonBlock, DividerBlock, CommunicationComponent, _, jQuery) {
], function(
App,
BaseBlock,
ButtonBlock,
DividerBlock,
CommunicationComponent,
MailPoet,
SuperModel,
_,
jQuery
) {
"use strict";
var Module = {},
base = BaseBlock;
Module.ALCSupervisor = SuperModel.extend({
initialize: function() {
this.listenTo(App.getChannel(), 'automatedLatestContentRefresh', this.refresh);
},
refresh: function() {
var models = App.findModels(function(model) {
return model.get('type') === 'automatedLatestContent';
}) || [];
if (models.length === 0) return;
var blocks = _.map(models, function(model) {
return model.toJSON();
});
CommunicationComponent.getBulkTransformedPosts({
blocks: blocks,
}).then(_.partial(this.refreshBlocks, models));
},
refreshBlocks: function(models, renderedBlocks) {
_.each(
_.zip(models, renderedBlocks),
function(args) {
var model = args[0],
contents = args[1];
model.trigger('refreshPosts', contents);
}
);
},
});
Module.AutomatedLatestContentBlockModel = base.BlockModel.extend({
stale: ['_container'],
defaults: function() {
@ -32,10 +74,10 @@ define([
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imagePadded: true, // true|false
imageFullWidth: false, // true|false
featuredImagePosition: 'belowTitle', // 'aboveTitle'|'belowTitle'|'none'
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:',
@ -62,34 +104,32 @@ define([
},
initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments);
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:featuredImagePosition 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('divider'), 'change', this._scheduleFetchPosts);
},
fetchPosts: function() {
var that = this;
CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
that.get('_container').get('blocks').reset(content, {parse: true});
that.trigger('postsChanged');
}).fail(function(error) {
console.log('ALC fetchPosts error', arguments);
this.on('add remove update reset', function(model, collection, options) {
App.getChannel().trigger('automatedLatestContentRefresh');
});
this.on('refreshPosts', this.updatePosts, this);
},
updatePosts: function(posts) {
this.get('_container.blocks').reset(posts, {parse: true});
},
/**
* Batch more changes during a specific time, instead of fetching
* ALC posts on each model change
*/
_scheduleFetchPosts: function() {
var timeout = 2000,
var TIMEOUT = 500,
that = this;
if (this._fetchPostsTimer !== undefined) {
clearTimeout(this._fetchPostsTimer);
}
this._fetchPostsTimer = setTimeout(function() {
that.fetchPosts();
//that.fetchPosts();
App.getChannel().trigger('automatedLatestContentRefresh');
that._fetchPostsTimer = undefined;
}, timeout);
}, TIMEOUT);
},
});
@ -114,6 +154,7 @@ define([
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay'),
};
this.toolsView = new Module.AutomatedLatestContentBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
@ -142,9 +183,9 @@ define([
"keyup .mailpoet_automated_latest_content_show_amount": _.partial(this.changeField, "amount"),
"change .mailpoet_automated_latest_content_content_type": _.partial(this.changeField, "contentType"),
"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_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_featured_image_position": _.partial(this.changeField, "featuredImagePosition"),
"change .mailpoet_automated_latest_content_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_automated_latest_content_show_categories": _.partial(this.changeField, "showCategories"),
@ -154,9 +195,6 @@ define([
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
@ -171,6 +209,7 @@ define([
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({
multiple: true,
allowClear: true,
placeholder: MailPoet.I18n.t('categoriesAndTags'),
ajax: {
data: function (params) {
return {
@ -273,12 +312,15 @@ define([
},
changeDisplayType: function(event) {
var value = jQuery(event.target).val();
if (value == 'titleOnly') {
this.$('.mailpoet_automated_latest_content_title_position_container').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_list').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_image_full_width_option').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_image_separator').addClass('mailpoet_hidden');
} else {
this.$('.mailpoet_automated_latest_content_title_position_container').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_list').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_image_full_width_option').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_image_separator').removeClass('mailpoet_hidden');
// Reset titleFormat if it was set to List when switching away from displayType=titleOnly
if (this.model.get('titleFormat') === 'ul') {
@ -287,6 +329,12 @@ define([
this.$('.mailpoet_automated_latest_content_title_as_link').removeClass('mailpoet_hidden');
}
}
if (value === 'excerpt') {
this.$('.mailpoet_automated_latest_content_featured_image_position_container').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_automated_latest_content_featured_image_position_container').addClass('mailpoet_hidden');
}
this.changeField('displayType', event);
},
changeTitleFormat: function(event) {
@ -346,5 +394,10 @@ define([
});
});
App.on('start', function() {
App._ALCSupervisor = new Module.ALCSupervisor();
App._ALCSupervisor.refresh();
});
return Module;
});

View File

@ -40,6 +40,9 @@ define([
// Remove stale attributes from resulting JSON object
return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
},
getChildren: function() {
return [];
},
});
Module.BlockView = AugmentedView.extend({
@ -77,6 +80,7 @@ define([
}
},
},
HighlightEditingBehavior: {},
},
templateHelpers: function() {
return {
@ -215,8 +219,12 @@ define([
Module.BlockSettingsView = Marionette.LayoutView.extend({
className: 'mailpoet_editor_settings',
initialize: function() {
MailPoet.Modal.panel({
behaviors: {
ColorPickerBehavior: {},
},
initialize: function(params) {
this.model.trigger('startEditing');
var panelParams = {
element: this.$el,
template: '',
position: 'right',
@ -224,7 +232,13 @@ define([
onCancel: function() {
this.destroy();
}.bind(this),
});
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
close: function(event) {
this.destroy();
@ -253,6 +267,7 @@ define([
},
onBeforeDestroy: function() {
MailPoet.Modal.close();
this.model.trigger('stopEditing');
},
});

View File

@ -32,6 +32,7 @@ define([
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '16px',
fontWeight: 'normal', // 'normal'|'bold'
textAlign: 'center',
},
},
@ -72,6 +73,7 @@ define([
"change .mailpoet_field_button_font_size": _.partial(this.changeField, "styles.block.fontSize"),
"change .mailpoet_field_button_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_button_border_color": _.partial(this.changeColorField, "styles.block.borderColor"),
"change .mailpoet_field_button_font_weight": "changeFontWeight",
"input .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
@ -97,23 +99,6 @@ define([
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
initialize: function(params) {
var panelParams = {
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
@ -128,6 +113,13 @@ define([
this.$(fieldToUpdate).val(jQuery(event.target).val());
callable(event);
},
changeFontWeight: function(event) {
var checked = !!jQuery(event.target).prop('checked');
this.model.set(
'styles.block.fontWeight',
(checked) ? jQuery(event.target).val() : 'normal'
);
}
});
Module.ButtonWidgetView = base.WidgetView.extend({

View File

@ -65,6 +65,13 @@ define([
}
return response;
},
getChildren: function() {
var models = this.get('blocks').map(function(model, index, list) {
return [model, model.getChildren()];
});
return _.flatten(models);
},
});
Module.ContainerBlockView = Marionette.CompositeView.extend({
@ -118,6 +125,7 @@ define([
return view.renderOptions.depth === 1;
},
},
HighlightEditingBehavior: {}
},
onDragSubstituteBy: function() {
// For two and three column layouts display their respective widgets,
@ -286,6 +294,7 @@ define([
templateHelpers: function() {
return {
isRoot: this.renderOptions.depth === 0,
emptyContainerMessage: this.renderOptions.emptyContainerMessage || '',
};
},
});
@ -302,9 +311,6 @@ define([
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
regions: {
columnsSettingsRegion: '.mailpoet_container_columns_settings',
},

View File

@ -102,23 +102,6 @@ define([
'change:styles.block.borderColor': 'repaintDividerStyleOptions',
};
},
behaviors: {
ColorPickerBehavior: {},
},
initialize: function(params) {
var panelParams = {
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateHelpers: function() {
return {
model: this.model.toJSON(),

View File

@ -16,7 +16,7 @@ define([
defaults: function() {
return this._getDefaults({
type: 'footer',
text: '<a href="[unsubscribeUrl]">Unsubscribe</a> | <a href="[manageSubscriptionUrl]">Manage subscription</a><br /><b>Add your postal address here!</b>',
text: '<a href="[link:subscription_unsubscribe_url]">Unsubscribe</a> | <a href="[link:subscription_manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
styles: {
block: {
backgroundColor: 'transparent',
@ -41,7 +41,7 @@ define([
getTemplate: function() { return templates.footerBlock; },
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',
}, base.BlockView.prototype.modelEvents),
}, _.omit(base.BlockView.prototype.modelEvents, 'change')),
onDragSubstituteBy: function() { return Module.FooterWidgetView; },
onRender: function() {
this.toolsView = new Module.FooterBlockToolsView({ model: this.model });
@ -56,15 +56,15 @@ define([
inline: true,
menubar: false,
toolbar: "bold italic link unlink forecolor mailpoet_custom_fields",
toolbar: "bold italic link unlink forecolor mailpoet_shortcodes",
valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],strong[class|style],em[class|style],strike,br",
invalid_elements: "script",
style_formats: [
{title: 'Paragraph', block: 'p'},
],
block_formats: 'Paragraph=p',
relative_urls: false,
remove_script_host: false,
plugins: "link textcolor mailpoet_custom_fields",
plugins: "link textcolor colorpicker mailpoet_shortcodes",
setup: function(editor) {
editor.on('change', function(e) {
@ -80,8 +80,8 @@ define([
});
},
mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(),
mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'),
mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(),
mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle'),
});
},
});
@ -106,9 +106,6 @@ define([
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),

View File

@ -16,7 +16,7 @@ define([
defaults: function() {
return this._getDefaults({
type: 'header',
text: 'Display problems? <a href="[viewInBrowserUrl]">View it in your browser</a>',
text: 'Display problems? <a href="[link:newsletter_view_in_browser_url]">View it in your browser</a>',
styles: {
block: {
backgroundColor: 'transparent',
@ -41,7 +41,7 @@ define([
getTemplate: function() { return templates.headerBlock; },
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',
}, base.BlockView.prototype.modelEvents),
}, _.omit(base.BlockView.prototype.modelEvents, 'change')),
onDragSubstituteBy: function() { return Module.HeaderWidgetView; },
onRender: function() {
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model });
@ -56,15 +56,15 @@ define([
inline: true,
menubar: false,
toolbar: "bold italic link unlink forecolor mailpoet_custom_fields",
toolbar: "bold italic link unlink forecolor mailpoet_shortcodes",
valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],strong[class|style],em[class|style],strike,br",
invalid_elements: "script",
style_formats: [
{title: 'Paragraph', block: 'p'},
],
block_formats: 'Paragraph=p',
relative_urls: false,
remove_script_host: false,
plugins: "link textcolor mailpoet_custom_fields",
plugins: "link textcolor colorpicker mailpoet_shortcodes",
setup: function(editor) {
editor.on('change', function(e) {
@ -80,8 +80,8 @@ define([
});
},
mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(),
mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'),
mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(),
mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle'),
});
},
});
@ -106,9 +106,6 @@ define([
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),

View File

@ -20,7 +20,7 @@ define([
link: 'http://example.org',
src: 'no-image.png',
alt: 'An image of...',
padded: true, // true | false - Padded or full width
fullWidth: true, // true | false
width: '64px',
height: '64px',
styles: {
@ -45,10 +45,10 @@ define([
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
if (this.model.get('padded')) {
this.$el.removeClass('mailpoet_full_image');
} else {
if (this.model.get('fullWidth')) {
this.$el.addClass('mailpoet_full_image');
} else {
this.$el.removeClass('mailpoet_full_image');
}
},
});
@ -64,7 +64,7 @@ define([
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
"change .mailpoet_field_image_padded": _.partial(this.changeBoolCheckboxField, "padded"),
"change .mailpoet_field_image_full_width": _.partial(this.changeBoolCheckboxField, "fullWidth"),
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
"click .mailpoet_field_image_select_another_image": "showMediaManager",
"click .mailpoet_done_editing": "close",

View File

@ -23,7 +23,19 @@ define([
'newsletter_editor/blocks/button',
'newsletter_editor/blocks/divider',
'select2'
], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, CommunicationComponent, BaseBlock, ButtonBlock, DividerBlock) {
], function(
Backbone,
Marionette,
Radio,
_,
jQuery,
MailPoet,
App,
CommunicationComponent,
BaseBlock,
ButtonBlock,
DividerBlock
) {
"use strict";
@ -43,10 +55,10 @@ define([
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imagePadded: true, // true|false
imageFullWidth: false, // true|false
featuredImagePosition: 'belowTitle', // 'aboveTitle'|'belowTitle'|'none'
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:',
@ -88,7 +100,7 @@ define([
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:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:showDivider', refreshTransformedPosts);
this.on('change:displayType change:titleFormat change:featuredImagePosition 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);
@ -101,7 +113,7 @@ define([
that.get('_selectedPosts').reset(); // Empty out the collection
that.trigger('change:_availablePosts');
}).fail(function() {
console.log('Posts fetchPosts error', arguments);
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchAvailablePosts'));
});
},
_refreshTransformedPosts: function() {
@ -111,14 +123,14 @@ define([
data.posts = this.get('_selectedPosts').pluck('ID');
if (data.posts.length === 0) {
this.get('_transformedPosts.blocks').reset();
this.get('_transformedPosts').get('blocks').reset();
return;
}
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
}).fail(function() {
console.log('Posts _refreshTransformedPosts error', arguments);
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
});
},
_insertSelectedPosts: function() {
@ -134,7 +146,7 @@ define([
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
collection.add(posts, { at: index });
}).fail(function() {
console.log('Posts fetchPosts error', arguments);
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
});
},
});
@ -163,6 +175,7 @@ define([
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay'),
};
this.postsRegion.show(new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
},
@ -195,6 +208,7 @@ define([
};
},
initialize: function() {
this.model.trigger('startEditing');
this.selectionView = new PostSelectionSettingsView({ model: this.model });
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
},
@ -202,21 +216,23 @@ define([
var that = this,
blockView = this.model.request('blockView');
this.selectionRegion.show(this.selectionView);
this.displayOptionsRegion.show(this.displayOptionsView);
this.showChildView('selectionRegion', this.selectionView);
this.showChildView('displayOptionsRegion', this.displayOptionsView);
MailPoet.Modal.panel({
element: this.$el,
template: '',
position: 'right',
overlay: true,
highlight: blockView.$el,
width: App.getConfig().get('sidepanelWidth'),
onCancel: function() {
// Self destroy the block if the user closes settings modal
that.model.destroy();
},
});
// Inform child views that they have been attached to document
this.selectionView.triggerMethod('attach');
this.displayOptionsView.triggerMethod('attach');
},
switchToDisplayOptions: function() {
// Switch content view
@ -266,14 +282,19 @@ define([
Marionette.CompositeView.apply(this, arguments);
},
onRender: function() {
// Dynamically update available post types
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
},
onAttach: function() {
var that = this;
// Dynamically update available post types
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
//CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
this.$('.mailpoet_posts_categories_and_tags').select2({
multiple: true,
allowClear: true,
placeholder: MailPoet.I18n.t('categoriesAndTags'),
ajax: {
data: function (params) {
return {
@ -394,9 +415,9 @@ define([
"keyup .mailpoet_posts_show_amount": _.partial(this.changeField, "amount"),
"change .mailpoet_posts_content_type": _.partial(this.changeField, "contentType"),
"change .mailpoet_posts_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_posts_title_position": _.partial(this.changeField, "titlePosition"),
"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_featured_image_position": _.partial(this.changeField, "featuredImagePosition"),
"change .mailpoet_posts_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_posts_show_categories": _.partial(this.changeField, "showCategories"),
@ -405,9 +426,6 @@ define([
"change .mailpoet_posts_sort_by": _.partial(this.changeField, "sortBy"),
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
@ -448,11 +466,13 @@ define([
changeDisplayType: function(event) {
var value = jQuery(event.target).val();
if (value == 'titleOnly') {
this.$('.mailpoet_posts_title_position_container').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_list').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_full_width_option').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_separator').addClass('mailpoet_hidden');
} else {
this.$('.mailpoet_posts_title_position_container').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_list').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_full_width_option').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_separator').removeClass('mailpoet_hidden');
// Reset titleFormat if it was set to List when switching away from displayType=titleOnly
if (this.model.get('titleFormat') === 'ul') {
@ -461,6 +481,13 @@ define([
this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden');
}
}
if (value === 'excerpt') {
this.$('.mailpoet_posts_featured_image_position_container').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_posts_featured_image_position_container').addClass('mailpoet_hidden');
}
this.changeField('displayType', event);
},
changeTitleFormat: function(event) {

View File

@ -139,6 +139,7 @@ define([
}
},
},
HighlightEditingBehavior: {},
},
onDragSubstituteBy: function() { return Module.SocialWidgetView; },
constructor: function() {

View File

@ -70,9 +70,6 @@ define([
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
});
Module.SpacerWidgetView = base.WidgetView.extend({

View File

@ -52,21 +52,17 @@ define([
inline: true,
menubar: false,
toolbar1: "styleselect bold italic forecolor | link unlink",
toolbar2: "alignleft aligncenter alignright alignjustify | bullist numlist blockquote | code mailpoet_custom_fields",
toolbar1: "formatselect bold italic forecolor | link unlink",
toolbar2: "alignleft aligncenter alignright alignjustify | bullist numlist blockquote | code mailpoet_shortcodes",
//forced_root_block: 'p',
valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],h1[class|style],h2[class|style],h3[class|style],ol[class|style],ul[class|style],li[class|style],strong[class|style],em[class|style],strike,br,blockquote[class|style],table[class|style],tr[class|style],th[class|style],td[class|style]",
invalid_elements: "script",
style_formats: [
{title: 'Heading 1', block: 'h1'},
{title: 'Heading 2', block: 'h2'},
{title: 'Heading 3', block: 'h3'},
block_formats: 'Heading 1=h1;Heading 2=h2;Heading 3=h3;Paragraph=p',
relative_urls: false,
remove_script_host: false,
{title: 'Paragraph', block: 'p'},
],
plugins: "link code textcolor mailpoet_custom_fields",
plugins: "link code textcolor colorpicker mailpoet_shortcodes",
setup: function(editor) {
editor.on('change', function(e) {
@ -82,8 +78,8 @@ define([
});
},
mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(),
mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'),
mailpoet_shortcodes: App.getConfig().get('shortcodes').toJSON(),
mailpoet_shortcodes_window_title: MailPoet.I18n.t('shortcodesWindowTitle'),
});
}
},

View File

@ -62,6 +62,13 @@ define([
});
};
Module.getBulkTransformedPosts = function(options) {
return Module._query({
action: 'getBulkTransformedPosts',
options: options,
});
};
Module.saveNewsletter = function(options) {
return MailPoet.Ajax.post({
endpoint: 'newsletters',

View File

@ -10,7 +10,6 @@ define([
availableStyles: {},
socialIcons: {},
blockDefaults: {},
translations: {},
sidepanelWidth: '331px',
validation: {},
urls: {},

View File

@ -60,6 +60,11 @@ define([
return Module.newsletter;
};
Module.findModels = function(predicate) {
var blocks = App._contentContainer.getChildren();
return _.filter(blocks, predicate);
};
App.on('before:start', function(options) {
// Expose block methods globally
App.registerBlockType = Module.registerBlockType;
@ -68,6 +73,7 @@ define([
App.toJSON = Module.toJSON;
App.getBody = Module.getBody;
App.getNewsletter = Module.getNewsletter;
App.findModels = Module.findModels;
Module.newsletter = new Module.NewsletterModel(_.omit(_.clone(options.newsletter), ['body']));
});

View File

@ -67,7 +67,28 @@ define([
};
Module.getThumbnail = function(element, options) {
return html2canvas(element, options || {});
var promise = html2canvas(element, options || {});
return promise.then(function(oldCanvas) {
// Temporary workaround for html2canvas-alpha2.
// Removes 1px left transparent border from resulting canvas.
var oldContext = oldCanvas.getContext('2d'),
newCanvas = document.createElement("canvas"),
newContext = newCanvas.getContext("2d"),
leftBorderWidth = 1;
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
newContext.drawImage(
oldCanvas,
leftBorderWidth, 0, oldCanvas.width - leftBorderWidth, oldCanvas.height,
0, 0, oldCanvas.width, oldCanvas.height
);
return newCanvas;
});
};
Module.saveTemplate = function(options) {
@ -170,7 +191,7 @@ define([
if (templateName === '') {
MailPoet.Notice.error(
App.getConfig().get('translations.templateNameMissing'),
MailPoet.I18n.t('templateNameMissing'),
{
positionAfter: that.$el,
scroll: true,
@ -178,30 +199,27 @@ define([
);
} else if (templateDescription === '') {
MailPoet.Notice.error(
App.getConfig().get('translations.templateDescriptionMissing'),
MailPoet.I18n.t('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'),
MailPoet.I18n.t('templateSaved'),
{
positionAfter: that.$el,
scroll: true,
}
);
}).fail(function() {
console.log('Template save failed', arguments);
MailPoet.Notice.error(
App.getConfig().get('translations.templateSaveFailed'),
MailPoet.I18n.t('templateSaveFailed'),
{
positionAfter: that.$el,
scroll: true,
@ -226,7 +244,7 @@ define([
if (templateName === '') {
MailPoet.Notice.error(
App.getConfig().get('translations.templateNameMissing'),
MailPoet.I18n.t('templateNameMissing'),
{
positionAfter: that.$el,
scroll: true,
@ -234,14 +252,13 @@ define([
);
} else if (templateDescription === '') {
MailPoet.Notice.error(
App.getConfig().get('translations.templateDescriptionMissing'),
MailPoet.I18n.t('templateDescriptionMissing'),
{
positionAfter: that.$el,
scroll: true,
}
);
} else {
console.log('Exporting template with ', templateName, templateDescription);
Module.exportTemplate({
name: templateName,
description: templateDescription,
@ -257,7 +274,6 @@ define([
next: function() {
this.hideOptionContents();
if(!this.$('.mailpoet_save_next').hasClass('button-disabled')) {
console.log('Next');
window.location.href = App.getConfig().get('urls.send');
}
},
@ -267,9 +283,11 @@ define([
return;
}
var contents = JSON.stringify(jsonObject);
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
JSON.stringify(jsonObject).indexOf("[unsubscribeUrl]") < 0) {
this.showValidationError(App.getConfig().get('translations.unsubscribeLinkMissing'));
contents.indexOf("[link:subscription_unsubscribe_url]") < 0 &&
contents.indexOf("[link:subscription_unsubscribe]") < 0) {
this.showValidationError(MailPoet.I18n.t('unsubscribeLinkMissing'));
return;
}

View File

@ -1,13 +1,24 @@
define([
'newsletter_editor/App',
'newsletter_editor/components/communication',
'mailpoet',
'backbone',
'backbone.marionette',
'backbone.supermodel',
'underscore',
'jquery',
'sticky-kit'
], function(App, CommunicationComponent, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
], function(
App,
CommunicationComponent,
MailPoet,
Backbone,
Marionette,
SuperModel,
_,
jQuery,
StickyKit
) {
"use strict";
@ -225,33 +236,56 @@ define([
showPreview: function() {
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.Modal.loading(true);
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'render',
action: 'showPreview',
data: json,
}).done(function(response){
console.log('Should open a new window');
window.open('data:text/html,' + encodeURIComponent(response.rendered_body), '_blank');
MailPoet.Modal.loading(false);
if (response.result === true) {
window.open(response.data.url, '_blank')
}
MailPoet.Notice.error(response.errors);
}).fail(function(error) {
console.log('Preview error', json);
alert('Something went wrong, check console');
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoet.I18n.t('newsletterPreviewFailed')
);
});
},
sendPreview: function() {
// testing sending method
console.log('trying to send a preview');
// get form data
var $emailField = this.$('#mailpoet_preview_to_email');
var data = {
subscriber: this.$('#mailpoet_preview_to_email').val(),
subscriber: $emailField.val(),
id: App.getNewsletter().get('id'),
};
if (data.subscriber.length <= 0) {
MailPoet.Notice.error(
MailPoet.I18n.t('newsletterPreviewEmailMissing'),
{
positionAfter: $emailField,
scroll: true,
}
);
return false;
}
// send test email
MailPoet.Modal.loading(true);
CommunicationComponent.previewNewsletter(data).done(function(response) {
if(response.result !== undefined && response.result === true) {
MailPoet.Notice.success(App.getConfig().get('translations.newsletterPreviewSent'), { scroll: true });
MailPoet.Notice.success(MailPoet.I18n.t('newsletterPreviewSent'), { scroll: true });
} else {
if (_.isArray(response.errors)) {
response.errors.map(function(error) {
@ -259,7 +293,7 @@ define([
});
} else {
MailPoet.Notice.error(
App.getConfig().get('translations.newsletterPreviewFailedToSend'),
MailPoet.I18n.t('newsletterPreviewFailedToSend'),
{
scroll: true,
static: true,

View File

@ -1,58 +0,0 @@
/**
* wysija_custom_fields/plugin.js
*
* TinyMCE plugin for adding dynamic data placeholders to newsletters.
*
* This adds a button to the editor toolbar which displays a modal window of
* available dynamic data placeholder buttons. On click each button inserts
* its placeholder into editor text.
*/
/*jshint unused:false */
/*global tinymce:true */
tinymce.PluginManager.add('mailpoet_custom_fields', function(editor, url) {
var appendLabelAndClose = function(text) {
editor.insertContent('[' + text + ']');
editor.windowManager.close();
},
generateOnClickFunc = function(id) {
return function() {
appendLabelAndClose(id);
};
};
editor.addButton('mailpoet_custom_fields', {
icon: 'mailpoet_custom_fields',
onclick: function() {
var customFields = [],
configCustomFields = editor.settings.mailpoet_custom_fields;
for (var segment in configCustomFields) {
if (configCustomFields.hasOwnProperty(segment)) {
customFields.push({
type: 'label',
text: segment,
});
for (var i = 0; i < configCustomFields[segment].length; i += 1) {
customFields.push({
type: 'button',
text: configCustomFields[segment][i].text,
onClick: generateOnClickFunc(configCustomFields[segment][i].shortcode)
});
}
}
}
// Open window
editor.windowManager.open({
height: parseInt(editor.getParam("plugin_mailpoet_custom_fields_height", 400)),
width: parseInt(editor.getParam("plugin_mailpoet_custom_fields_width", 450)),
autoScroll: true,
title: editor.settings.mailpoet_custom_fields_window_title,
body: customFields,
buttons: [],
});
},
});
});

View File

@ -0,0 +1,58 @@
/**
* wysija_shortcodes/plugin.js
*
* TinyMCE plugin for adding dynamic data placeholders to newsletters.
*
* This adds a button to the editor toolbar which displays a modal window of
* available dynamic data placeholder buttons. On click each button inserts
* its placeholder into editor text.
*/
/*jshint unused:false */
/*global tinymce:true */
tinymce.PluginManager.add('mailpoet_shortcodes', function(editor, url) {
var appendLabelAndClose = function(text) {
editor.insertContent('[' + text + ']');
editor.windowManager.close();
},
generateOnClickFunc = function(id) {
return function() {
appendLabelAndClose(id);
};
};
editor.addButton('mailpoet_shortcodes', {
icon: 'mailpoet_shortcodes',
onclick: function() {
var shortcodes = [],
configShortcodes = editor.settings.mailpoet_shortcodes;
for (var segment in configShortcodes) {
if (configShortcodes.hasOwnProperty(segment)) {
shortcodes.push({
type: 'label',
text: segment,
});
for (var i = 0; i < configShortcodes[segment].length; i += 1) {
shortcodes.push({
type: 'button',
text: configShortcodes[segment][i].text,
onClick: generateOnClickFunc(configShortcodes[segment][i].shortcode)
});
}
}
}
// Open window
editor.windowManager.open({
height: parseInt(editor.getParam("plugin_mailpoet_shortcodes_height", 400)),
width: parseInt(editor.getParam("plugin_mailpoet_shortcodes_width", 450)),
autoScroll: true,
title: editor.settings.mailpoet_shortcodes_window_title,
body: shortcodes,
buttons: [],
});
},
});
});

View File

@ -2,39 +2,38 @@ define(
[
'react',
'react-router',
'classnames'
'classnames',
'mailpoet'
],
function(
React,
Router,
classNames
classNames,
MailPoet
) {
var Link = Router.Link;
var Breadcrumb = React.createClass({
mixins: [
Router.History
],
getInitialState: function() {
return {
step: null,
steps: [
{
name: 'type',
label: 'Select type',
label: MailPoet.I18n.t('selectType'),
link: '/new'
},
{
name: 'template',
label: 'Template'
label: MailPoet.I18n.t('template')
},
{
name: 'editor',
label: 'Designer'
label: MailPoet.I18n.t('designer')
},
{
name: 'send',
label: 'Send'
label: MailPoet.I18n.t('send')
}
]
};
@ -73,4 +72,4 @@ define(
return Breadcrumb;
}
);
);

View File

@ -1,272 +0,0 @@
define(
[
'react',
'react-router',
'listing/listing.jsx',
'classnames',
'jquery'
],
function(
React,
Router,
Listing,
classNames,
jQuery
) {
var Link = Router.Link;
var columns = [
{
name: 'subject',
label: 'Subject',
sortable: true
},
{
name: 'status',
label: 'Status'
},
{
name: 'segments',
label: 'Lists'
},
{
name: 'created_at',
label: 'Created on',
sortable: true
},
{
name: 'updated_at',
label: 'Last modified on',
sortable: true
}
];
var messages = {
onTrash: function(response) {
var count = ~~response.newsletters;
var message = null;
if(count === 1 || response === true) {
message = (
'1 newsletter was moved to the trash.'
);
} else if(count > 1) {
message = (
'%$1d newsletters were moved to the trash.'
).replace('%$1d', count);
}
if(message !== null) {
MailPoet.Notice.success(message);
}
},
onDelete: function(response) {
var count = ~~response.newsletters;
var message = null;
if(count === 1 || response === true) {
message = (
'1 newsletter was permanently deleted.'
);
} else if(count > 1) {
message = (
'%$1d newsletters were permanently deleted.'
).replace('%$1d', count);
}
if(message !== null) {
MailPoet.Notice.success(message);
}
},
onRestore: function(response) {
var count = ~~response.newsletters;
var message = null;
if(count === 1 || response === true) {
message = (
'1 newsletter has been restored from the trash.'
);
} else if(count > 1) {
message = (
'%$1d newsletters have been restored from the trash.'
).replace('%$1d', count);
}
if(message !== null) {
MailPoet.Notice.success(message);
}
}
};
var bulk_actions = [
{
name: 'trash',
label: 'Trash',
onSuccess: messages.onTrash
}
];
var item_actions = [
{
name: 'edit',
link: function(item) {
return (
<a href={ `?page=mailpoet-newsletter-editor&id=${ item.id }` }>
Edit
</a>
);
}
},
{
name: 'trash'
}
];
var NewsletterList = React.createClass({
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(
'manage-column',
'column-primary',
'has-row-actions'
);
var segments = mailpoet_segments.filter(function(segment) {
return (jQuery.inArray(segment.id, newsletter.segments) !== -1);
}).map(function(segment) {
return segment.name;
}).join(', ');
return (
<div>
<td className={ rowClasses }>
<strong>
<a>{ newsletter.subject }</a>
</strong>
{ actions }
</td>
<td className="column" data-colname="Lists">
{ this.renderStatus(newsletter) }
</td>
<td className="column" data-colname="Lists">
{ segments }
</td>
<td className="column-date" data-colname="Subscribed on">
<abbr>{ newsletter.created_at }</abbr>
</td>
<td className="column-date" data-colname="Last modified on">
<abbr>{ newsletter.updated_at }</abbr>
</td>
</div>
);
},
render: function() {
return (
<div>
<h2 className="title">
Newsletters <Link className="add-new-h2" to="/new">New</Link>
</h2>
<Listing
params={ this.props.params }
endpoint="newsletters"
onRenderItem={this.renderItem}
columns={columns}
bulk_actions={ bulk_actions }
item_actions={ item_actions }
messages={ messages }
auto_refresh={ true } />
</div>
);
}
});
return NewsletterList;
}
);

View File

@ -0,0 +1,317 @@
import React from 'react'
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
import Listing from 'listing/listing.jsx'
import ListingTabs from 'newsletters/listings/tabs.jsx'
import classNames from 'classnames'
import jQuery from 'jquery'
import MailPoet from 'mailpoet'
import {
timeOfDayValues,
weekDayValues,
monthDayValues,
nthWeekDayValues
} from 'newsletters/scheduling/common.jsx'
const messages = {
onTrash(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterTrashed')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersTrashed')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onDelete(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterDeleted')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersDeleted')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onRestore(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterRestored')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersRestored')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
}
};
const columns = [
{
name: 'subject',
label: MailPoet.I18n.t('subject'),
sortable: true
},
{
name: 'status',
label: MailPoet.I18n.t('status'),
width: 100
},
{
name: 'settings',
label: MailPoet.I18n.t('settings')
},
{
name: 'history',
label: MailPoet.I18n.t('history'),
width: 100
},
{
name: 'updated_at',
label: MailPoet.I18n.t('lastModifiedOn'),
sortable: true
}
];
const bulk_actions = [
{
name: 'trash',
label: MailPoet.I18n.t('trash'),
onSuccess: messages.onTrash
}
];
const newsletter_actions = [
{
name: 'view',
link: function(newsletter) {
return (
<a href={ newsletter.preview_url } target="_blank">
{MailPoet.I18n.t('preview')}
</a>
);
}
},
{
name: 'edit',
link: function(newsletter) {
return (
<a href={ `?page=mailpoet-newsletter-editor&id=${ newsletter.id }` }>
{MailPoet.I18n.t('edit')}
</a>
);
}
},
{
name: 'duplicate',
label: MailPoet.I18n.t('duplicate'),
onClick: function(newsletter, refresh) {
return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'duplicate',
data: newsletter.id
}).done(function(response) {
if (response !== false && response.subject !== undefined) {
MailPoet.Notice.success(
(MailPoet.I18n.t('newsletterDuplicated')).replace(
'%$1s', response.subject
)
);
}
refresh();
});
}
},
{
name: 'trash'
}
];
const NewsletterListNotification = React.createClass({
updateStatus: function(e) {
// make the event persist so that we can still override the selected value
// in the ajax callback
e.persist();
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'setStatus',
data: {
id: ~~(e.target.getAttribute('data-id')),
status: e.target.value
}
}).done(function(response) {
if (response.result === false) {
MailPoet.Notice.error(MailPoet.I18n.t('postNotificationActivationFailed'));
// reset value to actual newsletter's status
e.target.value = response.status;
} else {
if (response.status === 'active') {
MailPoet.Notice.success(MailPoet.I18n.t('postNotificationActivated'));
}
// force refresh of listing so that groups are updated
this.forceUpdate();
}
}.bind(this));
},
renderStatus: function(newsletter) {
return (
<select
data-id={ newsletter.id }
defaultValue={ newsletter.status }
onChange={ this.updateStatus }
>
<option value="active">{ MailPoet.I18n.t('active') }</option>
<option value="draft">{ MailPoet.I18n.t('inactive') }</option>
</select>
);
},
renderSettings: function(newsletter) {
let sendingFrequency;
let sendingToSegments;
// get list of segments' name
const segments = newsletter.segments.map(function(segment) {
return segment.name
});
// check if the user has specified segments to send to
if(segments.length === 0) {
return (
<span className="mailpoet_error">
{ MailPoet.I18n.t('sendingToSegmentsNotSpecified') }
</span>
);
} else {
sendingToSegments = MailPoet.I18n.t('ifNewContentToSegments').replace(
'%$1s', segments.join(', ')
);
// set sending frequency
switch (newsletter.options.intervalType) {
case 'daily':
sendingFrequency = MailPoet.I18n.t('sendDaily').replace(
'%$1s', timeOfDayValues[newsletter.options.timeOfDay]
);
break;
case 'weekly':
sendingFrequency = MailPoet.I18n.t('sendWeekly').replace(
'%$1s', weekDayValues[newsletter.options.weekDay]
).replace(
'%$2s', timeOfDayValues[newsletter.options.timeOfDay]
);
break;
case 'monthly':
sendingFrequency = MailPoet.I18n.t('sendMonthly').replace(
'%$1s', monthDayValues[newsletter.options.monthDay]
).replace(
'%$2s', timeOfDayValues[newsletter.options.timeOfDay]
);
break;
case 'nthWeekDay':
sendingFrequency = MailPoet.I18n.t('sendNthWeekDay').replace(
'%$1s', nthWeekDayValues[newsletter.options.nthWeekDay]
).replace(
'%$2s', weekDayValues[newsletter.options.weekDay]
).replace(
'%$3s', timeOfDayValues[newsletter.options.timeOfDay]
);
break;
case 'immediately':
sendingFrequency = MailPoet.I18n.t('sendImmediately');
break;
}
}
return (
<span>
{ sendingFrequency } { sendingToSegments }
</span>
);
},
renderItem: function(newsletter, actions) {
const rowClasses = classNames(
'manage-column',
'column-primary',
'has-row-actions'
);
return (
<div>
<td className={ rowClasses }>
<strong>
<a
className="row-title"
href={ `?page=mailpoet-newsletter-editor&id=${ newsletter.id }` }
>{ newsletter.subject }</a>
</strong>
{ actions }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('status') }>
{ this.renderStatus(newsletter) }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('settings') }>
{ this.renderSettings(newsletter) }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('history') }>
<a href="#TODO">{ MailPoet.I18n.t('viewHistory') }</a>
</td>
<td className="column-date" data-colname={ MailPoet.I18n.t('lastModifiedOn') }>
<abbr>{ MailPoet.Date.format(newsletter.updated_at) }</abbr>
</td>
</div>
);
},
render: function() {
return (
<div>
<h1 className="title">
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
</h1>
<ListingTabs tab="notification" />
<Listing
limit={ mailpoet_listing_per_page }
location={ this.props.location }
params={ this.props.params }
endpoint="newsletters"
tab="notification"
onRenderItem={ this.renderItem }
columns={ columns }
bulk_actions={ bulk_actions }
item_actions={ newsletter_actions }
messages={ messages }
auto_refresh={ true }
sort_by="updated_at"
sort_order="desc"
/>
</div>
);
}
});
module.exports = NewsletterListNotification;

View File

@ -0,0 +1,339 @@
import React from 'react'
import { Router, Link } from 'react-router'
import classNames from 'classnames'
import jQuery from 'jquery'
import MailPoet from 'mailpoet'
import Listing from 'listing/listing.jsx'
import ListingTabs from 'newsletters/listings/tabs.jsx'
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
const messages = {
onTrash(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterTrashed')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersTrashed')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onDelete(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterDeleted')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersDeleted')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onRestore(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterRestored')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersRestored')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
}
};
const columns = [
{
name: 'subject',
label: MailPoet.I18n.t('subject'),
sortable: true
},
{
name: 'status',
label: MailPoet.I18n.t('status')
},
{
name: 'segments',
label: MailPoet.I18n.t('lists')
},
{
name: 'statistics',
label: MailPoet.I18n.t('statistics'),
display: mailpoet_tracking_enabled
},
{
name: 'updated_at',
label: MailPoet.I18n.t('lastModifiedOn'),
sortable: true
}
];
const bulk_actions = [
{
name: 'trash',
label: MailPoet.I18n.t('trash'),
onSuccess: messages.onTrash
}
];
const newsletter_actions = [
{
name: 'view',
link: function(newsletter) {
return (
<a href={ newsletter.preview_url } target="_blank">
{MailPoet.I18n.t('preview')}
</a>
);
}
},
{
name: 'edit',
link: function(newsletter) {
return (
<a href={ `?page=mailpoet-newsletter-editor&id=${ newsletter.id }` }>
{MailPoet.I18n.t('edit')}
</a>
);
}
},
{
name: 'duplicate',
label: MailPoet.I18n.t('duplicate'),
onClick: function(newsletter, refresh) {
return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'duplicate',
data: newsletter.id
}).done(function(response) {
if (response !== false && response.subject !== undefined) {
MailPoet.Notice.success(
(MailPoet.I18n.t('newsletterDuplicated')).replace(
'%$1s', response.subject
)
);
}
refresh();
});
}
},
{
name: 'trash'
}
];
const NewsletterListStandard = React.createClass({
pauseSending: function(newsletter) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'pause',
data: newsletter.id
}).done(function() {
jQuery('#resume_'+newsletter.id).show();
jQuery('#pause_'+newsletter.id).hide();
});
},
resumeSending: function(newsletter) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'resume',
data: newsletter.id
}).done(function() {
jQuery('#pause_'+newsletter.id).show();
jQuery('#resume_'+newsletter.id).hide();
});
},
renderStatus: function(newsletter) {
if (!newsletter.queue) {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>
);
} else {
if (newsletter.queue.status === 'scheduled') {
return (
<span>{MailPoet.I18n.t('scheduledFor')} { MailPoet.Date.format(newsletter.queue.scheduled_at) } </span>
)
}
const progressClasses = classNames(
'mailpoet_progress',
{ 'mailpoet_progress_complete': newsletter.queue.status === 'completed'}
);
// calculate percentage done
const percentage = Math.round(
(newsletter.queue.count_processed * 100) / (newsletter.queue.count_total)
);
let label;
if (newsletter.queue.status === 'completed') {
label = (
<span>
{
MailPoet.I18n.t('newsletterQueueCompleted')
.replace("%$1d", newsletter.queue.count_processed - newsletter.queue.count_failed)
.replace("%$2d", newsletter.queue.count_total)
}
</span>
);
} else {
label = (
<span>
{ newsletter.queue.count_processed } / { newsletter.queue.count_total }
&nbsp;&nbsp;
<a
id={ 'resume_'+newsletter.id }
className="button"
style={{ display: (newsletter.queue.status === 'paused') ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.resumeSending.bind(null, newsletter) }
>{MailPoet.I18n.t('resume')}</a>
<a
id={ 'pause_'+newsletter.id }
className="button mailpoet_pause"
style={{ display: (newsletter.queue.status === null) ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.pauseSending.bind(null, newsletter) }
>{MailPoet.I18n.t('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>
);
}
},
renderStatistics: function(newsletter) {
if (mailpoet_tracking_enabled === false) {
return;
}
if (newsletter.statistics && newsletter.queue && newsletter.queue.status !== 'scheduled') {
const total_sent = ~~(newsletter.queue.count_processed);
let percentage_clicked = 0;
let percentage_opened = 0;
let percentage_unsubscribed = 0;
if (total_sent > 0) {
percentage_clicked = Math.round(
(~~(newsletter.statistics.clicked) * 100) / total_sent
);
percentage_opened = Math.round(
(~~(newsletter.statistics.opened) * 100) / total_sent
);
percentage_unsubscribed = Math.round(
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
);
}
return (
<span>
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
</span>
);
} else {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>
);
}
},
renderItem: function(newsletter, actions) {
const rowClasses = classNames(
'manage-column',
'column-primary',
'has-row-actions'
);
const segments = newsletter.segments.map(function(segment) {
return segment.name
}).join(', ');
return (
<div>
<td className={ rowClasses }>
<strong>
<a
className="row-title"
href={ `?page=mailpoet-newsletter-editor&id=${ newsletter.id }` }
>{ newsletter.subject }</a>
</strong>
{ actions }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('status') }>
{ this.renderStatus(newsletter) }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('lists') }>
{ segments }
</td>
{ (mailpoet_tracking_enabled === true) ? (
<td className="column" data-colname={ MailPoet.I18n.t('statistics') }>
{ this.renderStatistics(newsletter) }
</td>
) : null }
<td className="column-date" data-colname={ MailPoet.I18n.t('lastModifiedOn') }>
<abbr>{ MailPoet.Date.format(newsletter.updated_at) }</abbr>
</td>
</div>
);
},
render: function() {
return (
<div>
<h1 className="title">
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
</h1>
<ListingTabs tab="standard" />
<Listing
limit={ mailpoet_listing_per_page }
location={ this.props.location }
params={ this.props.params }
endpoint="newsletters"
tab="standard"
onRenderItem={this.renderItem}
columns={columns}
bulk_actions={ bulk_actions }
item_actions={ newsletter_actions }
messages={ messages }
auto_refresh={ true }
sort_by="updated_at"
sort_order="desc"
/>
</div>
);
}
});
module.exports = NewsletterListStandard;

View File

@ -0,0 +1,53 @@
import React from 'react'
import { Link } from 'react-router'
import classNames from 'classnames'
import MailPoet from 'mailpoet'
const ListingTabs = React.createClass({
getInitialState() {
return {
tab: null,
tabs: [
{
name: 'standard',
label: MailPoet.I18n.t('tabStandardTitle'),
link: '/standard'
},
{
name: 'welcome',
label: MailPoet.I18n.t('tabWelcomeTitle'),
link: '/welcome'
},
{
name: 'notification',
label: MailPoet.I18n.t('tabNotificationTitle'),
link: '/notification'
}
]
};
},
render() {
const tabs = this.state.tabs.map((tab, index) => {
const tabClasses = classNames(
'nav-tab',
{ 'nav-tab-active': (this.props.tab === tab.name) }
);
return (
<Link
key={ 'tab-'+index }
className={ tabClasses }
to={ tab.link }
>{ tab.label }</Link>
);
});
return (
<h2 className="nav-tab-wrapper">
{ tabs }
</h2>
);
}
});
module.exports = ListingTabs;

View File

@ -0,0 +1,361 @@
import React from 'react'
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
import Listing from 'listing/listing.jsx'
import ListingTabs from 'newsletters/listings/tabs.jsx'
import classNames from 'classnames'
import jQuery from 'jquery'
import MailPoet from 'mailpoet'
import _ from 'underscore'
const mailpoet_roles = window.mailpoet_roles || {};
const mailpoet_segments = window.mailpoet_segments || {};
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
const messages = {
onTrash(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterTrashed')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersTrashed')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onDelete(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterDeleted')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersDeleted')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
},
onRestore(response) {
const count = ~~response;
let message = null;
if (count === 1) {
message = (
MailPoet.I18n.t('oneNewsletterRestored')
);
} else {
message = (
MailPoet.I18n.t('multipleNewslettersRestored')
).replace('%$1d', count);
}
MailPoet.Notice.success(message);
}
};
const columns = [
{
name: 'subject',
label: MailPoet.I18n.t('subject'),
sortable: true
},
{
name: 'status',
label: MailPoet.I18n.t('status'),
width: 145
},
{
name: 'settings',
label: MailPoet.I18n.t('settings')
},
{
name: 'statistics',
label: MailPoet.I18n.t('statistics'),
display: mailpoet_tracking_enabled
},
{
name: 'updated_at',
label: MailPoet.I18n.t('lastModifiedOn'),
sortable: true
}
];
const bulk_actions = [
{
name: 'trash',
label: MailPoet.I18n.t('trash'),
onSuccess: messages.onTrash
}
];
const newsletter_actions = [
{
name: 'view',
link: function(newsletter) {
return (
<a href={ newsletter.preview_url } target="_blank">
{MailPoet.I18n.t('preview')}
</a>
);
}
},
{
name: 'edit',
link: function(newsletter) {
return (
<a href={ `?page=mailpoet-newsletter-editor&id=${ newsletter.id }` }>
{MailPoet.I18n.t('edit')}
</a>
);
}
},
{
name: 'duplicate',
label: MailPoet.I18n.t('duplicate'),
onClick: function(newsletter, refresh) {
return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'duplicate',
data: newsletter.id
}).done(function(response) {
if (response !== false && response.subject !== undefined) {
MailPoet.Notice.success(
(MailPoet.I18n.t('newsletterDuplicated')).replace(
'%$1s', response.subject
)
);
}
refresh();
});
}
},
{
name: 'trash'
}
];
const NewsletterListWelcome = React.createClass({
updateStatus: function(e) {
// make the event persist so that we can still override the selected value
// in the ajax callback
e.persist();
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'setStatus',
data: {
id: ~~(e.target.getAttribute('data-id')),
status: e.target.value
}
}).done(function(response) {
if (response.result === false) {
MailPoet.Notice.error(MailPoet.I18n.t('welcomeEmailActivationFailed'));
// reset value to actual newsletter's status
e.target.value = response.status;
} else {
if (response.status === 'active') {
MailPoet.Notice.success(MailPoet.I18n.t('welcomeEmailActivated'));
}
// force refresh of listing so that groups are updated
this.forceUpdate();
}
}.bind(this));
},
renderStatus: function(newsletter) {
let total_sent;
total_sent = (
MailPoet.I18n.t('sentToXSubscribers')
.replace('%$1d', newsletter.total_sent.toLocaleString())
);
return (
<div>
<p>
<select
data-id={ newsletter.id }
defaultValue={ newsletter.status }
onChange={ this.updateStatus }
>
<option value="active">{ MailPoet.I18n.t('active') }</option>
<option value="draft">{ MailPoet.I18n.t('inactive') }</option>
</select>
</p>
<p>{ total_sent }</p>
</div>
);
},
renderSettings: function(newsletter) {
let sendingEvent;
let sendingDelay;
// set sending event
switch (newsletter.options.event) {
case 'user':
// WP User
if (newsletter.options.role === 'mailpoet_all') {
sendingEvent = MailPoet.I18n.t('welcomeEventWPUserAnyRole');
} else {
sendingEvent = MailPoet.I18n.t('welcomeEventWPUserWithRole').replace(
'%$1s', mailpoet_roles[newsletter.options.role]
);
}
break;
case 'segment':
// get segment
const segment = _.find(mailpoet_segments, function(segment) {
return (~~(segment.id) === ~~(newsletter.options.segment));
});
if (segment === undefined) {
return (
<span className="mailpoet_error">
{ MailPoet.I18n.t('sendingToSegmentsNotSpecified') }
</span>
);
} else {
sendingEvent = MailPoet.I18n.t('welcomeEventSegment').replace(
'%$1s', segment.name
);
}
break;
}
// set sending delay
if (sendingEvent) {
if (newsletter.options.afterTimeType !== 'immediate') {
switch (newsletter.options.afterTimeType) {
case 'hours':
sendingDelay = MailPoet.I18n.t('sendingDelayHours').replace(
'%$1d', newsletter.options.afterTimeNumber
);
break;
case 'days':
sendingDelay = MailPoet.I18n.t('sendingDelayDays').replace(
'%$1d', newsletter.options.afterTimeNumber
);
break;
case 'weeks':
sendingDelay = MailPoet.I18n.t('sendingDelayWeeks').replace(
'%$1d', newsletter.options.afterTimeNumber
);
break;
}
sendingEvent += ' [' + sendingDelay + ']';
}
// add a "period" at the end if we do have a sendingEvent
sendingEvent += '.';
}
return (
<span>
{ sendingEvent }
</span>
);
},
renderStatistics: function(newsletter) {
if (mailpoet_tracking_enabled === false) {
return;
}
if (newsletter.total_sent > 0 && newsletter.statistics) {
const total_sent = ~~(newsletter.total_sent);
const percentage_clicked = Math.round(
(~~(newsletter.statistics.clicked) * 100) / total_sent
);
const percentage_opened = Math.round(
(~~(newsletter.statistics.opened) * 100) / total_sent
);
const percentage_unsubscribed = Math.round(
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
);
return (
<span>
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
</span>
);
} else {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>
);
}
},
renderItem: function(newsletter, actions) {
const rowClasses = classNames(
'manage-column',
'column-primary',
'has-row-actions'
);
return (
<div>
<td className={ rowClasses }>
<strong>
<a
className="row-title"
href={ `?page=mailpoet-newsletter-editor&id=${ newsletter.id }` }
>{ newsletter.subject }</a>
</strong>
{ actions }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('status') }>
{ this.renderStatus(newsletter) }
</td>
<td className="column" data-colname={ MailPoet.I18n.t('settings') }>
{ this.renderSettings(newsletter) }
</td>
{ (mailpoet_tracking_enabled === true) ? (
<td className="column" data-colname={ MailPoet.I18n.t('statistics') }>
{ this.renderStatistics(newsletter) }
</td>
) : null }
<td className="column-date" data-colname={ MailPoet.I18n.t('lastModifiedOn') }>
<abbr>{ MailPoet.Date.format(newsletter.updated_at) }</abbr>
</td>
</div>
);
},
render: function() {
return (
<div>
<h1 className="title">
{ MailPoet.I18n.t('pageTitle') } <Link className="page-title-action" to="/new">{ MailPoet.I18n.t('new') }</Link>
</h1>
<ListingTabs tab="welcome" />
<Listing
limit={ mailpoet_listing_per_page }
location={ this.props.location }
params={ this.props.params }
endpoint="newsletters"
tab="welcome"
onRenderItem={ this.renderItem }
columns={ columns }
bulk_actions={ bulk_actions }
item_actions={ newsletter_actions }
messages={ messages }
auto_refresh={ true }
sort_by="updated_at"
sort_order="desc"
/>
</div>
);
}
});
module.exports = NewsletterListWelcome;

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