PluginProbe
picu – Online Photo Proofing Gallery / 3.9.0
picu – Online Photo Proofing Gallery v3.9.0
3.9.0 3.8.1 3.8.0 3.7.0 3.6.1 3.6.0 trunk 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.2.0 2.3.0 2.3.1.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 All 46 releases
picu / picu.php

picu.php in picu – Online Photo Proofing Gallery 3.9.0, at picu.php

700 lines 23.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: picu
4 * Plugin URI: https://picu.io/
5 * Description: Send a collection of photographs to your client for approval.
6 * Version: 3.9.0
7 * Requires at least: 6.0
8 * Requires PHP: 7.4
9 * Author: Haptiq
10 * Author URI: https://picu.io/
11 * License: GPLv3
12 * License URI: http://www.gnu.org/licenses/gpl-3.0.html
13 * Text Domain: picu
14 */
15 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
16
17
18 /**
19 * Include functions for picu
20 *
21 * @since 0.2.0
22 */
23 if ( ! function_exists( 'picu_setup' ) ) {
24
25 function picu_setup() {
26
27 // Define plugin version
28 define( 'PICU_VERSION', '3.9.0' );
29
30 // Define the minimum compatible version of picu Pro
31 define( 'PICU_PRO_REQUIRED', '2.10.0' );
32
33 // Define path for this plugin
34 define( 'PICU_PATH', plugin_dir_path(__FILE__) );
35
36 // Define URL for this plugin
37 define( 'PICU_URL', plugin_dir_url(__FILE__) );
38
39 // Define picu upload dir
40 $upload_dir = wp_upload_dir();
41 define( 'PICU_UPLOAD_DIR', $upload_dir['basedir'] . '/picu' );
42
43 // Define telemetry URL
44 define( 'PICU_TELEMETRY_URL', 'https://picu.io/wp-json/picu-telemetry/v1/' );
45
46 // Define telemetry version
47 Define( 'PICU_TELEMETRY_VERSION', '2.0' );
48
49 // Include functions to render admin menu and settings page
50 require PICU_PATH . 'backend/includes/picu-settings.php';
51
52 // Include functions to render add-ons page
53 require PICU_PATH . 'backend/includes/picu-addons-page.php';
54
55 // Include function for registering custom post type collection
56 require PICU_PATH . 'backend/includes/picu-cpt-collection.php';
57
58 // Include welcome screen
59 require PICU_PATH . 'backend/includes/picu-welcome-screen.php';
60
61 // Include functions for admin notices and error messages
62 require PICU_PATH . 'backend/includes/picu-admin-notices.php';
63
64 // Everything that doesn't fit anywhere else...
65 require PICU_PATH . 'backend/includes/picu-helper.php';
66
67 // Include custom metabox and our custom edit screen
68 require PICU_PATH . 'backend/includes/picu-edit-collection.php';
69
70 // Picu media handling
71 require PICU_PATH . 'backend/includes/picu-media.php';
72
73 // Handle front end ajax requests
74 require PICU_PATH . 'frontend/includes/save-collection.php';
75
76 // Handle backend ajax requests
77 require PICU_PATH . 'backend/includes/picu-ajax.php';
78
79 // Fix compatibility issues with third parties
80 require PICU_PATH . 'backend/includes/picu-compatibility.php';
81
82 // Picu Email sending class
83 require PICU_PATH . 'backend/includes/emails/class-picu-emails.php';
84
85 // Picu Email sending functions
86 require PICU_PATH . 'backend/includes/emails/picu-emails.php';
87
88 // Include template redirection etc. for collections
89 require PICU_PATH . 'frontend/includes/picu-template-functions.php';
90
91 // Deprecated functions, filters and hooks
92 require PICU_PATH . 'backend/includes/deprecated.php';
93
94 // Handle picu telemetry
95 require PICU_PATH . 'backend/includes/picu-telemetry.php';
96
97 // Register picu Blocks
98 require PICU_PATH . 'blocks/picu-blocks.php';
99
100 // Autoload classes installed via composer (emogrify)
101 require PICU_PATH . 'vendor/autoload.php';
102
103 // Add picu debug info to Site Health screen
104 require PICU_PATH . 'backend/includes/picu-site-health.php';
105
106 // Handle picu Pro upselling
107 require PICU_PATH . 'backend/includes/picu-pro.php';
108
109 // Check the settings version, run upgrader
110 $settings_version = get_option( 'picu_settings_version' );
111 // Version upgrade is needed
112 if ( empty( $settings_version ) ) {
113 picu_settings_upgrade();
114 }
115 }
116 }
117
118 add_action( 'after_setup_theme', 'picu_setup' );
119
120
121 /**
122 * Run upgrades after update
123 *
124 * Since 2.3.0
125 */
126 function picu_upgrade() {
127 $settings_version = get_option( 'picu_settings_version' );
128
129 if ( version_compare( $settings_version, PICU_VERSION, '<' ) ) {
130 picu_collections_upgrade();
131 }
132 }
133
134 // This needs to run after `init` so everything we need is in place!
135 add_action( 'init', 'picu_upgrade', 11 );
136
137
138 /**
139 * Set transient to display welcome screen on activation
140 *
141 * @since 0.7.0
142 */
143 function picu_activate_welcome_screen() {
144 // Set transient for redirect to activation screen
145 set_transient( '_picu_welcome_screen_activation_redirect', true, 30 );
146 }
147
148 register_activation_hook( __FILE__, 'picu_activate_welcome_screen' );
149
150
151 /**
152 * Flush rewrite rules on plugin activation/deactivation
153 *
154 * @since 0.7.0
155 */
156 function picu_flush_rewrites() {
157
158 // Include custom post type registration
159 include( plugin_dir_path(__FILE__) . 'backend/includes/picu-cpt-collection.php' );
160
161 // Make sure our custom post types are defined first
162 picu_register_cpt_collection();
163
164 // Flush the rewrite rules
165 flush_rewrite_rules();
166
167 }
168
169 register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
170 register_activation_hook( __FILE__, 'picu_flush_rewrites' );
171
172
173 /**
174 * Load some custom styling for our admin screens.
175 *
176 * @since 0.3.2
177 */
178 function picu_admin_styles_scripts() {
179
180 global $post;
181
182 $current_screen = get_current_screen();
183
184 // Prevent conflicts in case no current_screen is set
185 if ( empty( $current_screen ) ) {
186 return;
187 }
188
189 // Only load those styles on edit collection screens
190 if ( is_admin() ) {
191 wp_enqueue_style( 'picu-admin', PICU_URL . 'backend/css/picu-admin.css', false, filemtime( PICU_PATH . 'backend/css/picu-admin.css' ) );
192
193 global $pagenow;
194 if ( $current_screen->post_type == 'picu_collection' AND get_post_type() == 'picu_collection' AND $pagenow == 'post-new.php' || $pagenow == 'post.php' ) {
195 // Enqueue wp.media scripts manually, because we don't load the default editor on collections
196 // Post needs to be provided, or uploaded media will not get attached to the collection
197 $args = array( 'post' => $post->ID );
198 wp_enqueue_media( $args );
199 }
200
201 if ( $current_screen->base == 'picu_page_picu-design-appearance' ) {
202 // Add the color picker css file
203 wp_enqueue_style( 'wp-color-picker' );
204 }
205
206 // Make sure we are on the right screen
207 if ( ( $current_screen->post_type == 'picu_collection' && get_post_type() == 'picu_collection' && $pagenow == 'post-new.php' || $pagenow == 'post.php' ) || ( $current_screen->base == 'dashboard_page_picu-welcome-screen' ) || ( $current_screen->post_type == 'picu_collection' AND get_post_type() == 'picu_collection' AND $pagenow == 'edit.php' ) || strpos( $current_screen->base, 'picu_page_picu-' ) !== false ) {
208
209 // Enqueue media
210 wp_enqueue_media();
211
212 // Enqueue script
213 wp_enqueue_script( 'picu-admin', PICU_URL . 'backend/js/picu-admin.min.js', array( 'jquery', 'jquery-ui-draggable', 'jquery-ui-sortable', 'underscore', 'backbone', 'wp-color-picker' ), filemtime( PICU_PATH . 'backend/js/picu-admin.min.js' ), true );
214
215 $post_id = false;
216 if ( isset( $post->ID ) AND ! empty( $post->ID ) ) {
217 $post_id = $post->ID;
218 }
219
220 // Localize it
221 $picu_localization_strings = array(
222 'ajaxurl' => admin_url( 'admin-ajax.php' ),
223 'ajax_nonce' => wp_create_nonce( 'picu_ajax' ),
224 'postID' => $post_id,
225 'media_modal_title' => __( 'Upload Images', 'picu' ),
226 'media_modal_button_insert_text' => __( 'Insert Images', 'picu' ),
227 'sent_option_label' => __( 'Sent', 'picu' ),
228 'approved_option_label' => __( 'Approved', 'picu' ),
229 'expired_option_label' => __( 'Expired', 'picu' ),
230 'button_text_publish' => __( 'Publish', 'picu' ),
231 'button_text_send_to_client' => __( 'Send to Client', 'picu' ),
232 'selection_table_no_match' => __( 'No images found', 'picu' ),
233 );
234
235 $picu_localization_strings = apply_filters( 'picu_localization_strings', $picu_localization_strings );
236
237 wp_localize_script( 'picu-admin', 'picu_admin', $picu_localization_strings );
238 }
239
240 }
241
242 }
243
244 add_action( 'admin_enqueue_scripts', 'picu_admin_styles_scripts' );
245
246
247 /**
248 * Add settings link in plugins overview
249 *
250 * @param array $actions An array of links
251 * @since 1.0.0
252 */
253 function picu_plugin_action_links( $actions ) {
254
255 $action = sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=picu-settings' ), __( 'Settings', 'picu' ) );
256 array_unshift( $actions, $action );
257
258 return $actions;
259 }
260
261 add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ) , 'picu_plugin_action_links', 10 );
262
263
264 /**
265 * Custom capability to manage picu collections.
266 *
267 * Defaults to administrator privileges, can be filtered using 'picu_capability'.
268 *
269 * @since 1.1.0
270 */
271 function picu_capability() {
272 $picu_capability = 'manage_options';
273 $picu_capability = apply_filters( 'picu_capability', $picu_capability );
274
275 return $picu_capability;
276 }
277
278
279 /**
280 * Redirect /picu to collection overview in WordPress admin
281 *
282 * @since 1.2.1
283 */
284 function picu_redirect_to_overview() {
285 if ( $_SERVER['REQUEST_URI'] == '/picu' OR $_SERVER['REQUEST_URI'] == '/picu/' ) {
286 wp_redirect( admin_url() . 'edit.php?post_type=picu_collection' );
287 exit;
288 }
289 }
290
291 add_action( 'init', 'picu_redirect_to_overview' );
292
293
294 /**
295 * Check picu Pro compatibility
296 *
297 * @since 2.0.0
298 */
299 function picu_check_pro_compat() {
300 if ( defined( 'PICU_PRO' ) && version_compare( PICU_PRO, PICU_PRO_REQUIRED, '<' ) ) {
301 /* translators: Admin notice, %s = opening and closing link tags */
302 $notice = sprintf ( __( '🚨 <strong>Action required:</strong> The version of picu Pro you are using is not compatible with this version of picu. %sPlease update to the latest version of picu Pro%s.', 'picu' ), '<a href="' . admin_url( 'plugins.php?s=picu%20pro&plugin_status=all' ) . '">', '</a>' );
303 $notice_type = 'error';
304
305 // Display admin notice
306 add_action( 'admin_notices', function() use ( $notice, $notice_type ) {
307 ?>
308 <div class="picu-pro-module-notice notice notice-<?php echo $notice_type; ?>">
309 <p><?php echo $notice; ?></p>
310 </div>
311 <?php
312 });
313 }
314 }
315
316 add_action( 'init', 'picu_check_pro_compat' );
317
318
319 /**
320 * Add picu Pro upgrade box
321 *
322 * @since 1.3.1
323 */
324 function picu_add_pro_metabox() {
325 if ( ! picu_is_pro_license_valid() ) {
326
327 add_meta_box(
328 'picu-pro-metabox',
329 __( 'Get picu pro', 'picu' ),
330 'picu_display_pro_metabox',
331 'picu_collection',
332 'side',
333 'high'
334 );
335 }
336 }
337
338 add_action( 'add_meta_boxes', 'picu_add_pro_metabox' );
339
340
341 /**
342 * Render the Pro metabox.
343 *
344 * @since 1.3.1
345 * @since 2.0.0 New box design and content
346 */
347 function picu_display_pro_metabox() {
348 ?>
349 <div class="picu-pro-meta-box">
350 <h2 class="picu-pro-meta-box__title"><?php echo __( 'Upgrade Your Proofing Workflow', 'picu' ); ?></h2>
351 <div class="picu-pro-meta-box__content">
352 <ul>
353 <li><?php _e( 'Add personal branding', 'picu' ); ?></li>
354 <li><?php _e( 'Enable markers and comments on individual images', 'picu' ); ?></li>
355 <li><?php _e( 'Accept payments via PayPal or Stripe', 'picu' ); ?></li>
356 <li><?php _e( 'Many more professional features', 'picu' ); ?></li>
357 </ul>
358 <p class="picu-pro-meta-box__button-wrap"><a class="button button-primary picu-pro__button" href="https://go.picu.io/get-picu-pro" target="_blank"><?php _e( 'Get picu Pro', 'picu' ); ?> <svg style="transform: translateY(3px);" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h13M12 5l7 7-7 7"/></svg></a></p>
359 <div class="picu-pro-meta-box__reviews">
360 <p>
361 <svg width="16" height="16" viewBox="0 0 16 15" xmlns="http://www.w3.org/2000/svg"><path d="m6.75365082.46448007c.30569615-.61930676 1.1888161-.61930676 1.49451225 0l1.86507723 3.77985326 4.1748568.61109495c.6453032.09432073.9240557.84870204.5363234 1.33810964l-.0754029.08342717-3.0197773 2.94036824.7130072 4.15513057c.1106198.6449702-.5249381 1.144844-1.1119645.922308l-.0972521-.0438773-3.73279062-1.9635613-3.73145727 1.9635613c-.57918124.3045856-1.25104449-.1453116-1.22088044-.7723782l.01166386-.1060525.71167385-4.15513057-3.018444-2.94036824c-.46718009-.45503341-.25023224-1.22945432.35090953-1.39822842l.11001096-.02330839 4.17352351-.61109495z" fill="#ffd700" transform="translate(.5 .5)"/></svg>
362 <svg width="16" height="16" viewBox="0 0 16 15" xmlns="http://www.w3.org/2000/svg"><path d="m6.75365082.46448007c.30569615-.61930676 1.1888161-.61930676 1.49451225 0l1.86507723 3.77985326 4.1748568.61109495c.6453032.09432073.9240557.84870204.5363234 1.33810964l-.0754029.08342717-3.0197773 2.94036824.7130072 4.15513057c.1106198.6449702-.5249381 1.144844-1.1119645.922308l-.0972521-.0438773-3.73279062-1.9635613-3.73145727 1.9635613c-.57918124.3045856-1.25104449-.1453116-1.22088044-.7723782l.01166386-.1060525.71167385-4.15513057-3.018444-2.94036824c-.46718009-.45503341-.25023224-1.22945432.35090953-1.39822842l.11001096-.02330839 4.17352351-.61109495z" fill="#ffd700" transform="translate(.5 .5)"/></svg>
363 <svg width="16" height="16" viewBox="0 0 16 15" xmlns="http://www.w3.org/2000/svg"><path d="m6.75365082.46448007c.30569615-.61930676 1.1888161-.61930676 1.49451225 0l1.86507723 3.77985326 4.1748568.61109495c.6453032.09432073.9240557.84870204.5363234 1.33810964l-.0754029.08342717-3.0197773 2.94036824.7130072 4.15513057c.1106198.6449702-.5249381 1.144844-1.1119645.922308l-.0972521-.0438773-3.73279062-1.9635613-3.73145727 1.9635613c-.57918124.3045856-1.25104449-.1453116-1.22088044-.7723782l.01166386-.1060525.71167385-4.15513057-3.018444-2.94036824c-.46718009-.45503341-.25023224-1.22945432.35090953-1.39822842l.11001096-.02330839 4.17352351-.61109495z" fill="#ffd700" transform="translate(.5 .5)"/></svg>
364 <svg width="16" height="16" viewBox="0 0 16 15" xmlns="http://www.w3.org/2000/svg"><path d="m6.75365082.46448007c.30569615-.61930676 1.1888161-.61930676 1.49451225 0l1.86507723 3.77985326 4.1748568.61109495c.6453032.09432073.9240557.84870204.5363234 1.33810964l-.0754029.08342717-3.0197773 2.94036824.7130072 4.15513057c.1106198.6449702-.5249381 1.144844-1.1119645.922308l-.0972521-.0438773-3.73279062-1.9635613-3.73145727 1.9635613c-.57918124.3045856-1.25104449-.1453116-1.22088044-.7723782l.01166386-.1060525.71167385-4.15513057-3.018444-2.94036824c-.46718009-.45503341-.25023224-1.22945432.35090953-1.39822842l.11001096-.02330839 4.17352351-.61109495z" fill="#ffd700" transform="translate(.5 .5)"/></svg>
365 <svg width="16" height="16" viewBox="0 0 16 15" xmlns="http://www.w3.org/2000/svg"><g fill="none" transform="translate(.5 .5)"><path d="m6.75365082.46448007c.30569615-.61930676 1.1888161-.61930676 1.49451225 0l1.86507723 3.77985326 4.1748568.61109495c.6453032.09432073.9240557.84870204.5363234 1.33810964l-.0754029.08342717-3.0197773 2.94036824.7130072 4.15513057c.1106198.6449702-.5249381 1.144844-1.1119645.922308l-.0972521-.0438773-3.73279062-1.9635613-3.73145727 1.9635613c-.57918124.3045856-1.25104449-.1453116-1.22088044-.7723782l.01166386-.1060525.71167385-4.15513057-3.018444-2.94036824c-.46718009-.45503341-.25023224-1.22945432.35090953-1.39822842l.11001096-.02330839 4.17352351-.61109495z" fill="#fffdf0"/><path d="m7.50024028.00000364v12.28732966l-3.73145727 1.9635613c-.57918124.3045856-1.25104449-.1453116-1.22088044-.7723782l.01166386-.1060525.71167385-4.15513057-3.018444-2.94036824c-.46718009-.45503341-.25023224-1.22945432.35090953-1.39822842l.11001096-.02330839 4.17352351-.61109495 1.86641054-3.77985326c.13336744-.27018775.37733399-.42249948.63539651-.45693517z" fill="#ffd700"/></g></svg>
366 <span class="picu-pro-meta-box__rating">4.7 / 5</span>
367 </p>
368 <p><a class="picu-pro-meta-box__link" href="https://go.picu.io/user-reviews" target="_blank"><?php _e( 'Check out picu user reviews', 'picu' ); ?></a></p>
369 </div>
370 </div>
371 </div>
372 <?php
373 }
374
375
376 /**
377 * Hide admin bar for now
378 */
379 add_filter( 'picu_show_admin_bar', '__return_false' );
380
381
382 /**
383 * Check if collection slug has changed
384 *
385 * @since 1.5.0
386 */
387 function picu_check_collection_slug() {
388
389 // Only run if collection or 404
390 if ( 'picu_collection' != get_post_type() AND ! is_404() ) {
391 return;
392 }
393
394 // Only run, if pretty permalinks are active
395 if ( ! get_option( 'permalink_structure' ) ) {
396 return;
397 }
398
399 // Get current collection slug
400 $post_type = get_post_type_object( 'picu_collection' );
401 $post_type_slug = $post_type->rewrite['slug'];
402
403 // Get saved slug. Set, if empty
404 $saved_slug = get_transient( 'picu_collection_slug' );
405
406 if ( empty( $saved_slug ) ) {
407 set_transient( 'picu_collection_slug', $post_type_slug, 0 );
408 return;
409 }
410
411 // Compare current with saved collection slug
412 if ( $post_type_slug != $saved_slug ) {
413 $old_picu_slugs = get_transient( 'picu_collection_old_slugs' );
414 if ( ! is_array( $old_picu_slugs ) ) {
415 $old_picu_slugs = [];
416 }
417 $old_picu_slugs[] = $saved_slug;
418 set_transient( 'picu_collection_old_slugs', array_unique( $old_picu_slugs ) );
419 set_transient( 'picu_collection_slug', $post_type_slug, 0 );
420 flush_rewrite_rules( false );
421 }
422 }
423
424 add_action( 'wp', 'picu_check_collection_slug' );
425
426
427 /**
428 * Redirect when old collection base slug is used
429 *
430 * @since 1.5.0
431 */
432 function picu_redirect_from_old_slug() {
433
434 // Only run, if this is a 404
435 if ( ! is_404() ) {
436 return;
437 }
438
439 // Only run, if pretty permalinks are active
440 if ( ! get_option( 'permalink_structure' ) ) {
441 return;
442 }
443
444 // Check if the old slug transient is set
445 $old_picu_slugs = get_transient( 'picu_collection_old_slugs' );
446
447 if ( empty( $old_picu_slugs ) ) {
448 return;
449 }
450
451 // Get current url & path
452 global $wp;
453 $current_url = home_url( $wp->request );
454 $url = parse_url( $current_url );
455 $path = '';
456 if ( ! empty( $url['path'] ) ) {
457 $path = explode( '/', $url['path'] );
458 }
459
460 // Get current picu collection slug
461 $post_type_object = get_post_type_object( 'picu_collection' );
462 $current_slug = $post_type_object->rewrite['slug'];
463
464 if ( empty( $path[1] ) OR ! in_array( $path[1], $old_picu_slugs ) OR $path[1] == $current_slug ) {
465 return;
466 }
467
468 // Replace old slug with the new one
469 $new_url = $url['scheme'] . '://' . $url['host'] . '/' . $current_slug . '/' . $path[2] . '/';
470
471 // Get post type by url for the collection
472 $post_type = get_post_type( url_to_postid( $new_url ) );
473
474 // Redirect if the url is actually a collection
475 if ( 'picu_collection' == $post_type ) {
476 wp_redirect( trailingslashit( $new_url ), 301 );
477 }
478 }
479
480 add_filter( 'wp', 'picu_redirect_from_old_slug' );
481
482
483 /**
484 * Initialize proof file download
485 *
486 * @since 1.5.0
487 */
488 function picu_trigger_proof_file_download() {
489 if ( empty( $_REQUEST['picu-download'] ) || $_REQUEST['picu-download'] !== 'picu-proof-file' ) {
490 return;
491 }
492
493 if ( ! current_user_can( picu_capability() ) ) {
494 return;
495 }
496
497 $post_id = absint( $_REQUEST['post'] );
498
499 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'] ?? '', 'picu_download_proof_file_' . $post_id ) ) {
500 wp_die( __( 'Security check failed!', 'picu' ) );
501 }
502
503 if ( get_post_type( $post_id ) !== 'picu_collection' ) {
504 return;
505 }
506
507 picu_create_proof_file( $post_id );
508 exit;
509 }
510
511 add_action( 'init', 'picu_trigger_proof_file_download' );
512
513
514 /**
515 * Display Pro hint between image upload and sharing options
516 *
517 * @since 1.6.0
518 */
519 function picu_display_pro_hint() {
520 if ( ! picu_is_pro_active() ) {
521
522 $pro_hints = [
523 [
524 /* translators: Opening and closing link tags */
525 'text' => sprintf( __( '<strong>Get precise feedback</strong> &ndash; Learn %show to enable comments & markers%s on individual images.', 'picu' ), ' <a href="https://go.picu.io/get-feedback" target="_blank">', '</a>' ),
526 'icon' => '🖍️'
527 ],
528 [
529 /* translators: Opening and closing link tags */
530 'text' => sprintf( __( '<strong>Dealing with a lot of images?</strong> Learn %show to upload via FTP and import from your web server%s.', 'picu' ), '<a href="https://go.picu.io/lots-of-images" target="_blank">', '</a>' ),
531 'icon' => '😱'
532 ],
533 [
534 /* translators: Opening and closing link tags */
535 'text' => sprintf( __( '<strong>Protect your images</strong> &ndash; Learn %show to automatically add a watermark%s to your images.', 'picu' ), ' <a href="https://go.picu.io/protect-your-images" target="_blank">', '</a>' ),
536 'icon' => '🔒'
537 ],
538 [
539 /* translators: Opening and closing link tags */
540 'text' => sprintf( __( '<strong>Need more control?</strong> Learn %show to define the number of images%s your client needs to select.', 'picu' ), ' <a href="https://go.picu.io/more-control" target="_blank">', '</a>' ),
541 'icon' => '🎚️'
542 ],
543 [
544 /* translators: Opening and closing link tags */
545 'text' => sprintf( __( '<strong>Allow image downloads?</strong> Learn %show to enable image downloads%s for your collections.', 'picu' ), ' <a href="https://go.picu.io/allow-image-downloads" target="_blank">', '</a>' ),
546 'icon' => '⬇️'
547 ],
548 [
549 /* translators: Opening and closing link tags */
550 'text' => sprintf( __( '<strong>Done with post processing?</strong> Learn %show to deliver your final images%s to your clients.', 'picu' ), ' <a href="https://go.picu.io/done-post-processing" target="_blank">', '</a>' ),
551 'icon' => '�
552 '
553 ],
554 [
555 /* translators: Opening and closing link tags */
556 'text' => sprintf( __( '<strong>Sell images</strong> &ndash; Learn %show to accept payments via PayPal or Stripe%s right from your proofing galleries.', 'picu' ), ' <a href="https://go.picu.io/sell-images" target="_blank">', '</a>' ),
557 'icon' => '💸'
558 ],
559 ];
560
561 // Randomize which pro hint to display
562 $display_pro_hint = rand( 1, count( $pro_hints ) ) - 1;
563 ?>
564 <div class="picu-pro-hint">
565 <div class="picu-pro-hint-inner">
566 <div class="picu-pro-hint-content">
567 <span class="picu-pro-hint-icon"><?php echo $pro_hints[$display_pro_hint]['icon']; ?></span>
568 <?php echo $pro_hints[$display_pro_hint]['text']; ?>
569 </div>
570 <div class="picu-pro-hint__badge">Pro</div>
571
572 </div>
573 </div>
574 <?php
575 }
576 }
577
578
579 /**
580 * Determine if Pro is active and license is valid.
581 *
582 * @since 1.6.0
583 * @since 1.9.0 Use function from Pro plugin, also checking license status
584 * @since 2.0.1 No longer checking license status
585 *
586 * @return bool Whether Pro is active or not
587 */
588 function picu_is_pro_active() {
589 if ( is_plugin_active( 'picu-pro/picu-pro.php' ) ) {
590 return true;
591 }
592
593 return false;
594 }
595
596
597 /**
598 * Determine if there is a valid Pro license.
599 *
600 * @since 2.0.1
601 *
602 * @return bool Whether there is an active Pro license
603 */
604 function picu_is_pro_license_valid() {
605 $valid = false;
606
607 if ( function_exists( 'picu_pro_get_license_status' ) ) {
608 $license_status = picu_pro_get_license_status();
609 if ( $license_status == 'valid' ) {
610 $valid = true;
611 }
612 }
613
614 return $valid;
615 }
616
617
618 /**
619 * Never use "private" post status.
620 *
621 * @since 2.3.3
622 *
623 * @param array $data An array of slashed, sanitized, and processed post data
624 * @return array Filtered post data
625 */
626 function picu_remove_post_status_private( $data ) {
627 if ( $data['post_type'] == 'picu_collection' && $data['post_status'] == 'private' ) {
628 $data['post_status'] = 'approved';
629 }
630
631 return $data;
632 }
633
634 add_action( 'wp_insert_post_data', 'picu_remove_post_status_private', 10 );
635
636
637 /**
638 * Protect picu folders from browsing.
639 *
640 * This will disable browsing the folder even if directory listing
641 * is active by adding an index.php to existing collection folders.
642 *
643 * @since 2.5.4
644 */
645 function picu_protect_folders_from_browsing() {
646 // Get all upload folders
647 $picu_folders = array_filter( glob( PICU_UPLOAD_DIR . '/collections/*' ), 'is_dir' );
648
649 // Iterate through folders and add index.php
650 foreach( $picu_folders as $upload_path ) {
651 picu_add_folder_index( $upload_path );
652 }
653
654 // Allow Pro to hook into this action
655 do_action( 'picu_protect_folders', $picu_folders );
656 }
657
658
659 /**
660 * Put index.php file in a folder.
661 *
662 * @since 2.5.4
663 *
664 * @param string $path The folder path.
665 */
666 function picu_add_folder_index( $path ) {
667 // Set path to index file
668 $index_file = trailingslashit( $path ) . 'index.php';
669
670 // Check whether the file already exists
671 if ( ! file_exists( $index_file ) ) {
672 // Place index.php file
673 if ( is_writable( $path ) ) {
674 $index_content = "<?php\n// Silence is golden.";
675 file_put_contents( $index_file, $index_content );
676 }
677 else {
678 error_log( sprintf( __( 'Unable to protect folder: %s', 'picu' ), print_r( $path, true ) ) );
679 }
680 }
681 }
682
683
684 /**
685 * Schedule collection folder check.
686 *
687 * @since 2.5.4
688 */
689 if ( ! wp_next_scheduled( 'picu_collection_folders' ) ) {
690 wp_schedule_event( time(), 'daily', 'picu_collection_folders' );
691 }
692
693
694 /**
695 * Add action to check collection folders.
696 *
697 * @since 2.5.4
698 */
699 add_action( 'picu_collection_folders', 'picu_protect_folders_from_browsing' );
700