PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
← All changes | modules/email-report/class-cartflows-admin-report-emails.php +182 -3 3.1.1 → 3.3.0 View file →
@@ -91,9 +91,9 @@
91 91 wcf()->logger->log( 'Total Revenue: ' . $stats['total_revenue'] );
92 92
93 93 if ( isset( $stats['total_revenue_raw'] ) && $stats['total_revenue_raw'] > 0 ) {
94 94
95 - $subject = $this->get_email_subject();
95 + $subject = $this->get_email_subject( $stats );
96 96 $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n";
97 97 $headers .= "Content-Type: text/html;\r\n";
98 98
99 99 $emails = preg_split( "/[\f\r\n]+/", $emails );
@@ -172,15 +172,183 @@
172 172 }
173 173
174 174 /**
175 175 * Get admin report email subject.
176 + *
177 + * @param array<string, mixed> $stats Weekly stats for revenue interpolation.
178 + * @return string
176 179 */
177 - public function get_email_subject() {
180 + public function get_email_subject( $stats = array() ) {
178 181
179 - return esc_html__( 'Here’s how your store performed last week!', 'cartflows' );
182 + // Strip wc_price() HTML wrappers and decode numeric entities (e.g. &#8360; for ₨) so currency symbols render in plain-text subject lines.
183 + $revenue = isset( $stats['total_revenue'] ) && is_string( $stats['total_revenue'] )
184 + ? html_entity_decode( wp_strip_all_tags( $stats['total_revenue'] ), ENT_QUOTES, 'UTF-8' )
185 + : '';
186 +
187 + if ( '' === $revenue ) {
188 + return esc_html__( 'Your weekly CartFlows report', 'cartflows' );
189 + }
190 +
191 + /* translators: %s: Total revenue earned in the last 7 days, formatted with currency. */
192 + return sprintf( esc_html__( 'Your weekly CartFlows report — %s earned', 'cartflows' ), $revenue );
180 193 }
181 194
182 195 /**
196 + * Get a weekly tip for the "One thing to try this week" section.
197 + * Rotates by ISO week so the same tip shows for all sends in the same week.
198 + *
199 + * @since 3.1.2
200 + * @param array<string, mixed> $stats Weekly stats (allows the filter to choose data-driven tips).
201 + * @return array{headline: string, body: string}
202 + */
203 + public function get_weekly_tip( $stats = array() ) {
204 +
205 + $tips = array(
206 + array(
207 + 'headline' => __( 'Add a one-click upsell to your top flow.', 'cartflows' ),
208 + 'body' => __( 'Stores that add one see an average +12% lift in average order value.', 'cartflows' ),
209 + ),
210 + array(
211 + 'headline' => __( 'Enable an order bump on your highest-traffic checkout.', 'cartflows' ),
212 + 'body' => __( 'Order bumps consistently lift revenue per order with minimal effort.', 'cartflows' ),
213 + ),
214 + array(
215 + 'headline' => __( 'Run an A/B test on your top-performing flow.', 'cartflows' ),
216 + 'body' => __( 'Test a single change — headline, button, or image — and let the winner run.', 'cartflows' ),
217 + ),
218 + array(
219 + 'headline' => __( 'Add a thank-you page upsell.', 'cartflows' ),
220 + 'body' => __( 'Post-purchase offers convert better than pre-purchase upsells for many stores.', 'cartflows' ),
221 + ),
222 + array(
223 + 'headline' => __( 'Streamline your checkout to a single column.', 'cartflows' ),
224 + 'body' => __( 'Single-column checkouts reduce cognitive load and tend to convert better on mobile.', 'cartflows' ),
225 + ),
226 + array(
227 + 'headline' => __( 'Send abandoned cart recovery emails.', 'cartflows' ),
228 + 'body' => __( 'Most stores recover 8–15% of abandoned carts with a simple two-email sequence.', 'cartflows' ),
229 + ),
230 + array(
231 + 'headline' => __( 'Use a countdown timer on limited-time offers.', 'cartflows' ),
232 + 'body' => __( 'Urgency, when honest, lifts conversion on time-sensitive promotions.', 'cartflows' ),
233 + ),
234 + array(
235 + 'headline' => __( 'Pre-fill checkout fields whenever possible.', 'cartflows' ),
236 + 'body' => __( 'Each saved keystroke reduces friction at the highest-stakes step of the journey.', 'cartflows' ),
237 + ),
238 + );
239 +
240 + $index = (int) gmdate( 'W' ) % count( $tips );
241 + $tip = $tips[ $index ];
242 +
243 + /**
244 + * Filter the weekly tip shown in the report email.
245 + *
246 + * @param array<string, mixed> $tip Default tip with 'headline' and 'body'.
247 + * @param array<string, mixed> $stats Weekly stats.
248 + */
249 + $filtered = apply_filters( 'cartflows_weekly_email_tip', $tip, $stats );
250 +
251 + if (
252 + is_array( $filtered )
253 + && isset( $filtered['headline'], $filtered['body'] )
254 + && is_string( $filtered['headline'] )
255 + && is_string( $filtered['body'] )
256 + ) {
257 + return array(
258 + 'headline' => $filtered['headline'],
259 + 'body' => $filtered['body'],
260 + );
261 + }
262 +
263 + return $tip;
264 + }
265 +
266 + /**
267 + * Get top wins data (top flow, best step, top bump) for the highlights section.
268 + * Free returns an empty array; Pro hooks the filter to return real data.
269 + *
270 + * @since 3.1.2
271 + * @param string $start_date Range start (Y-m-d).
272 + * @param string $end_date Range end (Y-m-d).
273 + * @return array<int, array{label?: string, detail?: string}>
274 + */
275 + public function get_top_wins( $start_date, $end_date ) {
276 +
277 + /**
278 + * Filter top wins bullets shown in the weekly email.
279 + *
280 + * @param array<int, mixed> $bullets Default empty array.
281 + * @param string $start_date Range start.
282 + * @param string $end_date Range end.
283 + */
284 + $bullets = apply_filters( 'cartflows_weekly_email_top_wins', array(), $start_date, $end_date );
285 +
286 + if ( ! is_array( $bullets ) ) {
287 + return array();
288 + }
289 +
290 + $normalised = array();
291 + foreach ( $bullets as $bullet ) {
292 + if (
293 + is_array( $bullet )
294 + && isset( $bullet['label'], $bullet['detail'] )
295 + && is_string( $bullet['label'] )
296 + && is_string( $bullet['detail'] )
297 + ) {
298 + $normalised[] = array(
299 + 'label' => $bullet['label'],
300 + 'detail' => $bullet['detail'],
301 + );
302 + }
303 + }
304 +
305 + return $normalised;
306 + }
307 +
308 + /**
309 + * Get quick link destinations for the report email.
310 + *
311 + * @since 3.1.2
312 + * @return array<string, string>
313 + */
314 + public function get_quick_links() {
315 +
316 + $links = array(
317 + __( 'View full report', 'cartflows' ) => admin_url( 'admin.php?page=cartflows' ),
318 + __( 'Edit your flows', 'cartflows' ) => admin_url( 'admin.php?page=cartflows&path=flows' ),
319 + __( 'Browse templates', 'cartflows' ) => admin_url( 'admin.php?page=cartflows&path=library' ),
320 + __( 'Get help', 'cartflows' ) => 'https://cartflows.com/docs/',
321 + );
322 +
323 + if ( ! _is_cartflows_pro() ) {
324 + $links[ __( 'Try CartFlows Pro', 'cartflows' ) ] = 'https://cartflows.com/pricing/?utm_source=newsletter&utm_medium=weekly-report-email&utm_campaign=cartflows-pro';
325 + } else {
326 + $links[ __( 'Discover More Products', 'cartflows' ) ] = 'https://cartflows.com/products-to-scale-growth/?utm_source=newsletter&utm_medium=weekly-report-email&utm_campaign=cartflows-bsf';
327 + }
328 +
329 + /**
330 + * Filter the quick links shown in the weekly email.
331 + *
332 + * @param array<string, mixed> $links Map of label => URL.
333 + */
334 + $filtered = apply_filters( 'cartflows_weekly_email_quick_links', $links );
335 +
336 + if ( ! is_array( $filtered ) ) {
337 + return $links;
338 + }
339 +
340 + $normalised = array();
341 + foreach ( $filtered as $label => $url ) {
342 + if ( is_string( $label ) && is_string( $url ) ) {
343 + $normalised[ $label ] = $url;
344 + }
345 + }
346 +
347 + return $normalised;
348 + }
349 +
350 + /**
183 351 * Get admin report email content.
184 352 *
185 353 * @param array $stats reports details.
186 354 * @param string $user_name user name.
@@ -213,8 +381,19 @@
213 381 $total_revenue = $stats['total_revenue'];
214 382 $last_month_stats = $this->get_last_month_stats();
215 383 $last_month_revenue = $last_month_stats['total_revenue'];
216 384 $store_name = get_bloginfo( 'name' );
385 +
386 + $is_pro = _is_cartflows_pro();
387 + $start_date = gmdate( 'Y-m-d', strtotime( '-7 days' ) );
388 + $end_date = gmdate( 'Y-m-d' );
389 + $top_wins = $this->get_top_wins( $start_date, $end_date );
390 + $weekly_tip = $this->get_weekly_tip( $stats );
391 + $quick_links = $this->get_quick_links();
392 + $sign_off = __( 'The CartFlows team', 'cartflows' );
393 +
394 + /* translators: 1: total orders, 2: total visits. */
395 + $preheader = sprintf( esc_html__( '%1$s orders from %2$s visits — here\'s what to optimize next.', 'cartflows' ), (int) $total_orders, (int) $total_visits );
217 396
218 397 return include CARTFLOWS_DIR . 'modules/email-report/templates/email-body.php';
219 398 }
220 399 }