| | 1 | <?php |
| | 2 | |
| | 3 | /** |
| | 4 | * Sends e-mails at time-based intervals and triggers |
| | 5 | * @package WordCampOrganizerReminders |
| | 6 | */ |
| | 7 | class WCOR_Mailer { |
| | 8 | |
| | 9 | /** |
| | 10 | * Constructor |
| | 11 | */ |
| | 12 | public function __construct() { |
| | 13 | add_action( 'wcor_send_timed_emails', array( $this, 'send_timed_emails' ) ); |
| | 14 | add_action( 'post_updated', array( $this, 'send_trigger_added_to_schedule' ), 10, 2 ); |
| | 15 | } |
| | 16 | |
| | 17 | /** |
| | 18 | * Schedule cron job when plugin is activated |
| | 19 | */ |
| | 20 | public function activate() { |
| | 21 | if ( wp_next_scheduled( 'wcor_send_timed_emails' ) === false ) { |
| | 22 | wp_schedule_event( |
| | 23 | current_time( 'timestamp' ), |
| | 24 | 'daily', |
| | 25 | 'wcor_send_timed_emails' |
| | 26 | ); |
| | 27 | } |
| | 28 | } |
| | 29 | |
| | 30 | /** |
| | 31 | * Clear cron job when plugin is deactivated |
| | 32 | */ |
| | 33 | public function deactivate() { |
| | 34 | wp_clear_scheduled_hook( 'wcor_send_timed_emails' ); |
| | 35 | } |
| | 36 | |
| | 37 | /** |
| | 38 | * Wrapper for wp_mail() that adds our headers |
| | 39 | * |
| | 40 | * We want to make sure that replies go to support@wordcamp.org, rather than the fake address that WordPress sends from, but |
| | 41 | * we don't want to be flagged as spam for forging the From header, so we set the Sender header. |
| | 42 | * @see http://stackoverflow.com/q/4728393/450127 |
| | 43 | * |
| | 44 | * @param string $to |
| | 45 | * @param string $subject |
| | 46 | * @param string $body |
| | 47 | * @return bool |
| | 48 | */ |
| | 49 | protected function mail( $to, $subject, $body ) { |
| | 50 | $headers = array( |
| | 51 | 'From: WordCamp Central <support@wordcamp.org>', |
| | 52 | 'Sender: wordpress@' . strtolower( $_SERVER['SERVER_NAME'] ) |
| | 53 | ); |
| | 54 | |
| | 55 | // todo replace %%organizer_name%% with their name, etc? |
| | 56 | // maybe use shortcodes so it happens automatically? |
| | 57 | // maybe move other universal stuff here, like the checks for if the email has been sent or not |
| | 58 | |
| | 59 | // maybe move the subject prefix here, but then we'd have to know what the camp name is. but will need that for the shortcodes above too? |
| | 60 | |
| | 61 | return wp_mail( $to, $subject, $body, $headers ); |
| | 62 | } |
| | 63 | |
| | 64 | /** |
| | 65 | * Send e-mails that are scheduled to go out at a specific time (e.g., 3 days before the camp) |
| | 66 | */ |
| | 67 | public function send_timed_emails() { |
| | 68 | $recent_or_upcoming_wordcamps = get_posts( array( |
| | 69 | 'posts_per_page' => -1, |
| | 70 | 'post_type' => 'wordcamp', |
| | 71 | 'meta_query' => array( |
| | 72 | array( |
| | 73 | 'key' => 'Start Date (YYYY-mm-dd)', |
| | 74 | 'value' => strtotime( 'now - 3 months' ), |
| | 75 | 'compare' => '>=', |
| | 76 | ) |
| | 77 | ) |
| | 78 | ) ); |
| | 79 | |
| | 80 | $reminder_emails = get_posts( array( |
| | 81 | 'posts_per_page' => -1, |
| | 82 | 'post_type' => WCOR_Reminder::POST_TYPE_SLUG |
| | 83 | ) ); |
| | 84 | |
| | 85 | foreach ( $recent_or_upcoming_wordcamps as $wordcamp ) { |
| | 86 | $organizers_email = get_post_meta( $wordcamp->ID, 'E-mail Address', true ); |
| | 87 | $sent_email_ids = (array) get_post_meta( $wordcamp->ID, 'wcor_sent_email_ids', true ); |
| | 88 | |
| | 89 | if ( ! is_email( $organizers_email ) ) { |
| | 90 | continue; |
| | 91 | } |
| | 92 | |
| | 93 | foreach ( $reminder_emails as $email ) { |
| | 94 | |
| | 95 | echo '<br>'. $wordcamp->post_title .' - '. $email->post_title .': '; |
| | 96 | |
| | 97 | if ( $this->timed_email_is_ready_to_send( $wordcamp, $email, $sent_email_ids ) ) { |
| | 98 | /*$this->mail( |
| | 99 | $organizers_email, |
| | 100 | $wordcamp->post_title .' Reminder: ' . $email->post_title, // todo apply filters? |
| | 101 | $email->post_content // todo apply filters? |
| | 102 | ); |
| | 103 | |
| | 104 | $sent_email_ids[] = $email->ID; |
| | 105 | update_post_meta( $wordcamp->ID, 'wcor_sent_email_ids', $sent_email_ids ); |
| | 106 | sleep( 1 ); // don't send e-mails too fast, or it might increase the risk of being flagged as spam |
| | 107 | */ |
| | 108 | |
| | 109 | echo 'yes'; |
| | 110 | } |
| | 111 | |
| | 112 | else echo 'no'; |
| | 113 | } |
| | 114 | } |
| | 115 | } |
| | 116 | |
| | 117 | /** |
| | 118 | * Determines if a time-based e-mail is ready to be sent to a WordCamp |
| | 119 | * |
| | 120 | * E-mails should be sent if the current date matches the date that the e-mail is scheduled to be sent (e.g., 3 days before the camp starts). |
| | 121 | * |
| | 122 | * One exception to that is if a camp is added later than expected (e.g., we start sending e-mails 4 months before the start date, but a camp |
| | 123 | * isn't scheduled until 2 months before the start). When that happens, we want to send all the e-mails that they've missed. |
| | 124 | * |
| | 125 | * An exception to that exception is that we don't want to send e-mails to camps that have already been sent those e-mails manually, before we |
| | 126 | * started sending them automatically. |
| | 127 | * @todo this exception will no longer be relevant in a few months, and can be removed at that time. Approximately January 15th, 2014. |
| | 128 | * |
| | 129 | * @param WP_Post $wordcamp |
| | 130 | * @param WP_Post $email |
| | 131 | * @param array $sent_email_ids The IDs of emails that have already been sent to the $wordcamp post |
| | 132 | * @return bool |
| | 133 | */ |
| | 134 | protected function timed_email_is_ready_to_send( $wordcamp, $email, $sent_email_ids ) { |
| | 135 | $ready = false; |
| | 136 | $send_when = get_post_meta( $email->ID, 'wcor_send_when', true ); |
| | 137 | $start_date = get_post_meta( $wordcamp->ID, 'Start Date (YYYY-mm-dd)', true ); |
| | 138 | $end_date = get_post_meta( $wordcamp->ID, 'End Date (YYYY-mm-dd)', true ); |
| | 139 | |
| | 140 | if ( ! $end_date ) { |
| | 141 | $end_date = $start_date; |
| | 142 | } |
| | 143 | |
| | 144 | if ( ! in_array( $email->ID, $sent_email_ids ) ) { |
| | 145 | if ( 'wcor_send_before' == $send_when ) { |
| | 146 | $days_before = absint( get_post_meta( $email->ID, 'wcor_send_days_before', true ) ); |
| | 147 | |
| | 148 | if ( $days_before ) { |
| | 149 | $send_date = $start_date - ( $days_before * DAY_IN_SECONDS ); |
| | 150 | |
| | 151 | if ( $send_date <= current_time( 'timestamp' ) ) { |
| | 152 | $ready = true; |
| | 153 | } |
| | 154 | } |
| | 155 | } elseif ( 'wcor_send_after' == $send_when ) { |
| | 156 | $days_after = absint( get_post_meta( $email->ID, 'wcor_send_days_after', true ) ); |
| | 157 | |
| | 158 | if ( $days_after ) { |
| | 159 | $send_date = $end_date + ( $days_after * DAY_IN_SECONDS ); |
| | 160 | |
| | 161 | if ( $send_date <= current_time( 'timestamp' ) ) { |
| | 162 | $ready = true; |
| | 163 | } |
| | 164 | } |
| | 165 | } |
| | 166 | |
| | 167 | if ( $send_date <= strtotime( 'October 15th, 2013' ) ) { // todo update to the day before you deploy the code |
| | 168 | // Assume it was already sent manually before this plugin was activated |
| | 169 | $ready = false; |
| | 170 | } |
| | 171 | } |
| | 172 | |
| | 173 | // todo write unit tests for this function? |
| | 174 | // hard to do b/c have to mock get_post_meta and current_time() |
| | 175 | |
| | 176 | return $ready; |
| | 177 | } |
| | 178 | |
| | 179 | /** |
| | 180 | * Sends e-mails hooked to the wcor_added_to_schedule trigger. |
| | 181 | * |
| | 182 | * This fires when a WordCamp is added to the schedule (i.e., when they set the start date in their `wordcamp` post). |
| | 183 | * |
| | 184 | * Since Core doesn't support revisions on post meta, we're not actually checking to see if the start date was added during |
| | 185 | * the current post update, but just that it has a start data. By itself, that would lead to the e-mail being sent every time |
| | 186 | * the post is updated, but to avoid that we're checking the `wcor_sent_email_id` post meta for the `wordcamp` post to see if |
| | 187 | * we've already sent this particular e-mail in the past. |
| | 188 | * |
| | 189 | * @param int $post_id |
| | 190 | * @param WP_Post $post |
| | 191 | */ |
| | 192 | public function send_trigger_added_to_schedule( $post_id, $post ) { |
| | 193 | |
| | 194 | /* @todo Update time-based code to ignore trigger-based e-mails in multiple places. */ |
| | 195 | |
| | 196 | |
| | 197 | |
| | 198 | if ( 'wordcamp' == $post->post_type && 'publish' == $post->post_status ) { |
| | 199 | $start_date = get_post_meta( $post_id, 'Start Date (YYYY-mm-dd)', true ); |
| | 200 | $organizers_email = get_post_meta( $post_id, 'E-mail Address', true ); |
| | 201 | $sent_email_ids = (array) get_post_meta( $post_id, 'wcor_sent_email_ids', true ); |
| | 202 | |
| | 203 | if ( $start_date && is_email( $organizers_email ) ) { |
| | 204 | $emails = get_posts( array( |
| | 205 | 'posts_per_page' => -1, |
| | 206 | 'post_type' => WCOR_Reminder::POST_TYPE_SLUG, |
| | 207 | 'meta_query' => array( |
| | 208 | array( |
| | 209 | 'key' => 'wcor_which_trigger', |
| | 210 | 'value' => 'wcor_added_to_schedule', |
| | 211 | ) |
| | 212 | ) |
| | 213 | ) ); |
| | 214 | |
| | 215 | foreach( $emails as $email ) { |
| | 216 | if ( ! in_array( $email->ID, $sent_email_ids ) ) { |
| | 217 | $this->mail( |
| | 218 | $organizers_email, |
| | 219 | $post->post_title . ' Reminder: ' . $email->post_title, |
| | 220 | $email->post_body |
| | 221 | ); |
| | 222 | |
| | 223 | $sent_email_ids[] = $email->ID; |
| | 224 | // todo update_post_meta( $post_id, 'wcor_sent_email_ids', $sent_email_ids ); |
| | 225 | } |
| | 226 | } |
| | 227 | } |
| | 228 | } |
| | 229 | } |
| | 230 | } |
| | 231 | No newline at end of file |