PluginProbe
picu – Online Photo Proofing Gallery / 3.6.0
picu – Online Photo Proofing Gallery v3.6.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.6.0, at picu.php

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