Compare commits

...

198 Commits
0.0.3 ... 0.0.9

Author SHA1 Message Date
8d61866b77 Merge pull request #278 from mailpoet/drag_and_drop
Editor: Drag&Drop
2015-12-18 14:54:30 +01:00
0831c748b1 Merge pull request #275 from mailpoet/newsletter_template
Newsletter template preview
2015-12-18 14:54:21 +01:00
b2a0bc3860 Merge pull request #273 from mailpoet/review
Global Code Review.
2015-12-18 14:44:53 +01:00
1f99345e7b Remove obsolete code 2015-12-17 18:03:16 +02:00
89782bc94b Refactor editor JS, change blocks to extend base methods 2015-12-17 16:58:02 +02:00
155ff09280 Fix incorrect insertion order for containers 2015-12-17 15:21:56 +02:00
132e6d3342 Rename Wordpress component to Communication component, fix preview JS
syntax
2015-12-17 12:40:26 +02:00
f02699158f Apply newsletter background color to template preview images 2015-12-17 12:40:26 +02:00
be3462925d Change server returned newsletter body to be a json object, not a string 2015-12-17 12:40:26 +02:00
3d82230d10 Merge pull request #274 from mailpoet/acceptance_tests_update
Acceptance test related fixes
2015-12-15 13:21:34 +01:00
c20c46fd86 Removed useless 'use' in Router/Setup 2015-12-15 13:11:07 +01:00
6e63c72aa5 Reinstall feature
- implemented reinstall in Settings > Advanced
- shorten placeholder for Form name input
2015-12-15 13:07:43 +01:00
e059eec5ea Acceptance tests have been removed from the PHP project. 2015-12-14 16:09:20 +01:00
a2d38c9076 Merge pull request #270 from mailpoet/unit_tests_fix
Unit tests + Welcome page
2015-12-14 15:48:52 +01:00
8d507b2ee0 Merge pull request #271 from mailpoet/history_fix
Fix react-router dependency issue
2015-12-14 15:31:02 +01:00
5e2979c283 Changes history version to 1.13.1 2015-12-14 14:29:51 +02:00
84ec0de3cd Unit tests + Welcome page
- fixed unit tests
- commented out failing tests that require changes in the code
- added new welcome page
2015-12-11 17:17:59 +01:00
ee07780833 Version bump: 0.0.8. 2015-12-11 14:42:59 +01:00
ed104156a9 New build script as new plugin, independent from wysija-newsletters. 2015-12-11 14:42:15 +01:00
90f2e9ff9d Merge branch 'fix_history' 2015-12-11 14:13:15 +01:00
98fb7aa65e Fix react history pushState error. 2015-12-11 14:12:35 +01:00
a51eb59cf8 Merge pull request #266 from mailpoet/newsletter_template
Editor: Template related fixes
2015-12-11 13:09:54 +01:00
270023b89c Display error and success messages for template saving and export 2015-12-10 17:13:23 +02:00
7d77e075e9 Make long template name and description fit in the box 2015-12-10 17:13:23 +02:00
9bbe36b3cb Set subject of new newsletters to "Draft newsletter" 2015-12-10 17:13:23 +02:00
7a904ed093 Add SVG icons for standard, welcome and notification email types 2015-12-10 17:13:23 +02:00
e3c065b353 Add Post Notifications Blank Template 2015-12-10 17:13:22 +02:00
4ca2872e0e Add Welcome newsletter template 2015-12-10 17:13:22 +02:00
ce338f7fe2 Add border to template thumbnails 2015-12-10 17:13:22 +02:00
fa28b0a955 Merge pull request #267 from mailpoet/settings_round_1
Settings
2015-12-10 13:04:37 +01:00
a298650187 Settings
- added default from name & address based on wp_user on install
- fixed issue with Setting::setValue (added auto-serialize of value if is_array?)
- removed daily notifications from basics settings
2015-12-10 11:44:44 +01:00
95772ef68a Merge pull request #261 from mailpoet/form_editor_round_1
Form editor round 1
2015-12-09 14:07:33 +01:00
e1c94db516 edit form name follows newsletter's design 2015-12-08 17:10:45 +01:00
7be1a11d1e Form editor
- fixed validations on radio type
- fixed date format for months
- added custom fields storing on subscribe
- fixed date widget (select today's date)
- fixed validation on form widget
2015-12-08 16:55:30 +01:00
c04b95c09a Merge pull request #262 from mailpoet/editor_transitions
Editor: fix transitions and thumbnails
2015-12-07 20:00:07 +01:00
cdfeb8d512 Fix template thumbnails by reverting to earlier html2canvas version 2015-12-07 18:50:50 +02:00
3ef8067968 Remove transition class after it ends for editor content blocks 2015-12-07 18:50:17 +02:00
9fb04bc3c0 first round of fixes #255 2015-12-07 16:54:08 +01:00
25727ea0ba Version bump: 0.0.7 2015-12-07 14:43:16 +01:00
4c8ac369b7 Merge pull request #260 from mailpoet/newsletter_sending
Bugfixes on sending
2015-12-07 14:35:38 +01:00
268dabdc9f Bugfixes on sending
- added checks to prevent adding to the queue useless items
- fixed issue with mta settings (duplicated "host" input / renamed duplicate to "domain" for MailGun)
- fixed namespace issue on cron daemon/supervisor
- fixed typo in migration preventing the newsletter_templates table to be created.
- partially fixed cron.jsx
2015-12-07 13:29:42 +01:00
c0ba218949 Merge branch 'master' of github.com:mailpoet/mailpoet 2015-12-04 21:41:29 +01:00
ee85139089 Merge pull request #254 from mailpoet/queue
Sending Queue: start & track
2015-12-04 21:36:50 +01:00
b4f83fe1bd Merge pull request #257 from mailpoet/image_size
Custom newsletter image size
2015-12-04 21:22:15 +01:00
bd83001ef5 Merge pull request #258 from mailpoet/helpscout
HelpScout integration fixes
2015-12-04 21:20:28 +01:00
fb3a9f485f Merge pull request #259 from mailpoet/custom_fields
Add custom fields to newsletter editor
2015-12-04 21:20:09 +01:00
fd44776ae9 - Fixes SMTP issue 2015-12-04 14:33:43 -05:00
6dbe338b01 - Updates mailers to use HTML and/or TEXT body 2015-12-04 14:06:43 -05:00
1f06a7dd0b fixed naming of sending_queue & added validation of segments on step 3 2015-12-04 19:54:31 +01:00
37c218f782 fixed sending test email text version only 2015-12-04 19:46:44 +01:00
25016f2a8d test sending method works 2015-12-04 19:28:00 +01:00
792744a270 - Implements check for name/email in mailer router 2015-12-04 12:47:14 -05:00
c5fbfca132 Merge branch 'queue' of mailpoet:mailpoet/mailpoet into queue 2015-12-04 12:32:38 -05:00
c2fde308cb - Renames and updates sending queue worker
- Updates mailer router's send() method
2015-12-04 12:31:54 -05:00
533d9b0d38 Change custom field shortcode to follow MP2 format 2015-12-04 17:48:35 +02:00
2035b802e3 Add shortcodes for custom fields to newsletter editor 2015-12-04 17:47:18 +02:00
a5e66ec6a0 implemented sending test email 2015-12-04 16:20:07 +01:00
beb939df9e updated send with tab 2015-12-04 15:31:04 +01:00
44e342c692 Fix z-index, change icon, add instructional message 2015-12-04 15:30:59 +02:00
3a417d460f Add custom MailPoet image size for newsletters 2015-12-04 14:37:05 +02:00
1950d6661f Step 3 sender and reply to per newsletter
- added sender_address/sender_name/reply_to_address/reply_to_name
- added validation on all form fields (except checkbox and radio)
2015-12-04 12:29:11 +01:00
da6e154642 fixed conflicts 2015-12-04 11:25:06 +01:00
acebf669a7 - Updates queue worker to use mailer router for sending
- Updates mailer router to detect method type
- Rebases master
2015-12-03 22:01:33 -05:00
4a2bbe3f88 Sending Progress
- improved progress bar styles (with completed status)
- add pause/resume buttons
- fixed method case in settings.mta for MailPoet & SMTP Providers
- fixed parsley dependency
- added validation on from name & address on step 3
2015-12-03 22:01:19 -05:00
9b011c0281 - Places supervisor/daemon/worker under the new Cron class
- Updates endpoints
- Integrates queue worker with MailPoet mailer
- Fixes script activation check logic
2015-12-03 22:01:18 -05:00
bf58d8a22d Queue
- updated menu icon for our plugin
- added watchCss command to watch only CSS files
- added Status column in Newsletters listing
- added progress bar styles
- fixed issue with JS assets being loaded twice on non MP pages
- changed subscriber_ids to segment_ids in addQueue
2015-12-03 22:01:17 -05:00
72d1eb79a6 fixed too much recursion issue on selection JSX 2015-12-03 22:01:17 -05:00
bb4893c0a0 added missing messages on newsletter form 2015-12-03 22:01:16 -05:00
9929cf0aee - Adds router methods to add/delete/et queue status 2015-12-03 22:01:15 -05:00
83967e84ba - Implements starting/stopping/pausing daemon 2015-12-03 22:01:15 -05:00
9621cb3ca9 Merge pull request #253 from mailpoet/animations
Editor: Animations
2015-12-03 22:48:43 +01:00
a413f666fe Sending Progress
- improved progress bar styles (with completed status)
- add pause/resume buttons
- fixed method case in settings.mta for MailPoet & SMTP Providers
- fixed parsley dependency
- added validation on from name & address on step 3
2015-12-03 18:26:36 +01:00
d1e2c6c074 Add transition for "Delete block" icon 2015-12-03 15:53:56 +02:00
3b6a9f7a6e Add block, tool and widget transition animations 2015-12-03 14:44:32 +02:00
d2e5fb89c2 - Places supervisor/daemon/worker under the new Cron class
- Updates endpoints
- Integrates queue worker with MailPoet mailer
- Fixes script activation check logic
2015-12-02 22:48:15 -05:00
97d1e95037 Add transitions for content block addition and removal 2015-12-02 17:54:06 +02:00
48fbce22e7 Queue
- updated menu icon for our plugin
- added watchCss command to watch only CSS files
- added Status column in Newsletters listing
- added progress bar styles
- fixed issue with JS assets being loaded twice on non MP pages
- changed subscriber_ids to segment_ids in addQueue
2015-12-02 12:25:28 +01:00
916fe76795 Add transitions: sidebar contents, template preview, sidebar 2015-12-01 16:49:50 +02:00
e10310fb5c fixed too much recursion issue on selection JSX 2015-12-01 13:50:35 +01:00
367afcf814 added missing messages on newsletter form 2015-12-01 13:01:06 +01:00
67fa9e0993 - Adds router methods to add/delete/et queue status 2015-11-30 19:09:06 -05:00
d7553a5f27 Merge pull request #251 from mailpoet/newsletter_templates
Editor: Add two sample templates
2015-11-30 21:13:49 +01:00
8c847825fa - Implements starting/stopping/pausing daemon 2015-11-30 13:01:07 -05:00
d21d9b99b0 Add an option for certain templates to be read only 2015-11-30 18:23:43 +02:00
8461c13532 Include thumbnail on saved templates, add another sample template 2015-11-30 18:05:10 +02:00
3b7ffe9ba7 Update Marionette to 2.4.4 from 2.4.3 2015-11-30 13:21:28 +02:00
1724fa22c1 Merge pull request #250 from mailpoet/default_data_on_install
Default segments on install
2015-11-30 12:17:32 +01:00
01089d7a72 Bump version to 0.0.6 2015-11-27 22:00:54 +01:00
717ebfd20c Bump composer lockfile. 2015-11-27 22:00:17 +01:00
daff3d5016 Merge pull request #249 from mailpoet/sticky_kit_fix
Add back sticky-kit dependency
2015-11-27 19:14:03 +01:00
98fb838169 updated default lists' descriptions 2015-11-27 15:56:18 +01:00
f13a4dd4f3 Add back sticky-kit dependency 2015-11-27 16:44:04 +02:00
62a164e4c6 added creation of WP Users & default list on install 2015-11-27 15:27:50 +01:00
04238f3339 Phoenix Vagrant VM has been replaced with a new VM with test data. 2015-11-27 15:16:06 +01:00
bc62474f3c Merge pull request #240 from mailpoet/queue
Queue
2015-11-27 13:49:18 +01:00
98005a2a6f - Rebases master 2015-11-27 07:46:26 -05:00
b7fe8dc6d6 - Adds Daemon status (under Mailpoet->Queue) 2015-11-27 07:40:23 -05:00
436faea591 - Refactors and renames code
- Adds Queue menu option and displays status
2015-11-27 07:40:22 -05:00
4208b148b4 - Implements queue worker class 2015-11-27 07:35:16 -05:00
5ce5f0bf8a - Refactors code 2015-11-27 07:35:16 -05:00
18518de397 - Refactors some code 2015-11-27 07:35:15 -05:00
68f747211a - Adds a helper method to convert cameCase to under_score
- Removes unused method from Queue daemon
2015-11-27 07:35:15 -05:00
ebd26ddd98 - Silences fsockopen errors 2015-11-27 07:35:14 -05:00
924aa0439f - Minor regex changes
- Adds method to Env class that returns plugin activation status
- Prevents Supervisor from running if the plugin isn't activated
2015-11-27 07:35:13 -05:00
3124d6a61b - Fixes issue with ucwords() function on older PHP versions
- Updates Supervisor/Daemon to strip port from site_url() when it's
  running on localhost inside a virtual machine :)
2015-11-27 07:35:13 -05:00
149d031b52 - Adds session support
- Renames Queue to Daemon
- Updates router methods
2015-11-27 07:35:12 -05:00
fa96c4697d - Adds Queue router
- Updates logic for Queue and Supervisor
- #227
2015-11-27 07:35:12 -05:00
d46c9d5412 - Fixes issue with Supervisor when database tables do not exist 2015-11-27 07:35:11 -05:00
9ab8b1f0c5 added loading on wp users sync 2015-11-27 12:57:52 +01:00
9425ac1593 Merge pull request #244 from mailpoet/wp_users_segment
WP Users segment
2015-11-27 11:39:08 +01:00
4e32479609 Merge pull request #246 from mailpoet/import-fixes
Fixes import issues and updates code
2015-11-27 09:30:19 +01:00
7d95b38dc4 - Renames/refactors Import and Export classes/views/JS
- Updates Import and Export to ignore trashed subscribers
- Updates tests
Closes #245
2015-11-26 20:48:19 -05:00
17c83c5bd4 Merge pull request #242 from mailpoet/newsletter_templates
Newsletter template thumbnails
2015-11-26 21:26:23 +01:00
23bb2f111f Add a sample translatable template 2015-11-26 17:21:10 +02:00
4655b2c64c Switch template thumbnails to be generated in jpeg, not png 2015-11-26 17:20:34 +02:00
84fede11b8 removed dynamic lists from import 2015-11-26 16:02:53 +01:00
f37488fc0b Adds a Preview icon for previewing newsletter templates 2015-11-26 13:09:08 +02:00
20e2e03982 Hide non default segments on some pages
- added getPublic() method on Segment model
- filter out dynamic lists from add/move/remove segment in subscribers
2015-11-26 11:32:10 +01:00
a5d96f1534 WP Users list
- refactored and fixed listing issues (related to Segments)
- removed bulk actions from segments
- added synchronize methods for WP users
- Update action in segments only for WP Users list
- added "type" column to segments (default, wp_users, dynamic...)
- added "status" column to subscriber_segment table (useful soon)
2015-11-25 18:31:57 +01:00
a516eb1a95 Add overlay and newsletter thumbnail preview 2015-11-25 16:44:18 +02:00
6dd8270bec WP Users list
- migration for filters & segment_filter tables
- models for new tables
- update of Listing JSX to allow for conditional display of item actions
2015-11-24 17:12:14 +01:00
981cbd579f Fix clickable labels for editor settings views 2015-11-24 16:51:27 +02:00
52a0aae10f Add template thumbnail generation and display 2015-11-24 16:50:57 +02:00
ebca4257a6 Version bump: 0.0.5 2015-11-21 00:39:11 +01:00
c3944095bc Install npm deps in build command. 2015-11-21 00:36:12 +01:00
a1445d1b6a Vagrant Container and new build process. 2015-11-21 00:28:33 +01:00
e62c24879b Merge pull request #239 from mailpoet/revert-238-queue
Revert "Queue"
2015-11-20 23:51:19 +01:00
00f06ea202 Revert "Queue" 2015-11-20 23:51:02 +01:00
32ca24ce38 Merge pull request #238 from mailpoet/queue
Queue
2015-11-20 23:02:30 +01:00
44e3adb422 Merge pull request #235 from mailpoet/alc
Editor: ALC fixes
2015-11-20 23:00:47 +01:00
a1346ebecc Merge pull request #237 from mailpoet/changelog
welcome/update pages display logic
2015-11-20 23:00:30 +01:00
25b51d0446 - Adds queue management and supervisor. Issue #227 2015-11-20 16:20:54 -05:00
556a170903 - Bootstraps queue 2015-11-20 16:20:35 -05:00
7ac386a8e2 fixed welcome/update pages display logic 2015-11-20 13:37:52 +01:00
d91b55ec52 Include static sticky-kit library, patch it, fix sticky editor sidebar 2015-11-20 14:16:51 +02:00
786fbc36a2 Change post types to plural labels, move Posts/ALC UI elements 2015-11-19 19:08:48 +02:00
160e8b7a12 Merge pull request #232 from mailpoet/export-rename
Rename BootstrapMenu.php to BootStrapMenu.php
2015-11-19 15:01:24 +01:00
0b1f85da09 Rename BootstrapMenu.php to BootStrapMenu.php
- Renames BootStrapMenu class to use proper camelCase
2015-11-19 08:57:37 -05:00
fbc6f54ddc Merge pull request #230 from mailpoet/export
Export
2015-11-19 10:35:27 +01:00
a603e97b8c Merge pull request #229 from mailpoet/editor_posts
Editor: Posts widget
2015-11-19 10:31:31 +01:00
0875b627b6 Merge pull request #231 from mailpoet/newsletters_select_all_fix
fixed missing messages in newsletters listing
2015-11-19 10:27:45 +01:00
035784ece0 fixed missing messages in newsletters listing 2015-11-19 10:24:27 +01:00
aa93c7349f - Rebases master
- Adds tests for all Export class methods
Closes #221
2015-11-18 22:14:48 -05:00
82cf4a28fd - Updates export tests 2015-11-18 14:42:28 -05:00
832e5ef342 - Fixed minor import issue wrt to status not being set 2015-11-18 14:42:27 -05:00
e3de3a123a - Corrects exported subscriber count
- Properly exports subscribers not in any list
- Adds test for export class constructor method
Resolves #221
2015-11-18 14:42:27 -05:00
db91590159 Merge pull request #228 from mailpoet/welcome_redirect
Welcome page redirection
2015-11-18 14:33:48 +01:00
28c61fca0b Newsletters listing
- fixed listing actions on newsletters
2015-11-18 14:04:34 +01:00
e62a8c2ec5 Implement Becs' changes to Posts widget and post rendering 2015-11-18 14:44:57 +02:00
fdbd1245e3 Redirect to welcome or update page 2015-11-17 20:11:03 +01:00
0eef46db57 Fix post transformation to take titleIsLink option into account 2015-11-17 16:55:55 +02:00
080ae88a04 Fix Posts block tests 2015-11-17 16:31:57 +02:00
225be9f3cd Starting welcome page redirection 2015-11-17 13:43:25 +01:00
c9a42ebb76 Add auto-refresh of Posts block contents on option change 2015-11-17 14:40:39 +02:00
ae9b3df92d Merge pull request #223 from mailpoet/build
Fix JS lib inclusion in build step
2015-11-16 13:38:33 +01:00
63a08ebb55 Fix inclusion of js/lib/tinymce by disabling removal of node_modules/ 2015-11-16 14:27:29 +02:00
2ef5096fa9 Optimize symlinks when building. 2015-11-13 23:55:24 +01:00
95cfe2d8ec Version bump: 0.0.4. 2015-11-13 19:57:22 +01:00
49676b3fc5 Merge pull request #219 from mailpoet/export
Export
2015-11-13 18:49:32 +01:00
c96ac06423 - Moves ImportExport under Subscribers namespace
- Updates tests
2015-11-13 12:46:54 -05:00
6a3166c311 - Implements export
Closes #210
2015-11-13 12:25:33 -05:00
0017df1c2d - Work-in-progress on the UI 2015-11-13 12:25:32 -05:00
f2a0d4ce96 fixed wrong count value onGetItems in Listing 2015-11-13 12:25:32 -05:00
cb50517cbc toggle Export button depending on subscribers count 2015-11-13 12:25:31 -05:00
0fedd1779f - Moves Import and Export under ImportExport namespace
- Cretes a single BootStrapMenu class for Import and Export
- Updates tests
- Adds 2 new methods to Segments model
2015-11-13 12:25:31 -05:00
1625e1771b - Bootstraps export 2015-11-13 12:25:30 -05:00
bde78b607b Merge pull request #218 from mailpoet/listings_bugfix
Listings bugfixes & Welcome page
2015-11-13 16:01:23 +01:00
f3c58c27be set welcome page as default page 2015-11-13 15:58:36 +01:00
9fec460295 Merge pull request #220 from mailpoet/helpscout_beacon
Adds a HelpScout beacon to admin pages
2015-11-13 15:23:03 +01:00
162859529e reinstated removed tests 2015-11-13 13:56:59 +01:00
3bdaaf8368 Add a HelpScout beacon to admin pages 2015-11-13 14:45:25 +02:00
f6ab0050b2 added welcome page 2015-11-13 12:59:49 +01:00
10a20935c3 cleanup tests 2015-11-12 14:11:27 +01:00
8135b677f7 Merge pull request #216 from mailpoet/editor_layouts
Add layout settings to newsletter editor
2015-11-12 14:04:04 +01:00
90382bc86d Add layout block bg color, remove bg colors of individual columns 2015-11-11 16:55:44 +02:00
47af8939cc Merge pull request #214 from mailpoet/editor_images
Editor small image handling
2015-11-11 09:32:32 +01:00
4a0deb2182 Preserve image width for smaller than column width images 2015-11-10 18:09:36 +02:00
3a206b2c88 Adapt newsletter template selection to API change 2015-11-10 18:08:16 +02:00
70cfcbe7cc Merge pull request #209 from mailpoet/mixpanel
Add MixPanel analytics tracking
2015-11-10 10:41:24 +01:00
6342cb17bd Merge pull request #208 from mailpoet/listing_tests
Listings
2015-11-10 10:39:53 +01:00
64c35b12c8 Merge pull request #191 from mailpoet/import
Import
2015-11-09 23:23:08 +01:00
6a14f97419 Fix select2 results z-index issue for newsletter editor 2015-11-09 18:17:10 +02:00
dfec34eda9 Add Analytics integration with MixPanel 2015-11-09 18:11:06 +02:00
893231e8e5 List selection fix 2015-11-09 14:19:59 +01:00
e9110680ee Listings
- fixed selection field JSX
- fixed bulk actions (added filter function)
- added getPublished/getTrashed static methods on Model
- fixed step 3 of newsletter process
- updated save/get methods of all listing-able models to conform with the new norm
2015-11-09 13:26:33 +01:00
7b54285ca6 - Adds tests for the main Import class
- Updates tests for Env (proper host detection with port)
- Improves import
2015-11-09 00:25:24 -05:00
33ea16eb0f - Cleans up import
- Adds tests for modified models
- Adds tests for import BootStrapMenu and MailChimp classes
2015-11-08 15:57:43 -05:00
b1ae07d38e - Rebased master
- Cleaned up import & moved it under Subscribers menu
2015-11-07 21:16:38 -05:00
3f168d052f - Finishes import migration
- Updates models
- Improves Notice.js
2015-11-07 11:40:42 -05:00
158d26ef86 - Adds import's step 1 method selection logic 2015-11-07 11:31:40 -05:00
ae03ee2c46 - Bootstrapping import menu 2015-11-07 11:25:05 -05:00
ad0adb48e1 - Updates Migrator with new column for Segments
- Updates Segmnets tests
- Updates MailPoet's Notice.js with additional options
- Updates Import's router, WP menu bootstrap logic, client- and
  server-side logic
2015-11-07 11:24:02 -05:00
ff5353c894 - Completes MailChimp import 2015-11-07 11:15:49 -05:00
abb2389378 - Enables MailChimp key verification (import step 1) 2015-11-07 11:15:04 -05:00
3cf50810f9 - Fixed conflic with backbone router in settings and import
- WIP on step 1
2015-11-07 11:09:13 -05:00
20a6e6d6de - Adds import's step 1 method selection logic 2015-11-07 11:09:13 -05:00
0b1fc8f6c3 - Updates webpack config file
- Adds an option to watch/compile just the JS files with webpack
2015-11-07 11:05:48 -05:00
0a771acb02 - Bootstrapping import menu 2015-11-06 21:38:25 -05:00
226 changed files with 16078 additions and 1736 deletions

3
.gitignore vendored
View File

@ -15,4 +15,5 @@ temp
wysija-newsletters.zip
tests/javascript/testBundles
assets/css/*.css
assets/js/*.js
assets/js/*.js
.vagrant

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@ -0,0 +1,78 @@
.mailpoet_hidden, .mailpoet_validation_error
display none
.form-table
th
width 300px
#paste_input
width 100%
input[type="radio"]
margin-right 0.5em !important
& + span
margin-right 2.5em
span
&.mailpoet_mailchimp-key-status
&.mailpoet_mailchimp-ok
&:before
content "\2713"
color #0e90d2
margin-left 15px
&.mailpoet_mailchimp-error
&:before
content "\2717"
color #900
margin-left 15px
#subscribers_data
overflow auto
table
width auto
td
padding 0.5em
& > table
& > tbody
& > td
padding 0.5em
& > tr
&:nth-child(odd)
background #f9f9f9
.mailpoet_header
text-transform uppercase
font-weight 600
text-decoration underline
#subscribers_data th:first-child, #subscribers_data td:first-child
width 10em !important
text-align center !important
padding 0 1em 0 1em !important
vertical-align inherit !important
#subscribers_data
& > table
& > thead
& > tr
& > th
& > span
width 15em !important
.mailpoet_data_match
color #0e90d2
margin-left 0.25em
.mailpoet_import_error, .mailpoet_validation_error
color #900
tr
&.mailpoet_segments
& > td
& > a
margin-left 15px
span
&.select2-search
&.select2-search--dropdown
display none !important

View File

@ -159,23 +159,6 @@ body.mailpoet_modal_opened
margin: 0
text-align: right
.mailpoet_button
padding: 3px 15px
border: 1px solid #444
font-weight: normal
cursor: pointer
background-color: #222
color: #cfcfcf
font-size: 1em
.mailpoet_button:hover
background-color: #00aacc
color: #fff
.mailpoet_button:active
background-color: #00ccff
color: #fff
@media screen and (max-width: 782px)
#mailpoet_modal_overlay.mailpoet_panel_overlay
top: 46px

View File

@ -76,13 +76,24 @@ $layer-selector-width = 30px
display: inline-block
padding: 2px
vertical-align: top
animation-background-color()
.mailpoet_tool
padding: 0
.mailpoet_delete_block_activate
max-width: 100%
display: inline-block
opacity: 1
animation-fade-in-and-scale-horizontally()
.mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel
display: none
max-width: 0
opacity: 0
overflow: hidden
display: inline-block
animation-fade-in-and-scale-horizontally()
.mailpoet_delete_block_activated
width: auto
@ -93,11 +104,14 @@ $layer-selector-width = 30px
height: auto
.mailpoet_delete_block_activate
display: none
overflow: hidden
max-width: 0
opacity: 0
.mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel
display: inline-block
max-width: 100%
opacity: 1
.mailpoet_delete_block_confirm
color: $warning-text-color

View File

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

View File

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

View File

@ -32,7 +32,7 @@ $widget-icon-width = 30px
.mailpoet_region_content
max-height: 2000px
transition: max-height 0.2s ease
transition: max-height 300ms ease
padding: 0 20px
margin-top: 12px

View File

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

View File

@ -30,3 +30,8 @@ $block-hover-highlight-color = $primary-active-color
.mailpoet_content
position: relative
.mailpoet_block_transition_in
animation-fade-in-and-scale-up()
.mailpoet_block_transition_out
animation-fade-out-and-scale-down()

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/* Fix select2 z-index to work with MailPoet.Modal */
.select2-dropdown
z-index: 101000
z-index: 101000 !important
/* Remove input field styles from select2 type input */
.select2-container

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

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

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -231,7 +231,7 @@ var WysijaHistory = {
/* MailPoet Form */
var WysijaForm = {
version: '0.6',
version: '0.7',
options: {
container: 'mailpoet_form_container',
editor: 'mailpoet_form_editor',
@ -317,6 +317,7 @@ var WysijaForm = {
save: function() {
var position = 1,
data = {
'name': $F('mailpoet_form_name'),
'settings': $('mailpoet_form_settings').serialize(true),
'body': [],
'styles': (MailPoet.CodeEditor !== undefined) ? MailPoet.CodeEditor.getValue() : null

View File

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

View File

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

View File

@ -105,6 +105,9 @@ const item_actions = [
refresh();
});
}
},
{
name: 'trash'
}
];

View File

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

View File

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

View File

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

View File

@ -79,27 +79,48 @@ define(
if(custom_actions.length > 0) {
item_actions = custom_actions.map(function(action, index) {
if(action.refresh) {
if(action.display !== undefined) {
if(action.display(this.props.item) === false) {
return;
}
}
if(action.name === 'trash') {
return (
<span key={ 'action-'+index } className="trash">
{(index > 0) ? ' | ' : ''}
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
null,
this.props.item.id
) }>
Trash
</a>
</span>
);
} else if(action.refresh) {
return (
<span
onClick={ this.props.onRefreshItems }
key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
{ action.link(this.props.item) }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span>
);
} else if(action.link) {
return (
<span
key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
{ action.link(this.props.item) }
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span>
);
} else {
return (
<span
key={ 'action-'+index } className={ action.name }>
{(index > 0) ? ' | ' : ''}
<a href="javascript:;" onClick={
(action.onClick !== undefined)
? action.onClick.bind(null,
@ -108,7 +129,6 @@ define(
)
: false
}>{ action.label }</a>
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
</span>
);
}
@ -158,17 +178,6 @@ define(
<div>
<div className="row-actions">
{ item_actions }
{ ' | ' }
<span className="trash">
<a
href="javascript:;"
onClick={ this.handleTrashItem.bind(
null,
this.props.item.id
) }>
Trash
</a>
</span>
</div>
<button
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
@ -388,6 +397,12 @@ define(
if(this.isMounted()) {
const params = this.props.params || {}
this.initWithParams(params)
if(this.props.auto_refresh) {
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
this.getItems();
}.bind(this));
}
}
},
componentWillReceiveProps: function(nextProps) {
@ -419,7 +434,13 @@ define(
groups: response.groups || [],
count: response.count || 0,
loading: false
});
}, function() {
if(this.props['onGetItems'] !== undefined) {
this.props.onGetItems(
~~(this.state.groups[0]['count'])
);
}
}.bind(this));
}.bind(this));
}
},
@ -631,7 +652,7 @@ define(
// bulk actions
var bulk_actions = this.props.bulk_actions || [];
if(this.state.group === 'trash') {
if(this.state.group === 'trash' && bulk_actions.length > 0) {
bulk_actions = [
{
name: 'restore',

View File

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

View File

@ -11,10 +11,10 @@ define([
'newsletter_editor/blocks/base',
'newsletter_editor/blocks/button',
'newsletter_editor/blocks/divider',
'newsletter_editor/components/wordpress',
'newsletter_editor/components/communication',
'underscore',
'jquery'
], function(App, BaseBlock, ButtonBlock, DividerBlock, WordpressComponent, _, jQuery) {
], function(App, BaseBlock, ButtonBlock, DividerBlock, CommunicationComponent, _, jQuery) {
"use strict";
@ -69,7 +69,7 @@ define([
},
fetchPosts: function() {
var that = this;
WordpressComponent.getTransformedPosts(this.toJSON()).done(function(content) {
CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
console.log('ALC fetched', arguments);
that.get('_container').get('blocks').reset(content, {parse: true});
}).fail(function(error) {
@ -161,7 +161,7 @@ define([
var that = this;
// Dynamically update available post types
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({
multiple: true,
@ -174,10 +174,10 @@ define([
},
transport: function(options, success, failure) {
var taxonomies,
promise = WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched
var promise = WordpressComponent.getTerms({
var promise = CommunicationComponent.getTerms({
search: options.data.term,
taxonomies: _.keys(taxonomies)
}).then(function(terms) {
@ -226,11 +226,11 @@ define([
toggleDisplayOptions: function(event) {
var el = this.$('.mailpoet_automated_latest_content_display_options'),
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
if (el.hasClass('mailpoet_hidden')) {
el.removeClass('mailpoet_hidden');
if (el.hasClass('mailpoet_closed')) {
el.removeClass('mailpoet_closed');
showControl.addClass('mailpoet_hidden');
} else {
el.addClass('mailpoet_hidden');
el.addClass('mailpoet_closed');
showControl.removeClass('mailpoet_hidden');
}
},
@ -306,7 +306,7 @@ define([
_.each(postTypes, function(type) {
select.append(jQuery('<option>', {
value: type.name,
text: type.labels.singular_name,
text: type.label,
}));
});
select.val(selectedValue);

View File

@ -48,6 +48,7 @@ define([
},
modelEvents: {
'change': 'render',
'delete': 'deleteBlock',
},
events: {
"mouseenter": "showTools",
@ -88,7 +89,9 @@ define([
this.$el.addClass('mailpoet_editor_view_' + this.cid);
},
initialize: function() {
this.on('showSettings', this.showSettings);
this.on('showSettings', this.showSettings, this);
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
showTools: function(_event) {
if (!this.showingToolsDisabled) {
@ -114,12 +117,36 @@ define([
* Defines drop behavior of BlockView instance
*/
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
//that.model.destroy();
return newModel;
};
return this.model.clone();
}.bind(this);
},
showBlock: function() {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function() {
this.transitionOut().done(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('mailpoet_block_transition_in');
},
transitionOut: function() {
return this._transition('mailpoet_block_transition_out');
},
_transition: function(className) {
var promise = jQuery.Deferred();
this.$el.addClass(className);
this.$el.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd animationend', function() {
this.$el.removeClass(className);
promise.resolve();
}.bind(this));
return promise;
},
});
@ -168,24 +195,22 @@ define([
},
deleteBlock: function(event) {
event.preventDefault();
this.model.destroy();
this.model.trigger('delete');
return false;
}
},
});
Module.BlockSettingsView = Marionette.LayoutView.extend({
className: 'mailpoet_editor_settings',
initialize: function() {
var that = this;
MailPoet.Modal.panel({
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
onCancel: function() {
that.destroy();
},
this.destroy();
}.bind(this),
});
},
close: function(event) {
@ -203,6 +228,9 @@ define([
changeBoolField: function(field, event) {
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
},
changeBoolCheckboxField: function(field, event) {
this.model.set(field, (!!jQuery(event.target).prop('checked')));
},
changeColorField: function(field, event) {
var value = jQuery(event.target).val();
if (value === '') {

View File

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

View File

@ -75,7 +75,8 @@ define([
getEmptyView: function() { return Module.ContainerBlockEmptyView; },
emptyViewOptions: function() { return { renderOptions: this.renderOptions }; },
modelEvents: {
'change': 'render'
'change': 'render',
'delete': 'deleteBlock',
},
events: {
"mouseenter": "showTools",
@ -136,6 +137,8 @@ define([
},
initialize: function(options) {
this.renderOptions = _.defaults(options.renderOptions || {}, {});
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
// Determines which view type should be used for a child
getChildView: function(model) {
@ -162,10 +165,10 @@ define([
this.toolsView = new Module.ContainerBlockToolsView({
model: this.model,
tools: {
settings: this.renderOptions.depth > 1,
settings: this.renderOptions.depth === 1,
delete: this.renderOptions.depth === 1,
move: this.renderOptions.depth === 1,
layerSelector: this.renderOptions.depth === 1,
layerSelector: false,
},
});
this.toolsRegion.show(this.toolsView);
@ -229,12 +232,37 @@ define([
_.extend(this, this._buildRegions(this.regions));
},
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
that.model.destroy();
return newModel;
};
return this.model.clone();
}.bind(this);
},
showBlock: function() {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function() {
this.transitionOut().done(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('mailpoet_block_transition_in');
},
transitionOut: function() {
return this._transition('mailpoet_block_transition_out');
},
_transition: function(className) {
var that = this,
promise = jQuery.Deferred();
this.$el.addClass(className);
this.$el.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd animationend', function() {
that.$el.removeClass(className);
promise.resolve();
});
return promise;
},
});
@ -265,6 +293,41 @@ define([
behaviors: {
ColorPickerBehavior: {},
},
regions: {
columnsSettingsRegion: '.mailpoet_container_columns_settings',
},
initialize: function() {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
this._columnsSettingsView = new (Module.ContainerBlockColumnsSettingsView)({
collection: this.model.get('blocks'),
});
},
onRender: function() {
this.columnsSettingsRegion.show(this._columnsSettingsView);
},
});
Module.ContainerBlockColumnsSettingsView = Marionette.CollectionView.extend({
getChildView: function() { return Module.ContainerBlockColumnSettingsView; },
childViewOptions: function(model, index) {
return {
columnIndex: index,
};
},
});
Module.ContainerBlockColumnSettingsView = Marionette.ItemView.extend({
getTemplate: function() { return templates.containerBlockColumnSettings; },
initialize: function(options) {
this.columnNumber = (options.columnIndex || 0) + 1;
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
columnNumber: this.columnNumber,
};
},
});
Module.OneColumnContainerWidgetView = base.WidgetView.extend({

View File

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

View File

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

View File

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

View File

@ -37,11 +37,9 @@ define([
getTemplate: function() { return templates.imageBlock; },
onDragSubstituteBy: function() { return Module.ImageWidgetView; },
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
return _.extend({
imageMissingSrc: App.getConfig().get('urls.imageMissing'),
};
}, base.BlockView.prototype.templateHelpers.apply(this));
},
onRender: function() {
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
@ -66,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.changeBoolField, "padded"),
"change .mailpoet_field_image_padded": _.partial(this.changeBoolCheckboxField, "padded"),
"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

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

View File

@ -103,7 +103,8 @@ define([
getTemplate: function() { return templates.socialBlock; },
childViewContainer: '.mailpoet_social',
modelEvents: {
'change': 'render'
'change': 'render',
'delete': 'deleteBlock',
},
events: {
"mouseover": "showTools",
@ -145,6 +146,10 @@ define([
arguments[0].collection = arguments[0].model.get('icons');
Marionette.CompositeView.apply(this, arguments);
},
initialize: function() {
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
// Determines which view type should be used for a child
childView: SocialIconView,
templateHelpers: function() {
@ -170,12 +175,9 @@ define([
_event.stopPropagation();
},
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
//that.model.destroy();
return newModel;
};
return this.model.clone();
}.bind(this);
},
_buildRegions: function(regions) {
var that = this;
@ -194,6 +196,34 @@ define([
this.regionManager.destroy();
_.extend(this, this._buildRegions(this.regions));
},
showBlock: function() {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function() {
this.transitionOut().done(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('mailpoet_block_transition_in');
},
transitionOut: function() {
return this._transition('mailpoet_block_transition_out');
},
_transition: function(className) {
var that = this,
promise = jQuery.Deferred();
this.$el.addClass(className);
this.$el.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd animationend', function() {
that.$el.removeClass(className);
promise.resolve();
});
return promise;
},
});
Module.SocialBlockToolsView = base.BlockToolsView.extend({

View File

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

View File

@ -70,9 +70,10 @@ define([
};
Module.previewNewsletter = function(options) {
return Module._query({
return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'preview',
options: options,
data: options || {},
});
};

View File

@ -44,10 +44,10 @@ define([
};
Module.getBody = function() {
return JSON.stringify({
return {
content: App._contentContainer.toJSON(),
globalStyles: App.getGlobalStyles().toJSON(),
});
};
};
Module.toJSON = function() {
@ -73,8 +73,7 @@ define([
});
App.on('start', function(options) {
// TODO: Other newsletter information will be needed as well.
var body = JSON.parse(options.newsletter.body);
var body = options.newsletter.body;
App._contentContainer = new (App.getBlockTypeModel('container'))(body.content, {parse: true});
App._contentContainerView = new (App.getBlockTypeView('container'))({
model: App._contentContainer,

View File

@ -6,8 +6,9 @@ define([
'backbone.marionette',
'jquery',
'blob',
'filesaver'
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver) {
'filesaver',
'html2canvas'
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver, html2canvas) {
"use strict";
@ -46,26 +47,52 @@ define([
});
};
Module.getThumbnail = function(element, options) {
return html2canvas(element, options || {});
};
Module.saveTemplate = function(options) {
return MailPoet.Ajax.post({
endpoint: 'newsletterTemplates',
action: 'save',
data: _.extend(options || {}, {
var that = this,
promise = jQuery.Deferred();
promise.then(function(thumbnail) {
var data = _.extend(options || {}, {
thumbnail: thumbnail.toDataURL('image/jpeg'),
body: App.getBody(),
}),
});
return MailPoet.Ajax.post({
endpoint: 'newsletterTemplates',
action: 'save',
data: data,
});
});
Module.getThumbnail(
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
).then(function(thumbnail) {
promise.resolve(thumbnail);
});
return promise;
};
Module.exportTemplate = function(options) {
var data = _.extend(options || {}, {
body: App.getBody(),
});
var blob = new Blob(
[JSON.stringify(data)],
{ type: 'application/json;charset=utf-8' }
);
var that = this;
return Module.getThumbnail(
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
).then(function(thumbnail) {
var data = _.extend(options || {}, {
thumbnail: thumbnail.toDataURL('image/jpeg'),
body: App.getBody(),
});
var blob = new Blob(
[JSON.stringify(data)],
{ type: 'application/json;charset=utf-8' }
);
FileSaver.saveAs(blob, 'template.json');
FileSaver.saveAs(blob, 'template.json');
});
};
Module.SaveView = Marionette.LayoutView.extend({
@ -119,20 +146,48 @@ define([
},
saveAsTemplate: function() {
var templateName = this.$('.mailpoet_save_as_template_name').val(),
templateDescription = this.$('.mailpoet_save_as_template_description').val();
templateDescription = this.$('.mailpoet_save_as_template_description').val(),
that = this;
console.log('Saving template with ', templateName, templateDescription);
Module.saveTemplate({
name: templateName,
description: templateDescription,
}).done(function() {
console.log('Template saved', arguments);
}).fail(function() {
// TODO: Handle error messages
console.log('Template save failed', arguments);
});
if (templateName === '') {
MailPoet.Notice.error(
App.getConfig().get('translations.templateNameMissing'),
{
positionAfter: that.$el,
}
);
} else if (templateDescription === '') {
MailPoet.Notice.error(
App.getConfig().get('translations.templateDescriptionMissing'),
{
positionAfter: that.$el,
}
);
} else {
console.log('Saving template with ', templateName, templateDescription);
Module.saveTemplate({
name: templateName,
description: templateDescription,
}).done(function() {
console.log('Template saved', arguments);
MailPoet.Notice.success(
App.getConfig().get('translations.templateSaved'),
{
positionAfter: that.$el,
}
);
}).fail(function() {
console.log('Template save failed', arguments);
MailPoet.Notice.error(
App.getConfig().get('translations.templateSaveFailed'),
{
positionAfter: that.$el,
}
);
});
this.hideOptionContents();
}
this.hideOptionContents();
},
toggleExportTemplate: function() {
this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden');
@ -143,12 +198,23 @@ define([
},
exportTemplate: function() {
var templateName = this.$('.mailpoet_export_template_name').val(),
templateDescription = this.$('.mailpoet_export_template_description').val();
templateDescription = this.$('.mailpoet_export_template_description').val(),
that = this;
if (templateName === '') {
MailPoet.Notice.error(App.getConfig().get('translations.templateNameMissing'));
MailPoet.Notice.error(
App.getConfig().get('translations.templateNameMissing'),
{
positionAfter: that.$el,
}
);
} else if (templateDescription === '') {
MailPoet.Notice.error(App.getConfig().get('translations.templateDescriptionMissing'));
MailPoet.Notice.error(
App.getConfig().get('translations.templateDescriptionMissing'),
{
positionAfter: that.$el,
}
);
} else {
console.log('Exporting template with ', templateName, templateDescription);
Module.exportTemplate({

View File

@ -1,12 +1,13 @@
define([
'newsletter_editor/App',
'newsletter_editor/components/communication',
'backbone',
'backbone.marionette',
'backbone.supermodel',
'underscore',
'jquery',
'sticky-kit'
], function(App, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
], function(App, CommunicationComponent, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
"use strict";
@ -90,7 +91,6 @@ define([
});
},
onDomRefresh: function() {
var that = this;
this.$el.parent().stick_in_parent({
offset_top: 32,
});
@ -169,10 +169,8 @@ define([
},
initialize: function(options) {
this.availableStyles = options.availableStyles;
var that = this;
},
onRender: function() {
var that = this;
this.$('.mailpoet_color').spectrum({
clickoutFiresChange: true,
showInput: true,
@ -229,7 +227,7 @@ define([
MailPoet.Modal.loading(true);
// TODO: Migrate logic to new AJAX format
Wordpress.previewNewsletter(data).done(function(response) {
CommunicationComponent.previewNewsletter(data).done(function(response) {
if(response.success !== undefined && response.success === true) {
MailPoet.Notice.success(App.getConfig().get('translations.testEmailSent'));
} else if(response.error !== undefined) {

View File

@ -72,7 +72,7 @@ define([
App.getAvailableStyles = Module.getAvailableStyles;
var body = JSON.parse(options.newsletter.body);
var body = options.newsletter.body;
this.setGlobalStyles(body.globalStyles);
});

View File

@ -21,6 +21,10 @@ define(
label: 'Subject',
sortable: true
},
{
name: 'status',
label: 'Status'
},
{
name: 'segments',
label: 'Lists'
@ -112,12 +116,101 @@ define(
</a>
);
}
},
{
name: 'trash'
}
];
var NewsletterList = React.createClass({
renderItem: function(newsletter, actions) {
pauseSending: function(item) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'pause',
data: item.id
}).done(function() {
jQuery('#resume_'+item.id).show();
jQuery('#pause_'+item.id).hide();
});
},
resumeSending: function(item) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'resume',
data: item.id
}).done(function() {
jQuery('#pause_'+item.id).show();
jQuery('#resume_'+item.id).hide();
});
},
renderStatus: function(item) {
if(item.queue === null) {
return (
<span>Not sent yet.</span>
);
} else {
var progressClasses = classNames(
'mailpoet_progress',
{ 'mailpoet_progress_complete': item.queue.status === 'completed'}
);
// calculate percentage done
var percentage = Math.round(
(item.queue.count_processed * 100) / (item.queue.count_total)
);
var label = false;
if(item.queue.status === 'completed') {
label = (
<span>
Sent to {
item.queue.count_processed - item.queue.count_failed
} out of { item.queue.count_total }.
</span>
);
} else {
label = (
<span>
{ item.queue.count_processed } / { item.queue.count_total }
&nbsp;&nbsp;
<a
id={ 'resume_'+item.id }
className="button"
style={{ display: (item.queue.status === 'paused') ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.resumeSending.bind(null, item) }
>Resume</a>
<a
id={ 'pause_'+item.id }
className="button mailpoet_pause"
style={{ display: (item.queue.status === null) ? 'inline-block': 'none' }}
href="javascript:;"
onClick={ this.pauseSending.bind(null, item) }
>Pause</a>
</span>
);
}
return (
<div>
<div className={ progressClasses }>
<span
className="mailpoet_progress_bar"
style={ { width: percentage + "%"} }
></span>
<span className="mailpoet_progress_label">
{ percentage + "%" }
</span>
</div>
<p style={{ textAlign:'center' }}>
{ label }
</p>
</div>
);
}
},
renderItem: function(newsletter, actions) {
var rowClasses = classNames(
'manage-column',
'column-primary',
@ -138,6 +231,9 @@ define(
</strong>
{ actions }
</td>
<td className="column" data-colname="Lists">
{ this.renderStatus(newsletter) }
</td>
<td className="column" data-colname="Lists">
{ segments }
</td>
@ -164,7 +260,8 @@ define(
columns={columns}
bulk_actions={ bulk_actions }
item_actions={ item_actions }
messages={ messages } />
messages={ messages }
auto_refresh={ true } />
</div>
);
}

View File

@ -34,7 +34,13 @@ define(
placeholder: "Select a list",
id: "mailpoet_segments",
endpoint: "segments",
multiple: true
multiple: true,
filter: function(segment) {
return !!(!segment.deleted_at);
},
validation: {
'data-parsley-required': true
}
},
{
name: 'sender',
@ -42,17 +48,24 @@ define(
tip: "Name & email of yourself or your company.",
fields: [
{
name: 'from_name',
name: 'sender_name',
type: 'text',
placeholder: 'John Doe',
defaultValue: settings.from_name
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
validation: {
'data-parsley-required': true
}
},
{
name: 'from_email',
name: 'sender_address',
type: 'text',
placeholder: 'john.doe@email.com',
defaultValue: settings.from_address
},
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
validation: {
'data-parsley-required': true,
'data-parsley-type': 'email'
}
}
]
},
{
@ -65,20 +78,25 @@ define(
{
name: 'reply_to_name',
type: 'text',
placeholder: 'John Doe'
placeholder: 'John Doe',
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
},
{
name: 'reply_to_email',
name: 'reply_to_address',
type: 'text',
placeholder: 'john.doe@email.com'
placeholder: 'john.doe@email.com',
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
},
]
}
];
var messages = {
updated: function() {
MailPoet.Notice.success('The newsletter has been updated!');
onUpdate: function() {
MailPoet.Notice.success('Newsletter successfully updated!');
},
onCreate: function() {
MailPoet.Notice.success('Newsletter successfully added!');
}
};
@ -87,34 +105,43 @@ define(
Router.History
],
handleSend: function() {
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'send',
data: {
id: this.props.params.id,
newsletter: jQuery('#mailpoet_newsletter').serializeObject(),
segments: jQuery('#mailpoet_segments').val()
}
}).done(function(response) {
if(response === true) {
this.history.pushState(null, '/');
if(jQuery('#mailpoet_newsletter').parsley().validate() === true) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'add',
data: {
newsletter_id: this.props.params.id,
segments: jQuery('#mailpoet_segments').val()
}
}).done(function(response) {
if(response.result === true) {
this.history.pushState(null, '/');
MailPoet.Notice.success(
'The newsletter has been sent!'
);
} else {
if(response.errors) {
MailPoet.Notice.error(
response.errors.join("<br />")
MailPoet.Notice.success(
'The newsletter is being sent...'
);
} else {
MailPoet.Notice.error(
'An error occurred while trying to send. '+
'<a href="?page=mailpoet-settings">Check your settings.</a>'
);
if(response.errors) {
MailPoet.Notice.error(
response.errors.join("<br />")
);
} else {
MailPoet.Notice.error(
'An error occurred while trying to send. '+
'<a href="?page=mailpoet-settings">Check your settings.</a>'
);
}
}
}
}.bind(this));
}.bind(this));
}
},
componentDidMount: function() {
if(this.isMounted()) {
jQuery('#mailpoet_newsletter').parsley();
}
},
isValid: function() {
return (jQuery('#mailpoet_newsletter').parsley().validate());
},
render: function() {
return (
@ -128,7 +155,8 @@ define(
endpoint="newsletters"
fields={ fields }
params={ this.props.params }
messages={ messages }>
messages={ messages }
isValid={ this.isValid }>
<p className="submit">
<input

View File

@ -99,7 +99,7 @@ define(
"MailPoet's Guide",
description:
"This is the standard template that comes with MailPoet.",
readonly: true
readonly: "1"
}
]
}
@ -119,11 +119,11 @@ define(
body: template.body
}
}).done(function(response) {
if(response === true) {
if(response.result === true) {
// TODO: Move this URL elsewhere
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id;
} else {
response.map(function(error) {
response.errors.map(function(error) {
MailPoet.Notice.error(error);
});
}
@ -150,6 +150,13 @@ define(
this.setState({ loading: false });
}
},
handleShowTemplate: function(template) {
MailPoet.Modal.popup({
title: template.name,
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
data: template,
});
},
handleTemplateImport: function() {
this.getTemplates();
},
@ -164,11 +171,22 @@ define(
Delete
</a>
</div>
);
), thumbnail = '';
if (typeof template.thumbnail === 'string'
&& template.thumbnail.length > 0) {
thumbnail = (
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
<img src={ template.thumbnail } />
<div className="mailpoet_overlay"></div>
</a>
);
}
return (
<li key={ 'template-'+index }>
<div className="mailpoet_thumbnail">
{ thumbnail }
</div>
<div className="mailpoet_description">
@ -192,7 +210,7 @@ define(
Preview
</a>
</div>
{ (template.readonly) ? false : deleteLink }
{ (template.readonly === "1") ? false : deleteLink }
</li>
);
}.bind(this));

View File

@ -26,6 +26,7 @@ define(
action: 'create',
data: {
type: type,
subject: 'Draft newsletter',
}
}).done(function(response) {
if(response.id !== undefined) {

View File

@ -47,6 +47,9 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
type: 'success',
message: '',
static: false,
hideClose: false,
id: null,
positionAfter: false,
scroll: false,
timeout: 2000,
onOpen: null,
@ -60,11 +63,23 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
// clone element
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
// add data-id to the element
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
// remove id from clone
this.element.removeAttr('id');
// insert notice after its parent
jQuery('#mailpoet_notice_'+this.options.type).after(this.element);
var positionAfter;
if (typeof this.options.positionAfter === 'object') {
positionAfter = this.options.positionAfter;
} else if (typeof this.options.positionAfter === 'string') {
positionAfter = jQuery(this.options.positionAfter);
} else {
positionAfter = jQuery('#mailpoet_notice_'+this.options.type);
}
console.log('positionAfter', typeof this.options.positionAfter);
positionAfter.after(this.element);
// setup onClose callback
var onClose = null;
@ -73,7 +88,6 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
}
// listen to remove event
var element = this.element;
jQuery(this.element).on('close', function() {
jQuery(this).fadeOut(200, function() {
// on close callback
@ -148,7 +162,7 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
// if the notice is not static, it has to disappear after a timeout
if(this.options.static === false) {
this.element.delay(this.options.timeout).trigger('close');
} else {
} else if (this.options.hideClose === false) {
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
this.element.find('.mailpoet_notice_close').on('click', function() {
jQuery(this).trigger('close');
@ -163,6 +177,14 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
hide: function(all) {
if(all !== undefined && all === true) {
jQuery('.mailpoet_notice:not([id])').trigger('close');
} else if (all !== undefined && jQuery.isArray(all)) {
for (var id in all) {
jQuery('[data-id="notice_' + all[id] + '"]')
.trigger('close');
}
} if (all !== undefined) {
jQuery('[data-id="notice_' + all + '"]')
.trigger('close');
} else {
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
.trigger('close');
@ -188,4 +210,4 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
}, options));
}
};
});
});

View File

@ -20,7 +20,10 @@ function(
$('form.mailpoet_form').each(function() {
var form = $(this);
form.parsley().on('form:submit', function(parsley) {
form.parsley({
errorsWrapper: '<p></p>',
errorTemplate: '<span></span>'
}).on('form:submit', function(parsley) {
var data = form.serializeObject() || {};

View File

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

View File

@ -121,6 +121,32 @@ const item_actions = [
);
refresh();
});
},
display: function(segment) {
return (segment.type !== 'wp_users');
}
},
{
name: 'synchronize_segment',
label: 'Update',
className: 'update',
onClick: function(item, refresh) {
MailPoet.Modal.loading(true);
MailPoet.Ajax.post({
endpoint: 'segments',
action: 'synchronize'
}).done(function(response) {
MailPoet.Modal.loading(false);
if(response === true) {
MailPoet.Notice.success(
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
);
refresh();
}
});
},
display: function(segment) {
return (segment.type === 'wp_users');
}
},
{
@ -130,15 +156,16 @@ const item_actions = [
<a href={ item.subscribers_url }>View subscribers</a>
);
}
},
{
name: 'trash',
display: function(segment) {
return (segment.type !== 'wp_users');
}
}
];
const bulk_actions = [
{
name: 'trash',
label: 'Trash',
onSuccess: messages.onTrash
}
];
const SegmentList = React.createClass({
@ -148,7 +175,6 @@ const SegmentList = React.createClass({
'column-primary',
'has-row-actions'
);
return (
<div>
<td className={ rowClasses }>

View File

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

View File

@ -37,14 +37,25 @@ define(
'subscribed': 'Subscribed',
'unsubscribed': 'Unsubscribed'
}
},
{
name: 'segments',
label: 'Lists',
type: 'selection',
placeholder: "Select a list",
endpoint: "segments",
multiple: true,
filter: function(segment) {
return !!(!segment.deleted_at);
}
}
];
var messages = {
updated: function() {
onUpdate: function() {
MailPoet.Notice.success('Subscriber successfully updated!');
},
created: function() {
onCreate: function() {
MailPoet.Notice.success('Subscriber successfully added!');
}
};
@ -59,11 +70,7 @@ define(
return (
<div>
<h2 className="title">
Subscriber <a
href="javascript:;"
className="add-new-h2"
onClick={ this.history.goBack }
>Back to list</a>
Subscriber
</h2>
<Form
@ -71,7 +78,7 @@ define(
fields={ fields }
params={ this.props.params }
messages={ messages }
onSuccess={ this.history.goBack } />
/>
</div>
);
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

42
build
View File

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

42
build.sh Executable file
View File

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

View File

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

498
composer.lock generated
View File

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "7d7ef94b6e40ac2b2d594e5832d7e16d",
"content-hash": "2e70c335edf7429df0794ebf49e2f210",
"hash": "4720dce62e4a6a7bf4d3ba3944b9c2b9",
"content-hash": "748470f0803c52a798a4ecd1bb8a93b9",
"packages": [
{
"name": "cerdic/css-tidy",
@ -158,6 +158,97 @@
],
"time": "2014-09-23 10:49:36"
},
{
"name": "mtdowling/cron-expression",
"version": "v1.0.4",
"source": {
"type": "git",
"url": "https://github.com/mtdowling/cron-expression.git",
"reference": "fd92e883195e5dfa77720b1868cf084b08be4412"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mtdowling/cron-expression/zipball/fd92e883195e5dfa77720b1868cf084b08be4412",
"reference": "fd92e883195e5dfa77720b1868cf084b08be4412",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "4.*"
},
"type": "library",
"autoload": {
"psr-0": {
"Cron": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due",
"keywords": [
"cron",
"schedule"
],
"time": "2015-01-11 23:07:46"
},
{
"name": "nesbot/carbon",
"version": "1.21.0",
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/7b08ec6f75791e130012f206e3f7b0e76e18e3d7",
"reference": "7b08ec6f75791e130012f206e3f7b0e76e18e3d7",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/translation": "~2.6|~3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0|~5.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Carbon\\": "src/Carbon/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brian Nesbitt",
"email": "brian@nesbot.com",
"homepage": "http://nesbot.com"
}
],
"description": "A simple API extension for DateTime.",
"homepage": "http://carbon.nesbot.com",
"keywords": [
"date",
"datetime",
"time"
],
"time": "2015-11-04 20:07:17"
},
{
"name": "phpmailer/phpmailer",
"version": "v5.2.14",
@ -403,6 +494,69 @@
],
"time": "2015-06-06 14:19:39"
},
{
"name": "symfony/translation",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "e4ecb9c3ba1304eaf24de15c2d7a428101c1982f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/e4ecb9c3ba1304eaf24de15c2d7a428101c1982f",
"reference": "e4ecb9c3ba1304eaf24de15c2d7a428101c1982f",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"conflict": {
"symfony/config": "<2.7"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.7",
"symfony/intl": "~2.4",
"symfony/yaml": "~2.2"
},
"suggest": {
"psr/log": "To use logging capability in translator",
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Translation\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2015-11-18 13:41:01"
},
{
"name": "tburry/pquery",
"version": "v1.1.0",
@ -520,16 +674,16 @@
"packages-dev": [
{
"name": "codeception/codeception",
"version": "2.1.3",
"version": "2.1.4",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Codeception.git",
"reference": "cd810cb78a869408602e17271f9b7368b09a7ca8"
"reference": "6a812e8a0d1b1db939a29b4dc14cb398b21b6112"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/cd810cb78a869408602e17271f9b7368b09a7ca8",
"reference": "cd810cb78a869408602e17271f9b7368b09a7ca8",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/6a812e8a0d1b1db939a29b4dc14cb398b21b6112",
"reference": "6a812e8a0d1b1db939a29b4dc14cb398b21b6112",
"shasum": ""
},
"require": {
@ -596,7 +750,7 @@
"functional testing",
"unit testing"
],
"time": "2015-10-02 09:38:59"
"time": "2015-11-12 03:57:06"
},
{
"name": "codeception/verify",
@ -740,16 +894,16 @@
},
{
"name": "facebook/webdriver",
"version": "1.0.3",
"version": "1.0.4",
"source": {
"type": "git",
"url": "https://github.com/facebook/php-webdriver.git",
"reference": "d843e33fd19b49db5ac9daaef2610079daab0bad"
"reference": "a6e209a309bf7cd71acf15476f40b11a25d5a79d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/d843e33fd19b49db5ac9daaef2610079daab0bad",
"reference": "d843e33fd19b49db5ac9daaef2610079daab0bad",
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/a6e209a309bf7cd71acf15476f40b11a25d5a79d",
"reference": "a6e209a309bf7cd71acf15476f40b11a25d5a79d",
"shasum": ""
},
"require": {
@ -779,20 +933,20 @@
"selenium",
"webdriver"
],
"time": "2015-11-01 20:09:34"
"time": "2015-11-03 22:17:22"
},
{
"name": "guzzlehttp/guzzle",
"version": "6.1.0",
"version": "6.1.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "66fd14b4d0b8f2389eaf37c5458608c7cb793a81"
"reference": "c6851d6e48f63b69357cbfa55bca116448140e0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/66fd14b4d0b8f2389eaf37c5458608c7cb793a81",
"reference": "66fd14b4d0b8f2389eaf37c5458608c7cb793a81",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/c6851d6e48f63b69357cbfa55bca116448140e0c",
"reference": "c6851d6e48f63b69357cbfa55bca116448140e0c",
"shasum": ""
},
"require": {
@ -841,7 +995,7 @@
"rest",
"web service"
],
"time": "2015-09-08 17:36:26"
"time": "2015-11-23 00:47:50"
},
{
"name": "guzzlehttp/promises",
@ -1362,16 +1516,16 @@
},
{
"name": "phpunit/phpunit",
"version": "4.8.16",
"version": "4.8.18",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e"
"reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e",
"reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fa33d4ad96481b91df343d83e8c8aabed6b1dfd3",
"reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3",
"shasum": ""
},
"require": {
@ -1430,7 +1584,7 @@
"testing",
"xunit"
],
"time": "2015-10-23 06:48:33"
"time": "2015-11-11 11:32:49"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -1961,16 +2115,16 @@
},
{
"name": "symfony/browser-kit",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
"reference": "07d664a052572ccc28eb2ab7dbbe82155b1ad367"
"reference": "bd28847ea2193916074c7b11d4fdd78570049694"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/07d664a052572ccc28eb2ab7dbbe82155b1ad367",
"reference": "07d664a052572ccc28eb2ab7dbbe82155b1ad367",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/bd28847ea2193916074c7b11d4fdd78570049694",
"reference": "bd28847ea2193916074c7b11d4fdd78570049694",
"shasum": ""
},
"require": {
@ -1993,7 +2147,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\BrowserKit\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2011,20 +2168,20 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
"time": "2015-10-23 14:47:27"
"time": "2015-11-02 20:20:53"
},
{
"name": "symfony/config",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "831f88908b51b9ce945f5e6f402931d1ac544423"
"reference": "61973327bfb054f6f470de7be033a28b76c1dc20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/831f88908b51b9ce945f5e6f402931d1ac544423",
"reference": "831f88908b51b9ce945f5e6f402931d1ac544423",
"url": "https://api.github.com/repos/symfony/config/zipball/61973327bfb054f6f470de7be033a28b76c1dc20",
"reference": "61973327bfb054f6f470de7be033a28b76c1dc20",
"shasum": ""
},
"require": {
@ -2040,7 +2197,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Config\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2058,20 +2218,20 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-11-02 20:20:53"
},
{
"name": "symfony/console",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5efd632294c8320ea52492db22292ff853a43766"
"reference": "16bb1cb86df43c90931df65f529e7ebd79636750"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766",
"reference": "5efd632294c8320ea52492db22292ff853a43766",
"url": "https://api.github.com/repos/symfony/console/zipball/16bb1cb86df43c90931df65f529e7ebd79636750",
"reference": "16bb1cb86df43c90931df65f529e7ebd79636750",
"shasum": ""
},
"require": {
@ -2096,7 +2256,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2114,20 +2277,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2015-10-20 14:38:46"
"time": "2015-11-18 09:54:26"
},
{
"name": "symfony/css-selector",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "e1b865b26be4a56d22a8dee398375044a80c865b"
"reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/e1b865b26be4a56d22a8dee398375044a80c865b",
"reference": "e1b865b26be4a56d22a8dee398375044a80c865b",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/abb47717fb88aebd9437da2fc8bb01a50a36679f",
"reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f",
"shasum": ""
},
"require": {
@ -2142,7 +2305,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\CssSelector\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2164,20 +2330,20 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-10-30 20:10:21"
},
{
"name": "symfony/dom-crawler",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "5fef7d8b80d8f9992df99d8ee283f420484c9612"
"reference": "b33593cbfe1d81b50d48353f338aca76a08658d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/5fef7d8b80d8f9992df99d8ee283f420484c9612",
"reference": "5fef7d8b80d8f9992df99d8ee283f420484c9612",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b33593cbfe1d81b50d48353f338aca76a08658d8",
"reference": "b33593cbfe1d81b50d48353f338aca76a08658d8",
"shasum": ""
},
"require": {
@ -2198,7 +2364,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\DomCrawler\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2216,20 +2385,20 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-11-02 20:20:53"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8"
"reference": "7e2f9c31645680026c2372edf66f863fc7757af5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/7e2f9c31645680026c2372edf66f863fc7757af5",
"reference": "7e2f9c31645680026c2372edf66f863fc7757af5",
"shasum": ""
},
"require": {
@ -2255,7 +2424,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2273,20 +2445,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-10-30 20:10:21"
},
{
"name": "symfony/filesystem",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "56fd6df73be859323ff97418d97edc1d756df6df"
"reference": "8e173509d7fdbbba3cf34d6d072f2073c0210c1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/56fd6df73be859323ff97418d97edc1d756df6df",
"reference": "56fd6df73be859323ff97418d97edc1d756df6df",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/8e173509d7fdbbba3cf34d6d072f2073c0210c1d",
"reference": "8e173509d7fdbbba3cf34d6d072f2073c0210c1d",
"shasum": ""
},
"require": {
@ -2301,7 +2473,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Filesystem\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2319,20 +2494,20 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2015-10-18 20:23:18"
"time": "2015-11-18 13:41:01"
},
{
"name": "symfony/finder",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d"
"reference": "a06a0c0ff7db3736a50d530c908cca547bf13da9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d",
"reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d",
"url": "https://api.github.com/repos/symfony/finder/zipball/a06a0c0ff7db3736a50d530c908cca547bf13da9",
"reference": "a06a0c0ff7db3736a50d530c908cca547bf13da9",
"shasum": ""
},
"require": {
@ -2347,7 +2522,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Finder\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2365,20 +2543,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-10-30 20:10:21"
},
{
"name": "symfony/form",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/form.git",
"reference": "b93fcb816bec2b8470ea9d54e4b6658b2461b83c"
"reference": "0a2c2ce0d4bd3c50bb0ae4e75ac27e5274c25e81"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/form/zipball/b93fcb816bec2b8470ea9d54e4b6658b2461b83c",
"reference": "b93fcb816bec2b8470ea9d54e4b6658b2461b83c",
"url": "https://api.github.com/repos/symfony/form/zipball/0a2c2ce0d4bd3c50bb0ae4e75ac27e5274c25e81",
"reference": "0a2c2ce0d4bd3c50bb0ae4e75ac27e5274c25e81",
"shasum": ""
},
"require": {
@ -2416,7 +2594,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Form\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2434,20 +2615,20 @@
],
"description": "Symfony Form Component",
"homepage": "https://symfony.com",
"time": "2015-10-27 15:38:06"
"time": "2015-11-23 10:34:14"
},
{
"name": "symfony/intl",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
"reference": "330f52a996749eb6a2fdc1506c7a4868e070d678"
"reference": "6c6c3aa69f68aff72e48a9bfc11f24680e23eb2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/intl/zipball/330f52a996749eb6a2fdc1506c7a4868e070d678",
"reference": "330f52a996749eb6a2fdc1506c7a4868e070d678",
"url": "https://api.github.com/repos/symfony/intl/zipball/6c6c3aa69f68aff72e48a9bfc11f24680e23eb2d",
"reference": "6c6c3aa69f68aff72e48a9bfc11f24680e23eb2d",
"shasum": ""
},
"require": {
@ -2474,6 +2655,9 @@
],
"files": [
"Resources/stubs/functions.php"
],
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
@ -2508,20 +2692,20 @@
"l10n",
"localization"
],
"time": "2015-10-11 09:39:48"
"time": "2015-11-18 13:41:01"
},
{
"name": "symfony/options-resolver",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "85fd10e551677d3c9a4632def78b8ec4670b247d"
"reference": "d6b7d3452b4cfff89b642993e02fea7cc254530e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/85fd10e551677d3c9a4632def78b8ec4670b247d",
"reference": "85fd10e551677d3c9a4632def78b8ec4670b247d",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/d6b7d3452b4cfff89b642993e02fea7cc254530e",
"reference": "d6b7d3452b4cfff89b642993e02fea7cc254530e",
"shasum": ""
},
"require": {
@ -2536,7 +2720,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2559,20 +2746,20 @@
"configuration",
"options"
],
"time": "2015-10-11 09:39:48"
"time": "2015-11-18 13:41:01"
},
{
"name": "symfony/process",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7"
"reference": "f6290983c8725d0afa29bdc3e5295879de3e58f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/4a959dd4e19c2c5d7512689413921e0a74386ec7",
"reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7",
"url": "https://api.github.com/repos/symfony/process/zipball/f6290983c8725d0afa29bdc3e5295879de3e58f5",
"reference": "f6290983c8725d0afa29bdc3e5295879de3e58f5",
"shasum": ""
},
"require": {
@ -2587,7 +2774,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2605,20 +2795,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2015-10-23 14:47:27"
"time": "2015-11-19 16:11:24"
},
{
"name": "symfony/property-access",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
"reference": "368b784738fa932e6d86866038312b03e073a824"
"reference": "49d76463a54d8b3005fa58f3b8df41d0ae206eaa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/property-access/zipball/368b784738fa932e6d86866038312b03e073a824",
"reference": "368b784738fa932e6d86866038312b03e073a824",
"url": "https://api.github.com/repos/symfony/property-access/zipball/49d76463a54d8b3005fa58f3b8df41d0ae206eaa",
"reference": "49d76463a54d8b3005fa58f3b8df41d0ae206eaa",
"shasum": ""
},
"require": {
@ -2633,7 +2823,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\PropertyAccess\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2662,20 +2855,20 @@
"property path",
"reflection"
],
"time": "2015-10-23 14:47:27"
"time": "2015-11-18 13:41:01"
},
{
"name": "symfony/routing",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "f353e1f588679c3ec987624e6c617646bd01ba38"
"reference": "7450f6196711b124fb8b04a12286d01a0401ddfe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/f353e1f588679c3ec987624e6c617646bd01ba38",
"reference": "f353e1f588679c3ec987624e6c617646bd01ba38",
"url": "https://api.github.com/repos/symfony/routing/zipball/7450f6196711b124fb8b04a12286d01a0401ddfe",
"reference": "7450f6196711b124fb8b04a12286d01a0401ddfe",
"shasum": ""
},
"require": {
@ -2708,7 +2901,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Routing\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2732,85 +2928,25 @@
"uri",
"url"
],
"time": "2015-10-27 15:38:06"
},
{
"name": "symfony/translation",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/6ccd9289ec1c71d01a49d83480de3b5293ce30c8",
"reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"conflict": {
"symfony/config": "<2.7"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "~2.7",
"symfony/intl": "~2.4",
"symfony/yaml": "~2.2"
},
"suggest": {
"psr/log": "To use logging capability in translator",
"symfony/config": "",
"symfony/yaml": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Translation\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2015-10-27 15:38:06"
"time": "2015-11-18 13:41:01"
},
{
"name": "symfony/twig-bridge",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
"reference": "3dd44937b1e08af8c8f6b14850f4b9c4d1039c6f"
"reference": "7c491aa71af4320747f81ab0140eac4f0ad5509b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/3dd44937b1e08af8c8f6b14850f4b9c4d1039c6f",
"reference": "3dd44937b1e08af8c8f6b14850f4b9c4d1039c6f",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/7c491aa71af4320747f81ab0140eac4f0ad5509b",
"reference": "7c491aa71af4320747f81ab0140eac4f0ad5509b",
"shasum": ""
},
"require": {
"php": ">=5.3.9",
"twig/twig": "~1.20|~2.0"
"twig/twig": "~1.23|~2.0"
},
"require-dev": {
"symfony/asset": "~2.7",
@ -2852,7 +2988,10 @@
"autoload": {
"psr-4": {
"Symfony\\Bridge\\Twig\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2870,20 +3009,20 @@
],
"description": "Symfony Twig Bridge",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-11-02 20:25:31"
},
{
"name": "symfony/yaml",
"version": "v2.7.6",
"version": "v2.7.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d"
"reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d",
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d",
"url": "https://api.github.com/repos/symfony/yaml/zipball/4cfcd7a9fceba662b3c036b7d9a91f6197af046c",
"reference": "4cfcd7a9fceba662b3c036b7d9a91f6197af046c",
"shasum": ""
},
"require": {
@ -2898,7 +3037,10 @@
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
}
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -2916,7 +3058,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-10-11 09:39:48"
"time": "2015-11-18 13:41:01"
},
{
"name": "twig/extensions",

View File

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

View File

@ -23,4 +23,9 @@ class Activator {
$populator = new Populator();
$populator->up();
}
function deactivate() {
$migrator = new Migrator();
$migrator->down();
}
}

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

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

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

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

View File

@ -4,53 +4,89 @@ namespace MailPoet\Config;
if(!defined('ABSPATH')) exit;
class Env {
public static $version;
public static $plugin_name;
public static $file;
public static $path;
public static $views_path;
public static $assets_path;
public static $assets_url;
public static $languages_path;
public static $lib_path;
public static $plugin_prefix;
public static $db_prefix;
public static $db_source_name;
public static $db_host;
public static $db_name;
public static $db_username;
public static $db_password;
public static $db_charset;
static $version;
static $plugin_name;
static $plugin_url;
static $plugin_path;
static $file;
static $path;
static $views_path;
static $assets_path;
static $assets_url;
static $temp_name;
static $temp_path;
static $languages_path;
static $lib_path;
static $plugin_prefix;
static $db_prefix;
static $db_source_name;
static $db_host;
static $db_socket;
static $db_port;
static $db_name;
static $db_username;
static $db_password;
static $db_charset;
public static function init($file, $version) {
static function init($file, $version) {
global $wpdb;
self::$version = $version;
self::$plugin_name = 'mailpoet';
self::$file = $file;
self::$path = dirname(self::$file);
self::$plugin_name = 'mailpoet';
self::$plugin_url = plugin_dir_url(__FILE__);
self::$views_path = self::$path . '/views';
self::$assets_path = self::$path . '/assets';
self::$assets_url = plugins_url('/assets', $file);
self::$temp_name = 'temp';
self::$temp_path = self::$path . '/' . self::$temp_name;
self::$languages_path = self::$path . '/lang';
self::$lib_path = self::$path . '/lib';
self::$plugin_prefix = 'mailpoet_';
self::$db_prefix = $wpdb->prefix . self::$plugin_prefix;
self::$db_source_name = self::dbSourceName();
self::$db_host = DB_HOST;
self::$db_port = 3306;
self::$db_socket = false;
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
list(self::$db_host, self::$db_port) = explode(':', DB_HOST);
} else {
if(preg_match('/:/', DB_HOST)) {
self::$db_socket = true;
}
}
self::$db_name = DB_NAME;
self::$db_username = DB_USER;
self::$db_password = DB_PASSWORD;
self::$db_charset = $wpdb->get_charset_collate();
self::$db_source_name = self::dbSourceName(self::$db_host, self::$db_socket, self::$db_port);
}
private static function dbSourceName() {
private static function dbSourceName($host, $socket, $port) {
$source_name = array(
'mysql:host=',
DB_HOST,
(!$socket) ? 'mysql:host=' : 'mysql:unix_socket=',
$host,
';',
'port=',
$port,
';',
'dbname=',
DB_NAME
);
return implode('', $source_name);
}
}
static function isPluginActivated() {
$activatesPlugins = get_option('active_plugins');
$isActivated = (
in_array(
sprintf('%s/%s.php', basename(self::$path), self::$plugin_name),
$activatesPlugins
) ||
in_array(
sprintf('%s/%s.php', explode('/', plugin_basename(__FILE__))[0], self::$plugin_name),
$activatesPlugins
)
);
return ($isActivated) ? true : false;
}
}

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

@ -0,0 +1,56 @@
<?php
namespace MailPoet\Config;
class Hooks {
function __construct() {
}
function init() {
// WP Users synchronization
add_action(
'user_register',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'added_existing_user',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'profile_update',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'delete_user',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
// multisite
add_action(
'deleted_user',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_action(
'remove_user_from_blog',
'\MailPoet\Segments\WP::synchronizeUser',
1
);
add_filter(
'image_size_names_choose',
array(
$this,
'appendImageSizes'
)
);
}
function appendImageSizes($sizes) {
return array_merge($sizes, array(
'mailpoet_newsletter_max' => __('MailPoet Newsletter'),
));
}
}

View File

@ -2,7 +2,9 @@
namespace MailPoet\Config;
use MailPoet\Models;
use MailPoet\Cron\Supervisor;
use MailPoet\Router;
use MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
@ -22,7 +24,14 @@ class Initializer {
$this->setupMenu();
$this->setupRouter();
$this->setupWidget();
$this->setupAnalytics();
$this->setupPermissions();
$this->setupChangelog();
$this->setupPublicAPI();
$this->runQueueSupervisor();
$this->setupShortcodes();
$this->setupHooks();
$this->setupImages();
}
function setupDB() {
@ -31,7 +40,8 @@ class Initializer {
\ORM::configure('password', Env::$db_password);
\ORM::configure('logging', WP_DEBUG);
\ORM::configure('driver_options', array(
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET TIME_ZONE = "+00:00"'
));
$subscribers = Env::$db_prefix . 'subscribers';
@ -39,6 +49,8 @@ class Initializer {
$newsletters = Env::$db_prefix . 'newsletters';
$newsletter_templates = Env::$db_prefix . 'newsletter_templates';
$segments = Env::$db_prefix . 'segments';
$filters = Env::$db_prefix . 'filters';
$segment_filter = Env::$db_prefix . 'segment_filter';
$forms = Env::$db_prefix . 'forms';
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
@ -46,11 +58,15 @@ class Initializer {
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
$newsletter_option = Env::$db_prefix . 'newsletter_option';
$sending_queues = Env::$db_prefix . 'sending_queues';
$newsletter_statistics = Env::$db_prefix . 'newsletter_statistics';
define('MP_SUBSCRIBERS_TABLE', $subscribers);
define('MP_SETTINGS_TABLE', $settings);
define('MP_NEWSLETTERS_TABLE', $newsletters);
define('MP_SEGMENTS_TABLE', $segments);
define('MP_FILTERS_TABLE', $filters);
define('MP_SEGMENT_FILTER_TABLE', $segment_filter);
define('MP_FORMS_TABLE', $forms);
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates);
@ -59,6 +75,8 @@ class Initializer {
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
define('MP_SENDING_QUEUE_TABLE', $sending_queues);
define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics);
}
function setupActivator() {
@ -94,8 +112,44 @@ class Initializer {
$widget->init();
}
function setupAnalytics() {
$widget = new Analytics();
$widget->init();
}
function setupPermissions() {
$permissions = new Permissions();
$permissions->init();
}
function setupChangelog() {
$changelog = new Changelog();
$changelog->init();
}
function setupShortcodes() {
$shortcodes = new Shortcodes();
$shortcodes->init();
}
function setupHooks() {
$hooks = new Hooks();
$hooks->init();
}
function setupPublicAPI() {
$publicAPI = new PublicAPI();
$publicAPI->init();
}
function runQueueSupervisor() {
try {
$supervisor = new Supervisor();
$supervisor->checkDaemon();
} catch (\Exception $e) {}
}
function setupImages() {
add_image_size('mailpoet_newsletter_max', 1320);
}
}

View File

@ -1,15 +1,18 @@
<?php
namespace MailPoet\Config;
use \MailPoet\Models\Segment;
use \MailPoet\Models\Setting;
use \MailPoet\Models\Form;
use \MailPoet\Form\Block;
use \MailPoet\Form\Renderer as FormRenderer;
use \MailPoet\Settings\Hosts;
use \MailPoet\Settings\Pages;
use \MailPoet\Settings\Charsets;
use \MailPoet\Util\Permissions;
use \MailPoet\Util\DKIM;
use MailPoet\Form\Block;
use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Models\CustomField;
use MailPoet\Models\Form;
use MailPoet\Models\Segment;
use MailPoet\Models\Setting;
use MailPoet\Settings\Charsets;
use MailPoet\Settings\Hosts;
use MailPoet\Settings\Pages;
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use MailPoet\Util\DKIM;
use MailPoet\Util\Permissions;
if(!defined('ABSPATH')) exit;
@ -22,7 +25,10 @@ class Menu {
function init() {
add_action(
'admin_menu',
array($this, 'setup')
array(
$this,
'setup'
)
);
}
@ -32,7 +38,10 @@ class Menu {
'MailPoet',
'manage_options',
'mailpoet',
array($this, 'home'),
array(
$this,
'home'
),
$this->assets_url . '/img/menu_icon.png',
30
);
@ -42,7 +51,10 @@ class Menu {
__('Newsletters'),
'manage_options',
'mailpoet-newsletters',
array($this, 'newsletters')
array(
$this,
'newsletters'
)
);
add_submenu_page(
'mailpoet',
@ -50,7 +62,10 @@ class Menu {
__('Forms'),
'manage_options',
'mailpoet-forms',
array($this, 'forms')
array(
$this,
'forms'
)
);
add_submenu_page(
'mailpoet',
@ -58,7 +73,10 @@ class Menu {
__('Subscribers'),
'manage_options',
'mailpoet-subscribers',
array($this, 'subscribers')
array(
$this,
'subscribers'
)
);
add_submenu_page(
'mailpoet',
@ -66,7 +84,10 @@ class Menu {
__('Segments'),
'manage_options',
'mailpoet-segments',
array($this, 'segments')
array(
$this,
'segments'
)
);
add_submenu_page(
'mailpoet',
@ -74,32 +95,93 @@ class Menu {
__('Settings'),
'manage_options',
'mailpoet-settings',
array($this, 'settings')
array(
$this,
'settings'
)
);
add_submenu_page(
null,
__('Import'),
__('Import'),
'manage_options',
'mailpoet-import',
array(
$this,
'import'
)
);
add_submenu_page(
null,
__('Export'),
__('Export'),
'manage_options',
'mailpoet-export',
array(
$this,
'export'
)
);
// add_submenu_page(
// 'mailpoet',
// __('Newsletter editor'),
// __('Newsletter editor'),
// 'manage_options',
// 'mailpoet-newsletter-editor',
// array($this, 'newletterEditor')
// );
$this->registered_pages();
}
function registered_pages() {
global $_registered_pages;
$pages = array(
'mailpoet-form-editor' => array($this, 'formEditor'),
'mailpoet-newsletter-editor' => array($this, 'newletterEditor')
add_submenu_page(
null,
__('Welcome'),
__('Welcome'),
'manage_options',
'mailpoet-welcome',
array(
$this,
'welcome'
)
);
add_submenu_page(
null,
__('Update'),
__('Update'),
'manage_options',
'mailpoet-update',
array(
$this,
'update'
)
);
add_submenu_page(
null,
__('Form editor'),
__('Form editor'),
'manage_options',
'mailpoet-form-editor',
array(
$this,
'formEditor'
)
);
add_submenu_page(
null,
__('Newsletter editor'),
__('Newsletter editor'),
'manage_options',
'mailpoet-newsletter-editor',
array(
$this,
'newletterEditor'
)
);
add_submenu_page(
'mailpoet',
__('Cron'),
__('Cron'),
'manage_options',
'mailpoet-cron',
array(
$this,
'cron'
)
);
foreach($pages as $menu_slug => $callback) {
$hookname = get_plugin_page_hookname($menu_slug, null);
if(!empty($hookname)) {
add_action($hookname, $callback);
}
$_registered_pages[$hookname] = true;
}
}
function home() {
@ -107,26 +189,56 @@ class Menu {
echo $this->renderer->render('index.html', $data);
}
function settings() {
// flags (available features on WP install)
$flags = array();
function welcome() {
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
$redirect_url =
(!empty($_GET['mailpoet_redirect']))
? urldecode($_GET['mailpoet_redirect'])
: wp_get_referer();
if(is_multisite()) {
// get multisite registration option
$registration = apply_filters(
'wpmu_registration_enabled',
get_site_option('registration', 'all')
);
// check if users can register
$flags['registration_enabled'] =
!(in_array($registration, array('none', 'blog')));
} else {
// check if users can register
$flags['registration_enabled'] =
(bool)get_option('users_can_register', false);
if(
$redirect_url === $current_url
or
strpos($redirect_url, 'mailpoet') === false
) {
$redirect_url = admin_url('admin.php?page=mailpoet');
}
$data = array(
'settings' => Setting::getAll(),
'current_user' => wp_get_current_user(),
'redirect_url' => $redirect_url
);
echo $this->renderer->render('welcome.html', $data);
}
function update() {
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
$redirect_url =
(!empty($_GET['mailpoet_redirect']))
? urldecode($_GET['mailpoet_redirect'])
: wp_get_referer();
if(
$redirect_url === $current_url
or
strpos($redirect_url, 'mailpoet') === false
) {
$redirect_url = admin_url('admin.php?page=mailpoet');
}
$data = array(
'settings' => Setting::getAll(),
'current_user' => wp_get_current_user(),
'redirect_url' => $redirect_url
);
echo $this->renderer->render('update.html', $data);
}
function settings() {
$settings = Setting::getAll();
// dkim: check if public/private keys have been generated
@ -146,9 +258,10 @@ class Menu {
$data = array(
'settings' => $settings,
'segments' => Segment::findArray(),
'segments' => Segment::getPublished()
->findArray(),
'pages' => Pages::getAll(),
'flags' => $flags,
'flags' => $this->_getFlags(),
'charsets' => Charsets::getAll(),
'current_user' => wp_get_current_user(),
'permissions' => Permissions::getAll(),
@ -161,12 +274,38 @@ class Menu {
echo $this->renderer->render('settings.html', $data);
}
private function _getFlags() {
// flags (available features on WP install)
$flags = array();
if(is_multisite()) {
// get multisite registration option
$registration = apply_filters(
'wpmu_registration_enabled',
get_site_option('registration', 'all')
);
// check if users can register
$flags['registration_enabled'] =
!(in_array($registration, array(
'none',
'blog'
)));
} else {
// check if users can register
$flags['registration_enabled'] =
(bool) get_option('users_can_register', false);
}
return $flags;
}
function subscribers() {
$data = array();
$data['segments'] = Segment::findArray();
echo $this->renderer->render('subscribers.html', $data);
echo $this->renderer->render('subscribers/subscribers.html', $data);
}
function segments() {
@ -187,25 +326,42 @@ class Menu {
$data = array();
$data['segments'] = Segment::findArray();
$settings = Setting::findArray();
$data['settings'] = array();
foreach($settings as $setting) {
$data['settings'][$setting['name']] = $setting['value'];
}
$data['settings'] = Setting::getAll();
$data['roles'] = $wp_roles->get_names();
echo $this->renderer->render('newsletters.html', $data);
}
function newletterEditor() {
$data = array();
$custom_fields = array_map(function($field) {
return array(
'text' => $field['name'],
'shortcode' => 'field:' . $field['id'],
);
}, CustomField::findArray());
$data = array(
'customFields' => $custom_fields,
);
wp_enqueue_media();
wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js'));
wp_enqueue_style('editor', includes_url('css/editor.css'));
echo $this->renderer->render('newsletter/form.html', $data);
}
function import() {
$import = new BootStrapMenu('import');
$data = $import->bootstrap();
echo $this->renderer->render('subscribers/importExport/import.html', $data);
}
function export() {
$export = new BootStrapMenu('export');
$data = $export->bootstrap();
echo $this->renderer->render('subscribers/importExport/export.html', $data);
}
function formEditor() {
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0);
$id = (isset($_GET['id']) ? (int) $_GET['id'] : 0);
$form = Form::findOne($id);
if($form !== false) {
$form = $form->asArray();
@ -214,12 +370,18 @@ class Menu {
$data = array(
'form' => $form,
'pages' => Pages::getAll(),
'segments' => Segment::findArray(),
'segments' => Segment::getPublic()
->findArray(),
'styles' => FormRenderer::getStyles($form),
'date_types' => Block\Date::getDateTypes(),
'date_formats' => Block\Date::getDateFormats()
'date_formats' => Block\Date::getDateFormats(),
'month_names' => Block\Date::getMonthNames()
);
echo $this->renderer->render('form/editor.html', $data);
}
function cron() {
echo $this->renderer->render('cron.html');
}
}

View File

@ -21,6 +21,8 @@ class Migrator {
'subscriber_custom_field',
'newsletter_option_fields',
'newsletter_option',
'sending_queues',
'newsletter_statistics',
'forms'
);
}
@ -39,7 +41,7 @@ class Migrator {
function down() {
global $wpdb;
$drop_table = function($model) {
$drop_table = function($model) use($wpdb) {
$table = $this->prefix . $model;
$wpdb->query("DROP TABLE {$table}");
};
@ -50,6 +52,7 @@ class Migrator {
function subscribers() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'wp_user_id bigint(20) NULL,',
'first_name tinytext NOT NULL,',
'last_name tinytext NOT NULL,',
'email varchar(150) NOT NULL,',
@ -81,6 +84,10 @@ class Migrator {
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'subject varchar(250) NOT NULL,',
'type varchar(20) NOT NULL DEFAULT "standard",',
'sender_address varchar(150) NOT NULL,',
'sender_name varchar(150) NOT NULL,',
'reply_to_address varchar(150) NOT NULL,',
'reply_to_name varchar(150) NOT NULL,',
'preheader varchar(250) NOT NULL,',
'body longtext,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
@ -96,7 +103,9 @@ class Migrator {
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(250) NOT NULL,',
'description varchar(250) NOT NULL,',
'body longtext,',
'body LONGTEXT,',
'thumbnail LONGTEXT,',
'readonly TINYINT(1) DEFAULT 0,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)'
@ -106,14 +115,15 @@ class Migrator {
function segments() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,',
'description varchar(250) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'UNIQUE KEY name (name)'
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'name varchar(90) NOT NULL,',
'type varchar(90) NOT NULL DEFAULT "default",',
'description varchar(250) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'UNIQUE KEY name (name)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}
@ -123,6 +133,7 @@ class Migrator {
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'subscriber_id mediumint(9) NOT NULL,',
'segment_id mediumint(9) NOT NULL,',
'status varchar(12) NOT NULL DEFAULT "subscribed",',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
@ -166,7 +177,8 @@ class Migrator {
'value varchar(255) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)'
'PRIMARY KEY (id),',
'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_id)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}
@ -192,7 +204,43 @@ class Migrator {
'value varchar(255) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)'
'PRIMARY KEY (id),',
'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_id)'
);
return $this->sqlify(__FUNCTION__, $attributes);
}
function sending_queues() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'newsletter_id mediumint(9) NOT NULL,',
'subscribers longtext,',
'status varchar(12) NULL DEFAULT NULL,',
'priority mediumint(9) NOT NULL DEFAULT 0,',
'count_total mediumint(9) NOT NULL DEFAULT 0,',
'count_processed mediumint(9) NOT NULL DEFAULT 0,',
'count_to_process mediumint(9) NOT NULL DEFAULT 0,',
'count_failed mediumint(9) NOT NULL DEFAULT 0,',
'processed_at TIMESTAMP NOT NULL DEFAULT 0,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'PRIMARY KEY (id)',
);
return $this->sqlify(__FUNCTION__, $attributes);
}
function newsletter_statistics() {
$attributes = array(
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
'newsletter_id mediumint(9) NOT NULL,',
'subscriber_id mediumint(9) NOT NULL,',
'queue_id mediumint(9) NOT NULL,',
'sent_at TIMESTAMP NOT NULL DEFAULT 0,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
'PRIMARY KEY (id)',
);
return $this->sqlify(__FUNCTION__, $attributes);
}

View File

@ -1,120 +1,196 @@
<?php
namespace MailPoet\Config;
if (!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
class Populator {
function __construct() {
$this->prefix = Env::$db_prefix;
$this->models = array(
'newsletter_option_fields',
);
}
function up() {
global $wpdb;
$_this = $this;
$populate = function($model) use($_this, $wpdb) {
$fields = $_this->$model();
$table = $_this->prefix . $model;
array_map(function($field) use ($wpdb, $table) {
$column_conditions = array_map(function($key) use ($field) {
return $key . '=' . $field[$key];
}, $field);
if ($wpdb->get_var("SELECT COUNT(*) FROM " . $table . " WHERE " . implode(' AND ', $column_conditions)) === 0) {
$wpdb->insert(
$table,
$field
);
}
}, $fields);
};
array_map(array($this, 'populate'), $this->models);
}
function newsletter_option_fields() {
return array(
array(
'name' => 'event',
'newsletter_type' => 'welcome',
),
array(
'name' => 'segment',
'newsletter_type' => 'welcome',
),
array(
'name' => 'role',
'newsletter_type' => 'welcome',
),
array(
'name' => 'afterTimeNumber',
'newsletter_type' => 'welcome',
),
array(
'name' => 'afterTimeType',
'newsletter_type' => 'welcome',
),
array(
'name' => 'intervalType',
'newsletter_type' => 'notification',
),
array(
'name' => 'timeOfDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'weekDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'monthDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'nthWeekDay',
'newsletter_type' => 'notification',
),
);
}
private function populate($model) {
$rows = $this->$model();
$table = $this->prefix . $model;
$_this = $this;
array_map(function($row) use ($_this, $table) {
if (!$_this->rowExists($table, $row)) {
$_this->insertRow($table, $row);
}
}, $rows);
}
private function rowExists($table, $columns) {
global $wpdb;
$conditions = array_map(function($key) use ($columns) {
return $key . '=%s';
}, array_keys($columns));
return $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table WHERE " . implode(' AND ', $conditions),
array_values($columns)
)) > 0;
}
private function insertRow($table, $row) {
global $wpdb;
return $wpdb->insert(
$table,
$row
);
}
}
<?php
namespace MailPoet\Config;
use MailPoet\Config\PopulatorData\Templates\FranksRoastHouseTemplate;
use MailPoet\Config\PopulatorData\Templates\BlankTemplate;
use MailPoet\Config\PopulatorData\Templates\WelcomeTemplate;
use MailPoet\Config\PopulatorData\Templates\PostNotificationsBlankTemplate;
use \MailPoet\Models\Segment;
use \MailPoet\Segments\WP;
use \MailPoet\Models\Setting;
if (!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
class Populator {
function __construct() {
$this->prefix = Env::$db_prefix;
$this->models = array(
'newsletter_option_fields',
'newsletter_templates',
);
}
function up() {
global $wpdb;
$_this = $this;
$populate = function($model) use($_this, $wpdb) {
$fields = $_this->$model();
$table = $_this->prefix . $model;
array_map(function($field) use ($wpdb, $table) {
$column_conditions = array_map(function($key) use ($field) {
return $key . '=' . $field[$key];
}, $field);
if ($wpdb->get_var("SELECT COUNT(*) FROM " . $table . " WHERE " . implode(' AND ', $column_conditions)) === 0) {
$wpdb->insert(
$table,
$field
);
}
}, $fields);
};
array_map(array($this, 'populate'), $this->models);
$this->createDefaultSegments();
$this->createDefaultSettings();
}
private function createDefaultSettings() {
$current_user = wp_get_current_user();
// user name
$user_name = '';
if($current_user->user_firstname) {
$user_name = $current_user->user_firstname;
}
if($current_user->user_lastname) {
if($user_name) {
$user_name .= ' '.$current_user->user_lastname;
}
}
if(!$user_name) {
$user_name = $current_user->display_name;
}
// default from name & address
Setting::setValue('sender', array(
'name' => $user_name,
'address' => $current_user->user_email
));
}
private function createDefaultSegments() {
// WP Users segment
$wp_users_segment = Segment::getWPUsers();
if($wp_users_segment === false) {
// create the wp users list
$wp_users_segment = Segment::create();
$wp_users_segment->hydrate(array(
'name' => __('WordPress Users'),
'description' =>
__('The list containing all of your WordPress users.'),
'type' => 'wp_users'
));
$wp_users_segment->save();
}
// Synchronize WP Users
WP::synchronizeUsers();
// Default segment
if(Segment::where('type', 'default')->count() === 0) {
$default_segment = Segment::create();
$default_segment->hydrate(array(
'name' => __('My First List'),
'description' =>
__('The list created automatically on install of MailPoet')
));
$default_segment->save();
}
}
function newsletter_option_fields() {
return array(
array(
'name' => 'event',
'newsletter_type' => 'welcome',
),
array(
'name' => 'segment',
'newsletter_type' => 'welcome',
),
array(
'name' => 'role',
'newsletter_type' => 'welcome',
),
array(
'name' => 'afterTimeNumber',
'newsletter_type' => 'welcome',
),
array(
'name' => 'afterTimeType',
'newsletter_type' => 'welcome',
),
array(
'name' => 'intervalType',
'newsletter_type' => 'notification',
),
array(
'name' => 'timeOfDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'weekDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'monthDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'nthWeekDay',
'newsletter_type' => 'notification',
),
);
}
private function newsletter_templates() {
return array(
(new FranksRoastHouseTemplate(Env::$assets_url))->get(),
(new BlankTemplate(Env::$assets_url))->get(),
(new WelcomeTemplate(Env::$assets_url))->get(),
(new PostNotificationsBlankTemplate(Env::$assets_url))->get(),
);
}
private function populate($model) {
$rows = $this->$model();
$table = $this->prefix . $model;
$_this = $this;
array_map(function($row) use ($_this, $table) {
if (!$_this->rowExists($table, $row)) {
$_this->insertRow($table, $row);
}
}, $rows);
}
private function rowExists($table, $columns) {
global $wpdb;
$conditions = array_map(function($key) use ($columns) {
return $key . '=%s';
}, array_keys($columns));
return $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM $table WHERE " . implode(' AND ', $conditions),
array_values($columns)
)) > 0;
}
private function insertRow($table, $row) {
global $wpdb;
return $wpdb->insert(
$table,
$row
);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

49
lib/Config/PublicAPI.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace MailPoet\Config;
use MailPoet\Cron\Daemon;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class PublicAPI {
function __construct() {
# http://example.com/?mailpoet-api&section=&action=&payload=
$this->api = isset($_GET['mailpoet-api']) ? true : false;
$this->section = isset($_GET['section']) ? $_GET['section'] : false;
$this->action = isset($_GET['action']) ?
Helpers::underscoreToCamelCase($_GET['action']) :
false;
$this->payload = isset($_GET['payload']) ?
json_decode(urldecode($_GET['payload']), true) :
false;
}
function init() {
if(!$this->api && !$this->section) return;
$this->_checkAndCallMethod($this, $this->section, $terminate = true);
}
function queue() {
try {
$queue = new Daemon($this->payload);
$this->_checkAndCallMethod($queue, $this->action);
} catch(\Exception $e) {
// mailer configuration error
}
}
private function _checkAndCallMethod($class, $method, $terminate = false) {
if(!method_exists($class, $method)) {
if(!$terminate) return;
header('HTTP/1.0 404 Not Found');
exit;
}
call_user_func(
array(
$class,
$method
)
);
}
}

26
lib/Config/Shortcodes.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace MailPoet\Config;
class Shortcodes {
function __construct() {
}
function init() {
// form widget shortcode
add_shortcode('mailpoet_form', array($this, 'formWidget'));
add_shortcode('wysija_form', array($this, 'formWidget'));
}
function formWidget($params = array()) {
// IMPORTANT: fixes conflict with MagicMember
remove_shortcode('user_list');
if(isset($params['id']) && (int)$params['id'] > 0) {
$form_widget = new \MailPoet\Form\Widget();
return $form_widget->widget(array(
'form' => (int)$params['id'],
'form_type' => 'shortcode'
));
}
}
}

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