PluginProbe
Image Source Control Lite – Show Image Credits and Captions / 1.2.0.3
Image Source Control Lite – Show Image Credits and Captions v1.2.0.3
3.12.0 3.11.0 trunk 1.1 1.1.1 1.1.2 1.1.2.1 1.1.3 1.10 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.2 1.2.0.1 1.2.0.2 1.2.0.3 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.4.1 1.3.5 All 110 releases
image-source-control-isc / isc.php

isc.php in Image Source Control Lite – Show Image Credits and Captions 1.2.0.3, at isc.php

1,330 lines 56.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Image Source Control
4 Version: 1.2.0.3
5 Plugin URI: http://webgilde.com/en/image-source-control/
6 Description: The Image Source Control saves the source of an image, lists them and warns if it is missing.
7 Author: Thomas Maier
8 Author URI: http://www.webgilde.com/
9 License: GPL v3
10
11 Image Source Control Plugin for WordPress
12 Copyright (C) 2012, Thomas Maier - thomas.maier@webgilde.com
13
14 This program is free software: you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * Followed the following tutorials
28 * http://wpengineer.com/2076/add-custom-field-attachment-in-wordpress/
29 * http://bueltge.de/eigene-felder-dateiverwaltung-wordpress/1226/ (same like above, but in German)
30 *
31 *
32 */
33
34 //avoid direct calls to this file
35 if (!function_exists('add_action')) {
36 header('Status: 403 Forbidden');
37 header('HTTP/1.1 403 Forbidden');
38 exit();
39 }
40
41 define('ISCVERSION', '1.2.0.3');
42 define('ISCNAME', 'Image Source Control');
43 define('ISCTEXTDOMAIN', 'isc');
44 define('ISCDIR', basename(dirname(__FILE__)));
45 define('ISCPATH', plugin_dir_path(__FILE__));
46 define('WEBGILDE', 'http://webgilde.com/en/image-source-control');
47
48 load_plugin_textdomain(ISCTEXTDOMAIN, false, dirname(plugin_basename(__FILE__)) . '/languages/');
49
50 if (!class_exists('ISC_CLASS')) {
51
52 class ISC_CLASS
53 {
54 /**
55 * define default meta fields
56 */
57 protected $_fields = array(
58 'image_source' => array(
59 'id' => 'isc_image_source',
60 'default' => '',
61 ),
62 'image_source_own' => array(
63 'id' => 'isc_image_source_own',
64 'default' => '',
65 ),
66 'image_posts' => array(
67 'id' => 'isc_image_posts',
68 'default' => array()
69 )
70 );
71
72 /**
73 * allowed image file types/extensions
74 * @since 1.1
75 */
76 protected $_allowedExtensions = array(
77 'jpg', 'png', 'gif'
78 );
79
80 /**
81 * This array is used to count the maximum upgrade step count.
82 *
83 * IMPORTANT!
84 * Do not forget to add each new version AFTER EDITING THE ISCVERSION CONSTANT.
85 * @since 1.2
86 */
87 protected $_upgrade_step = array(
88 '1.2',
89 '1.2.0.1',
90 '1.2.0.2',
91 '1.2.0.3'
92 );
93
94 /**
95 * Thumbnail size in list of all images.
96 * @since 1.2
97 */
98 protected $_thumbnail_size = array('thumbnail', 'medium', 'large', 'custom');
99
100 /**
101 * options saved in the db
102 * @since 1.2
103 */
104 protected $_options = array();
105
106 /**
107 * Setup registers filterts and actions.
108 */
109 public function __construct()
110 {
111
112 // load all plugin options
113 $this->_options = get_option('isc_options');
114
115 // insert all function for the frontend here
116
117 add_shortcode('isc_list', array($this, 'list_post_attachments_with_sources_shortcode'));
118 add_shortcode('isc_list_all', array($this, 'list_all_post_attachments_sources_shortcode'));
119
120 // insert all backend functions below this check
121 if (!current_user_can('upload_files')) {
122 return false;
123 }
124
125 register_activation_hook(ISCPATH . '/isc.php', array($this, 'activation'));
126
127 add_filter('attachment_fields_to_edit', array($this, 'add_isc_fields'), 10, 2);
128 add_filter('attachment_fields_to_save', array($this, 'isc_fields_save'), 10, 2);
129
130 add_action('admin_menu', array($this, 'create_menu'));
131 add_action('admin_init', array($this, 'SAPI_init'));
132
133 add_action('admin_enqueue_scripts', array($this, 'add_admin_scripts'));
134
135 // ajax function; 'add_meta_fields' is the action defined in isc.js as the action to be called via ajax
136 add_action('wp_ajax_add_meta_fields', array($this, 'add_meta_values_to_attachments'));
137
138 // save image information in meta field when a post is saved
139 add_action('save_post', array($this, 'save_image_information_on_post_save'));
140 }
141
142 /**
143 * create the menu pages for isc
144 */
145 public function create_menu()
146 {
147 global $isc_missing;
148 global $isc_setting;
149
150 /**
151 * Check if the page is already created.
152 */
153 if (empty($isc_missing)) {
154 // these pages should be accessible by editors and higher
155 $isc_missing = add_submenu_page('upload.php', 'missing image sources by Image Source Control Plugin', __('Missing Sources', ISCTEXTDOMAIN), 'edit_others_posts', ISCPATH . '/templates/missing_sources.php', '');
156 $isc_setting = add_options_page(__('Image control - ISC plugin', ISCTEXTDOMAIN), __('Image Control', ISCTEXTDOMAIN), 'edit_others_posts', 'isc_settings_page', array($this, 'render_isc_settings_page'));
157 }
158 }
159
160 /**
161 * add scripts to admin pages
162 * @since 1.0
163 * @update 1.1.1
164 */
165 public function add_admin_scripts($hook)
166 {
167 global $isc_setting;
168 if ($hook != $isc_setting) {
169 return;
170 }
171 wp_enqueue_script('isc_script', plugins_url('/js/isc.js', __FILE__), false, ISCVERSION);
172 // this is to define ajaxurl to be able to use this in its own js script
173 // wp_localize_script( 'isc_script', 'IscAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
174 }
175
176 /**
177 * add custom field to attachment
178 * @param arr $form_fields
179 * @param object $post
180 * @return arr
181 * @since 1.0
182 * @updated 1.1
183 */
184 public function add_isc_fields($form_fields, $post)
185 {
186 // add input field for source
187 $form_fields['isc_image_source']['label'] = __('Image Source', ISCTEXTDOMAIN);
188 $form_fields['isc_image_source']['value'] = get_post_meta($post->ID, 'isc_image_source', true);
189 $form_fields['isc_image_source']['helps'] = __('Include the image source here.', ISCTEXTDOMAIN);
190
191 // add checkbox to mark as your own image
192 $form_fields['isc_image_source_own']['input'] = 'html';
193 $form_fields['isc_image_source_own']['label'] = '';
194 $form_fields['isc_image_source_own']['helps'] =
195 __('Check this box if this is your own image and doesn\'t need a source.', ISCTEXTDOMAIN);
196 $form_fields['isc_image_source_own']['html'] =
197 "<input type='checkbox' value='1' name='attachments[{$post->ID}][isc_image_source_own]' id='attachments[{$post->ID}][isc_image_source_own]' "
198 . checked(get_post_meta($post->ID, 'isc_image_source_own', true), 1, false )
199 . " style=\"width:14px\"/> "
200 . __('This is my image', ISCTEXTDOMAIN);
201
202 return $form_fields;
203 }
204
205 /**
206 * save image source to post_meta
207 * @param object $post
208 * @param $attachment
209 * @return object $post
210 */
211 public function isc_fields_save($post, $attachment)
212 {
213 if (isset($attachment['isc_image_source'])) {
214 update_post_meta($post['ID'], 'isc_image_source', $attachment['isc_image_source']);
215 }
216
217 update_post_meta($post['ID'], 'isc_image_source_own', $attachment['isc_image_source_own']);
218 return $post;
219 }
220
221 /**
222 * create image sources list for all images of this post
223 * @since 1.0
224 * @update 1.1
225 * @param int $post_id id of the current post/page
226 * @return echo output
227 */
228 public function list_post_attachments_with_sources($post_id = 0)
229 {
230 global $post;
231
232 if (empty($post_id)) {
233 if (!empty($post->ID)) {
234 $post_id = $post->ID;
235 } else {
236 return;
237 }
238 }
239
240 $attachments = get_post_meta($post_id, 'isc_post_images', true);
241 // if attachments is an empty string, search for images in it
242 if ($attachments == '') {
243 $this->save_image_information_on_load();
244 $this->update_image_posts_meta($post_id, $post->post_content);
245
246 $attachments = get_post_meta($post_id, 'isc_post_images', true);
247 }
248
249 $authorname = '';
250 if (!empty($post->post_author))
251 $authorname = get_the_author_meta('display_name', $post->post_author);
252
253 $return = '';
254 if (!empty($attachments)) {
255 $atts = array();
256 foreach ($attachments as $attachment_id => $attachment_array) {
257 $atts[$attachment_id]['title'] = get_the_title($attachment_id);
258 $own = get_post_meta($attachment_id, 'isc_image_source_own', true);
259 $source = get_post_meta($attachment_id, 'isc_image_source', true);
260
261 if ( $own == '' && $source == '' ) {
262 // remove if no information set
263 unset($atts[$attachment_id]);
264 continue;
265 } elseif ($own != '') {
266 if ($this->_options['use_authorname'] && !empty($authorname)) {
267 $atts[$attachment_id ]['source'] = $authorname;
268 } else {
269 $atts[$attachment_id ]['source'] = $this->_options['by_author_text'];
270 }
271 } else {
272 $atts[$attachment_id ]['source'] = $source;
273 }
274 }
275
276 $return = $this->_renderAttachments($atts);
277 }
278
279 return $return;
280 }
281
282 /**
283 * @param array $attachments
284 */
285 protected function _renderAttachments($attachments)
286 {
287 // don't display anything, if no image sources displayed
288 if ($attachments == array()) {
289 return ;
290 }
291 ob_start();
292
293 $headline = $this->_options['image_list_headline'];
294 printf('<p class="isc_image_list_title">%s</p>', $headline); ?>
295 <ul class="isc_image_list"><?php
296
297 foreach ($attachments as $atts_id => $atts_array) {
298 if (empty($atts_array['source'])) {
299 continue;
300 }
301 printf('<li>%1$s: %2$s</li>', $atts_array['title'], $atts_array['source']);
302 }
303 ?></ul><?php
304 return ob_get_clean();
305 }
306
307 /**
308 * shortcode function to list all image sources
309 * @param arr $atts
310 */
311 public function list_post_attachments_with_sources_shortcode($atts = array())
312 {
313 global $post;
314 extract(shortcode_atts(array('id' => 0), $atts));
315
316 // if $id not set, use the current ID from the post
317 if (empty($id)) {
318 $id = $post->ID;
319 }
320
321 if (empty($id)) {
322 return;
323 }
324 return $this->list_post_attachments_with_sources($id);
325 }
326
327 /**
328 * get all attachments without sources
329 * the downside of this function: is there is not even an empty metakey field, nothing is going to be retrieved
330 * @todo fix this in WP 3.5 with compare => 'NOT EXISTS'
331 */
332 public function get_attachments_without_sources()
333 {
334 $args = array(
335 'post_type' => 'attachment',
336 'numberposts' => -1,
337 'post_status' => null,
338 'post_parent' => null,
339 'meta_query' => array(
340 // image source is empty
341 array(
342 'key' => 'isc_image_source',
343 'value' => '',
344 'compare' => '=',
345 ),
346 // and image source is not set
347 array(
348 'key' => 'isc_image_source_own',
349 'value' => '1',
350 'compare' => '!=',
351 ),
352 )
353 );
354
355 $attachments = get_posts($args);
356 if (!empty($attachments)) {
357 return $attachments;
358 }
359 }
360
361 /**
362 * add meta values to all attachments
363 * @todo probably need to fix this when more fields are added along the way
364 * @todo use compare => 'NOT EXISTS' when WP 3.5 is up to retrieve only values where it is not set
365 * @todo this currently updates all empty fields; empty in this context is empty string, 0, false or not existing; add check if meta field already existed before
366 */
367 public function add_meta_values_to_attachments()
368 {
369 // retrieve all attachments
370 $args = array(
371 'post_type' => 'attachment',
372 'numberposts' => -1,
373 'post_status' => null,
374 'post_parent' => null,
375 );
376
377 $attachments = get_posts($args);
378 if (empty($attachments)) {
379 return;
380 }
381
382 $count = 0;
383 foreach ($attachments as $_attachment) {
384 $set = false;
385 setup_postdata($_attachment);
386 foreach ($this->_fields as $_field) {
387 $meta = get_post_meta($_attachment->ID, $_field['id'], true);
388 if (empty($meta)) {
389 update_post_meta($_attachment->ID, $_field['id'], $_field['default']);
390 $set = true;
391 }
392 }
393 if ($set) {
394 $count++;
395 }
396 }
397 }
398
399 /**
400 * show the loading image from wp-admin/images/loading.gif
401 * @param bool $display should this be displayed directly or hidden? via inline css
402 */
403 public function show_loading_image($display = true)
404 {
405 $img_path = admin_url("/images/loading.gif");
406 $file_path = ABSPATH . "wp-admin/images/loading.gif";
407 if (file_exists($file_path)) {
408 echo '<span id="isc_loading_img" style="display: none;"><img src="' . $img_path . '" width="16" height="16" alt="loading"/></span>';
409 }
410 }
411
412 /**
413 * this is an entry function to save image information to a post when it is saved
414 * @since 1.1
415 * @param type $post_id
416 */
417 public function save_image_information_on_post_save($post_id)
418 {
419 // return, if save_post is called more than one time
420 if (did_action('save_post') !== 1) {
421 return;
422 }
423
424 if (isset($_POST['post_type']) && 'attachment' == $_POST['post_type']) {
425 return;
426 }
427
428 // check if this is a revision and if so, use parent post id
429 if ($_id = wp_is_post_revision($post_id)) {
430 $post_id = $_id;
431 }
432
433 $_content = '';
434 if ( !empty( $_REQUEST['content']) ) $_content = stripslashes($_REQUEST['content']);
435
436 // Needs to be called before the 'isc_post_images' field is updated.
437 $this->update_image_posts_meta($post_id, $_content);
438
439 $this->save_image_information($post_id, $_content);
440 }
441
442 /**
443 * save image information for a post when it is viewed and the image source list is enabled
444 * (this is in case the plugin is new and the current post wasn't saved before)
445 *
446 * @since 1.1
447 */
448 public function save_image_information_on_load()
449 {
450 global $post;
451 if (empty($post->ID)) {
452 return;
453 }
454
455 $post_id = $post->ID;
456 $_content = $post->post_content;
457
458 $this->save_image_information($post_id, $_content);
459 }
460
461 /**
462 * retrieve images added to a post or page and save all information as a meta value
463 * @since 1.1
464 * @todo check for more post types that maybe should not be parsed here
465 */
466 public function save_image_information($post_id, $_content)
467 {
468 $_image_urls = $this->_filter_src_attributes($_content);
469 $_imgs = array();
470
471 foreach ($_image_urls as $_image_url) {
472 // get ID of images by url
473 $img_id = $this->get_image_by_url($_image_url);
474 $_imgs[$img_id] = array(
475 'src' => $_image_url
476 );
477 }
478
479 // add thumbnail information
480 $thumb_id = get_post_thumbnail_id($post_id);
481
482 /**
483 * if an image is used both inside the post and as post thumbnail, the thumbnail entry overrides the regular image.
484 */
485 if ( !empty( $thumb_id )) {
486 $_imgs[$thumb_id] = array(
487 'src' => wp_get_attachment_url($thumb_id),
488 'thumbnail' => true
489 );
490 }
491
492 if (empty($_imgs)) {
493 $_imgs = false;
494 }
495 update_post_meta($post_id, 'isc_post_images', $_imgs);
496 }
497
498 /**
499 * filter image src attribute from text
500 * @since 1.1
501 * @updated 1.1.3
502 * @return array with image src uris
503 */
504 public function _filter_src_attributes($content = '')
505 {
506 $srcs = array();
507 if (empty($content))
508 return $srcs;
509
510 // parse HTML with DOM
511 $dom = new DOMDocument;
512
513 libxml_use_internal_errors(true);
514 $dom->loadHTML($content);
515
516 // Prevents from sending E_WARNINGs notice (Outputs are forbidden during activation)
517 libxml_clear_errors();
518
519 foreach ($dom->getElementsByTagName('img') as $node) {
520 $srcs[] = $node->getAttribute('src');
521 }
522
523 return $srcs;
524 }
525
526 /**
527 * get image by url accessing the database directly
528 * @since 1.1
529 * @updated 1.1.3
530 * @param string $url url of the image
531 * @return id of the image
532 */
533 public function get_image_by_url($url = '')
534 {
535 if (empty($url)) {
536 return 0;
537 }
538 $types = implode('|', $this->_allowedExtensions);
539 // check for the format 'image-title-(e12452112-)300x200.jpg' and remove the image size and edit mark from it
540 $newurl = preg_replace("/(-e\d+){0,1}-(\d+)x(\d+)\.({$types})$/i", '.${4}', $url);
541 global $wpdb;
542 $query = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE guid = %s", $newurl);
543 $id = $wpdb->get_var($query);
544 return $id;
545 }
546
547 /**
548 * Update isc_image_posts meta field for all images found in a post with a given ID.
549 * @param $post_id ID of the target post
550 * @param $content content of the target post
551 */
552 public function update_image_posts_meta($post_id, $content)
553 {
554 $image_urls = $this->_filter_src_attributes($content);
555 $image_ids = array();
556 $added_images = array();
557 $removed_images = array();
558
559 $isc_post_images = get_post_meta($post_id, 'isc_post_images', true);
560
561 foreach ($image_urls as $url) {
562 $id = intval($this->get_image_by_url($url));
563 array_push($image_ids, $id);
564 if (is_array($isc_post_images) && !array_key_exists($id, $isc_post_images)) {
565 array_push($added_images, $id);
566 }
567 }
568 if (is_array($isc_post_images)) {
569 foreach ($isc_post_images as $old_id => $value) {
570 if (!in_array($old_id, $image_ids)) {
571 array_push($removed_images, $old_id);
572 } else {
573 if (!empty($old_id)) {
574 $meta = get_post_meta($old_id, 'isc_image_posts', true);
575 if (empty($meta)) {
576 update_post_meta($old_id, 'isc_image_posts', array($post_id));
577 } else {
578 // In case the isc_image_posts is not up to date
579 if (is_array($meta) && !in_array($post_id, $meta)) {
580 array_push($meta, $post_id);
581 update_post_meta($old_id, 'isc_image_posts', $meta);
582 }
583 }
584 }
585 }
586 }
587 }
588
589 foreach ($added_images as $id) {
590 $meta = get_post_meta($id, 'isc_image_posts', true);
591 if (!is_array($meta) || array() == $meta) {
592 update_post_meta($id, 'isc_image_posts', array($post_id));
593 } else {
594 array_push($meta, $post_id);
595 update_post_meta($id, 'isc_image_posts', $meta);
596 }
597 }
598
599 foreach ($removed_images as $id) {
600 $image_meta = get_post_meta($id, 'isc_image_posts', true);
601 if (is_array($image_meta)) {
602 $offset = array_search($post_id, $image_meta);
603 if (false !== $offset) {
604 array_splice($image_meta, $offset, 1);
605 update_post_meta($id, 'isc_image_posts', $image_meta);
606 }
607 }
608 }
609 }
610
611 /**
612 * create shortcode to list all image sources in the frontend
613 * @param array $atts
614 * @since 1.1.3
615 * @todo link to the post
616 */
617 public function list_all_post_attachments_sources_shortcode($atts = array())
618 {
619
620 /**
621 * @todo why not translate here with the code below?
622 * > Because the two if statements below will need to call gettext (again) for comparing values.
623 */
624 extract(shortcode_atts(array(
625 'per_page' => 99999,
626 'before_links' => '',
627 'after_links' => '',
628 'prev_text' => '&#171; Previous',
629 'next_text' => 'Next &#187;'
630 ),
631 $atts));
632
633 /**
634 * @todo why not include this into the array above?
635 */
636 if ('&#171; Previous' == $prev_text)
637 $prev_text = __('&#171; Previous', ISCTEXTDOMAIN);
638 if ('Next &#187;' == $next_text)
639 $next_text = __('Next &#187;', ISCTEXTDOMAIN);
640
641 // retrieve all attachments
642 $args = array(
643 'post_type' => 'attachment',
644 'numberposts' => -1,
645 'post_status' => null,
646 'post_parent' => null,
647 'meta_query' => array(
648 array(
649 'key' => 'isc_image_posts',
650 'value' => 'a:0:{}',
651 'compare' => '!='
652 )
653 )
654 /** @todo maybe add offset to not retrieve the first results when not on first page */
655 /** @todo maybe add limit to not retrieve more results than on the current page */
656 /** >No, we need to get total count of attachment with parents for $max_page in the pagination link. */
657 );
658
659 $attachments = get_posts($args);
660 if (empty($attachments)) {
661 return;
662 }
663
664 $options = $this->get_isc_options();
665
666 $connected_atts = array();
667
668 //Keeps only those ones who have parent
669
670 foreach ($attachments as $_attachment) {
671 $connected_atts[$_attachment->ID]['source'] = get_post_meta($_attachment->ID, 'isc_image_source', true);
672 $connected_atts[$_attachment->ID]['own'] = get_post_meta($_attachment->ID, 'isc_image_source_own', true);
673 $connected_atts[$_attachment->ID]['title'] = $_attachment->post_title;
674 $connected_atts[$_attachment->ID]['author_name'] = '';
675 if ('' != $connected_atts[$_attachment->ID]['own']) {
676 $parent = get_post($_attachment->post_parent);
677 $connected_atts[$_attachment->ID]['author_name'] = get_the_author_meta('display_name', $parent->post_author);
678 }
679
680 $metadata = get_post_meta($_attachment->ID, 'isc_image_posts', true);
681 $parents_data = '';
682
683 if (is_array($metadata) && array() != $metadata) {
684 $parents_data .= "<ul style='margin: 0;'>";
685 foreach($metadata as $data) {
686 $parents_data .= sprintf(__('<li><a href="%1$s" title="View %2$s">%3$s</a></li>', ISCTEXTDOMAIN),
687 esc_url(get_permalink($data)),
688 esc_attr(get_the_title($data)),
689 esc_html(get_the_title($data))
690 );
691 }
692 $parents_data .= "</ul>";
693 }
694
695 $connected_atts[$_attachment->ID]['posts'] = $parents_data;
696 }
697
698 $total = count($connected_atts);
699
700 if (0 == $total)
701 return;
702
703 $page = isset($_GET['isc-page']) ? intval($_GET['isc-page']) : 1;
704 $down_limit = 1; // First page
705
706 $up_limit = 1;
707
708 if ($per_page < $total) {
709 $rem = $total % $per_page; // The Remainder of $total/$per_page
710 $up_limit = ($total - $rem) / $per_page;
711 if (0 < $rem) {
712 $up_limit++; //If rem is positive, add the last page that contains less than $per_page attachment;
713 }
714 }
715
716 ob_start();
717 if ( 2 > $up_limit ) {
718 $this->display_all_attachment_list($connected_atts);
719 } else {
720 $starting_atts = $per_page * ($page - 1); // for page 2 and 3 $per_page start display on $connected_atts[3*(2-1) = 3]
721 $paged_atts = array_slice($connected_atts, $starting_atts, $per_page, true);
722 $this->display_all_attachment_list($paged_atts);
723 $this->pagination_links($up_limit, $before_links, $after_links, $prev_text, $next_text);
724 }
725 if (isset($options['webgilde']) && true == $options['webgilde']) {
726 ?>
727 <p class="isc-backlink"><?php printf(__('Image list created by <a href="%s" title="Image Source Control">Image Source Control Plugin</a>', ISCTEXTDOMAIN), WEBGILDE); ?></p>
728 <?php
729 }
730
731 $output = ob_get_clean();
732 return $output;
733 }
734
735
736 /**
737 * performs rendering of all attachments list
738 * @since 1.1.3
739 */
740 public function display_all_attachment_list($atts)
741 {
742 if (!is_array($atts) || $atts == array())
743 return;
744 $options = $this->get_isc_options();
745 ?>
746 <table>
747 <thead>
748 <?php if ($options['thumbnail_in_list']) : ?>
749 <th><?php _e('Thumbnail', ISCTEXTDOMAIN); ?></th>
750 <?php endif; ?>
751 <th><?php _e("Attachment's ID", ISCTEXTDOMAIN); ?></th>
752 <th><?php _e('Title', ISCTEXTDOMAIN); ?></th>
753 <th><?php _e('Attached to', ISCTEXTDOMAIN); ?></th>
754 <th><?php _e('Source', ISCTEXTDOMAIN); ?></th>
755 </thead>
756 <tbody>
757 <?php foreach ($atts as $id => $data) : ?>
758 <?php
759 /** @todo ment for later: this text was used above already; find a place to but it so it is defined only once and used where needed */
760 $source = __('Not available', ISCTEXTDOMAIN);
761 if ('' != $data['own']) {
762 /** @todo ment for later: this text was used above already; find a place to but it so it is defined only once and used where needed */
763 if ($this->_options['use_authorname']) {
764 $source = $data['author_name'];
765 } else {
766 $source = $this->_options['by_author_text'];
767 }
768 } else {
769 if (!empty($data['source']))
770 $source = $data['source'];
771 }
772 ?>
773 <tr>
774 <?php
775 $v_align = '';
776 if ($options['thumbnail_in_list']) :
777 $v_align = 'style="vertical-align: top;"';
778 ?>
779 <?php if ('custom' != $options['thumbnail_size']) : ?>
780 <td><?php echo wp_get_attachment_image($id, $options['thumbnail_size']); ?></td>
781 <?php else : ?>
782 <td><?php echo wp_get_attachment_image($id, array($options['thumbnail_width'], $options['thumbnail_height'])); ?></td>
783 <?php endif; ?>
784 <?php endif; ?>
785 <td <?php echo $v_align;?>><?php echo $id; ?></td>
786 <td <?php echo $v_align;?>><?php echo $data['title']; ?></td>
787 <td <?php echo $v_align;?>><?php echo $data['posts']; ?></td>
788 <td <?php echo $v_align;?>><?php echo esc_html($source); ?></td>
789 </tr>
790 <?php endforeach; ?>
791 </tbody>
792 </table>
793 <?php
794 }
795
796 /**
797 * Render pagination links, use $before_links and after_links to wrap pagination links inside an additional block
798 * @param int $max_page total page count
799 * @param string $before_links optional html to display before pagination links
800 * @param string $after_links optional html to display after pagination links
801 * @param string $prev_text text for the previous page link
802 * @param string $next_text text for the next page link
803 * @since 1.1.3
804 *
805 */
806 public function pagination_links($max_page, $before_links, $after_links, $prev_text, $next_text)
807 {
808 if ((!isset($max_page)) || (!isset($before_links)) || (!isset($after_links)) || (!isset($prev_text)) || (!isset($next_text)))
809 return;
810 if (!empty($before_links))
811 echo $before_links;
812 ?>
813 <div class="isc-paginated-links">
814 <?php
815 $page = isset($_GET['isc-page']) ? intval($_GET['isc-page']) : 1;
816 if ($max_page < $page) {
817 $page = $max_page;
818 }
819 if ($page < 1) {
820 $page = 1;
821 }
822 $min_page = 1;
823 $backward_distance = $page - $min_page;
824 $forward_distance = $max_page - $page;
825
826 $page_link = get_page_link();
827
828 /**
829 * Remove the query_string of the page_link (?page_id=xyz for the standard permalink structure),
830 * which is already captured in $_SERVER['QUERY_STRING'].
831 * @todo replace regex with other value (does WP store the url path without attributes somewhere?
832 * >get_page_link() returns the permalink but for the default WP permalink structure, the permalink looks like "http://domain.tld/?p=52", while $_GET
833 * still has a field named 'page_id' with the same value of 52.
834 */
835
836 $pos = strpos($page_link, '?');
837 if (false !== $pos) {
838 $page_link = substr($page_link, 0, $pos);
839 }
840
841 /**
842 * Unset the actual "$_GET['isc-page']" variable (if is set). Pagination variable will be appended to the new query string with a different value for each
843 * pagination link.
844 */
845
846 if (isset($_GET['isc-page'])) {
847 unset($_GET['isc-page']);
848 }
849
850 $query_string = http_build_query($_GET);
851
852 $isc_query_tag = '';
853 if (empty($query_string)) {
854 $isc_query_tag = '?isc-page=';
855 } else {
856 $query_string = '?' . $query_string;
857 $isc_query_tag = '&isc-page=';
858 }
859
860 if ($min_page != $page) {
861 ?>
862 <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page-1); ?>" class="prev page-numbers"><?php echo $prev_text; ?></a>
863 <?php
864 }
865
866 if (5 < $max_page) {
867
868 if (3 < $backward_distance) {
869 ?>
870 <a href="<?php echo $page_link . $query_string . $isc_query_tag; ?>1" class="page-numbers">1</a>
871 <span class="page-numbers dots">...</span>
872 <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page-2);?>" class="page-numbers"><?php echo $page-2; ?></a>
873 <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page-1);?>" class="page-numbers"><?php echo $page-1; ?></a>
874 <span class="page-numbers current"><?php echo $page; ?></span>
875 <?php
876 } else {
877 for ($i = 1; $i <= $page; $i++) {
878 if ($i == $page) {
879 ?>
880 <span class="page-numbers current"><?php echo $i; ?></span>
881 <?php
882 } else {
883 ?>
884 <a href="<?php echo $page_link . $query_string . $isc_query_tag . $i;?>" class="page-numbers"><?php echo $i; ?></a>
885 <?php
886 }
887 }
888 }
889
890 if (3 < $forward_distance) {
891 ?>
892 <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page+1);?>" class="page-numbers"><?php echo $page+1; ?></a>
893 <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page+2);?>" class="page-numbers"><?php echo $page+2; ?></a>
894 <span class="page-numbers dots">...</span>
895 <a href="<?php echo $page_link . $query_string . $isc_query_tag . $max_page;?>" class="page-numbers"><?php echo $max_page; ?></a>
896 <?php
897 } else {
898 for ($i = $page+1; $i <= $max_page; $i++) {
899 ?>
900 <a href="<?php echo $page_link . $query_string . $isc_query_tag . $i;?>" class="page-numbers"><?php echo $i; ?></a>
901 <?php
902 }
903 }
904 } else {
905 for ($i = 1; $i <= $max_page; $i++) {
906 if ($i == $page) {
907 ?>
908 <span class="page-numbers current"><?php echo $i; ?></span>
909 <?php
910 } else {
911 ?>
912 <a href="<?php echo $page_link . $query_string . $isc_query_tag . $i;?>" class="page-numbers"><?php echo $i; ?></a>
913 <?php
914 }
915 }
916 }
917 if ($page != $max_page) {
918 ?>
919 <a href="<?php echo $page_link . $query_string . $isc_query_tag . ($page+1);?>" class="next page-numbers"><?php echo $next_text; ?></a>
920 <?php
921 }
922 ?>
923 </div>
924 <?php
925 echo $after_links;
926 }
927
928 /**
929 * The activation function
930 */
931 public function activation()
932 {
933 if (!is_array(get_option('isc_options'))) {
934 update_option( 'isc_options', $this->default_options() );
935 }
936 $options = $this->get_isc_options();
937 if (!$options['installed']) {
938 /**
939 * Here, all jobs to perform during first installation, especially options and custom fields.
940 * Important: No add_action('something', 'somefunction').
941 */
942
943 // adds meta fields for attachments
944 $this->add_meta_values_to_attachments();
945
946 // set all isc_image_posts meta fields.
947 $this->init_image_posts_metafield();
948
949 $options['installed'] = true;
950 update_option('isc_options', $options);
951 }
952 }
953
954 /**
955 * Returns default options
956 */
957 public function default_options()
958 {
959 $default['image_list_headline'] = __('image sources', ISCTEXTDOMAIN);
960 $default['use_authorname'] = true;
961 $default['by_author_text'] = __('Owned by the author', ISCTEXTDOMAIN);
962 $default['installed'] = false;
963 $default['version'] = ISCVERSION;
964 $default['webgilde'] = false;
965 $default['thumbnail_in_list'] = false;
966 $default['thumbnail_size'] = 'thumbnail';
967 $default['thumbnail_width'] = 150;
968 $default['thumbnail_height'] = 150;
969 return $default;
970 }
971
972 /**
973 * Settings API initialization
974 */
975 public function SAPI_init()
976 {
977 $this->upgrade_management();
978 register_setting('isc_options_group', 'isc_options', array($this, 'settings_validation'));
979 add_settings_section('isc_settings_section', '', '__return_false', 'isc_settings_page');
980 add_settings_field('image_list_headline', __('Image list headline', ISCTEXTDOMAIN), array($this, 'renderfield_list_headline'), 'isc_settings_page', 'isc_settings_section');
981 add_settings_field('use_authorname', __('Use authors names', ISCTEXTDOMAIN), array($this, 'renderfield_use_authorname'), 'isc_settings_page', 'isc_settings_section');
982 add_settings_field('by_author_text', __('Custom text for owned images', ISCTEXTDOMAIN), array($this, 'renderfield_byauthor_text'), 'isc_settings_page', 'isc_settings_section');
983 add_settings_field('webgilde_backlink', __("Link to webgilde's website", ISCTEXTDOMAIN), array($this, 'renderfield_webgile'), 'isc_settings_page', 'isc_settings_section');
984 add_settings_field('use_thumbnail', __("Use thumbnails in images list", ISCTEXTDOMAIN), array($this, 'renderfield_use_thumbnail'), 'isc_settings_page', 'isc_settings_section');
985 add_settings_field('thumbnail_width', __("Thumbnails max-width", ISCTEXTDOMAIN), array($this, 'renderfield_thumbnail_width'), 'isc_settings_page', 'isc_settings_section');
986 add_settings_field('thumbnail_height', __("Thumbnails max-height", ISCTEXTDOMAIN), array($this, 'renderfield_thumbnail_height'), 'isc_settings_page', 'isc_settings_section');
987 }
988
989 /**
990 * manage data structure upgrading of outdated versions
991 */
992 public function upgrade_management() {
993 /*
994 * Since the activation hook is not executed on plugin upgrade, this function checks options in database
995 * during the admin_init hook to handle plugin's upgrade.
996 */
997 $options = get_option('isc_options');
998 if (!is_array($options)) {
999 $options = array();
1000 }
1001
1002 $max_step = count($this->_upgrade_step);
1003 $step_count = 0;
1004
1005 if (!isset($options['version'])) {
1006 /**
1007 * If the current version is older than 1.2, upgrade data structure to 1.2.0.1 (same data structure for 1.2 and 1.2.0.1)
1008 * This is because some specific upgrade steps can be necessary (possibly) in future versions. So it may be usefull to perform the
1009 * progressive upgrade process (starting to 1.2.0.1) in case an user upgrades his plugin from a very old version.
1010 */
1011
1012 /**
1013 * These are default values of $isc_options's fields in version 1.2.0.1
1014 */
1015 $particular_options_version['image_list_headline'] = __('image sources', ISCTEXTDOMAIN);
1016 $particular_options_version['use_authorname'] = true;
1017 $particular_options_version['by_author_text'] = __('Owned by the author', ISCTEXTDOMAIN);
1018 $particular_options_version['version'] = '1.2.0.1';
1019 $particular_options_version['webgilde'] = false;
1020 $particular_options_version['thumbnail_in_list'] = false;
1021 $particular_options_version['thumbnail_size'] = 'thumbnail';
1022 $particular_options_version['thumbnail_width'] = 150;
1023 $particular_options_version['thumbnail_height'] = 150;
1024
1025 $options = $options + $particular_options_version;
1026
1027 // No isc_image_posts for version prior to 1.2
1028 $this->init_image_posts_metafield();
1029 $options['installed'] = true;
1030
1031 update_option('isc_options', $options);
1032 /**
1033 * At this point isc_options['version'] has the value '1.2.0.1'
1034 */
1035 $this->upgrade_management();
1036
1037 } elseif (ISCVERSION != $options['version']) {
1038
1039 while (ISCVERSION != $options['version'] && $step_count < $max_step) {
1040
1041 switch ($options['version']) {
1042
1043 /**
1044 * IMPORTANT!
1045 *
1046 * AFTER EDITING THE ISCVERSION CONSTANT, add one case with the following structure, otherwise ... infinite loop!
1047 *
1048 * case 'PREVIOUS VERSION' :
1049 * $options['version'] = 'NEW VERSION';
1050 * $step_count++;
1051 * break;
1052 *
1053 * BEFORE RELEASING NEW VERSION, the case should looks like the following,
1054 *
1055 * case 'PREVIOUS VERSION' :
1056 * (1) append default options to current options once and remove all "$options = $options + $this->default_options()" outside of upgrade_management().
1057 * (2) execute any other particular function related to upgrading to the new version like init_image_posts_metafield().
1058 * $options['version'] = 'NEW VERSION';
1059 * $step_count++;
1060 * break;
1061 *
1062 * @todo add a 'one_shot_function_launcher' utility function and the corresponding data in options to execute once functions mentionned in (2) in dev-mode.
1063 */
1064
1065 case '1.2' : // 1.2 to 1.2.0.1
1066 /**
1067 * No data structure modifications. Data structures are identical from 1.2 to 1.2.0.3
1068 */
1069 $options['version'] = '1.2.0.1';
1070 $step_count++;
1071 break;
1072
1073 case '1.2.0.1' : // 1.2.0.1 to 1.2.0.2
1074 $options['version'] = '1.2.0.2';
1075 $step_count++;
1076 break;
1077
1078 case '1.2.0.2' : // 1.2.0.2 to 1.2.0.3
1079 $options['version'] = '1.2.0.3';
1080
1081 /**
1082 * Adds missings indexes in isc_options. This addition should be performed on the last step.
1083 * Deletion of particular index and operations on custom fields can occur in any step.
1084 */
1085 $options = $options + $this->default_options();
1086
1087 $step_count++;
1088 break;
1089
1090 default :
1091 /**
1092 * In production mode, the program should not meet the default case if $this->_upgrade_step is up to date (all released version are in the array).
1093 * So, if for any reason (dev-mode) , this case happens, reset $step_count (to avoid saving changes in isc_options) and exit both from SWITCH and from WHILE.
1094 * We will have a permanent inequality between $isc_options['version'] in options and ISCVERSION in the file.
1095 */
1096 $step_count = 0;
1097 break 2;
1098
1099 }
1100
1101 }
1102 /**
1103 * If the program has entered the WHILE loop ($step_count has been incremented, i.e. one or more upgrade step executed), then save options in database.
1104 */
1105 if (0 != $step_count) {
1106 update_option('isc_options', $options);
1107 }
1108 }
1109 }
1110
1111 /**
1112 * Image_control's page callback
1113 */
1114 public function render_isc_settings_page()
1115 {
1116 ?>
1117 <div id="icon-options-general" class="icon32"><br></div>
1118 <h2><?php _e('Images control settings', ISCTEXTDOMAIN); ?></h2>
1119 <form id="image-control-form" method="post" action="options.php">
1120 <?php
1121 settings_fields( 'isc_options_group' );
1122 do_settings_sections( 'isc_settings_page' );
1123 submit_button();
1124 ?>
1125 </form>
1126 <?php
1127 }
1128
1129 /**
1130 * image_list field callbacks
1131 */
1132 public function renderfield_list_headline()
1133 {
1134 $options = $this->get_isc_options();
1135 $description = __('The headline of the image list added via shortcode or function in your theme.', ISCTEXTDOMAIN);
1136 ?>
1137 <div id="image-list-headline-block">
1138 <label for="list-head"><?php __('Image list headline', ISCTEXTDOMAIN); ?></label>
1139 <input type="text" name="isc_options[image_list_headline_field]" id="list-head" value="<?php echo $options['image_list_headline'] ?>" class="regular-text" />
1140 <p><em><?php echo $description; ?></em></p>
1141 </div>
1142 <?php
1143 }
1144
1145 public function renderfield_use_authorname()
1146 {
1147 $options = $this->get_isc_options();
1148 $description = __("Display the author's public name as source when the image is owned by the author. Uncheck to use a custom text instead.", ISCTEXTDOMAIN);
1149 ?>
1150 <div id="use-authorname-block">
1151 <label for="use_authorname"><?php _e('Use author name') ?></label>
1152 <input type="checkbox" name="isc_options[use_authorname_ckbox]" id="use_authorname" <?php checked($options['use_authorname']); ?> />
1153 <p><em><?php echo $description; ?></em></p>
1154 </div>
1155 <?php
1156 }
1157
1158 public function renderfield_byauthor_text()
1159 {
1160 $options = $this->get_isc_options();
1161 $description = __("Enter the custom text to display if you do not want to use the author's public name.", ISCTEXTDOMAIN);
1162 ?>
1163 <div id="by-author-text">
1164 <input type="text" id="byauthor" name="isc_options[by_author_text_field]" value="<?php echo $options['by_author_text']; ?>" <?php disabled($options['use_authorname']); ?> class="regular-text" />
1165 <p><em><?php echo $description; ?></em></p>
1166 </div>
1167 <?php
1168 }
1169
1170 public function renderfield_webgile()
1171 {
1172 $options = $this->get_isc_options();
1173 /**
1174 * Avoid warning notices because of the absence of webgilde field in isc_options throughout development steps.
1175 * This 'if' block can be removed for the next release.
1176 */
1177 $description = sprintf(__('Display a link to <a href="%s">Image Source Control plugin&#39;s website</a> below the list of all images in the blog?', ISCTEXTDOMAIN), WEBGILDE);
1178 ?>
1179 <div id="webgilde-block">
1180 <input type="checkbox" id="webgilde-link" name="isc_options[webgilde_field]" <?php checked($options['webgilde']); ?> />
1181 <p><em><?php echo $description; ?></em></p>
1182 </div>
1183 <?php
1184 }
1185
1186 public function renderfield_use_thumbnail()
1187 {
1188 $options = $this->get_isc_options();
1189 $description = __('Display thumbnails on the list of all images in the blog.' ,ISCTEXTDOMAIN);
1190 ?>
1191 <div id="use-thumbnail-block">
1192 <input type="checkbox" id="use-thumbnail" name="isc_options[use_thumbnail]" <?php checked($options['thumbnail_in_list']); ?> />
1193 <select id="thumbnail-size-select" name="isc_options[size_select]" <?php disabled(!$options['thumbnail_in_list']) ?>>
1194 <?php foreach ($this->_thumbnail_size as $size) : ?>
1195 <option value="<?php echo $size; ?>" <?php selected($size, $options['thumbnail_size']);?>><?php echo $size; ?></option>
1196 <?php endforeach; ?>
1197 </select>
1198 <p><em><?php echo $description; ?></em></p>
1199 </div>
1200 <?php
1201 }
1202
1203 public function renderfield_thumbnail_width()
1204 {
1205 $options = $this->get_isc_options();
1206 $description = __('Custom value of the maximum allowed width for thumbnail.' ,ISCTEXTDOMAIN);
1207 ?>
1208 <div id="thumbnail-custom-width">
1209 <input type="text" id="custom-width" name="isc_options[thumbnail_width]" class="small-text" value="<?php echo $options['thumbnail_width'] ?>" /> px
1210 <p><em><?php echo $description; ?></em></p>
1211 </div>
1212 <?php
1213 }
1214
1215 public function renderfield_thumbnail_height()
1216 {
1217 $options = $this->get_isc_options();
1218 $description = __('Custom value of the maximum allowed height for thumbnail.' ,ISCTEXTDOMAIN);
1219 ?>
1220 <div id="thumbnail-custom-height">
1221 <input type="text" id="custom-height" name="isc_options[thumbnail_height]" class="small-text" value="<?php echo $options['thumbnail_height'] ?>"/> px
1222 <p><em><?php echo $description; ?></em></p>
1223 </div>
1224 <?php
1225 }
1226
1227 /**
1228 * Returns isc_options if it exists, returns the default options otherwise.
1229 */
1230 public function get_isc_options() {
1231 return get_option('isc_options', $this->default_options());
1232 }
1233
1234 /*
1235 * Input validation function.
1236 * @param array $input values from the admin panel
1237 */
1238 public function settings_validation($input)
1239 {
1240 $output = $this->get_isc_options();
1241 $output['image_list_headline'] = esc_html($input['image_list_headline_field']);
1242 if (isset($input['use_authorname_ckbox'])) {
1243 // Don't worry about the custom text if the author name is selected.
1244 $output['use_authorname'] = true;
1245 } else {
1246 $output['use_authorname'] = false;
1247 $output['by_author_text'] = esc_html($input['by_author_text_field']);
1248 }
1249 if (isset($input['webgilde_field'])) {
1250 $output['webgilde'] = true;
1251 } else {
1252 $output['webgilde'] = false;
1253 }
1254 if (isset($input['use_thumbnail'])) {
1255 $output['thumbnail_in_list'] = true;
1256 if (in_array($input['size_select'], $this->_thumbnail_size)) {
1257 $output['thumbnail_size'] = $input['size_select'];
1258 }
1259 if ('custom' == $input['size_select']) {
1260 if (is_numeric($input['thumbnail_width'])) {
1261 // Ensures that the value stored in database in a positive integer.
1262 $output['thumbnail_width'] = abs(intval(round($input['thumbnail_width'])));
1263 }
1264 if (is_numeric($input['thumbnail_height'])) {
1265 $output['thumbnail_height'] = abs(intval(round($input['thumbnail_height'])));
1266 }
1267 }
1268 } else {
1269 $output['thumbnail_in_list'] = false;
1270 }
1271 return $output;
1272 }
1273
1274 /**
1275 * Adds isc_image_posts on all attachments. Launched during first installation.
1276 */
1277 public function init_image_posts_metafield()
1278 {
1279 $args = array(
1280 'post_type' => 'any',
1281 'numberposts' => -1,
1282 'post_status' => null,
1283 'post_parent' => null,
1284 );
1285 $posts = get_posts($args);
1286 foreach ($posts as $post) {
1287 setup_postdata($post);
1288 $image_urls = $this->_filter_src_attributes($post->post_content);
1289 $image_ids = array();
1290 foreach ($image_urls as $url) {
1291 $image_id = intval($this->get_image_by_url($url));
1292 array_push($image_ids,$image_id);
1293 }
1294 foreach ($image_ids as $id) {
1295 $meta = get_post_meta($id, 'isc_image_posts', true);
1296 if (empty($meta)) {
1297 update_post_meta($id, 'isc_image_posts', array($post->ID));
1298 } else {
1299 if (!in_array($post->ID, $meta)) {
1300 array_push($meta, $post->ID);
1301 update_post_meta($id, 'isc_image_posts', $meta);
1302 }
1303 }
1304 }
1305 }
1306 }
1307
1308 }// end of class
1309
1310 $inc_path = ABSPATH . 'wp-includes/';
1311 /**
1312 * "pluggable.php" is not defined at this point. Not sure about the reason.
1313 */
1314 require_once($inc_path . 'pluggable.php');
1315
1316 /**
1317 * Need an instance of ISC_CLASS when the register_activation_hook is called (earlier than the "plugins_loaded" hook).
1318 */
1319 global $my_isc;
1320 $my_isc = new ISC_CLASS();
1321
1322 /**
1323 * the next functions are just to have an easier access from outside the class
1324 */
1325 function isc_list($post_id = 0) {
1326 $isc = new ISC_CLASS();
1327 echo $isc->list_post_attachments_with_sources($post_id);
1328 }
1329 }
1330