/products/photocrati_nextgen/modules/nextgen_data/package.module.nextgen_data.php

  1. <?php 
  2. /** 
  3. * Modifies a custom post datamapper to use the WordPress built-in 'attachment' 
  4. * custom post type, as used by the Media Library 
  5. * 
  6. * @todo Not used yet 
  7. */ 
  8. class A_Attachment_DataMapper extends Mixin 
  9. function initialize() 
  10. $this->object->_object_name = 'attachment'; 
  11. /** 
  12. * Saves the entity using the wp_insert_attachment function 
  13. * instead of the wp_insert_post 
  14. * @param stdObject $entity 
  15. */ 
  16. function _save_entity($entity) 
  17. $post = $this->object->_convert_entity_to_post($entity); 
  18. $filename = property_exists($entity, 'filename') ? $entity->filename : FALSE; 
  19. $primary_key = $this->object->get_primary_key_column(); 
  20. if ($post_id = $attachment_id = wp_insert_attachment($post, $filename)) { 
  21. $new_entity = $this->object->find($post_id); 
  22. foreach ($new_entity as $key => $value) { 
  23. $entity->{$key} = $value; 
  24. // Merge meta data with WordPress Attachment Meta Data 
  25. if (property_exists($entity, 'meta_data')) { 
  26. $meta_data = wp_get_attachment_metadata($attachment_id); 
  27. if (isset($meta_data['image_meta'])) { 
  28. $entity->meta_data = array_merge_recursive($meta_data['image_meta'], $entity->meta_data); 
  29. wp_update_attachment_metadata($attachment_id, $entity->meta_data); 
  30. // Save properties are post meta as well 
  31. $this->object->_flush_and_update_postmeta($attachment_id, $entity instanceof stdClass ? $entity : $entity->get_entity(), array('_wp_attached_file', '_wp_attachment_metadata', '_mapper')); 
  32. $entity->id_field = $primary_key; 
  33. return $attachment_id; 
  34. function select($fields = '*') 
  35. $ret = $this->call_parent('select', $fields); 
  36. $this->object->_query_args['datamapper_attachment'] = true; 
  37. return $ret; 
  38. /** 
  39. * Class A_NextGen_Data_Factory 
  40. * @mixin C_Component_Factory 
  41. * @adapts I_Component_Factory 
  42. */ 
  43. class A_NextGen_Data_Factory extends Mixin 
  44. function gallery($properties = array(), $mapper = FALSE, $context = FALSE) 
  45. return new C_Gallery($properties, $mapper, $context); 
  46. function gallery_image($properties = array(), $mapper = FALSE, $context = FALSE) 
  47. return new C_Image($properties, $mapper, $context); 
  48. function image($properties = array(), $mapper = FALSE, $context = FALSE) 
  49. return new C_Image($properties, $mapper, $context); 
  50. function album($properties = array(), $mapper = FALSE, $context = FALSE) 
  51. return new C_Album($properties, $mapper, $context); 
  52. function ngglegacy_gallery_storage($context = FALSE) 
  53. return new C_NggLegacy_GalleryStorage_Driver($context); 
  54. function wordpress_gallery_storage($context = FALSE) 
  55. return new C_WordPress_GalleryStorage_Driver($context); 
  56. function gallery_storage($context = FALSE) 
  57. return new C_Gallery_Storage($context); 
  58. function extra_fields($properties = array(), $mapper = FALSE, $context = FALSE) 
  59. return new C_Datamapper_Model($mapper, $properties, $context); 
  60. function gallerystorage($context = FALSE) 
  61. return $this->object->gallery_storage($context); 
  62. /** 
  63. * Class C_Album 
  64. * @mixin Mixin_NextGen_Album_Instance_Methods 
  65. * @implements I_Album 
  66. */ 
  67. class C_Album extends C_DataMapper_Model 
  68. var $_mapper_interface = 'I_Album_Mapper'; 
  69. function define($properties = array(), $mapper = FALSE, $context = FALSE) 
  70. parent::define($mapper, $properties, $context); 
  71. $this->add_mixin('Mixin_NextGen_Album_Instance_Methods'); 
  72. $this->implement('I_Album'); 
  73. /** 
  74. * Instantiates an Album object 
  75. * @param bool|\C_DataMapper|\FALSE $mapper 
  76. * @param array $properties 
  77. */ 
  78. function initialize($properties = array(), $mapper = FALSE, $context = FALSE) 
  79. // Get the mapper is not specified 
  80. if (!$mapper) { 
  81. $mapper = $this->get_registry()->get_utility($this->_mapper_interface); 
  82. // Initialize 
  83. parent::initialize($mapper, $properties); 
  84. /** 
  85. * Provides instance methods for the album 
  86. */ 
  87. class Mixin_NextGen_Album_Instance_Methods extends Mixin 
  88. function validation() 
  89. $this->validates_presence_of('name'); 
  90. $this->validates_numericality_of('previewpic'); 
  91. return $this->object->is_valid(); 
  92. /** 
  93. * Gets all galleries associated with the album 
  94. */ 
  95. function get_galleries($models = FALSE) 
  96. $retval = array(); 
  97. $mapper = C_Gallery_Mapper::get_instance(); 
  98. $gallery_key = $mapper->get_primary_key_column(); 
  99. $retval = $mapper->find_all(array("{$gallery_key} IN %s", $this->object->sortorder), $models); 
  100. return $retval; 
  101. /** 
  102. * Class C_Album_Mapper 
  103. * @mixin Mixin_NextGen_Table_Extras 
  104. * @mixin Mixin_Album_Mapper 
  105. * @implements I_Album_Mapper 
  106. */ 
  107. class C_Album_Mapper extends C_CustomTable_DataMapper_Driver 
  108. static $_instance = NULL; 
  109. function initialize($object_name = FALSE) 
  110. parent::initialize('ngg_album'); 
  111. function define($context = FALSE, $not_used = FALSE) 
  112. // Define the context 
  113. if (!is_array($context)) { 
  114. $context = array($context); 
  115. array_push($context, 'album'); 
  116. $this->_primary_key_column = 'id'; 
  117. // Define the mapper 
  118. parent::define('ngg_album', $context); 
  119. $this->add_mixin('Mixin_NextGen_Table_Extras'); 
  120. $this->add_mixin('Mixin_Album_Mapper'); 
  121. $this->implement('I_Album_Mapper'); 
  122. $this->set_model_factory_method('album'); 
  123. // Define the columns 
  124. $this->define_column('id', 'BIGINT', 0); 
  125. $this->define_column('name', 'VARCHAR(255)'); 
  126. $this->define_column('slug', 'VARCHAR(255'); 
  127. $this->define_column('previewpic', 'BIGINT', 0); 
  128. $this->define_column('albumdesc', 'TEXT'); 
  129. $this->define_column('sortorder', 'TEXT'); 
  130. $this->define_column('pageid', 'BIGINT', 0); 
  131. $this->define_column('extras_post_id', 'BIGINT', 0); 
  132. // Mark the columns which should be unserialized 
  133. $this->add_serialized_column('sortorder'); 
  134. /** 
  135. * Returns an instance of the album datamapper 
  136. * @param bool|mixed $context 
  137. * @return C_Album_Mapper 
  138. */ 
  139. static function get_instance($context = FALSE) 
  140. if (is_null(self::$_instance)) { 
  141. $klass = get_class(); 
  142. self::$_instance = new $klass($context); 
  143. return self::$_instance; 
  144. /** 
  145. * Provides album-specific methods for the datamapper 
  146. */ 
  147. class Mixin_Album_Mapper extends Mixin 
  148. /** 
  149. * Gets the post title when the Custom Post driver is used 
  150. * @param C_DataMapper_Model|C_Album|stdClass $entity 
  151. * @return string 
  152. */ 
  153. function get_post_title($entity) 
  154. return $entity->name; 
  155. function _save_entity($entity) 
  156. $retval = $this->call_parent('_save_entity', $entity); 
  157. if ($retval) { 
  158. do_action('ngg_album_updated', $entity); 
  159. C_Photocrati_Transient_Manager::flush('displayed_gallery_rendering'); 
  160. return $retval; 
  161. /** 
  162. * Sets the defaults for an album 
  163. * @param C_DataMapper_Model|C_Album|stdClass $entity 
  164. */ 
  165. function set_defaults($entity) 
  166. $this->object->_set_default_value($entity, 'name', ''); 
  167. $this->object->_set_default_value($entity, 'albumdesc', ''); 
  168. $this->object->_set_default_value($entity, 'sortorder', array()); 
  169. $this->object->_set_default_value($entity, 'previewpic', 0); 
  170. $this->object->_set_default_value($entity, 'exclude', 0); 
  171. if (isset($entity->name) && !isset($entity->slug)) { 
  172. $entity->slug = nggdb::get_unique_slug(sanitize_title($entity->name), 'album'); 
  173. class Mixin_NextGen_Gallery_Validation 
  174. /** 
  175. * Validates whether the gallery can be saved 
  176. */ 
  177. function validation() 
  178. // If a title is present, we can auto-populate some other properties 
  179. if ($this->object->title) { 
  180. // Strip html 
  181. $this->object->title = M_NextGen_Data::strip_html($this->object->title, TRUE); 
  182. $sanitized_title = str_replace(' ', '-', $this->object->title); 
  183. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 
  184. $sanitized_title = remove_accents($sanitized_title); 
  185. // If no name is present, use the title to generate one 
  186. if (!$this->object->name) { 
  187. $this->object->name = apply_filters('ngg_gallery_name', sanitize_file_name($sanitized_title)); 
  188. // If no slug is set, use the title to generate one 
  189. if (!$this->object->slug) { 
  190. $this->object->slug = preg_replace('|[^a-z0-9 \\-~+_.#=!&;, /:%@$\\|*\'()\\x80-\\xff]|i', '', $sanitized_title); 
  191. $this->object->slug = nggdb::get_unique_slug($this->object->slug, 'gallery'); 
  192. // Set what will be the path to the gallery 
  193. if (!$this->object->path) { 
  194. $storage = C_Gallery_Storage::get_instance(); 
  195. $this->object->path = $storage->get_upload_relpath($this->object); 
  196. unset($storage); 
  197. } else { 
  198. $this->object->path = M_NextGen_Data::strip_html($this->object->path); 
  199. $this->object->path = str_replace(array('"', "''", ">", "<"), array('', '', '', ''), $this->object->path); 
  200. $this->object->validates_presence_of('title'); 
  201. $this->object->validates_presence_of('name'); 
  202. $this->object->validates_uniqueness_of('slug'); 
  203. $this->object->validates_numericality_of('author'); 
  204. return $this->object->is_valid(); 
  205. /** 
  206. * Creates a model representing a NextGEN Gallery object 
  207. * @mixin Mixin_NextGen_Gallery_Validation 
  208. * @implements I_Gallery 
  209. */ 
  210. class C_Gallery extends C_DataMapper_Model 
  211. var $_mapper_interface = 'I_Gallery_Mapper'; 
  212. /** 
  213. * Defines the interfaces and methods (through extensions and hooks) 
  214. * that this class provides 
  215. */ 
  216. function define($properties = array(), $mapper = FALSE, $context = FALSE) 
  217. parent::define($mapper, $properties, $context); 
  218. $this->add_mixin('Mixin_NextGen_Gallery_Validation'); 
  219. $this->implement('I_Gallery'); 
  220. /** 
  221. * Instantiates a new model 
  222. * @param array|stdClass $properties 
  223. * @param C_DataMapper $mapper 
  224. * @param string $context 
  225. */ 
  226. function initialize($properties = array(), $mapper = FALSE, $context = FALSE) 
  227. // Get the mapper is not specified 
  228. if (!$mapper) { 
  229. $mapper = $this->get_registry()->get_utility($this->_mapper_interface); 
  230. // Initialize 
  231. parent::initialize($mapper, $properties); 
  232. function get_images() 
  233. $mapper = C_Image_Mapper::get_instance(); 
  234. return $mapper->select()->where(array('galleryid = %d', $this->gid))->order_by('sortorder')->run_query(); 
  235. /** 
  236. * Provides a datamapper for galleries 
  237. * @mixin Mixin_NextGen_Table_Extras 
  238. * @mixin Mixin_Gallery_Mapper 
  239. * @implements I_Gallery_Mapper 
  240. */ 
  241. class C_Gallery_Mapper extends C_CustomTable_DataMapper_Driver 
  242. public static $_instance = NULL; 
  243. /** 
  244. * Define the object 
  245. * @param string $context 
  246. */ 
  247. function define($context = FALSE, $not_used = FALSE) 
  248. // Add 'gallery' context 
  249. if (!is_array($context)) { 
  250. $context = array($context); 
  251. array_push($context, 'gallery'); 
  252. $this->_primary_key_column = 'gid'; 
  253. // Continue defining the object 
  254. parent::define('ngg_gallery', $context); 
  255. $this->set_model_factory_method('gallery'); 
  256. $this->add_mixin('Mixin_NextGen_Table_Extras'); 
  257. $this->add_mixin('Mixin_Gallery_Mapper'); 
  258. $this->implement('I_Gallery_Mapper'); 
  259. // Define the columns 
  260. $this->define_column('gid', 'BIGINT', 0); 
  261. $this->define_column('name', 'VARCHAR(255)'); 
  262. $this->define_column('slug', 'VARCHAR(255)'); 
  263. $this->define_column('path', 'TEXT'); 
  264. $this->define_column('title', 'TEXT'); 
  265. $this->define_column('pageid', 'INT', 0); 
  266. $this->define_column('previewpic', 'INT', 0); 
  267. $this->define_column('author', 'INT', 0); 
  268. $this->define_column('extras_post_id', 'BIGINT', 0); 
  269. function initialize($object_name = FALSE) 
  270. parent::initialize('ngg_gallery'); 
  271. /** 
  272. * Returns a singleton of the gallery mapper 
  273. * @param string $context 
  274. * @return C_Gallery_Mapper 
  275. */ 
  276. public static function get_instance($context = False) 
  277. if (is_null(self::$_instance)) { 
  278. $klass = get_class(); 
  279. self::$_instance = new $klass($context); 
  280. return self::$_instance; 
  281. class Mixin_Gallery_Mapper extends Mixin 
  282. /** 
  283. * Uses the title property as the post title when the Custom Post driver 
  284. * is used 
  285. */ 
  286. function get_post_title($entity) 
  287. return $entity->title; 
  288. function _save_entity($entity) 
  289. // A bug in NGG 2.1.24 allowed galleries to be created with spaces in the directory name, unreplaced by dashes 
  290. // This causes a few problems everywhere, so we here allow users a way to fix those galleries by just re-saving 
  291. if (FALSE !== strpos($entity->path, ' ')) { 
  292. $storage = C_Gallery_Storage::get_instance(); 
  293. $abspath = $storage->get_gallery_abspath($entity->{$entity->id_field}); 
  294. $pre_path = $entity->path; 
  295. $entity->path = str_replace(' ', '-', $entity->path); 
  296. $new_abspath = str_replace($pre_path, $entity->path, $abspath); 
  297. // Begin adding -1, -2, etc until we have a safe target: rename() will overwrite existing directories 
  298. if (@file_exists($new_abspath)) { 
  299. $max_count = 100; 
  300. $count = 0; 
  301. $corrected_abspath = $new_abspath; 
  302. while (@file_exists($corrected_abspath) && $count <= $max_count) { 
  303. $count++; 
  304. $corrected_abspath = $new_abspath . '-' . $count; 
  305. $new_abspath = $corrected_abspath; 
  306. $entity->path = $entity->path . '-' . $count; 
  307. @rename($abspath, $new_abspath); 
  308. $slug = $entity->slug; 
  309. $entity->slug = str_replace(' ', '-', $entity->slug); 
  310. // Note: we do the following to mimic the behaviour of esc_url so that slugs are always valid in URLs after escaping 
  311. $entity->slug = preg_replace('|[^a-z0-9 \\-~+_.#=!&;, /:%@$\\|*\'()\\x80-\\xff]|i', '', $entity->slug); 
  312. if ($slug != $entity->slug) { 
  313. // creating new slug for the gallery 
  314. $entity->slug = nggdb::get_unique_slug($entity->slug, 'gallery'); 
  315. $retval = $this->call_parent('_save_entity', $entity); 
  316. if ($retval) { 
  317. do_action('ngg_created_new_gallery', $entity->{$entity->id_field}); 
  318. C_Photocrati_Transient_Manager::flush('displayed_gallery_rendering'); 
  319. return $retval; 
  320. function destroy($gallery, $with_dependencies = FALSE) 
  321. $retval = FALSE; 
  322. if ($gallery) { 
  323. $gallery_id = is_numeric($gallery) ? $gallery : $gallery->{$gallery->id_field}; 
  324. // TODO: Look into making this operation more efficient 
  325. if ($with_dependencies) { 
  326. $image_mapper = C_Image_Mapper::get_instance(); 
  327. // Delete the image files from the filesystem 
  328. $settings = C_NextGen_Settings::get_instance(); 
  329. if ($settings->deleteImg) { 
  330. $storage = C_Gallery_Storage::get_instance(); 
  331. $storage->delete_gallery($gallery); 
  332. // Delete the image records from the DB 
  333. $image_mapper->delete()->where(array("galleryid = %d", $gallery_id))->run_query(); 
  334. $image_key = $image_mapper->get_primary_key_column(); 
  335. $image_table = $image_mapper->get_table_name(); 
  336. // Delete tag associations no longer needed. The following SQL statement 
  337. // deletes all tag associates for images that no longer exist 
  338. global $wpdb; 
  339. $wpdb->query("\n\t\t\t\t\tDELETE wptr.* FROM {$wpdb->term_relationships} wptr\n\t\t\t\t\tINNER JOIN {$wpdb->term_taxonomy} wptt\n\t\t\t\t\tON wptt.term_taxonomy_id = wptr.term_taxonomy_id\n\t\t\t\t\tWHERE wptt.term_taxonomy_id = wptr.term_taxonomy_id\n\t\t\t\t\tAND wptt.taxonomy = 'ngg_tag'\n\t\t\t\t\tAND wptr.object_id NOT IN (SELECT {$image_key} FROM {$image_table})"); 
  340. $retval = $this->call_parent('destroy', $gallery); 
  341. if ($retval) { 
  342. do_action('ngg_delete_gallery', $gallery); 
  343. C_Photocrati_Transient_Manager::flush('displayed_gallery_rendering'); 
  344. return $retval; 
  345. function set_preview_image($gallery, $image, $only_if_empty = FALSE) 
  346. $retval = FALSE; 
  347. // We need the gallery object 
  348. if (is_numeric($gallery)) { 
  349. $gallery = $this->object->find($gallery); 
  350. // We need the image id 
  351. if (!is_numeric($image)) { 
  352. if (method_exists($image, 'id')) { 
  353. $image = $image->id(); 
  354. } else { 
  355. $image = $image->{$image->id_field}; 
  356. if ($gallery && $image) { 
  357. if ($only_if_empty && !$gallery->previewpic or !$only_if_empty) { 
  358. $gallery->previewpic = $image; 
  359. $retval = $this->object->save($gallery); 
  360. return $retval; 
  361. /** 
  362. * Sets default values for the gallery 
  363. */ 
  364. function set_defaults($entity) 
  365. // If author is missing, then set to the current user id 
  366. // TODO: Using wordpress function. Should use abstraction 
  367. $this->object->_set_default_value($entity, 'author', get_current_user_id()); 
  368. class GalleryStorageDriverNotSelectedException extends RuntimeException 
  369. function __construct($message = '', $code = NULL, $previous = NULL) 
  370. if (!$message) { 
  371. $message = "No gallery storage driver selected."; 
  372. parent::__construct($message, $code, $previous); 
  373. class Mixin_GalleryStorage extends Mixin 
  374. /** 
  375. * Returns the name of the class which provides the gallerystorage 
  376. * implementation 
  377. * @return string 
  378. */ 
  379. function _get_driver_factory_method($context = FALSE) 
  380. $factory_method = ''; 
  381. // No constant has been defined to establish a global gallerystorage driver 
  382. if (!defined('GALLERYSTORAGE_DRIVER')) { 
  383. // Get the datamapper configured in the database 
  384. $factory_method = C_NextGen_Settings::get_instance()->gallerystorage_driver; 
  385. // Define a constant and use this as the global gallerystorage driver,  
  386. // unless running in a SimpleTest Environment 
  387. if (!isset($GLOBALS['SIMPLE_TEST_RUNNING'])) { 
  388. define('GALLERYSTORAGE_DRIVER', $factory_method); 
  389. } else { 
  390. $factory_method = GALLERYSTORAGE_DRIVER; 
  391. return $factory_method; 
  392. class C_GalleryStorage_Base extends C_Component 
  393. /** 
  394. * Gets the url or path of an image of a particular size 
  395. * @param string $method 
  396. * @param array $args 
  397. */ 
  398. function __call($method, $args) 
  399. if (preg_match("/^get_(\\w+)_(abspath|url|dimensions|html|size_params)\$/", $method, $match)) { 
  400. if (isset($match[1]) && isset($match[2]) && !$this->has_method($method)) { 
  401. $method = 'get_image_' . $match[2]; 
  402. $args[] = $match[1]; 
  403. // array($image, $size) 
  404. return parent::__call($method, $args); 
  405. return parent::__call($method, $args); 
  406. /** 
  407. * Class C_Gallery_Storage 
  408. * @mixin Mixin_GalleryStorage 
  409. * @implements I_Gallery_Storage 
  410. */ 
  411. class C_Gallery_Storage extends C_GalleryStorage_Base 
  412. public static $_instances = array(); 
  413. function define($context = FALSE) 
  414. parent::define($context); 
  415. $this->add_mixin('Mixin_GalleryStorage'); 
  416. $this->wrap('I_GalleryStorage_Driver', array(&$this, '_get_driver'), $context); 
  417. $this->implement('I_Gallery_Storage'); 
  418. static function get_instance($context = False) 
  419. if (!isset(self::$_instances[$context])) { 
  420. self::$_instances[$context] = new C_Gallery_Storage($context); 
  421. return self::$_instances[$context]; 
  422. /** 
  423. * Returns the implementation for the gallerystorage 
  424. * @param array $args 
  425. * @return mixed 
  426. */ 
  427. function _get_driver($context) 
  428. $factory_method = $this->_get_driver_factory_method($context); 
  429. $factory = C_Component_Factory::get_instance(); 
  430. return $factory->create($factory_method, FALSE, $context); 
  431. class E_UploadException extends E_NggErrorException 
  432. function __construct($message = '', $code = NULL, $previous = NULL) 
  433. if (!$message) { 
  434. $message = "There was a problem uploading the file."; 
  435. if (PHP_VERSION_ID >= 50300) { 
  436. parent::__construct($message, $code, $previous); 
  437. } else { 
  438. parent::__construct($message, $code); 
  439. class E_InsufficientWriteAccessException extends E_NggErrorException 
  440. function __construct($message = FALSE, $filename = NULL, $code = NULL, $previous = NULL) 
  441. if (!$message) { 
  442. $message = "Could not write to file. Please check filesystem permissions."; 
  443. if ($filename) { 
  444. $message .= " Filename: {$filename}"; 
  445. if (PHP_VERSION_ID >= 50300) { 
  446. parent::__construct($message, $code, $previous); 
  447. } else { 
  448. parent::__construct($message, $code); 
  449. class E_NoSpaceAvailableException extends E_NggErrorException 
  450. function __construct($message = '', $code = NULL, $previous = NULL) 
  451. if (!$message) { 
  452. $message = "You have exceeded your storage capacity. Please remove some files and try again."; 
  453. if (PHP_VERSION_ID >= 50300) { 
  454. parent::__construct($message, $code, $previous); 
  455. } else { 
  456. parent::__construct($message, $code); 
  457. class E_No_Image_Library_Exception extends E_NggErrorException 
  458. function __construct($message = '', $code = NULL, $previous = NULL) 
  459. if (!$message) { 
  460. $message = "The site does not support the GD Image library. Please ask your hosting provider to enable it."; 
  461. if (PHP_VERSION_ID >= 50300) { 
  462. parent::__construct($message, $code, $previous); 
  463. } else { 
  464. parent::__construct($message, $code); 
  465. class Mixin_GalleryStorage_Driver_Base extends Mixin 
  466. /** 
  467. * Set correct file permissions (taken from wp core). Should be called 
  468. * after writing any file 
  469. * 
  470. * @class nggAdmin 
  471. * @param string $filename 
  472. * @return bool $result 
  473. */ 
  474. function _chmod($filename = '') 
  475. $stat = @stat(dirname($filename)); 
  476. $perms = $stat['mode'] & 0666; 
  477. // Remove execute bits for files 
  478. if (@chmod($filename, $perms)) { 
  479. return TRUE; 
  480. return FALSE; 
  481. /** 
  482. * Gets the id of a gallery, regardless of whether an integer 
  483. * or object was passed as an argument 
  484. * @param mixed $gallery_obj_or_id 
  485. */ 
  486. function _get_gallery_id($gallery_obj_or_id) 
  487. $retval = NULL; 
  488. $gallery_key = $this->object->_gallery_mapper->get_primary_key_column(); 
  489. if (is_object($gallery_obj_or_id)) { 
  490. if (isset($gallery_obj_or_id->{$gallery_key})) { 
  491. $retval = $gallery_obj_or_id->{$gallery_key}; 
  492. } elseif (is_numeric($gallery_obj_or_id)) { 
  493. $retval = $gallery_obj_or_id; 
  494. return $retval; 
  495. /** 
  496. * Gets the id of an image, regardless of whether an integer 
  497. * or object was passed as an argument 
  498. * @param type $image_obj_or_id 
  499. */ 
  500. function _get_image_id($image_obj_or_id) 
  501. $retval = NULL; 
  502. $image_key = $this->object->_image_mapper->get_primary_key_column(); 
  503. if (is_object($image_obj_or_id)) { 
  504. if (isset($image_obj_or_id->{$image_key})) { 
  505. $retval = $image_obj_or_id->{$image_key}; 
  506. } elseif (is_numeric($image_obj_or_id)) { 
  507. $retval = $image_obj_or_id; 
  508. return $retval; 
  509. function convert_slashes($path) 
  510. $search = array('/', "\\"); 
  511. $replace = array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR); 
  512. return str_replace($search, $replace, $path); 
  513. function delete_directory($abspath) 
  514. $retval = FALSE; 
  515. if (@file_exists($abspath)) { 
  516. $files = scandir($abspath); 
  517. array_shift($files); 
  518. array_shift($files); 
  519. foreach ($files as $file) { 
  520. $file_abspath = implode(DIRECTORY_SEPARATOR, array(rtrim($abspath, "/\\"), $file)); 
  521. if (is_dir($file_abspath)) { 
  522. $this->object->delete_directory($file_abspath); 
  523. } else { 
  524. unlink($file_abspath); 
  525. rmdir($abspath); 
  526. $retval = @file_exists($abspath); 
  527. return $retval; 
  528. /** 
  529. * Backs up an image file 
  530. * 
  531. * @param int|object $image 
  532. */ 
  533. function backup_image($image) 
  534. $retval = FALSE; 
  535. $image_path = $this->object->get_image_abspath($image); 
  536. if ($image_path && @file_exists($image_path)) { 
  537. $retval = copy($image_path, $this->object->get_backup_abspath($image)); 
  538. // Store the dimensions of the image 
  539. if (function_exists('getimagesize')) { 
  540. $mapper = C_Image_Mapper::get_instance(); 
  541. if (!is_object($image)) { 
  542. $image = $mapper->find($image); 
  543. if ($image) { 
  544. if (!property_exists($image, 'meta_data')) { 
  545. $image->meta_data = array(); 
  546. $dimensions = getimagesize($image_path); 
  547. $image->meta_data['backup'] = array('filename' => basename($image_path), 'width' => $dimensions[0], 'height' => $dimensions[1], 'generated' => microtime()); 
  548. $mapper->save($image); 
  549. return $retval; 
  550. /** 
  551. * Copies images into another gallery 
  552. * @param array $images 
  553. * @param int|object $gallery 
  554. * @param boolean $db optionally only copy the image files 
  555. * @param boolean $move move the image instead of copying 
  556. */ 
  557. function copy_images($images, $gallery, $db = TRUE, $move = FALSE) 
  558. $retval = FALSE; 
  559. // Ensure we have a valid gallery 
  560. if ($gallery = $this->object->_get_gallery_id($gallery)) { 
  561. $gallery_path = $this->object->get_gallery_abspath($gallery); 
  562. $image_key = $this->object->_image_mapper->get_primary_key_column(); 
  563. $retval = TRUE; 
  564. // Iterate through each image to copy... 
  565. foreach ($images as $image) { 
  566. // Copy each image size 
  567. foreach ($this->object->get_image_sizes() as $size) { 
  568. $image_path = $this->object->get_image_abspath($image, $size); 
  569. $dst = implode(DIRECTORY_SEPARATOR, array($gallery_path, M_I18n::mb_basename($image_path))); 
  570. $success = $move ? move($image_path, $dst) : copy($image_path, $dst); 
  571. if (!$success) { 
  572. $retval = FALSE; 
  573. // Copy the db entry 
  574. if ($db) { 
  575. if (is_numeric($image)) { 
  576. $this->object->_image_mapper($image); 
  577. unset($image->{$image_key}); 
  578. $image->galleryid = $gallery; 
  579. return $retval; 
  580. /** 
  581. * Empties the gallery cache directory of content 
  582. */ 
  583. function flush_cache($gallery) 
  584. $cache = C_Cache::get_instance(); 
  585. $cache->flush_directory($this->object->get_cache_abspath($gallery)); 
  586. /** 
  587. * Gets the absolute path of the backup of an original image 
  588. * @param string $image 
  589. */ 
  590. function get_backup_abspath($image) 
  591. $retval = null; 
  592. if ($image_path = $this->object->get_image_abspath($image)) { 
  593. $retval = $image_path . '_backup'; 
  594. return $retval; 
  595. function get_backup_dimensions($image) 
  596. return $this->object->get_image_dimensions($image, 'backup'); 
  597. function get_backup_url($image) 
  598. return $this->object->get_image_url($image, 'backup'); 
  599. /** 
  600. * Returns the absolute path to the cache directory of a gallery. 
  601. * 
  602. * Without the gallery parameter the legacy (pre 2.0) shared directory is returned. 
  603. * 
  604. * @param int|stdClass|C_Gallery $gallery (optional) 
  605. * @return string Absolute path to cache directory 
  606. */ 
  607. function get_cache_abspath($gallery = FALSE) 
  608. $retval = NULL; 
  609. if (FALSE == $gallery) { 
  610. $gallerypath = C_NextGen_Settings::get_instance()->gallerypath; 
  611. $retval = implode(DIRECTORY_SEPARATOR, array(rtrim(C_Fs::get_instance()->get_document_root('gallery'), "/\\"), rtrim($gallerypath, "/\\"), 'cache')); 
  612. } else { 
  613. if (is_numeric($gallery)) { 
  614. $gallery = $this->object->_gallery_mapper->find($gallery); 
  615. $retval = rtrim(implode(DIRECTORY_SEPARATOR, array($this->object->get_gallery_abspath($gallery), 'dynamic')), "/\\"); 
  616. return $retval; 
  617. /** 
  618. * Gets the absolute path where the full-sized image is stored 
  619. * @param int|object $image 
  620. */ 
  621. function get_full_abspath($image) 
  622. return $this->object->get_image_abspath($image, 'full'); 
  623. /** 
  624. * Alias to get_image_dimensions() 
  625. * @param int|object $image 
  626. * @return array 
  627. */ 
  628. function get_full_dimensions($image) 
  629. return $this->object->get_image_dimensions($image, 'full'); 
  630. /** 
  631. * Alias to get_image_html() 
  632. * @param int|object $image 
  633. * @return string 
  634. */ 
  635. function get_full_html($image) 
  636. return $this->object->get_image_html($image, 'full'); 
  637. /** 
  638. * Alias for get_original_url() 
  639. * 
  640. * @param int|stdClass|C_Image $image 
  641. * @return string 
  642. */ 
  643. function get_full_url($image, $check_existance = FALSE) 
  644. return $this->object->get_image_url($image, 'full', $check_existance); 
  645. function get_image_checksum($image, $size = 'full') 
  646. $retval = NULL; 
  647. if ($image_abspath = $this->get_image_abspath($image, $size, TRUE)) { 
  648. $retval = md5_file($image_abspath); 
  649. return $retval; 
  650. /** 
  651. * Gets the dimensions for a particular-sized image 
  652. * 
  653. * @param int|object $image 
  654. * @param string $size 
  655. * @return array 
  656. */ 
  657. function get_image_dimensions($image, $size = 'full') 
  658. $retval = NULL; 
  659. // If an image id was provided, get the entity 
  660. if (is_numeric($image)) { 
  661. $image = $this->object->_image_mapper->find($image); 
  662. // Ensure we have a valid image 
  663. if ($image) { 
  664. // Adjust size parameter 
  665. switch ($size) { 
  666. case 'original': 
  667. $size = 'full'; 
  668. break; 
  669. case 'thumbnails': 
  670. case 'thumbnail': 
  671. case 'thumb': 
  672. case 'thumbs': 
  673. $size = 'thumbnail'; 
  674. break; 
  675. // Image dimensions are stored in the $image->meta_data 
  676. // property for all implementations 
  677. if (isset($image->meta_data) && isset($image->meta_data[$size])) { 
  678. $retval = $image->meta_data[$size]; 
  679. } else { 
  680. $abspath = $this->object->get_image_abspath($image, $size); 
  681. if (@file_exists($abspath)) { 
  682. $dims = getimagesize($abspath); 
  683. if ($dims) { 
  684. $retval['width'] = $dims[0]; 
  685. $retval['height'] = $dims[1]; 
  686. } elseif ($size == 'backup') { 
  687. $retval = $this->object->get_image_dimensions($image, 'full'); 
  688. return $retval; 
  689. /** 
  690. * Gets the HTML for an image 
  691. * @param int|object $image 
  692. * @param string $size 
  693. * @return string 
  694. */ 
  695. function get_image_html($image, $size = 'full', $attributes = array()) 
  696. $retval = ""; 
  697. if (is_numeric($image)) { 
  698. $image = $this->object->_image_mapper->find($image); 
  699. if ($image) { 
  700. // Set alt text if not already specified 
  701. if (!isset($attributes['alttext'])) { 
  702. $attributes['alt'] = esc_attr($image->alttext); 
  703. // Set the title if not already set 
  704. if (!isset($attributes['title'])) { 
  705. $attributes['title'] = esc_attr($image->alttext); 
  706. // Set the dimensions if not set already 
  707. if (!isset($attributes['width']) or !isset($attributes['height'])) { 
  708. $dimensions = $this->object->get_image_dimensions($image, $size); 
  709. if (!isset($attributes['width'])) { 
  710. $attributes['width'] = $dimensions['width']; 
  711. if (!isset($attributes['height'])) { 
  712. $attributes['height'] = $dimensions['height']; 
  713. // Set the url if not already specified 
  714. if (!isset($attributes['src'])) { 
  715. $attributes['src'] = $this->object->get_image_url($image, $size); 
  716. // Format attributes 
  717. $attribs = array(); 
  718. foreach ($attributes as $attrib => $value) { 
  719. $attribs[] = "{$attrib}=\"{$value}\""; 
  720. $attribs = implode(" ", $attribs); 
  721. // Return HTML string 
  722. $retval = "<img {$attribs} />"; 
  723. return $retval; 
  724. /** 
  725. * An alias for get_full_abspath() 
  726. * @param int|object $image 
  727. */ 
  728. function get_original_abspath($image, $check_existance = FALSE) 
  729. return $this->object->get_image_abspath($image, 'full', $check_existance); 
  730. /** 
  731. * Alias to get_image_dimensions() 
  732. * @param int|object $image 
  733. * @return array 
  734. */ 
  735. function get_original_dimensions($image) 
  736. return $this->object->get_image_dimensions($image, 'full'); 
  737. /** 
  738. * Alias to get_image_html() 
  739. * @param int|object $image 
  740. * @return string 
  741. */ 
  742. function get_original_html($image) 
  743. return $this->object->get_image_html($image, 'full'); 
  744. /** 
  745. * Gets the url to the original-sized image 
  746. * @param int|stdClass|C_Image $image 
  747. * @return string 
  748. */ 
  749. function get_original_url($image, $check_existance = FALSE) 
  750. return $this->object->get_image_url($image, 'full', $check_existance); 
  751. /** 
  752. * Gets the upload path, optionally for a particular gallery 
  753. * @param int|C_Gallery|stdClass $gallery 
  754. */ 
  755. function get_upload_relpath($gallery = FALSE) 
  756. $fs = C_Fs::get_instance(); 
  757. $retval = str_replace($fs->get_document_root('gallery'), '', $this->object->get_upload_abspath($gallery)); 
  758. return DIRECTORY_SEPARATOR . ltrim($retval, "/\\"); 
  759. /** 
  760. * Moves images from to another gallery 
  761. * @param array $images 
  762. * @param int|object $gallery 
  763. * @param boolean $db optionally only move the image files, not the db entries 
  764. * @return boolean 
  765. */ 
  766. function move_images($images, $gallery, $db = TRUE) 
  767. return $this->object->copy_images($images, $gallery, $db, TRUE); 
  768. function is_image_file($filename = NULL) 
  769. $retval = FALSE; 
  770. if (!$filename && isset($_FILES['file']) && $_FILES['file']['error'] == 0) { 
  771. $filename = $_FILES['file']['tmp_name']; 
  772. $valid_types = array('image/gif', 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png'); 
  773. // If we can, we'll verify the mime type 
  774. if (function_exists('exif_imagetype')) { 
  775. if (($image_type = @exif_imagetype($filename)) !== FALSE) { 
  776. $retval = in_array(image_type_to_mime_type($image_type), $valid_types); 
  777. } else { 
  778. $file_info = @getimagesize($filename); 
  779. if (isset($file_info[2])) { 
  780. $retval = in_array(image_type_to_mime_type($file_info[2]), $valid_types); 
  781. return $retval; 
  782. function is_zip() 
  783. $retval = FALSE; 
  784. if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) { 
  785. $file_info = $_FILES['file']; 
  786. if (isset($file_info['type'])) { 
  787. $type = $file_info['type']; 
  788. $type_parts = explode('/', $type); 
  789. if (strtolower($type_parts[0]) == 'application') { 
  790. $spec = $type_parts[1]; 
  791. $spec_parts = explode('-', $spec); 
  792. $spec_parts = array_map('strtolower', $spec_parts); 
  793. if (in_array($spec, array('zip', 'octet-stream')) || in_array('zip', $spec_parts)) { 
  794. $retval = true; 
  795. return $retval; 
  796. function upload_zip($gallery_id) 
  797. $memory_limit = intval(ini_get('memory_limit')); 
  798. if (!extension_loaded('suhosin') && $memory_limit < 256) { 
  799. @ini_set('memory_limit', '256M'); 
  800. $retval = FALSE; 
  801. if ($this->object->is_zip()) { 
  802. $fs = C_Fs::get_instance(); 
  803. // Uses the WordPress ZIP abstraction API 
  804. include_once $fs->join_paths(ABSPATH, 'wp-admin', 'includes', 'file.php'); 
  805. WP_Filesystem(); 
  806. // Ensure that we truly have the gallery id 
  807. $gallery_id = $this->_get_gallery_id($gallery_id); 
  808. $zipfile = $_FILES['file']['tmp_name']; 
  809. $dest_path = implode(DIRECTORY_SEPARATOR, array(rtrim(get_temp_dir(), "/\\"), 'unpacked-' . M_I18n::mb_basename($zipfile))); 
  810. wp_mkdir_p($dest_path); 
  811. if (unzip_file($zipfile, $dest_path) === TRUE) { 
  812. $dest_dir = $dest_path . DIRECTORY_SEPARATOR; 
  813. $files = glob($dest_dir . '*'); 
  814. $size = 0; 
  815. foreach ($files as $file) { 
  816. if (is_file($dest_dir . $file)) { 
  817. $size += filesize($dest_dir . $file); 
  818. if ($size == 0) { 
  819. $this->object->delete_directory($dest_path); 
  820. $destination = wp_upload_dir(); 
  821. $destination_path = $destination['basedir']; 
  822. $dest_path = implode(DIRECTORY_SEPARATOR, array(rtrim($destination_path, "/\\"), 'unpacked-' . M_I18n::mb_basename($zipfile))); 
  823. wp_mkdir_p($dest_path); 
  824. if (unzip_file($zipfile, $dest_path) === TRUE) { 
  825. $retval = $this->object->import_gallery_from_fs($dest_path, $gallery_id); 
  826. } else { 
  827. $retval = $this->object->import_gallery_from_fs($dest_path, $gallery_id); 
  828. $this->object->delete_directory($dest_path); 
  829. if (!extension_loaded('suhosin')) { 
  830. @ini_set('memory_limit', $memory_limit . 'M'); 
  831. return $retval; 
  832. function is_current_user_over_quota() 
  833. $retval = FALSE; 
  834. $settings = C_NextGen_Settings::get_instance(); 
  835. if (is_multisite() && $settings->get('wpmuQuotaCheck')) { 
  836. require_once ABSPATH . 'wp-admin/includes/ms.php'; 
  837. $retval = upload_is_user_over_quota(FALSE); 
  838. return $retval; 
  839. /** 
  840. * Uploads base64 file to a gallery 
  841. * @param int|stdClass|C_Gallery $gallery 
  842. * @param $data base64-encoded string of data representing the image 
  843. * @param type $filename specifies the name of the file 
  844. * @return C_Image 
  845. */ 
  846. function upload_base64_image($gallery, $data, $filename = FALSE, $image_id = FALSE, $override = FALSE) 
  847. $settings = C_NextGen_Settings::get_instance(); 
  848. $memory_limit = intval(ini_get('memory_limit')); 
  849. if (!extension_loaded('suhosin') && $memory_limit < 256) { 
  850. @ini_set('memory_limit', '256M'); 
  851. $retval = NULL; 
  852. if ($gallery_id = $this->object->_get_gallery_id($gallery)) { 
  853. if ($this->object->is_current_user_over_quota()) { 
  854. $message = sprintf(__('Sorry, you have used your space allocation. Please delete some files to upload more files.', 'nggallery')); 
  855. throw new E_NoSpaceAvailableException($message); 
  856. // Get path information. The use of get_upload_abspath() might 
  857. // not be the best for some drivers. For example, if using the 
  858. // WordPress Media Library for uploading, then the wp_upload_bits() 
  859. // function should perhaps be used 
  860. $upload_dir = $this->object->get_upload_abspath($gallery); 
  861. // Perhaps a filename was given instead of base64 data? 
  862. if (preg_match("#/\\\\#", $data[0]) && @file_exists($data)) { 
  863. if (!$filename) { 
  864. $filename = M_I18n::mb_basename($data); 
  865. $data = file_get_contents($data); 
  866. // Determine filenames 
  867. $original_filename = $filename; 
  868. $filename = $filename ? sanitize_file_name($original_filename) : uniqid('nextgen-gallery'); 
  869. if (preg_match("/\\-(png|jpg|gif|jpeg)\$/i", $filename, $match)) { 
  870. $filename = str_replace($match[0], '.' . $match[1], $filename); 
  871. $abs_filename = implode(DIRECTORY_SEPARATOR, array($upload_dir, $filename)); 
  872. // Ensure that the filename is valid 
  873. if (!preg_match("/(png|jpeg|jpg|gif)\$/i", $abs_filename)) { 
  874. throw new E_UploadException(__('Invalid image file. Acceptable formats: JPG, GIF, and PNG.', 'nggallery')); 
  875. // Prevent duplicate filenames: check if the filename exists and 
  876. // begin appending '-i' until we find an open slot 
  877. if (!ini_get('safe_mode') && @file_exists($abs_filename) && !$override) { 
  878. $file_exists = TRUE; 
  879. $i = 0; 
  880. do { 
  881. $i++; 
  882. $parts = explode('.', $filename); 
  883. $extension = array_pop($parts); 
  884. $new_filename = implode('.', $parts) . '-' . $i . '.' . $extension; 
  885. $new_abs_filename = implode(DIRECTORY_SEPARATOR, array($upload_dir, $new_filename)); 
  886. if (!@file_exists($new_abs_filename)) { 
  887. $file_exists = FALSE; 
  888. $filename = $new_filename; 
  889. $abs_filename = $new_abs_filename; 
  890. } while ($file_exists == TRUE); 
  891. // Create or retrieve the image object 
  892. $image = NULL; 
  893. if ($image_id) { 
  894. $image = $this->object->_image_mapper->find($image_id, TRUE); 
  895. if ($image) { 
  896. unset($image->meta_data['saved']); 
  897. if (!$image) { 
  898. $image = $this->object->_image_mapper->create(); 
  899. $retval = $image; 
  900. // Create or update the database record 
  901. $image->alttext = str_replace('.' . M_I18n::mb_pathinfo($original_filename, PATHINFO_EXTENSION), '', M_I18n::mb_basename($original_filename)); 
  902. $image->galleryid = $this->object->_get_gallery_id($gallery); 
  903. $image->filename = $filename; 
  904. $image->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($image->alttext), 'image'); 
  905. $image_key = $this->object->_image_mapper->get_primary_key_column(); 
  906. // If we can't write to the directory, then there's no point in continuing 
  907. if (!@file_exists($upload_dir)) { 
  908. @wp_mkdir_p($upload_dir); 
  909. if (!is_writable($upload_dir)) { 
  910. throw new E_InsufficientWriteAccessException(FALSE, $upload_dir, FALSE); 
  911. // Save the image 
  912. if ($image_id = $this->object->_image_mapper->save($image)) { 
  913. try { 
  914. // Try writing the image 
  915. $fp = fopen($abs_filename, 'w'); 
  916. fwrite($fp, $data); 
  917. fclose($fp); 
  918. if ($settings->imgBackup) { 
  919. $this->object->backup_image($image); 
  920. if ($settings->imgAutoResize) { 
  921. $this->object->generate_image_clone($abs_filename, $abs_filename, $this->object->get_image_size_params($image_id, 'full')); 
  922. // Ensure that fullsize dimensions are added to metadata array 
  923. $dimensions = getimagesize($abs_filename); 
  924. $full_meta = array('width' => $dimensions[0], 'height' => $dimensions[1], 'md5' => $this->object->get_image_checksum($image, 'full')); 
  925. if (!isset($image->meta_data) or is_string($image->meta_data) && strlen($image->meta_data) == 0) { 
  926. $image->meta_data = array(); 
  927. $image->meta_data = array_merge($image->meta_data, $full_meta); 
  928. $image->meta_data['full'] = $full_meta; 
  929. // Generate a thumbnail for the image 
  930. $this->object->generate_thumbnail($image); 
  931. // Set gallery preview image if missing 
  932. C_Gallery_Mapper::get_instance()->set_preview_image($gallery, $image_id, TRUE); 
  933. // Notify other plugins that an image has been added 
  934. do_action('ngg_added_new_image', $image); 
  935. // delete dirsize after adding new images 
  936. delete_transient('dirsize_cache'); 
  937. // Seems redundant to above hook. Maintaining for legacy purposes 
  938. do_action('ngg_after_new_images_added', $gallery_id, array($image->{$image_key})); 
  939. } catch (E_No_Image_Library_Exception $ex) { 
  940. throw $ex; 
  941. } catch (E_Clean_Exit $ex) { 
  942. // pass 
  943. } catch (Exception $ex) { 
  944. throw new E_InsufficientWriteAccessException(FALSE, $abs_filename, FALSE, $ex); 
  945. } else { 
  946. throw new E_InvalidEntityException(); 
  947. } else { 
  948. throw new E_EntityNotFoundException(); 
  949. if (!extension_loaded('suhosin')) { 
  950. @ini_set('memory_limit', $memory_limit . 'M'); 
  951. return $retval; 
  952. function import_gallery_from_fs($abspath, $gallery_id = FALSE, $create_new_gallerypath = TRUE, $gallery_title = NULL, $filenames = array()) 
  953. $retval = FALSE; 
  954. if (@file_exists($abspath)) { 
  955. $fs = C_Fs::get_instance(); 
  956. // Ensure that this folder has images 
  957. // Ensure that this folder has images 
  958. $i = 0; 
  959. $files = array(); 
  960. foreach (scandir($abspath) as $file) { 
  961. if ($file == '.' || $file == '..') { 
  962. continue; 
  963. $file_abspath = $fs->join_paths($abspath, $file); 
  964. // The first directory is considered valid 
  965. if (is_dir($file_abspath) && $i === 0) { 
  966. $files[] = $file_abspath; 
  967. } elseif ($this->is_image_file($file_abspath)) { 
  968. if ($filenames && array_search($file_abspath, $filenames) !== FALSE) { 
  969. $files[] = $file_abspath; 
  970. } else { 
  971. if (!$filenames) { 
  972. $files[] = $file_abspath; 
  973. if (!empty($files)) { 
  974. // Get needed utilities 
  975. $gallery_mapper = C_Gallery_Mapper::get_instance(); 
  976. // Sometimes users try importing a directory, which actually has all images under another directory 
  977. if (is_dir($files[0])) { 
  978. return $this->import_gallery_from_fs($files[0], $gallery_id, $create_new_gallerypath, $gallery_title, $filenames); 
  979. // If no gallery has been specified, then use the directory name as the gallery name 
  980. if (!$gallery_id) { 
  981. // Create the gallery 
  982. $gallery = $gallery_mapper->create(array('title' => $gallery_title ? $gallery_title : M_I18n::mb_basename($abspath))); 
  983. if (!$create_new_gallerypath) { 
  984. $gallery->path = str_ireplace(ABSPATH, '', $abspath); 
  985. // Save the gallery 
  986. if ($gallery->save()) { 
  987. $gallery_id = $gallery->id(); 
  988. // Ensure that we have a gallery id 
  989. if ($gallery_id) { 
  990. $retval = array('gallery_id' => $gallery_id, 'image_ids' => array()); 
  991. foreach ($files as $file_abspath) { 
  992. if (!preg_match("/\\.(jpg|jpeg|gif|png)\$/i", $file_abspath)) { 
  993. continue; 
  994. $image = null; 
  995. if ($create_new_gallerypath) { 
  996. $image = $this->object->upload_base64_image($gallery_id, file_get_contents($file_abspath), str_replace(' ', '_', M_I18n::mb_basename($file_abspath))); 
  997. } else { 
  998. // Create the database record ... TODO cleanup, some duplication here from upload_base64_image 
  999. $factory = C_Component_Factory::get_instance(); 
  1000. $image = $factory->create('image'); 
  1001. $image->alttext = sanitize_title_with_dashes(str_replace('.' . M_I18n::mb_pathinfo($file_abspath, PATHINFO_EXTENSION), '', M_I18n::mb_basename($file_abspath))); 
  1002. $image->galleryid = $this->object->_get_gallery_id($gallery_id); 
  1003. $image->filename = M_I18n::mb_basename($file_abspath); 
  1004. $image->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($image->alttext), 'image'); 
  1005. $image_key = $this->object->_image_mapper->get_primary_key_column(); 
  1006. $abs_filename = $file_abspath; 
  1007. if ($image_id = $this->object->_image_mapper->save($image)) { 
  1008. try { 
  1009. if (C_NextGen_settings::get_instance()->imgBackup) { 
  1010. $this->object->backup_image($image); 
  1011. # if ($settings->imgAutoResize) 
  1012. # $this->object->generate_image_clone( 
  1013. # $abs_filename,  
  1014. # $abs_filename,  
  1015. # $this->object->get_image_size_params($image_id, 'full') 
  1016. # ); 
  1017. // Ensure that fullsize dimensions are added to metadata array 
  1018. $dimensions = getimagesize($abs_filename); 
  1019. $full_meta = array('width' => $dimensions[0], 'height' => $dimensions[1]); 
  1020. if (!isset($image->meta_data) or is_string($image->meta_data) && strlen($image->meta_data) == 0) { 
  1021. $image->meta_data = array(); 
  1022. $image->meta_data = array_merge($image->meta_data, $full_meta); 
  1023. $image->meta_data['full'] = $full_meta; 
  1024. // Generate a thumbnail for the image 
  1025. $this->object->generate_thumbnail($image); 
  1026. // Set gallery preview image if missing 
  1027. C_Gallery_Mapper::get_instance()->set_preview_image($gallery, $image_id, TRUE); 
  1028. // Notify other plugins that an image has been added 
  1029. do_action('ngg_added_new_image', $image); 
  1030. // delete dirsize after adding new images 
  1031. delete_transient('dirsize_cache'); 
  1032. // Seems redundant to above hook. Maintaining for legacy purposes 
  1033. do_action('ngg_after_new_images_added', $gallery_id, array($image->{$image_key})); 
  1034. } catch (Exception $ex) { 
  1035. throw new E_InsufficientWriteAccessException(FALSE, $abs_filename, FALSE, $ex); 
  1036. } else { 
  1037. throw new E_InvalidEntityException(); 
  1038. $retval['image_ids'][] = $image->{$image->id_field}; 
  1039. // Add the gallery name to the result 
  1040. $gallery = $gallery_mapper->find($gallery_id); 
  1041. $retval['gallery_name'] = $gallery->title; 
  1042. unset($gallery); 
  1043. return $retval; 
  1044. function get_image_format_list() 
  1045. $format_list = array(IMAGETYPE_GIF => 'gif', IMAGETYPE_JPEG => 'jpg', IMAGETYPE_PNG => 'png'); 
  1046. return $format_list; 
  1047. /** 
  1048. * Returns an array of properties of a resulting clone image if and when generated 
  1049. * @param string $image_path 
  1050. * @param string $clone_path 
  1051. * @param array $params 
  1052. * @return array 
  1053. */ 
  1054. function calculate_image_clone_result($image_path, $clone_path, $params) 
  1055. $width = isset($params['width']) ? $params['width'] : NULL; 
  1056. $height = isset($params['height']) ? $params['height'] : NULL; 
  1057. $quality = isset($params['quality']) ? $params['quality'] : NULL; 
  1058. $type = isset($params['type']) ? $params['type'] : NULL; 
  1059. $crop = isset($params['crop']) ? $params['crop'] : NULL; 
  1060. $watermark = isset($params['watermark']) ? $params['watermark'] : NULL; 
  1061. $rotation = isset($params['rotation']) ? $params['rotation'] : NULL; 
  1062. $reflection = isset($params['reflection']) ? $params['reflection'] : NULL; 
  1063. $crop_frame = isset($params['crop_frame']) ? $params['crop_frame'] : NULL; 
  1064. $result = NULL; 
  1065. // Ensure we have a valid image 
  1066. if ($image_path && @file_exists($image_path)) { 
  1067. // Ensure target directory exists, but only create 1 subdirectory 
  1068. $image_dir = dirname($image_path); 
  1069. $clone_dir = dirname($clone_path); 
  1070. $image_extension = M_I18n::mb_pathinfo($image_path, PATHINFO_EXTENSION); 
  1071. $image_extension_str = null; 
  1072. $clone_extension = M_I18n::mb_pathinfo($clone_path, PATHINFO_EXTENSION); 
  1073. $clone_extension_str = null; 
  1074. if ($image_extension != null) { 
  1075. $image_extension_str = '.' . $image_extension; 
  1076. if ($clone_extension != null) { 
  1077. $clone_extension_str = '.' . $clone_extension; 
  1078. $image_basename = M_I18n::mb_basename($image_path, $image_extension_str); 
  1079. $clone_basename = M_I18n::mb_basename($clone_path, $clone_extension_str); 
  1080. // We use a default suffix as passing in null as the suffix will make WordPress use a default 
  1081. $clone_suffix = null; 
  1082. $format_list = $this->object->get_image_format_list(); 
  1083. $clone_format = null; 
  1084. // format is determined below and based on $type otherwise left to null 
  1085. // suffix is only used to reconstruct paths for image_resize function 
  1086. if (strpos($clone_basename, $image_basename) === 0) { 
  1087. $clone_suffix = substr($clone_basename, strlen($image_basename)); 
  1088. if ($clone_suffix != null && $clone_suffix[0] == '-') { 
  1089. // WordPress adds '-' on its own 
  1090. $clone_suffix = substr($clone_suffix, 1); 
  1091. // Get original image dimensions 
  1092. $dimensions = getimagesize($image_path); 
  1093. if ($width == null && $height == null) { 
  1094. if ($dimensions != null) { 
  1095. if ($width == null) { 
  1096. $width = $dimensions[0]; 
  1097. if ($height == null) { 
  1098. $height = $dimensions[1]; 
  1099. } else { 
  1100. // XXX Don't think there's any other option here but to fail miserably...use some hard-coded defaults maybe? 
  1101. return null; 
  1102. if ($dimensions != null) { 
  1103. $dimensions_ratio = $dimensions[0] / $dimensions[1]; 
  1104. if ($width == null) { 
  1105. $width = (int) round($height * $dimensions_ratio); 
  1106. if ($width == $dimensions[0] - 1) { 
  1107. $width = $dimensions[0]; 
  1108. } else { 
  1109. if ($height == null) { 
  1110. $height = (int) round($width / $dimensions_ratio); 
  1111. if ($height == $dimensions[1] - 1) { 
  1112. $height = $dimensions[1]; 
  1113. if ($width > $dimensions[0]) { 
  1114. $width = $dimensions[0]; 
  1115. if ($height > $dimensions[1]) { 
  1116. $height = $dimensions[1]; 
  1117. $image_format = $dimensions[2]; 
  1118. if ($type != null) { 
  1119. if (is_string($type)) { 
  1120. $type = strtolower($type); 
  1121. // Indexes in the $format_list array correspond to IMAGETYPE_XXX values appropriately 
  1122. if (($index = array_search($type, $format_list)) !== false) { 
  1123. $type = $index; 
  1124. if ($type != $image_format) { 
  1125. // Note: this only changes the FORMAT of the image but not the extension 
  1126. $clone_format = $type; 
  1127. if ($width == null || $height == null) { 
  1128. // Something went wrong... 
  1129. return null; 
  1130. $result['clone_path'] = $clone_path; 
  1131. $result['clone_directory'] = $clone_dir; 
  1132. $result['clone_suffix'] = $clone_suffix; 
  1133. $result['clone_format'] = $clone_format; 
  1134. $result['base_width'] = $dimensions[0]; 
  1135. $result['base_height'] = $dimensions[1]; 
  1136. // image_resize() has limitations: 
  1137. // - no easy crop frame support 
  1138. // - fails if the dimensions are unchanged 
  1139. // - doesn't support filename prefix, only suffix so names like thumbs_original_name.jpg for $clone_path are not supported 
  1140. // also suffix cannot be null as that will make WordPress use a default suffix...we could use an object that returns empty string from __toString() but for now just fallback to ngg generator 
  1141. if (FALSE) { 
  1142. // disabling the WordPress method for Iteration #6 
  1143. // if (($crop_frame == null || !$crop) && ($dimensions[0] != $width && $dimensions[1] != $height) && $clone_suffix != null) 
  1144. $result['method'] = 'wordpress'; 
  1145. $new_dims = image_resize_dimensions($dimensions[0], $dimensions[1], $width, $height, $crop); 
  1146. if ($new_dims) { 
  1147. list($dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) = $new_dims; 
  1148. $width = $dst_w; 
  1149. $height = $dst_h; 
  1150. } else { 
  1151. $result['error'] = new WP_Error('error_getting_dimensions', __('Could not calculate resized image dimensions')); 
  1152. } else { 
  1153. $result['method'] = 'nextgen'; 
  1154. $original_width = $dimensions[0]; 
  1155. $original_height = $dimensions[1]; 
  1156. $aspect_ratio = $width / $height; 
  1157. $orig_ratio_x = $original_width / $width; 
  1158. $orig_ratio_y = $original_height / $height; 
  1159. if ($crop) { 
  1160. $algo = 'shrink'; 
  1161. // either 'adapt' or 'shrink' 
  1162. if ($crop_frame != null) { 
  1163. $crop_x = (int) round($crop_frame['x']); 
  1164. $crop_y = (int) round($crop_frame['y']); 
  1165. $crop_width = (int) round($crop_frame['width']); 
  1166. $crop_height = (int) round($crop_frame['height']); 
  1167. $crop_final_width = (int) round($crop_frame['final_width']); 
  1168. $crop_final_height = (int) round($crop_frame['final_height']); 
  1169. $crop_width_orig = $crop_width; 
  1170. $crop_height_orig = $crop_height; 
  1171. $crop_factor_x = $crop_width / $crop_final_width; 
  1172. $crop_factor_y = $crop_height / $crop_final_height; 
  1173. $crop_ratio_x = $crop_width / $width; 
  1174. $crop_ratio_y = $crop_height / $height; 
  1175. if ($algo == 'adapt') { 
  1176. // XXX not sure about this...don't use for now 
  1177. # $crop_width = (int) round($width * $crop_factor_x); 
  1178. # $crop_height = (int) round($height * $crop_factor_y); 
  1179. } else { 
  1180. if ($algo == 'shrink') { 
  1181. if ($crop_ratio_x < $crop_ratio_y) { 
  1182. $crop_width = max($crop_width, $width); 
  1183. $crop_height = (int) round($crop_width / $aspect_ratio); 
  1184. } else { 
  1185. $crop_height = max($crop_height, $height); 
  1186. $crop_width = (int) round($crop_height * $aspect_ratio); 
  1187. if ($crop_width == $crop_width_orig - 1) { 
  1188. $crop_width = $crop_width_orig; 
  1189. if ($crop_height == $crop_height_orig - 1) { 
  1190. $crop_height = $crop_height_orig; 
  1191. $crop_diff_x = (int) round(($crop_width_orig - $crop_width) / 2); 
  1192. $crop_diff_y = (int) round(($crop_height_orig - $crop_height) / 2); 
  1193. $crop_x += $crop_diff_x; 
  1194. $crop_y += $crop_diff_y; 
  1195. $crop_max_x = $crop_x + $crop_width; 
  1196. $crop_max_y = $crop_y + $crop_height; 
  1197. // Check if we're overflowing borders 
  1198. // 
  1199. if ($crop_x < 0) { 
  1200. $crop_x = 0; 
  1201. } else { 
  1202. if ($crop_max_x > $original_width) { 
  1203. $crop_x -= $crop_max_x - $original_width; 
  1204. if ($crop_y < 0) { 
  1205. $crop_y = 0; 
  1206. } else { 
  1207. if ($crop_max_y > $original_height) { 
  1208. $crop_y -= $crop_max_y - $original_height; 
  1209. } else { 
  1210. if ($orig_ratio_x < $orig_ratio_y) { 
  1211. $crop_width = $original_width; 
  1212. $crop_height = (int) round($height * $orig_ratio_x); 
  1213. } else { 
  1214. $crop_height = $original_height; 
  1215. $crop_width = (int) round($width * $orig_ratio_y); 
  1216. if ($crop_width == $width - 1) { 
  1217. $crop_width = $width; 
  1218. if ($crop_height == $height - 1) { 
  1219. $crop_height = $height; 
  1220. $crop_x = (int) round(($original_width - $crop_width) / 2); 
  1221. $crop_y = (int) round(($original_height - $crop_height) / 2); 
  1222. $result['crop_area'] = array('x' => $crop_x, 'y' => $crop_y, 'width' => $crop_width, 'height' => $crop_height); 
  1223. } else { 
  1224. // Just constraint dimensions to ensure there's no stretching or deformations 
  1225. list($width, $height) = wp_constrain_dimensions($original_width, $original_height, $width, $height); 
  1226. $result['width'] = $width; 
  1227. $result['height'] = $height; 
  1228. $result['quality'] = $quality; 
  1229. $real_width = $width; 
  1230. $real_height = $height; 
  1231. if ($rotation && in_array(abs($rotation), array(90, 270))) { 
  1232. $real_width = $height; 
  1233. $real_height = $width; 
  1234. if ($reflection) { 
  1235. // default for nextgen was 40%, this is used in generate_image_clone as well 
  1236. $reflection_amount = 40; 
  1237. // Note, round() would probably be best here but using the same code that C_NggLegacy_Thumbnail uses for compatibility 
  1238. $reflection_height = intval($real_height * ($reflection_amount / 100)); 
  1239. $real_height = $real_height + $reflection_height; 
  1240. $result['real_width'] = $real_width; 
  1241. $result['real_height'] = $real_height; 
  1242. return $result; 
  1243. /** 
  1244. * Returns an array of dimensional properties (width, height, real_width, real_height) of a resulting clone image if and when generated 
  1245. * @param string $image_path 
  1246. * @param string $clone_path 
  1247. * @param array $params 
  1248. * @return array 
  1249. */ 
  1250. function calculate_image_clone_dimensions($image_path, $clone_path, $params) 
  1251. $retval = null; 
  1252. $result = $this->object->calculate_image_clone_result($image_path, $clone_path, $params); 
  1253. if ($result != null) { 
  1254. $retval = array('width' => $result['width'], 'height' => $result['height'], 'real_width' => $result['real_width'], 'real_height' => $result['real_height']); 
  1255. return $retval; 
  1256. /** 
  1257. * Generates a "clone" for an existing image, the clone can be altered using the $params array 
  1258. * @param string $image_path 
  1259. * @param string $clone_path 
  1260. * @param array $params 
  1261. * @return object 
  1262. */ 
  1263. function generate_image_clone($image_path, $clone_path, $params) 
  1264. $crop = isset($params['crop']) ? $params['crop'] : NULL; 
  1265. $watermark = isset($params['watermark']) ? $params['watermark'] : NULL; 
  1266. $reflection = isset($params['reflection']) ? $params['reflection'] : NULL; 
  1267. $rotation = isset($params['rotation']) ? $params['rotation'] : NULL; 
  1268. $flip = isset($params['flip']) ? $params['flip'] : NULL; 
  1269. $destpath = NULL; 
  1270. $thumbnail = NULL; 
  1271. $result = $this->object->calculate_image_clone_result($image_path, $clone_path, $params); 
  1272. // XXX this should maybe be removed and extra settings go into $params? 
  1273. $settings = apply_filters('ngg_settings_during_image_generation', C_NextGen_Settings::get_instance()->to_array()); 
  1274. // Ensure we have a valid image 
  1275. if ($image_path && @file_exists($image_path) && $result != null && !isset($result['error'])) { 
  1276. $image_dir = dirname($image_path); 
  1277. $clone_path = $result['clone_path']; 
  1278. $clone_dir = $result['clone_directory']; 
  1279. $clone_format = $result['clone_format']; 
  1280. $format_list = $this->object->get_image_format_list(); 
  1281. // Ensure target directory exists, but only create 1 subdirectory 
  1282. if (!@file_exists($clone_dir)) { 
  1283. if (strtolower(realpath($image_dir)) != strtolower(realpath($clone_dir))) { 
  1284. if (strtolower(realpath($image_dir)) == strtolower(realpath(dirname($clone_dir)))) { 
  1285. wp_mkdir_p($clone_dir); 
  1286. $method = $result['method']; 
  1287. $width = $result['width']; 
  1288. $height = $result['height']; 
  1289. $quality = $result['quality']; 
  1290. if ($quality == null) { 
  1291. $quality = 100; 
  1292. if ($method == 'wordpress') { 
  1293. $original = wp_get_image_editor($image_path); 
  1294. $destpath = $clone_path; 
  1295. if (!is_wp_error($original)) { 
  1296. $original->resize($width, $height, $crop); 
  1297. $original->set_quality($quality); 
  1298. $original->save($clone_path); 
  1299. } else { 
  1300. if ($method == 'nextgen') { 
  1301. $destpath = $clone_path; 
  1302. $thumbnail = new C_NggLegacy_Thumbnail($image_path, true); 
  1303. if (!$thumbnail->error) { 
  1304. if ($crop) { 
  1305. $crop_area = $result['crop_area']; 
  1306. $crop_x = $crop_area['x']; 
  1307. $crop_y = $crop_area['y']; 
  1308. $crop_width = $crop_area['width']; 
  1309. $crop_height = $crop_area['height']; 
  1310. $thumbnail->crop($crop_x, $crop_y, $crop_width, $crop_height); 
  1311. $thumbnail->resize($width, $height); 
  1312. } else { 
  1313. $thumbnail = NULL; 
  1314. // We successfully generated the thumbnail 
  1315. if (is_string($destpath) && (@file_exists($destpath) || $thumbnail != null)) { 
  1316. if ($clone_format != null) { 
  1317. if (isset($format_list[$clone_format])) { 
  1318. $clone_format_extension = $format_list[$clone_format]; 
  1319. $clone_format_extension_str = null; 
  1320. if ($clone_format_extension != null) { 
  1321. $clone_format_extension_str = '.' . $clone_format_extension; 
  1322. $destpath_info = M_I18n::mb_pathinfo($destpath); 
  1323. $destpath_extension = $destpath_info['extension']; 
  1324. if (strtolower($destpath_extension) != strtolower($clone_format_extension)) { 
  1325. $destpath_dir = $destpath_info['dirname']; 
  1326. $destpath_basename = $destpath_info['filename']; 
  1327. $destpath_new = $destpath_dir . DIRECTORY_SEPARATOR . $destpath_basename . $clone_format_extension_str; 
  1328. if (@file_exists($destpath) && rename($destpath, $destpath_new) || $thumbnail != null) { 
  1329. $destpath = $destpath_new; 
  1330. if (is_null($thumbnail)) { 
  1331. $thumbnail = new C_NggLegacy_Thumbnail($destpath, true); 
  1332. if ($thumbnail->error) { 
  1333. $thumbnail = null; 
  1334. return null; 
  1335. } else { 
  1336. $thumbnail->fileName = $destpath; 
  1337. // This is quite odd, when watermark equals int(0) it seems all statements below ($watermark == 'image') and ($watermark == 'text') both evaluate as true 
  1338. // so we set it at null if it evaluates to any null-like value 
  1339. if ($watermark == null) { 
  1340. $watermark = null; 
  1341. if ($watermark == 1 || $watermark === true) { 
  1342. if (in_array(strval($settings['wmType']), array('image', 'text'))) { 
  1343. $watermark = $settings['wmType']; 
  1344. } else { 
  1345. $watermark = 'text'; 
  1346. $watermark = strval($watermark); 
  1347. if ($watermark == 'image') { 
  1348. $thumbnail->watermarkImgPath = $settings['wmPath']; 
  1349. $thumbnail->watermarkImage($settings['wmPos'], $settings['wmXpos'], $settings['wmYpos']); 
  1350. } else { 
  1351. if ($watermark == 'text') { 
  1352. $thumbnail->watermarkText = $settings['wmText']; 
  1353. $thumbnail->watermarkCreateText($settings['wmColor'], $settings['wmFont'], $settings['wmSize'], $settings['wmOpaque']); 
  1354. $thumbnail->watermarkImage($settings['wmPos'], $settings['wmXpos'], $settings['wmYpos']); 
  1355. if ($rotation && in_array(abs($rotation), array(90, 180, 270))) { 
  1356. $thumbnail->rotateImageAngle($rotation); 
  1357. $flip = strtolower($flip); 
  1358. if ($flip && in_array($flip, array('h', 'v', 'hv'))) { 
  1359. $flip_h = in_array($flip, array('h', 'hv')); 
  1360. $flip_v = in_array($flip, array('v', 'hv')); 
  1361. $thumbnail->flipImage($flip_h, $flip_v); 
  1362. if ($reflection) { 
  1363. $thumbnail->createReflection(40, 40, 50, FALSE, '#a4a4a4'); 
  1364. if ($clone_format != null && isset($format_list[$clone_format])) { 
  1365. // Force format 
  1366. $thumbnail->format = strtoupper($format_list[$clone_format]); 
  1367. $thumbnail = apply_filters('ngg_before_save_thumbnail', $thumbnail); 
  1368. $thumbnail->save($destpath, $quality); 
  1369. // IF the original contained IPTC metadata we should attempt to copy it 
  1370. if (isset($detailed_size['APP13']) && function_exists('iptcembed')) { 
  1371. $metadata = @iptcembed($detailed_size['APP13'], $destpath); 
  1372. $fp = @fopen($destpath, 'wb'); 
  1373. @fwrite($fp, $metadata); 
  1374. @fclose($fp); 
  1375. return $thumbnail; 
  1376. /** 
  1377. * Class C_GalleryStorage_Driver_Base 
  1378. * @mixin Mixin_GalleryStorage_Driver_Base 
  1379. * @implements I_GalleryStorage_Driver 
  1380. */ 
  1381. class C_GalleryStorage_Driver_Base extends C_GalleryStorage_Base 
  1382. public static $_instances = array(); 
  1383. function define($context = FALSE) 
  1384. parent::define($context); 
  1385. $this->add_mixin('Mixin_GalleryStorage_Driver_Base'); 
  1386. $this->implement('I_GalleryStorage_Driver'); 
  1387. function initialize() 
  1388. parent::initialize(); 
  1389. $this->_gallery_mapper = C_Gallery_Mapper::get_instance(); 
  1390. $this->_image_mapper = C_Image_Mapper::get_instance(); 
  1391. public static function get_instance($context = False) 
  1392. if (!isset(self::$_instances[$context])) { 
  1393. self::$_instances[$context] = new C_GalleryStorage_Driver_Base($context); 
  1394. return self::$_instances[$context]; 
  1395. /** 
  1396. * Gets the class name of the driver used 
  1397. * @return string 
  1398. */ 
  1399. function get_driver_class_name() 
  1400. return get_called_class(); 
  1401. class Mixin_NextGen_Gallery_Image_Validation extends Mixin 
  1402. function validation() 
  1403. // Additional checks... 
  1404. if (isset($this->object->description)) { 
  1405. $this->object->description = M_NextGen_Data::strip_html($this->object->description, TRUE); 
  1406. if (isset($this->object->alttext)) { 
  1407. $this->object->alttext = M_NextGen_Data::strip_html($this->object->alttext, TRUE); 
  1408. $this->validates_presence_of('galleryid', 'filename', 'alttext', 'exclude', 'sortorder', 'imagedate'); 
  1409. $this->validates_numericality_of('galleryid'); 
  1410. $this->validates_numericality_of($this->id()); 
  1411. $this->validates_numericality_of('sortorder'); 
  1412. return $this->object->is_valid(); 
  1413. /** 
  1414. * Model for NextGen Gallery Images 
  1415. * @mixin Mixin_NextGen_Gallery_Image_Validation 
  1416. * @implements I_Image 
  1417. */ 
  1418. class C_Image extends C_DataMapper_Model 
  1419. var $_mapper_interface = 'I_Image_Mapper'; 
  1420. function define($properties = array(), $mapper = FALSE, $context = FALSE) 
  1421. parent::define($mapper, $properties, $context); 
  1422. $this->add_mixin('Mixin_NextGen_Gallery_Image_Validation'); 
  1423. $this->implement('I_Image'); 
  1424. /** 
  1425. * Instantiates a new model 
  1426. * @param array|stdClass $properties 
  1427. * @param C_DataMapper $mapper 
  1428. * @param string $context 
  1429. */ 
  1430. function initialize($properties = array(), $mapper = FALSE, $context = FALSE) 
  1431. // Get the mapper is not specified 
  1432. if (!$mapper) { 
  1433. $mapper = $this->get_registry()->get_utility($this->_mapper_interface); 
  1434. // Initialize 
  1435. parent::initialize($mapper, $properties); 
  1436. /** 
  1437. * Returns the model representing the gallery associated with this image 
  1438. * @return C_Gallery|stdClass 
  1439. */ 
  1440. function get_gallery($model = FALSE) 
  1441. $gallery_mapper = C_Gallery_Mapper::get_instance(); 
  1442. return $gallery_mapper->find($this->galleryid, $model); 
  1443. /** 
  1444. * Class C_Image_Mapper 
  1445. * @mixin Mixin_NextGen_Table_Extras 
  1446. * @mixin Mixin_Gallery_Image_Mapper 
  1447. * @implements I_Image_Mapper 
  1448. */ 
  1449. class C_Image_Mapper extends C_CustomTable_DataMapper_Driver 
  1450. public static $_instance = NULL; 
  1451. /** 
  1452. * Defines the gallery image mapper 
  1453. * @param type $context 
  1454. */ 
  1455. function define($context = FALSE, $not_used = FALSE) 
  1456. // Add 'attachment' context 
  1457. if (!is_array($context)) { 
  1458. $context = array($context); 
  1459. array_push($context, 'attachment'); 
  1460. // Define the mapper 
  1461. $this->_primary_key_column = 'pid'; 
  1462. parent::define('ngg_pictures', $context); 
  1463. $this->add_mixin('Mixin_NextGen_Table_Extras'); 
  1464. $this->add_mixin('Mixin_Gallery_Image_Mapper'); 
  1465. $this->implement('I_Image_Mapper'); 
  1466. $this->set_model_factory_method('image'); 
  1467. // Define the columns 
  1468. $this->define_column('pid', 'BIGINT', 0); 
  1469. $this->define_column('image_slug', 'VARCHAR(255)'); 
  1470. $this->define_column('post_id', 'BIGINT', 0); 
  1471. $this->define_column('galleryid', 'BIGINT', 0); 
  1472. $this->define_column('filename', 'VARCHAR(255)'); 
  1473. $this->define_column('description', 'TEXT'); 
  1474. $this->define_column('alttext', 'TEXT'); 
  1475. $this->define_column('imagedate', 'DATETIME'); 
  1476. $this->define_column('exclude', 'INT', 0); 
  1477. $this->define_column('sortorder', 'BIGINT', 0); 
  1478. $this->define_column('meta_data', 'TEXT'); 
  1479. $this->define_column('extras_post_id', 'BIGINT', 0); 
  1480. $this->define_column('updated_at', 'BIGINT'); 
  1481. // Mark the columns which should be unserialized 
  1482. $this->add_serialized_column('meta_data'); 
  1483. function initialize($object_name = FALSE) 
  1484. parent::initialize('ngg_pictures'); 
  1485. static function get_instance($context = False) 
  1486. if (is_null(self::$_instance)) { 
  1487. $klass = get_class(); 
  1488. self::$_instance = new $klass($context); 
  1489. return self::$_instance; 
  1490. /** 
  1491. * Sets the alttext property as the post title 
  1492. */ 
  1493. class Mixin_Gallery_Image_Mapper extends Mixin 
  1494. function destroy($image) 
  1495. $retval = $this->call_parent('destroy', $image); 
  1496. // Delete tag associations with the image 
  1497. if (!is_numeric($image)) { 
  1498. $image = $image->{$image->id_field}; 
  1499. wp_delete_object_term_relationships($image, 'ngg_tag'); 
  1500. C_Photocrati_Transient_Manager::flush('displayed_gallery_rendering'); 
  1501. return $retval; 
  1502. function _save_entity($entity) 
  1503. $entity->updated_at = time(); 
  1504. // If successfully saved, then import metadata and 
  1505. $retval = $this->call_parent('_save_entity', $entity); 
  1506. if ($retval) { 
  1507. include_once NGGALLERY_ABSPATH . '/admin/functions.php'; 
  1508. $image_id = $this->get_id($entity); 
  1509. if (!isset($entity->meta_data['saved'])) { 
  1510. nggAdmin::import_MetaData($image_id); 
  1511. C_Photocrati_Transient_Manager::flush('displayed_gallery_rendering'); 
  1512. return $retval; 
  1513. function reimport_metadata($image_or_id) 
  1514. // Get the image 
  1515. $image = NULL; 
  1516. if (is_int($image_or_id)) { 
  1517. $image = $this->object->find($image_or_id); 
  1518. } else { 
  1519. $image = $image_or_id; 
  1520. // Reset all image details that would have normally been imported 
  1521. $image->alttext = ''; 
  1522. $image->description = ''; 
  1523. if (is_array($image->meta_data)) { 
  1524. unset($image->meta_data['saved']); 
  1525. wp_delete_object_term_relationships($image->{$image->id_field}, 'ngg_tag'); 
  1526. nggAdmin::import_MetaData($image); 
  1527. return $this->object->save($image); 
  1528. /** 
  1529. * Retrieves the id from an image 
  1530. * @param $image 
  1531. * @return bool 
  1532. */ 
  1533. function get_id($image) 
  1534. $retval = FALSE; 
  1535. // Have we been passed an entity and is the id_field set? 
  1536. if ($image instanceof stdClass) { 
  1537. if (isset($image->id_field)) { 
  1538. $retval = $image->{$image->id_field}; 
  1539. } else { 
  1540. $retval = $image->id(); 
  1541. // If we still don't have an id, then we'll lookup the primary key 
  1542. // and try fetching it manually 
  1543. if (!$retval) { 
  1544. $key = $this->object->get_primary_key_column(); 
  1545. $retval = $image->{$key}; 
  1546. return $retval; 
  1547. function get_post_title($entity) 
  1548. return $entity->alttext; 
  1549. function set_defaults($entity) 
  1550. // If not set already, we'll add an exclude property. This is used 
  1551. // by NextGEN Gallery itself, as well as the Attach to Post module 
  1552. $this->object->_set_default_value($entity, 'exclude', 0); 
  1553. // Ensure that the object has a description attribute 
  1554. $this->object->_set_default_value($entity, 'description', ''); 
  1555. // If not set already, set a default sortorder 
  1556. $this->object->_set_default_value($entity, 'sortorder', 0); 
  1557. // The imagedate must be set 
  1558. if (!isset($entity->imagedate) or is_null($entity->imagedate) or $entity->imagedate == '0000-00-00 00:00:00') { 
  1559. $entity->imagedate = date("Y-m-d H:i:s"); 
  1560. // If a filename is set, and no alttext is set, then set the alttext 
  1561. // to the basename of the filename (legacy behavior) 
  1562. if (isset($entity->filename)) { 
  1563. $path_parts = M_I18n::mb_pathinfo($entity->filename); 
  1564. $alttext = !isset($path_parts['filename']) ? substr($path_parts['basename'], 0, strpos($path_parts['basename'], '.')) : $path_parts['filename']; 
  1565. $this->object->_set_default_value($entity, 'alttext', $alttext); 
  1566. // Set unique slug 
  1567. if (isset($entity->alttext) && empty($entity->image_slug)) { 
  1568. $entity->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($entity->alttext), 'image'); 
  1569. // Ensure that the exclude parameter is an integer or boolean-evaluated 
  1570. // value 
  1571. if (is_string($entity->exclude)) { 
  1572. $entity->exclude = intval($entity->exclude); 
  1573. // Trim alttext and description 
  1574. $entity->description = trim($entity->description); 
  1575. $entity->alttext = trim($entity->alttext); 
  1576. /** 
  1577. * Finds all images for a gallery 
  1578. * @param $gallery 
  1579. * @param bool $model 
  1580. * 
  1581. * @return array 
  1582. */ 
  1583. function find_all_for_gallery($gallery, $model = FALSE) 
  1584. $retval = array(); 
  1585. $gallery_id = 0; 
  1586. if (is_object($gallery)) { 
  1587. if (isset($gallery->id_field)) { 
  1588. $gallery_id = $gallery->{$gallery->id_field}; 
  1589. } else { 
  1590. $key = $this->object->get_primary_key_column(); 
  1591. if (isset($gallery->{$key})) { 
  1592. $gallery_id = $gallery->{$key}; 
  1593. } elseif (is_numeric($gallery)) { 
  1594. $gallery_id = $gallery; 
  1595. if ($gallery_id) { 
  1596. $retval = $this->object->select()->where(array("galleryid = %s", $gallery_id))->run_query(FALSE, $model); 
  1597. return $retval; 
  1598. /** 
  1599. * This class provides a lazy-loading wrapper to the NextGen-Legacy "nggImage" class for use in legacy style templates 
  1600. */ 
  1601. class C_Image_Wrapper 
  1602. public $_cache; 
  1603. // cache of retrieved values 
  1604. public $_settings; 
  1605. // I_Settings_Manager cache 
  1606. public $_storage; 
  1607. // I_Gallery_Storage cache 
  1608. public $_galleries; 
  1609. // cache of I_Gallery_Mapper (plural) 
  1610. public $_orig_image; 
  1611. // original provided image 
  1612. public $_orig_image_id; 
  1613. // original image ID 
  1614. public $_cache_overrides; 
  1615. // allow for forcing variable values 
  1616. public $_legacy = FALSE; 
  1617. public $_displayed_gallery; 
  1618. // cached object 
  1619. /** 
  1620. * Constructor. Converts the image class into an array and fills from defaults any missing values 
  1621. * 
  1622. * @param object $gallery Individual result from displayed_gallery->get_entities() 
  1623. * @param object $displayed_gallery Displayed gallery -- MAY BE NULL 
  1624. * @param bool $legacy Whether the image source is from NextGen Legacy or NextGen 
  1625. * @return void 
  1626. */ 
  1627. public function __construct($image, $displayed_gallery = NULL, $legacy = FALSE) 
  1628. // for clarity 
  1629. if ($displayed_gallery && isset($displayed_gallery->display_settings['number_of_columns'])) { 
  1630. $columns = $displayed_gallery->display_settings['number_of_columns']; 
  1631. } else { 
  1632. $columns = 0; 
  1633. // Public variables 
  1634. $defaults = array('errmsg' => '', 'error' => FALSE, 'imageURL' => '', 'thumbURL' => '', 'imagePath' => '', 'thumbPath' => '', 'href' => '', 'thumbPrefix' => 'thumbs_', 'thumbFolder' => '/thumbs/', 'galleryid' => 0, 'pid' => 0, 'filename' => '', 'description' => '', 'alttext' => '', 'imagedate' => '', 'exclude' => '', 'thumbcode' => '', 'name' => '', 'path' => '', 'title' => '', 'pageid' => 0, 'previewpic' => 0, 'style' => $columns > 0 ? 'style="width:' . floor(100 / $columns) . '%;"' : '', 'hidden' => FALSE, 'permalink' => '', 'tags' => ''); 
  1635. // convert the image to an array and apply the defaults 
  1636. $this->_orig_image = $image; 
  1637. $image = (array) $image; 
  1638. foreach ($defaults as $key => $val) { 
  1639. if (!isset($image[$key])) { 
  1640. $image[$key] = $val; 
  1641. // cache the results 
  1642. ksort($image); 
  1643. $id_field = !empty($image['id_field']) ? $image['id_field'] : 'pid'; 
  1644. $this->_cache = (array) apply_filters('ngg_image_object', (object) $image, $image[$id_field]); 
  1645. $this->_orig_image_id = $image[$id_field]; 
  1646. $this->_legacy = $legacy; 
  1647. $this->_displayed_gallery = $displayed_gallery; 
  1648. public function __set($name, $value) 
  1649. $this->_cache[$name] = $value; 
  1650. public function __isset($name) 
  1651. return isset($this->_cache[$name]); 
  1652. public function __unset($name) 
  1653. unset($this->_cache[$name]); 
  1654. /** 
  1655. * Lazy-loader for image variables. 
  1656. * 
  1657. * @param string $name Parameter name 
  1658. * @return mixed 
  1659. */ 
  1660. public function __get($name) 
  1661. if (isset($this->_cache_overrides[$name])) { 
  1662. return $this->_cache_overrides[$name]; 
  1663. // at the bottom we default to returning $this->_cache[$name]. 
  1664. switch ($name) { 
  1665. case 'alttext': 
  1666. $this->_cache['alttext'] = empty($this->_cache['alttext']) ? ' ' : html_entity_decode(stripslashes($this->_cache['alttext'])); 
  1667. return $this->_cache['alttext']; 
  1668. case 'author': 
  1669. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1670. $this->_cache['author'] = $gallery->name; 
  1671. return $this->_cache['author']; 
  1672. case 'caption': 
  1673. $caption = html_entity_decode(stripslashes($this->__get('description'))); 
  1674. if (empty($caption)) { 
  1675. $caption = ' '; 
  1676. $this->_cache['caption'] = $caption; 
  1677. return $this->_cache['caption']; 
  1678. case 'description': 
  1679. $this->_cache['description'] = empty($this->_cache['description']) ? ' ' : html_entity_decode(stripslashes($this->_cache['description'])); 
  1680. return $this->_cache['description']; 
  1681. case 'galdesc': 
  1682. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1683. $this->_cache['galdesc'] = $gallery->name; 
  1684. return $this->_cache['galdesc']; 
  1685. case 'gid': 
  1686. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1687. $this->_cache['gid'] = $gallery->{$gallery->id_field}; 
  1688. return $this->_cache['gid']; 
  1689. case 'href': 
  1690. return $this->__get('imageHTML'); 
  1691. case 'id': 
  1692. return $this->_orig_image_id; 
  1693. case 'imageHTML': 
  1694. $tmp = '<a href="' . $this->__get('imageURL') . '" title="' . htmlspecialchars(stripslashes($this->__get('description'))) . '" ' . $this->get_thumbcode($this->__get('name')) . '>' . '<img alt="' . $this->__get('alttext') . '" src="' . $this->__get('imageURL') . '"/>' . '</a>'; 
  1695. $this->_cache['href'] = $tmp; 
  1696. $this->_cache['imageHTML'] = $tmp; 
  1697. return $this->_cache['imageHTML']; 
  1698. case 'imagePath': 
  1699. $storage = $this->get_storage(); 
  1700. $this->_cache['imagePath'] = $storage->get_image_abspath($this->_orig_image, 'full'); 
  1701. return $this->_cache['imagePath']; 
  1702. case 'imageURL': 
  1703. $storage = $this->get_storage(); 
  1704. $this->_cache['imageURL'] = $storage->get_image_url($this->_orig_image, 'full'); 
  1705. return $this->_cache['imageURL']; 
  1706. case 'linktitle': 
  1707. $this->_cache['linktitle'] = htmlspecialchars(stripslashes($this->__get('description'))); 
  1708. return $this->_cache['linktitle']; 
  1709. case 'name': 
  1710. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1711. $this->_cache['name'] = $gallery->name; 
  1712. return $this->_cache['name']; 
  1713. case 'pageid': 
  1714. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1715. $this->_cache['pageid'] = $gallery->name; 
  1716. return $this->_cache['pageid']; 
  1717. case 'path': 
  1718. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1719. $this->_cache['path'] = $gallery->name; 
  1720. return $this->_cache['path']; 
  1721. case 'permalink': 
  1722. $this->_cache['permalink'] = $this->__get('imageURL'); 
  1723. return $this->_cache['permalink']; 
  1724. case 'pid': 
  1725. return $this->_orig_image_id; 
  1726. case 'id_field': 
  1727. $this->_cache['id_field'] = !empty($this->_orig_image->id_field) ? $this->_orig_image->id_field : 'pid'; 
  1728. return $this->_cache['id_field']; 
  1729. case 'pidlink': 
  1730. $application = C_Router::get_instance()->get_routed_app(); 
  1731. $controller = C_Display_Type_Controller::get_instance(); 
  1732. $this->_cache['pidlink'] = $controller->set_param_for($application->get_routed_url(TRUE), 'pid', $this->__get('image_slug')); 
  1733. return $this->_cache['pidlink']; 
  1734. case 'previewpic': 
  1735. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1736. $this->_cache['previewpic'] = $gallery->name; 
  1737. return $this->_cache['previewpic']; 
  1738. case 'size': 
  1739. $w = 0; 
  1740. $h = 0; 
  1741. if ($this->_displayed_gallery && isset($this->_displayed_gallery->display_settings)) { 
  1742. $ds = $this->_displayed_gallery->display_settings; 
  1743. if (isset($ds['override_thumbnail_settings']) && $ds['override_thumbnail_settings']) { 
  1744. $w = $ds['thumbnail_width']; 
  1745. $h = $ds['thumbnail_height']; 
  1746. if (!$w || !$h) { 
  1747. if (is_string($this->_orig_image->meta_data)) { 
  1748. $this->_orig_image = C_Image_Mapper::get_instance()->unserialize($this->_orig_image->meta_data); 
  1749. if (!isset($this->_orig_image->meta_data['thumbnail'])) { 
  1750. $storage = $this->get_storage(); 
  1751. $storage->generate_thumbnail($this->_orig_image); 
  1752. $w = $this->_orig_image->meta_data['thumbnail']['width']; 
  1753. $h = $this->_orig_image->meta_data['thumbnail']['height']; 
  1754. return "width='{$w}' height='{$h}'"; 
  1755. case 'slug': 
  1756. $gallery = $this->get_legacy_gallery($this->__get('galleryid')); 
  1757. $this->_cache['slug'] = $gallery->name; 
  1758. return $this->_cache['slug']; 
  1759. case 'tags': 
  1760. $this->_cache['tags'] = wp_get_object_terms($this->__get('id'), 'ngg_tag', 'fields=all'); 
  1761. return $this->_cache['tags']; 
  1762. case 'thumbHTML': 
  1763. $tmp = '<a href="' . $this->__get('imageURL') . '" title="' . htmlspecialchars(stripslashes($this->__get('description'))) . '" ' . $this->get_thumbcode($this->__get('name')) . '>' . '<img alt="' . $this->__get('alttext') . '" src="' . $this->thumbURL . '"/>' . '</a>'; 
  1764. $this->_cache['href'] = $tmp; 
  1765. $this->_cache['thumbHTML'] = $tmp; 
  1766. return $this->_cache['thumbHTML']; 
  1767. case 'thumbPath': 
  1768. $storage = $this->get_storage(); 
  1769. $this->_cache['thumbPath'] = $storage->get_image_abspath($this->_orig_image, 'thumbnail'); 
  1770. return $this->_cache['thumbPath']; 
  1771. case 'thumbnailURL': 
  1772. $storage = $this->get_storage(); 
  1773. $thumbnail_size_name = 'thumbnail'; 
  1774. if ($this->_displayed_gallery && isset($this->_displayed_gallery->display_settings)) { 
  1775. $ds = $this->_displayed_gallery->display_settings; 
  1776. if (isset($ds['override_thumbnail_settings']) && $ds['override_thumbnail_settings']) { 
  1777. $dynthumbs = C_Component_Registry::get_instance()->get_utility('I_Dynamic_Thumbnails_Manager'); 
  1778. $dyn_params = array('width' => $ds['thumbnail_width'], 'height' => $ds['thumbnail_height']); 
  1779. if ($ds['thumbnail_quality']) { 
  1780. $dyn_params['quality'] = $ds['thumbnail_quality']; 
  1781. if ($ds['thumbnail_crop']) { 
  1782. $dyn_params['crop'] = TRUE; 
  1783. if ($ds['thumbnail_watermark']) { 
  1784. $dyn_params['watermark'] = TRUE; 
  1785. $thumbnail_size_name = $dynthumbs->get_size_name($dyn_params); 
  1786. $this->_cache['thumbnailURL'] = $storage->get_image_url($this->_orig_image, $thumbnail_size_name); 
  1787. return $this->_cache['thumbnailURL']; 
  1788. case 'thumbcode': 
  1789. if ($this->_displayed_gallery && isset($this->_displayed_gallery->display_settings) && isset($this->_displayed_gallery->display_settings['use_imagebrowser_effect']) && $this->_displayed_gallery->display_settings['use_imagebrowser_effect'] && !empty($this->_orig_image->thumbcode)) { 
  1790. $this->_cache['thumbcode'] = $this->_orig_image->thumbcode; 
  1791. } else { 
  1792. $this->_cache['thumbcode'] = $this->get_thumbcode($this->__get('name')); 
  1793. return $this->_cache['thumbcode']; 
  1794. case 'thumbURL': 
  1795. return $this->__get('thumbnailURL'); 
  1796. case 'title': 
  1797. $this->_cache['title'] = stripslashes($this->__get('name')); 
  1798. return $this->_cache['title']; 
  1799. case 'url': 
  1800. $storage = $this->get_storage(); 
  1801. $this->_cache['url'] = $storage->get_image_url($this->_orig_image, 'full'); 
  1802. return $this->_cache['url']; 
  1803. default: 
  1804. return $this->_cache[$name]; 
  1805. // called on initial nggLegacy image at construction. not sure what to do with it now. 
  1806. function construct_ngg_Image($gallery) 
  1807. do_action_ref_array('ngg_get_image', array(&$this)); 
  1808. unset($this->tags); 
  1809. /** 
  1810. * Retrieves and caches an I_Settings_Manager instance 
  1811. * 
  1812. * @return mixed 
  1813. */ 
  1814. function get_settings() 
  1815. if (is_null($this->_settings)) { 
  1816. $this->_settings = C_NextGen_Settings::get_instance(); 
  1817. return $this->_settings; 
  1818. /** 
  1819. * Retrieves and caches an I_Gallery_Storage instance 
  1820. * 
  1821. * @return mixed 
  1822. */ 
  1823. function get_storage() 
  1824. if (is_null($this->_storage)) { 
  1825. $this->_storage = C_Gallery_Storage::get_instance(); 
  1826. return $this->_storage; 
  1827. /** 
  1828. * Retrieves I_Gallery_Mapper instance. 
  1829. * 
  1830. * @param int $gallery_id Gallery ID 
  1831. * @return mixed 
  1832. */ 
  1833. function get_gallery($gallery_id) 
  1834. if (isset($this->container) && method_exists($this->container, 'get_gallery')) { 
  1835. return $this->container->get_gallery($gallery_id); 
  1836. return C_Gallery_Mapper::get_instance()->find($gallery_id); 
  1837. /** 
  1838. * Retrieves I_Gallery_Mapper instance. 
  1839. * 
  1840. * @param int $gallery_id Gallery ID 
  1841. * @return mixed 
  1842. */ 
  1843. function get_legacy_gallery($gallery_id) 
  1844. return C_Gallery_Mapper::get_instance()->find($gallery_id); 
  1845. /** 
  1846. * Get the thumbnail code (to add effects on thumbnail click) 
  1847. * 
  1848. * Applies the filter 'ngg_get_thumbcode' 
  1849. */ 
  1850. function get_thumbcode($gallery_name = '') 
  1851. if (empty($this->_displayed_gallery)) { 
  1852. $effect_code = C_NextGen_Settings::get_instance()->thumbCode; 
  1853. $effect_code = str_replace('%GALLERY_ID%', $gallery_name, $effect_code); 
  1854. $effect_code = str_replace('%GALLERY_NAME%', $gallery_name, $effect_code); 
  1855. $retval = $effect_code; 
  1856. } else { 
  1857. $controller = C_Display_Type_Controller::get_instance(); 
  1858. $retval = $controller->get_effect_code($this->_displayed_gallery); 
  1859. // This setting requires that we disable the effect code 
  1860. $ds = $this->_displayed_gallery->display_settings; 
  1861. if (isset($ds['use_imagebrowser_effect']) && $ds['use_imagebrowser_effect']) { 
  1862. $retval = ''; 
  1863. $retval = apply_filters('ngg_get_thumbcode', $retval, $this); 
  1864. // ensure some additional data- fields are added; provides Pro-Lightbox compatibility 
  1865. $retval .= ' data-image-id="' . $this->__get('id') . '"'; 
  1866. $retval .= ' data-src="' . $this->__get('imageURL') . '"'; 
  1867. $retval .= ' data-thumbnail="' . $this->__get('thumbnailURL') . '"'; 
  1868. $retval .= ' data-title="' . esc_attr($this->__get('alttext')) . '"'; 
  1869. $retval .= ' data-description="' . esc_attr($this->__get('description')) . '"'; 
  1870. $this->_cache['thumbcode'] = $retval; 
  1871. return $retval; 
  1872. /** 
  1873. * For compatibility support 
  1874. * 
  1875. * @return mixed 
  1876. */ 
  1877. function get_href_link() 
  1878. return $this->__get('imageHTML'); 
  1879. /** 
  1880. * For compatibility support 
  1881. * 
  1882. * @return mixed 
  1883. */ 
  1884. function get_href_thumb_link() 
  1885. return $this->__get('thumbHTML'); 
  1886. /** 
  1887. * Function exists for legacy support but has been gutted to not do anything 
  1888. * 
  1889. * @param int $width 
  1890. * @param int $height 
  1891. * @param string $mode could be watermark | web20 | crop 
  1892. * @return the url for the image or false if failed 
  1893. */ 
  1894. function cached_singlepic_file($width = '', $height = '', $mode = '') 
  1895. $dynthumbs = C_Dynamic_Thumbnails_Manager::get_instance(); 
  1896. $storage = $this->get_storage(); 
  1897. // determine what to do with 'mode' 
  1898. $display_reflection = FALSE; 
  1899. $display_watermark = FALSE; 
  1900. if (!is_array($mode)) { 
  1901. $mode = explode(', ', $mode); 
  1902. if (in_array('web20', $mode)) { 
  1903. $display_reflection = TRUE; 
  1904. if (in_array('watermark', $mode)) { 
  1905. $display_watermark = TRUE; 
  1906. // and go for it 
  1907. $params = array('width' => $width, 'height' => $height, 'watermark' => $display_watermark, 'reflection' => $display_reflection); 
  1908. return $storage->get_image_url((object) $this->_cache, $dynthumbs->get_size_name($params)); 
  1909. /** 
  1910. * Get the tags associated to this image 
  1911. */ 
  1912. function get_tags() 
  1913. return $this->__get('tags'); 
  1914. /** 
  1915. * Get the permalink to the image 
  1916. * 
  1917. * TODO: Get a permalink to a page presenting the image 
  1918. */ 
  1919. function get_permalink() 
  1920. return $this->__get('permalink'); 
  1921. /** 
  1922. * Returns the _cache array; used by nggImage 
  1923. * @return array 
  1924. */ 
  1925. function _get_image() 
  1926. return $this->_cache; 
  1927. class C_Image_Wrapper_Collection implements ArrayAccess 
  1928. public $container = array(); 
  1929. public $galleries = array(); 
  1930. public function offsetExists($offset) 
  1931. return isset($this->container[$offset]); 
  1932. public function offsetGet($offset) 
  1933. return isset($this->container[$offset]) ? $this->container[$offset] : null; 
  1934. public function offsetSet($offset, $value) 
  1935. if (is_object($value)) { 
  1936. $value->container = $this; 
  1937. if (is_null($offset)) { 
  1938. $this->container[] = $value; 
  1939. } else { 
  1940. $this->container[$offset] = $value; 
  1941. public function offsetUnset($offset) 
  1942. unset($this->container[$offset]); 
  1943. /** 
  1944. * Retrieves and caches an I_Gallery_Mapper instance for this gallery id 
  1945. * 
  1946. * @param int $gallery_id Gallery ID 
  1947. * @return mixed 
  1948. */ 
  1949. public function get_gallery($gallery_id) 
  1950. if (!isset($this->galleries[$gallery_id]) || is_null($this->galleries[$gallery_id])) { 
  1951. $this->galleries[$gallery_id] = C_Gallery_Mapper::get_instance(); 
  1952. return $this->galleries[$gallery_id]; 
  1953. class C_NextGen_Data_Installer extends C_NggLegacy_Installer 
  1954. function get_registry() 
  1955. return C_Component_Registry::get_instance(); 
  1956. function install() 
  1957. $this->remove_table_extra_options(); 
  1958. function remove_table_extra_options() 
  1959. global $wpdb; 
  1960. $likes = array("option_name LIKE '%ngg_gallery%'", "option_name LIKE '%ngg_pictures%'", "option_name LIKE '%ngg_album%'"); 
  1961. $sql = "DELETE FROM {$wpdb->options} WHERE " . implode(" OR ", $likes); 
  1962. $wpdb->query($sql); 
  1963. function uninstall($hard = FALSE) 
  1964. if ($hard) { 
  1965. /** Yes: this is commented twice. 
  1966. // TODO for now never delete galleries/albums/content 
  1967. # $mappers = array( 
  1968. # $this->get_registry()->get_utility('I_Album_Mapper'),  
  1969. # $this->get_registry()->get_utility('I_Gallery_Mapper'),  
  1970. # $this->get_registry()->get_utility('I_Image_Mapper'),  
  1971. # ); 
  1972.  
  1973. # foreach ($mappers as $mapper) { 
  1974. # $mapper->delete()->run_query(); 
  1975. # } 
  1976.  
  1977. # // Remove ngg tags 
  1978. # global $wpdb; 
  1979. # $wpdb->query("DELETE FROM {$wpdb->terms} WHERE term_id IN (SELECT term_id FROM {$wpdb->term_taxonomy} WHERE taxonomy='ngg_tag')"); 
  1980. # $wpdb->query("DELETE FROM {$wpdb->term_taxonomy} WHERE taxonomy='ngg_tag'"); 
  1981. */ 
  1982. class C_NextGen_Metadata extends C_Component 
  1983. // Image data 
  1984. public $image = ''; 
  1985. // The image object 
  1986. public $file_path = ''; 
  1987. // Path to the image file 
  1988. public $size = FALSE; 
  1989. // The image size 
  1990. public $exif_data = FALSE; 
  1991. // EXIF data array 
  1992. public $iptc_data = FALSE; 
  1993. // IPTC data array 
  1994. public $xmp_data = FALSE; 
  1995. // XMP data array 
  1996. // Filtered Data 
  1997. public $exif_array = FALSE; 
  1998. // EXIF data array 
  1999. public $iptc_array = FALSE; 
  2000. // IPTC data array 
  2001. public $xmp_array = FALSE; 
  2002. // XMP data array 
  2003. public $sanitize = FALSE; 
  2004. // sanitize meta data on request 
  2005. /** 
  2006. * Class constructor 
  2007. *  
  2008. * @param int $image Image ID 
  2009. * @param bool $onlyEXIF TRUE = will parse only EXIF data 
  2010. * @return bool FALSE if the file does not exist or metadat could not be read 
  2011. */ 
  2012. public function __construct($image, $onlyEXIF = FALSE) 
  2013. if (is_numeric($image)) { 
  2014. $image = C_Image_Mapper::get_instance()->find($image); 
  2015. $this->image = apply_filters('ngg_find_image_meta', $image); 
  2016. $this->file_path = C_Gallery_Storage::get_instance()->get_image_abspath($this->image); 
  2017. if (!@file_exists($this->file_path)) { 
  2018. return FALSE; 
  2019. $this->size = @getimagesize($this->file_path, $metadata); 
  2020. if ($this->size && is_array($metadata)) { 
  2021. // get exif - data 
  2022. if (is_callable('exif_read_data')) { 
  2023. $this->exif_data = @exif_read_data($this->file_path, NULL, TRUE); 
  2024. // stop here if we didn't need other meta data 
  2025. if ($onlyEXIF) { 
  2026. return TRUE; 
  2027. // get the iptc data - should be in APP13 
  2028. if (is_callable('iptcparse') && isset($metadata['APP13'])) { 
  2029. $this->iptc_data = @iptcparse($metadata['APP13']); 
  2030. // get the xmp data in a XML format 
  2031. if (is_callable('xml_parser_create')) { 
  2032. $this->xmp_data = $this->extract_XMP($this->file_path); 
  2033. return TRUE; 
  2034. return FALSE; 
  2035. /** 
  2036. * return the saved meta data from the database 
  2037. * 
  2038. * @since 1.4.0 
  2039. * @param string $object (optional) 
  2040. * @return array|mixed return either the complete array or the single object 
  2041. */ 
  2042. function get_saved_meta($object = false) 
  2043. $meta = $this->image->meta_data; 
  2044. if (!isset($meta['saved'])) { 
  2045. $meta['saved'] = FALSE; 
  2046. //check if we already import the meta data to the database 
  2047. if (!is_array($meta) || $meta['saved'] != true) { 
  2048. return false; 
  2049. // return one element if requested 
  2050. if ($object) { 
  2051. return $meta[$object]; 
  2052. //removed saved parameter we don't need that to show 
  2053. unset($meta['saved']); 
  2054. // and remove empty tags or arrays 
  2055. foreach ($meta as $key => $value) { 
  2056. if (empty($value) or is_array($value)) { 
  2057. unset($meta[$key]); 
  2058. // on request sanitize the output 
  2059. if ($this->sanitize == true) { 
  2060. array_walk($meta, create_function('&$value', '$value = esc_html($value);')); 
  2061. return $meta; 
  2062. /** 
  2063. * nggMeta::get_EXIF() 
  2064. * See also http://trac.wordpress.org/changeset/6313 
  2065. * 
  2066. * @return structured EXIF data 
  2067. */ 
  2068. function get_EXIF($object = false) 
  2069. if (!$this->exif_data) { 
  2070. return false; 
  2071. if (!is_array($this->exif_array)) { 
  2072. $meta = array(); 
  2073. if (isset($this->exif_data['EXIF'])) { 
  2074. $exif = $this->exif_data['EXIF']; 
  2075. if (!empty($exif['FNumber'])) { 
  2076. $meta['aperture'] = 'F ' . round($this->exif_frac2dec($exif['FNumber']), 2); 
  2077. if (!empty($exif['Model'])) { 
  2078. $meta['camera'] = trim($exif['Model']); 
  2079. if (!empty($exif['DateTimeDigitized'])) { 
  2080. $meta['created_timestamp'] = $this->exif_date2ts($exif['DateTimeDigitized']); 
  2081. } else { 
  2082. if (!empty($exif['DateTimeOriginal'])) { 
  2083. $meta['created_timestamp'] = $this->exif_date2ts($exif['DateTimeOriginal']); 
  2084. } else { 
  2085. if (!empty($exif['FileDateTime'])) { 
  2086. $meta['created_timestamp'] = $this->exif_date2ts($exif['FileDateTime']); 
  2087. if (!empty($exif['FocalLength'])) { 
  2088. $meta['focal_length'] = $this->exif_frac2dec($exif['FocalLength']) . __(' mm', 'nggallery'); 
  2089. if (!empty($exif['ISOSpeedRatings'])) { 
  2090. $meta['iso'] = $exif['ISOSpeedRatings']; 
  2091. if (!empty($exif['ExposureTime'])) { 
  2092. $meta['shutter_speed'] = $this->exif_frac2dec($exif['ExposureTime']); 
  2093. $meta['shutter_speed'] = ($meta['shutter_speed'] > 0.0 and $meta['shutter_speed'] < 1.0) ? '1/' . round(1 / $meta['shutter_speed'], -1) : $meta['shutter_speed']; 
  2094. $meta['shutter_speed'] .= __(' sec', 'nggallery'); 
  2095. //Bit 0 indicates the flash firing status 
  2096. if (!empty($exif['Flash'])) { 
  2097. $meta['flash'] = $exif['Flash'] & 1 ? __('Fired', 'nggallery') : __('Not fired', ' nggallery'); 
  2098. // additional information 
  2099. if (isset($this->exif_data['IFD0'])) { 
  2100. $exif = $this->exif_data['IFD0']; 
  2101. if (!empty($exif['Model'])) { 
  2102. $meta['camera'] = $exif['Model']; 
  2103. if (!empty($exif['Make'])) { 
  2104. $meta['make'] = $exif['Make']; 
  2105. if (!empty($exif['ImageDescription'])) { 
  2106. $meta['title'] = $this->utf8_encode($exif['ImageDescription']); 
  2107. if (!empty($exif['Orientation'])) { 
  2108. $meta['Orientation'] = $exif['Orientation']; 
  2109. // this is done by Windows 
  2110. if (isset($this->exif_data['WINXP'])) { 
  2111. $exif = $this->exif_data['WINXP']; 
  2112. if (!empty($exif['Title']) && empty($meta['title'])) { 
  2113. $meta['title'] = $this->utf8_encode($exif['Title']); 
  2114. if (!empty($exif['Author'])) { 
  2115. $meta['author'] = $this->utf8_encode($exif['Author']); 
  2116. if (!empty($exif['Keywords'])) { 
  2117. $meta['tags'] = $this->utf8_encode($exif['Keywords']); 
  2118. if (!empty($exif['Subject'])) { 
  2119. $meta['subject'] = $this->utf8_encode($exif['Subject']); 
  2120. if (!empty($exif['Comments'])) { 
  2121. $meta['caption'] = $this->utf8_encode($exif['Comments']); 
  2122. $this->exif_array = $meta; 
  2123. // return one element if requested 
  2124. if ($object == true) { 
  2125. $value = isset($this->exif_array[$object]) ? $this->exif_array[$object] : false; 
  2126. return $value; 
  2127. // on request sanitize the output 
  2128. if ($this->sanitize == true) { 
  2129. array_walk($this->exif_array, create_function('&$value', '$value = esc_html($value);')); 
  2130. return $this->exif_array; 
  2131. // convert a fraction string to a decimal 
  2132. function exif_frac2dec($str) 
  2133. @(list($n, $d) = explode('/', $str)); 
  2134. if (!empty($d)) { 
  2135. return $n / $d; 
  2136. return $str; 
  2137. // convert the exif date format to a unix timestamp 
  2138. function exif_date2ts($str) 
  2139. $retval = is_numeric($str) ? $str : @strtotime($str); 
  2140. if (!$retval && $str) { 
  2141. @(list($date, $time) = explode(' ', trim($str))); 
  2142. @(list($y, $m, $d) = explode(':', $date)); 
  2143. $retval = strtotime("{$y}-{$m}-{$d} {$time}"); 
  2144. return $retval; 
  2145. /** 
  2146. * nggMeta::readIPTC() - IPTC Data Information for EXIF Display 
  2147. * 
  2148. * @param mixed $output_tag 
  2149. * @return IPTC-tags 
  2150. */ 
  2151. function get_IPTC($object = false) 
  2152. if (!$this->iptc_data) { 
  2153. return false; 
  2154. if (!is_array($this->iptc_array)) { 
  2155. // --------- Set up Array Functions --------- // 
  2156. $iptcTags = array("2#005" => 'title', "2#007" => 'status', "2#012" => 'subject', "2#015" => 'category', "2#025" => 'keywords', "2#055" => 'created_date', "2#060" => 'created_time', "2#080" => 'author', "2#085" => 'position', "2#090" => 'city', "2#092" => 'location', "2#095" => 'state', "2#100" => 'country_code', "2#101" => 'country', "2#105" => 'headline', "2#110" => 'credit', "2#115" => 'source', "2#116" => 'copyright', "2#118" => 'contact', "2#120" => 'caption'); 
  2157. $meta = array(); 
  2158. foreach ($iptcTags as $key => $value) { 
  2159. if (isset($this->iptc_data[$key])) { 
  2160. $meta[$value] = trim($this->utf8_encode(implode(", ", $this->iptc_data[$key]))); 
  2161. $this->iptc_array = $meta; 
  2162. // return one element if requested 
  2163. if ($object) { 
  2164. return isset($this->iptc_array[$object]) ? $this->iptc_array[$object] : NULL; 
  2165. // on request sanitize the output 
  2166. if ($this->sanitize == true) { 
  2167. array_walk($this->iptc_array, create_function('&$value', '$value = esc_html($value);')); 
  2168. return $this->iptc_array; 
  2169. /** 
  2170. * nggMeta::extract_XMP() 
  2171. * get XMP DATA 
  2172. * code by Pekka Saarinen http://photography-on-the.net 
  2173. * 
  2174. * @param mixed $filename 
  2175. * @return XML data 
  2176. */ 
  2177. function extract_XMP($filename) 
  2178. //TODO:Require a lot of memory, could be better 
  2179. ob_start(); 
  2180. @readfile($filename); 
  2181. $source = ob_get_contents(); 
  2182. ob_end_clean(); 
  2183. $start = strpos($source, "<x:xmpmeta"); 
  2184. $end = strpos($source, "</x:xmpmeta>"); 
  2185. if (!$start === false && !$end === false) { 
  2186. $lenght = $end - $start; 
  2187. $xmp_data = substr($source, $start, $lenght + 12); 
  2188. unset($source); 
  2189. return $xmp_data; 
  2190. unset($source); 
  2191. return false; 
  2192. /** 
  2193. * nggMeta::get_XMP() 
  2194. * 
  2195. * @package Taken from http://php.net/manual/en/function.xml-parse-into-struct.php 
  2196. * @author Alf Marius Foss Olsen & Alex Rabe 
  2197. * @return XML Array or object 
  2198. * 
  2199. */ 
  2200. function get_XMP($object = false) 
  2201. if (!$this->xmp_data) { 
  2202. return false; 
  2203. if (!is_array($this->xmp_array)) { 
  2204. $parser = xml_parser_create(); 
  2205. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 
  2206. // Dont mess with my cAsE sEtTings 
  2207. xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); 
  2208. // Dont bother with empty info 
  2209. xml_parse_into_struct($parser, $this->xmp_data, $values); 
  2210. xml_parser_free($parser); 
  2211. $xmlarray = array(); 
  2212. // The XML array 
  2213. $this->xmp_array = array(); 
  2214. // The returned array 
  2215. $stack = array(); 
  2216. // tmp array used for stacking 
  2217. $list_array = array(); 
  2218. // tmp array for list elements 
  2219. $list_element = false; 
  2220. // rdf:li indicator 
  2221. foreach ($values as $val) { 
  2222. if ($val['type'] == "open") { 
  2223. array_push($stack, $val['tag']); 
  2224. } elseif ($val['type'] == "close") { 
  2225. // reset the compared stack 
  2226. if ($list_element == false) { 
  2227. array_pop($stack); 
  2228. // reset the rdf:li indicator & array 
  2229. $list_element = false; 
  2230. $list_array = array(); 
  2231. } elseif ($val['type'] == "complete") { 
  2232. if ($val['tag'] == "rdf:li") { 
  2233. // first go one element back 
  2234. if ($list_element == false) { 
  2235. array_pop($stack); 
  2236. $list_element = true; 
  2237. // do not parse empty tags 
  2238. if (empty($val['value'])) { 
  2239. continue; 
  2240. // save it in our temp array 
  2241. $list_array[] = $val['value']; 
  2242. // in the case it's a list element we seralize it 
  2243. $value = implode(", ", $list_array); 
  2244. $this->setArrayValue($xmlarray, $stack, $value); 
  2245. } else { 
  2246. array_push($stack, $val['tag']); 
  2247. // do not parse empty tags 
  2248. if (!empty($val['value'])) { 
  2249. $this->setArrayValue($xmlarray, $stack, $val['value']); 
  2250. array_pop($stack); 
  2251. // foreach 
  2252. // don't parse a empty array 
  2253. if (empty($xmlarray) || empty($xmlarray['x:xmpmeta'])) { 
  2254. return false; 
  2255. // cut off the useless tags 
  2256. $xmlarray = $xmlarray['x:xmpmeta']['rdf:RDF']['rdf:Description']; 
  2257. // --------- Some values from the XMP format--------- // 
  2258. $xmpTags = array('xap:CreateDate' => 'created_timestamp', 'xap:ModifyDate' => 'last_modfied', 'xap:CreatorTool' => 'tool', 'dc:format' => 'format', 'dc:title' => 'title', 'dc:creator' => 'author', 'dc:subject' => 'keywords', 'dc:description' => 'caption', 'photoshop:AuthorsPosition' => 'position', 'photoshop:City' => 'city', 'photoshop:Country' => 'country'); 
  2259. foreach ($xmpTags as $key => $value) { 
  2260. // if the kex exist 
  2261. if (isset($xmlarray[$key])) { 
  2262. switch ($key) { 
  2263. case 'xap:CreateDate': 
  2264. case 'xap:ModifyDate': 
  2265. $this->xmp_array[$value] = strtotime($xmlarray[$key]); 
  2266. break; 
  2267. default: 
  2268. $this->xmp_array[$value] = $xmlarray[$key]; 
  2269. // return one element if requested 
  2270. if ($object != false) { 
  2271. return isset($this->xmp_array[$object]) ? $this->xmp_array[$object] : false; 
  2272. // on request sanitize the output 
  2273. if ($this->sanitize == true) { 
  2274. array_walk($this->xmp_array, create_function('&$value', '$value = esc_html($value);')); 
  2275. return $this->xmp_array; 
  2276. function setArrayValue(&$array, $stack, $value) 
  2277. if ($stack) { 
  2278. $key = array_shift($stack); 
  2279. $this->setArrayValue($array[$key], $stack, $value); 
  2280. return $array; 
  2281. } else { 
  2282. $array = $value; 
  2283. /** 
  2284. * nggMeta::get_META() - return a meta value form the available list 
  2285. * 
  2286. * @param string $object 
  2287. * @return mixed $value 
  2288. */ 
  2289. function get_META($object = false) 
  2290. // defined order first look into database, then XMP, IPTC and EXIF. 
  2291. if ($value = $this->get_saved_meta($object)) { 
  2292. return $value; 
  2293. if ($value = $this->get_XMP($object)) { 
  2294. return $value; 
  2295. if ($object == 'created_timestamp' && ($d = $this->get_IPTC('created_date')) && ($t = $this->get_IPTC('created_time'))) { 
  2296. return $this->exif_date2ts($d . ' ' . $t); 
  2297. if ($value = $this->get_IPTC($object)) { 
  2298. return $value; 
  2299. if ($value = $this->get_EXIF($object)) { 
  2300. return $value; 
  2301. // nothing found ? 
  2302. return false; 
  2303. /** 
  2304. * nggMeta::i8n_name() - localize the tag name 
  2305. * 
  2306. * @param mixed $key 
  2307. * @return translated $key 
  2308. */ 
  2309. function i18n_name($key) 
  2310. $tagnames = array('aperture' => __('Aperture', 'nggallery'), 'credit' => __('Credit', 'nggallery'), 'camera' => __('Camera', 'nggallery'), 'caption' => __('Caption', 'nggallery'), 'created_timestamp' => __('Date/Time', 'nggallery'), 'copyright' => __('Copyright', 'nggallery'), 'focal_length' => __('Focal length', 'nggallery'), 'iso' => __('ISO', 'nggallery'), 'shutter_speed' => __('Shutter speed', 'nggallery'), 'title' => __('Title', 'nggallery'), 'author' => __('Author', 'nggallery'), 'tags' => __('Tags', 'nggallery'), 'subject' => __('Subject', 'nggallery'), 'make' => __('Make', 'nggallery'), 'status' => __('Edit Status', 'nggallery'), 'category' => __('Category', 'nggallery'), 'keywords' => __('Keywords', 'nggallery'), 'created_date' => __('Date Created', 'nggallery'), 'created_time' => __('Time Created', 'nggallery'), 'position' => __('Author Position', 'nggallery'), 'city' => __('City', 'nggallery'), 'location' => __('Location', 'nggallery'), 'state' => __('Province/State', 'nggallery'), 'country_code' => __('Country code', 'nggallery'), 'country' => __('Country', 'nggallery'), 'headline' => __('Headline', 'nggallery'), 'credit' => __('Credit', 'nggallery'), 'source' => __('Source', 'nggallery'), 'copyright' => __('Copyright Notice', 'nggallery'), 'contact' => __('Contact', 'nggallery'), 'last_modfied' => __('Last modified', 'nggallery'), 'tool' => __('Program tool', 'nggallery'), 'format' => __('Format', 'nggallery'), 'width' => __('Image Width', 'nggallery'), 'height' => __('Image Height', 'nggallery'), 'flash' => __('Flash', 'nggallery')); 
  2311. if (isset($tagnames[$key])) { 
  2312. $key = $tagnames[$key]; 
  2313. return $key; 
  2314. /** 
  2315. * Return the Timestamp from the image , if possible it's read from exif data 
  2316. * @return int 
  2317. */ 
  2318. function get_date_time() 
  2319. $date = time(); 
  2320. // Try getting the created_timestamp field 
  2321. $date = $this->exif_date2ts($this->get_META('created_timestamp')); 
  2322. if (!$date) { 
  2323. $image_path = C_Gallery_Storage::get_instance()->get_backup_abspath($this->image); 
  2324. $date = @filectime($image_path); 
  2325. // Failback 
  2326. if (!$date) { 
  2327. $date = time(); 
  2328. // Return the MySQL format 
  2329. $date_time = date('Y-m-d H:i:s', $date); 
  2330. return $date_time; 
  2331. /** 
  2332. * This function return the most common metadata, via a filter we can add more 
  2333. * Reason : GD manipulation removes that options 
  2334. * 
  2335. * @since V1.4.0 
  2336. * @return void 
  2337. */ 
  2338. function get_common_meta() 
  2339. global $wpdb; 
  2340. $meta = array('aperture' => 0, 'credit' => '', 'camera' => '', 'caption' => '', 'created_timestamp' => 0, 'copyright' => '', 'focal_length' => 0, 'iso' => 0, 'shutter_speed' => 0, 'flash' => 0, 'title' => '', 'keywords' => ''); 
  2341. $meta = apply_filters('ngg_read_image_metadata', $meta); 
  2342. // meta should be still an array 
  2343. if (!is_array($meta)) { 
  2344. return false; 
  2345. foreach ($meta as $key => $value) { 
  2346. $meta[$key] = $this->get_META($key); 
  2347. //let's add now the size of the image 
  2348. $meta['width'] = $this->size[0]; 
  2349. $meta['height'] = $this->size[1]; 
  2350. return $meta; 
  2351. /** 
  2352. * If needed sanitize each value before output 
  2353. * 
  2354. * @return void 
  2355. */ 
  2356. function sanitize() 
  2357. $this->sanitize = true; 
  2358. /** 
  2359. * Wrapper to utf8_encode() that avoids double encoding 
  2360. * 
  2361. * Regex adapted from http://www.w3.org/International/questions/qa-forms-utf-8.en.php 
  2362. * to determine if the given string is already UTF-8. mb_detect_encoding() is not 
  2363. * always available and is limited in accuracy 
  2364. * 
  2365. * @param string $str 
  2366. * @return string 
  2367. */ 
  2368. function utf8_encode($str) 
  2369. $is_utf8 = preg_match('%^(?: 
  2370. [\\x09\\x0A\\x0D\\x20-\\x7E] # ASCII 
  2371. | [\\xC2-\\xDF][\\x80-\\xBF] # non-overlong 2-byte 
  2372. | \\xE0[\\xA0-\\xBF][\\x80-\\xBF] # excluding overlongs 
  2373. | [\\xE1-\\xEC\\xEE\\xEF][\\x80-\\xBF]{2} # straight 3-byte 
  2374. | \\xED[\\x80-\\x9F][\\x80-\\xBF] # excluding surrogates 
  2375. | \\xF0[\\x90-\\xBF][\\x80-\\xBF]{2} # planes 1-3 
  2376. | [\\xF1-\\xF3][\\x80-\\xBF]{3} # planes 4-15 
  2377. | \\xF4[\\x80-\\x8F][\\x80-\\xBF]{2} # plane 16 
  2378. )*$%xs', $str); 
  2379. if (!$is_utf8) { 
  2380. utf8_encode($str); 
  2381. return $str; 
  2382. class Mixin_NggLegacy_GalleryStorage_Driver extends Mixin 
  2383. /** 
  2384. * Returns the named sizes available for images 
  2385. * @return array 
  2386. */ 
  2387. function get_image_sizes() 
  2388. return array('full', 'thumbnail'); 
  2389. function get_upload_abspath($gallery = FALSE) 
  2390. // Base upload path 
  2391. $retval = C_NextGen_Settings::get_instance()->gallerypath; 
  2392. $fs = C_Fs::get_instance(); 
  2393. // If a gallery has been specified, then we'll 
  2394. // append the slug 
  2395. if ($gallery) { 
  2396. $retval = $this->get_gallery_abspath($gallery); 
  2397. // We need to make this an absolute path 
  2398. if (strpos($retval, $fs->get_document_root('gallery')) !== 0) { 
  2399. $retval = rtrim($fs->join_paths($fs->get_document_root('gallery'), $retval), "/\\"); 
  2400. // Convert slashes 
  2401. return $this->object->convert_slashes($retval); 
  2402. /** 
  2403. * Get the gallery path persisted in the database for the gallery 
  2404. * @param int|stdClass|C_NextGen_Gallery $gallery 
  2405. */ 
  2406. function get_gallery_abspath($gallery) 
  2407. $retval = NULL; 
  2408. $fs = C_Fs::get_instance(); 
  2409. // Get the gallery entity from the database 
  2410. if ($gallery) { 
  2411. if (is_numeric($gallery)) { 
  2412. $gallery = $this->object->_gallery_mapper->find($gallery); 
  2413. // It just doesn't exist 
  2414. if (!$gallery || is_numeric($gallery)) { 
  2415. return $retval; 
  2416. // We we have a gallery, determine it's path 
  2417. if ($gallery) { 
  2418. if (isset($gallery->path)) { 
  2419. $retval = $gallery->path; 
  2420. } elseif (isset($gallery->slug)) { 
  2421. $fs = C_Fs::get_instance(); 
  2422. $basepath = C_NextGen_Settings::get_instance()->gallerypath; 
  2423. $retval = $fs->join_paths($basepath, sanitize_file_name(sanitize_title($gallery->slug))); 
  2424. $root_type = defined('NGG_GALLERY_ROOT_TYPE') ? NGG_GALLERY_ROOT_TYPE : 'site'; 
  2425. if ($root_type == 'content') { 
  2426. // This requires explanation: in case our content root ends with the same directory name 
  2427. // that the gallery path begins with we remove the duplicate name from $retval. This is 
  2428. // necessary because the default WP_CONTENT_DIR setting ends in /wp-content/ and 
  2429. // NextGEN's default gallery path begins with /wp-content/. This also allows gallery 
  2430. // paths to also be expressed as simply "/gallery-name/" 
  2431. $exploded_root = explode(DIRECTORY_SEPARATOR, trim($fs->get_document_root('content'), '/\\')); 
  2432. $exploded_gallery = explode(DIRECTORY_SEPARATOR, trim($retval, '/\\')); 
  2433. $exploded_gallery = array_values($exploded_gallery); 
  2434. $last_gallery_dirname = $exploded_gallery[0]; 
  2435. $last_root_dirname = end($exploded_root); 
  2436. if ($last_root_dirname === $last_gallery_dirname) { 
  2437. unset($exploded_gallery[0]); 
  2438. $retval = implode(DIRECTORY_SEPARATOR, $exploded_gallery); 
  2439. // Ensure that the path is absolute 
  2440. if (strpos($retval, $fs->get_document_root('gallery')) !== 0) { 
  2441. $retval = rtrim($fs->join_paths($fs->get_document_root('gallery'), $retval), "/\\"); 
  2442. return $this->object->convert_slashes(rtrim($retval, "/\\")); 
  2443. /** 
  2444. * Gets the absolute path where the image is stored 
  2445. * Can optionally return the path for a particular sized image 
  2446. */ 
  2447. function get_image_abspath($image, $size = 'full', $check_existance = FALSE) 
  2448. $retval = NULL; 
  2449. $fs = C_Fs::get_instance(); 
  2450. // Ensure that we have a size 
  2451. if (!$size) { 
  2452. $size = 'full'; 
  2453. // If we have the id, get the actual image entity 
  2454. if (is_numeric($image)) { 
  2455. $image = $this->object->_image_mapper->find($image); 
  2456. // Ensure we have the image entity - user could have passed in an 
  2457. // incorrect id 
  2458. if (is_object($image)) { 
  2459. if ($gallery_path = $this->object->get_gallery_abspath($image->galleryid)) { 
  2460. $folder = $prefix = $size; 
  2461. switch ($size) { 
  2462. # Images are stored in the associated gallery folder 
  2463. case 'full': 
  2464. case 'original': 
  2465. case 'image': 
  2466. $retval = $fs->join_paths($gallery_path, $image->filename); 
  2467. break; 
  2468. case 'backup': 
  2469. $retval = $fs->join_paths($gallery_path, $image->filename . '_backup'); 
  2470. if ($check_existance && !@file_exists($retval)) { 
  2471. $retval = $fs->join_paths($gallery_path, $image->filename); 
  2472. break; 
  2473. case 'thumbnails': 
  2474. case 'thumbnail': 
  2475. case 'thumb': 
  2476. case 'thumbs': 
  2477. $size = 'thumbnail'; 
  2478. $folder = 'thumbs'; 
  2479. $prefix = 'thumbs'; 
  2480. // deliberately no break here 
  2481. // We assume any other size of image is stored in the a 
  2482. //subdirectory of the same name within the gallery folder 
  2483. // gallery folder, but with the size appended to the filename 
  2484. // deliberately no break here 
  2485. // We assume any other size of image is stored in the a 
  2486. //subdirectory of the same name within the gallery folder 
  2487. // gallery folder, but with the size appended to the filename 
  2488. default: 
  2489. $image_path = $fs->join_paths($gallery_path, $folder); 
  2490. // NGG 2.0 stores relative filenames in the meta data of 
  2491. // an image. It does this because it uses filenames 
  2492. // that follow conventional WordPress naming scheme. 
  2493. if (isset($image->meta_data) && isset($image->meta_data[$size]) && isset($image->meta_data[$size]['filename'])) { 
  2494. $image_path = $fs->join_paths($image_path, $image->meta_data[$size]['filename']); 
  2495. } else { 
  2496. $image_path = $fs->join_paths($image_path, "{$prefix}_{$image->filename}"); 
  2497. $retval = $image_path; 
  2498. break; 
  2499. // Check the existance of the file 
  2500. if ($retval && $check_existance) { 
  2501. if (!@file_exists($retval)) { 
  2502. $retval = NULL; 
  2503. return $retval ? rtrim($retval, "/\\") : $retval; 
  2504. /** 
  2505. * Gets the url of a particular-sized image 
  2506. * @param int|object $image 
  2507. * @param string $size 
  2508. * @returns array 
  2509. */ 
  2510. function get_image_url($image, $size = 'full', $check_existance = FALSE, $image_abspath = FALSE) 
  2511. $retval = NULL; 
  2512. $fs = C_Fs::get_instance(); 
  2513. $router = C_Router::get_instance(); 
  2514. if (!$image_abspath) { 
  2515. $image_abspath = $this->object->get_image_abspath($image, $size, $check_existance); 
  2516. if ($image_abspath) { 
  2517. // Use multibyte pathinfo() in case of UTF8 gallery or file names 
  2518. $parts = M_I18n::mb_pathinfo($image_abspath); 
  2519. $image_abspath = $parts['dirname'] . DIRECTORY_SEPARATOR . $parts['basename']; 
  2520. $doc_root = $fs->get_document_root('gallery'); 
  2521. if ($doc_root != null) { 
  2522. $doc_root = rtrim($doc_root, "/\\") . DIRECTORY_SEPARATOR; 
  2523. // if docroot is "/" we would generate urls like /wp-contentpluginsnextgen-galleryetcetc 
  2524. if ($doc_root !== '/') { 
  2525. $request_uri = str_replace($doc_root, '', $image_abspath); 
  2526. } else { 
  2527. $request_uri = $image_abspath; 
  2528. $request_uri = '/' . ltrim(str_replace("\\", '/', $request_uri), "/"); 
  2529. // Because like%@this.jpg is a valid directory and filename 
  2530. $request_uri = explode('/', $request_uri); 
  2531. foreach ($request_uri as $ndx => $segment) { 
  2532. $request_uri[$ndx] = rawurlencode($segment); 
  2533. $request_uri = implode('/', $request_uri); 
  2534. $retval = $router->remove_url_segment('/index.php', $router->get_url($request_uri, FALSE, 'gallery')); 
  2535. return apply_filters('ngg_get_image_url', $retval, $image, $size); 
  2536. /** 
  2537. * Uploads an image for a particular gallerys 
  2538. * @param int|stdClass|C_NextGEN_Gallery $gallery 
  2539. * @param type $filename, specifies the name of the file 
  2540. * @param type $data if specified, expects base64 encoded string of data 
  2541. * @return C_Image 
  2542. */ 
  2543. function upload_image($gallery, $filename = FALSE, $data = FALSE) 
  2544. $retval = NULL; 
  2545. // Ensure that we have the data present that we require 
  2546. if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) { 
  2547. // $_FILES = Array( 
  2548. // [file] => Array ( 
  2549. // [name] => Canada_landscape4.jpg 
  2550. // [type] => image/jpeg 
  2551. // [tmp_name] => /private/var/tmp/php6KO7Dc 
  2552. // [error] => 0 
  2553. // [size] => 64975 
  2554. // ) 
  2555. // 
  2556. $file = $_FILES['file']; 
  2557. if ($this->object->is_zip()) { 
  2558. $retval = $this->object->upload_zip($gallery); 
  2559. } else { 
  2560. if ($this->is_image_file()) { 
  2561. $retval = $this->object->upload_base64_image($gallery, file_get_contents($file['tmp_name']), $filename ? $filename : (isset($file['name']) ? $file['name'] : FALSE)); 
  2562. } else { 
  2563. // Remove the non-valid (and potentially insecure) file from the PHP upload directory 
  2564. if (isset($_FILES['file']['tmp_name'])) { 
  2565. $filename = $_FILES['file']['tmp_name']; 
  2566. @unlink($filename); 
  2567. throw new E_UploadException(__('Invalid image file. Acceptable formats: JPG, GIF, and PNG.', 'nggallery')); 
  2568. } elseif ($data) { 
  2569. $retval = $this->object->upload_base64_image($filename, $data); 
  2570. } else { 
  2571. throw new E_UploadException(); 
  2572. return $retval; 
  2573. function get_image_size_params($image, $size, $params = null, $skip_defaults = false) 
  2574. // Get the image entity 
  2575. if (is_numeric($image)) { 
  2576. $image = $this->object->_image_mapper->find($image); 
  2577. $params = apply_filters('ngg_get_image_size_params', $params, $size, $image); 
  2578. // Ensure we have a valid image 
  2579. if ($image) { 
  2580. $settings = C_NextGen_Settings::get_instance(); 
  2581. if (!$skip_defaults) { 
  2582. // Get default settings 
  2583. if ($size == 'full') { 
  2584. if (!isset($params['quality'])) { 
  2585. $params['quality'] = $settings->imgQuality; 
  2586. } else { 
  2587. if (!isset($params['crop'])) { 
  2588. $params['crop'] = $settings->thumbfix; 
  2589. if (!isset($params['quality'])) { 
  2590. $params['quality'] = $settings->thumbquality; 
  2591. // Not sure why this was here... commenting out for now, always require watermark parameters to be explicit 
  2592. # if (!isset($params['watermark'])) { 
  2593. # $params['watermark'] = $settings->wmType; 
  2594. # } 
  2595. // width and height when omitted make generate_image_clone create a clone with original size, so try find defaults regardless of $skip_defaults 
  2596. if (!isset($params['width']) || !isset($params['height'])) { 
  2597. // First test if this is a "known" image size, i.e. if we store these sizes somewhere when users re-generate these sizes from the UI...this is required to be compatible with legacy 
  2598. // try the 2 default built-in sizes, first thumbnail... 
  2599. if ($size == 'thumbnail') { 
  2600. if (!isset($params['width'])) { 
  2601. $params['width'] = $settings->thumbwidth; 
  2602. if (!isset($params['height'])) { 
  2603. $params['height'] = $settings->thumbheight; 
  2604. } else { 
  2605. if ($size == 'full') { 
  2606. if (!isset($params['width'])) { 
  2607. if ($settings->imgAutoResize) { 
  2608. $params['width'] = $settings->imgWidth; 
  2609. if (!isset($params['height'])) { 
  2610. if ($settings->imgAutoResize) { 
  2611. $params['height'] = $settings->imgHeight; 
  2612. } else { 
  2613. if (isset($image->meta_data) && isset($image->meta_data[$size])) { 
  2614. $dimensions = $image->meta_data[$size]; 
  2615. if (!isset($params['width'])) { 
  2616. $params['width'] = $dimensions['width']; 
  2617. if (!isset($params['height'])) { 
  2618. $params['height'] = $dimensions['height']; 
  2619. if (!isset($params['crop_frame'])) { 
  2620. $crop_frame_size_name = 'thumbnail'; 
  2621. if (isset($image->meta_data[$size]['crop_frame'])) { 
  2622. $crop_frame_size_name = $size; 
  2623. if (isset($image->meta_data[$crop_frame_size_name]['crop_frame'])) { 
  2624. $params['crop_frame'] = $image->meta_data[$crop_frame_size_name]['crop_frame']; 
  2625. if (!isset($params['crop_frame']['final_width'])) { 
  2626. $params['crop_frame']['final_width'] = $image->meta_data[$crop_frame_size_name]['width']; 
  2627. if (!isset($params['crop_frame']['final_height'])) { 
  2628. $params['crop_frame']['final_height'] = $image->meta_data[$crop_frame_size_name]['height']; 
  2629. } else { 
  2630. if (!isset($params['crop_frame']['final_width'])) { 
  2631. $params['crop_frame']['final_width'] = $params['width']; 
  2632. if (!isset($params['crop_frame']['final_height'])) { 
  2633. $params['crop_frame']['final_height'] = $params['height']; 
  2634. return $params; 
  2635. /** 
  2636. * Returns an array of dimensional properties (width, height, real_width, real_height) of a resulting clone image if and when generated 
  2637. * @param string $image_path 
  2638. * @param string $clone_path 
  2639. * @param array $params 
  2640. * @return array 
  2641. */ 
  2642. function calculate_image_size_dimensions($image, $size, $params = null, $skip_defaults = false) 
  2643. $retval = FALSE; 
  2644. // Get the image entity 
  2645. if (is_numeric($image)) { 
  2646. $image = $this->object->_image_mapper->find($image); 
  2647. // Ensure we have a valid image 
  2648. if ($image) { 
  2649. $params = $this->object->get_image_size_params($image, $size, $params, $skip_defaults); 
  2650. // Get the image filename 
  2651. $image_path = $this->object->get_original_abspath($image, 'original'); 
  2652. $clone_path = $this->object->get_image_abspath($image, $size); 
  2653. $retval = $this->object->calculate_image_clone_dimensions($image_path, $clone_path, $params); 
  2654. return $retval; 
  2655. /** 
  2656. * Generates a specific size for an image 
  2657. * @param int|stdClass|C_Image $image 
  2658. * @return bool|object 
  2659. */ 
  2660. function generate_image_size($image, $size, $params = null, $skip_defaults = false) 
  2661. $retval = FALSE; 
  2662. // Get the image entity 
  2663. if (is_numeric($image)) { 
  2664. $image = $this->object->_image_mapper->find($image); 
  2665. // Ensure we have a valid image 
  2666. if ($image) { 
  2667. $params = $this->object->get_image_size_params($image, $size, $params, $skip_defaults); 
  2668. $settings = C_NextGen_Settings::get_instance(); 
  2669. // Get the image filename 
  2670. $filename = $this->object->get_original_abspath($image, 'original'); 
  2671. $thumbnail = null; 
  2672. if ($size == 'full' && $settings->imgBackup == 1) { 
  2673. // XXX change this? 'full' should be the resized path and 'original' the _backup path 
  2674. $backup_path = $this->object->get_backup_abspath($image); 
  2675. if (!@file_exists($backup_path)) { 
  2676. @copy($filename, $backup_path); 
  2677. // Generate the thumbnail using WordPress 
  2678. $existing_image_abpath = $this->object->get_image_abspath($image, $size); 
  2679. $existing_image_dir = dirname($existing_image_abpath); 
  2680. // removing the old thumbnail is actually not needed as generate_image_clone() will replace it, leaving commented in as reminder in case there are issues in the future 
  2681. if (@file_exists($existing_image_abpath)) { 
  2682. //unlink($existing_image_abpath); 
  2683. wp_mkdir_p($existing_image_dir); 
  2684. $clone_path = $existing_image_abpath; 
  2685. $thumbnail = $this->object->generate_image_clone($filename, $clone_path, $params); 
  2686. // We successfully generated the thumbnail 
  2687. if ($thumbnail != null) { 
  2688. $clone_path = $thumbnail->fileName; 
  2689. if (function_exists('getimagesize')) { 
  2690. $dimensions = getimagesize($clone_path); 
  2691. } else { 
  2692. $dimensions = array($params['width'], $params['height']); 
  2693. if (!isset($image->meta_data)) { 
  2694. $image->meta_data = array(); 
  2695. $size_meta = array('width' => $dimensions[0], 'height' => $dimensions[1], 'filename' => M_I18n::mb_basename($clone_path), 'generated' => microtime()); 
  2696. if (isset($params['crop_frame'])) { 
  2697. $size_meta['crop_frame'] = $params['crop_frame']; 
  2698. $image->meta_data[$size] = $size_meta; 
  2699. if ($size == 'full') { 
  2700. $image->meta_data['width'] = $size_meta['width']; 
  2701. $image->meta_data['height'] = $size_meta['height']; 
  2702. $retval = $this->object->_image_mapper->save($image); 
  2703. do_action('ngg_generated_image', $image, $size, $params); 
  2704. if ($retval == 0) { 
  2705. $retval = false; 
  2706. if ($retval) { 
  2707. $retval = $thumbnail; 
  2708. } else { 
  2709. // Something went wrong. Thumbnail generation failed! 
  2710. return $retval; 
  2711. /** 
  2712. * Generates a thumbnail for an image 
  2713. * @param int|stdClass|C_Image $image 
  2714. * @return bool 
  2715. */ 
  2716. function generate_thumbnail($image, $params = null, $skip_defaults = false) 
  2717. $sized_image = $this->object->generate_image_size($image, 'thumbnail', $params, $skip_defaults); 
  2718. $retval = false; 
  2719. if ($sized_image != null) { 
  2720. $retval = true; 
  2721. $sized_image->destruct(); 
  2722. return $retval; 
  2723. /** 
  2724. * Outputs/renders an image 
  2725. * @param int|stdClass|C_NextGen_Gallery_Image $image 
  2726. * @return bool 
  2727. */ 
  2728. function render_image($image, $size = FALSE) 
  2729. $format_list = $this->object->get_image_format_list(); 
  2730. $abspath = $this->get_image_abspath($image, $size, true); 
  2731. if ($abspath == null) { 
  2732. $thumbnail = $this->object->generate_image_size($image, $size); 
  2733. if ($thumbnail != null) { 
  2734. $abspath = $thumbnail->fileName; 
  2735. $thumbnail->destruct(); 
  2736. if ($abspath != null) { 
  2737. $data = @getimagesize($abspath); 
  2738. $format = 'jpg'; 
  2739. if ($data != null && is_array($data) && isset($format_list[$data[2]])) { 
  2740. $format = $format_list[$data[2]]; 
  2741. // Clear output 
  2742. while (ob_get_level() > 0) { 
  2743. ob_end_clean(); 
  2744. $format = strtolower($format); 
  2745. // output image and headers 
  2746. header('Content-type: image/' . $format); 
  2747. readfile($abspath); 
  2748. return true; 
  2749. return false; 
  2750. function delete_gallery($gallery) 
  2751. $retval = FALSE; 
  2752. $fs = C_Fs::get_instance(); 
  2753. $safe_dirs = array(DIRECTORY_SEPARATOR, $fs->get_document_root('plugins'), $fs->get_document_root('plugins_mu'), $fs->get_document_root('templates'), $fs->get_document_root('stylesheets'), $fs->get_document_root('content'), $fs->get_document_root('galleries'), $fs->get_document_root()); 
  2754. $abspath = $this->object->get_gallery_abspath($gallery); 
  2755. if ($abspath && file_exists($abspath) && !in_array(stripslashes($abspath), $safe_dirs)) { 
  2756. // delete the directory and everything in it 
  2757. $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($abspath), RecursiveIteratorIterator::CHILD_FIRST); 
  2758. foreach ($iterator as $file) { 
  2759. if (in_array($file->getBasename(), array('.', '..'))) { 
  2760. continue; 
  2761. } elseif ($file->isDir()) { 
  2762. rmdir($file->getPathname()); 
  2763. } elseif ($file->isFile() || $file->isLink()) { 
  2764. unlink($file->getPathname()); 
  2765. $retval = @rmdir($abspath); 
  2766. return $retval; 
  2767. function delete_image($image, $size = FALSE) 
  2768. $retval = FALSE; 
  2769. // Ensure that we have the image entity 
  2770. if (is_numeric($image)) { 
  2771. $image = $this->object->_image_mapper->find($image); 
  2772. if ($image) { 
  2773. $image_id = $image->{$image->id_field}; 
  2774. do_action('ngg_delete_image', $image_id, $size); 
  2775. // Delete only a particular image size 
  2776. if ($size) { 
  2777. $abspath = $this->object->get_image_abspath($image, $size); 
  2778. if ($abspath && @file_exists($abspath)) { 
  2779. unlink($abspath); 
  2780. if (isset($image->meta_data) && isset($image->meta_data[$size])) { 
  2781. unset($image->meta_data[$size]); 
  2782. $this->object->_image_mapper->save($image); 
  2783. } else { 
  2784. // Get the paths to fullsize and thumbnail files 
  2785. $abspaths = array($this->object->get_full_abspath($image), $this->object->get_thumb_abspath($image), $this->object->get_backup_abspath($image)); 
  2786. if (isset($image->meta_data)) { 
  2787. foreach (array_keys($image->meta_data) as $size) { 
  2788. $abspaths[] = $this->object->get_image_abspath($image, $size); 
  2789. // Delete each image 
  2790. foreach ($abspaths as $abspath) { 
  2791. if ($abspath && @file_exists($abspath)) { 
  2792. unlink($abspath); 
  2793. // Delete the entity 
  2794. $this->object->_image_mapper->destroy($image); 
  2795. $retval = TRUE; 
  2796. return $retval; 
  2797. function set_post_thumbnail($post, $image) 
  2798. $attachment_id = null; 
  2799. // Get the post id 
  2800. $post_id = $post; 
  2801. if (is_object($post)) { 
  2802. if (property_exists($post, 'ID')) { 
  2803. $post_id = $post->ID; 
  2804. } elseif (property_exists($post, 'post_id')) { 
  2805. $post_id = $post->post_id; 
  2806. } elseif (is_array($post)) { 
  2807. if (isset($post['ID'])) { 
  2808. $post_id = $post['ID']; 
  2809. } elseif (isset($post['post_id'])) { 
  2810. $post_id = $post['post_id']; 
  2811. // Get the image object 
  2812. if (is_int($image)) { 
  2813. $image = C_Image_Mapper::get_instance()->find($image); 
  2814. // Do we have what we need? 
  2815. if ($image && is_int($post_id)) { 
  2816. $args = array('post_type' => 'attachment', 'meta_key' => '_ngg_image_id', 'meta_compare' => '==', 'meta_value' => $image->{$image->id_field}); 
  2817. $upload_dir = wp_upload_dir(); 
  2818. $basedir = $upload_dir['basedir']; 
  2819. $thumbs_dir = implode(DIRECTORY_SEPARATOR, array($basedir, 'ngg_featured')); 
  2820. $gallery_abspath = $this->object->get_gallery_abspath($image->galleryid); 
  2821. $image_abspath = $this->object->get_full_abspath($image); 
  2822. $target_path = null; 
  2823. $copy_image = TRUE; 
  2824. // Have we previously set the post thumbnail? 
  2825. if ($posts = get_posts($args)) { 
  2826. $attachment_id = $posts[0]->ID; 
  2827. $attachment_file = get_attached_file($attachment_id); 
  2828. $target_path = $attachment_file; 
  2829. if (filemtime($image_abspath) > filemtime($target_path)) { 
  2830. $copy_image = TRUE; 
  2831. } else { 
  2832. $url = $this->object->get_full_url($image); 
  2833. $target_relpath = null; 
  2834. $target_basename = M_I18n::mb_basename($image_abspath); 
  2835. if (strpos($image_abspath, $gallery_abspath) === 0) { 
  2836. $target_relpath = substr($image_abspath, strlen($gallery_abspath)); 
  2837. } else { 
  2838. if ($image->galleryid) { 
  2839. $target_relpath = path_join(strval($image->galleryid), $target_basename); 
  2840. } else { 
  2841. $target_relpath = $target_basename; 
  2842. $target_relpath = trim($target_relpath, '\\/'); 
  2843. $target_path = path_join($thumbs_dir, $target_relpath); 
  2844. $max_count = 100; 
  2845. $count = 0; 
  2846. while (@file_exists($target_path) && $count <= $max_count) { 
  2847. $count++; 
  2848. $pathinfo = M_I18n::mb_pathinfo($target_path); 
  2849. $dirname = $pathinfo['dirname']; 
  2850. $filename = $pathinfo['filename']; 
  2851. $extension = $pathinfo['extension']; 
  2852. $rand = mt_rand(1, 9999); 
  2853. $basename = $filename . '_' . sprintf('%04d', $rand) . '.' . $extension; 
  2854. $target_path = path_join($dirname, $basename); 
  2855. if (@file_exists($target_path)) { 
  2856. // XXX handle very rare case in which $max_count wasn't enough? 
  2857. $target_dir = dirname($target_path); 
  2858. wp_mkdir_p($target_dir); 
  2859. if ($copy_image) { 
  2860. @copy($image_abspath, $target_path); 
  2861. if (!$attachment_id) { 
  2862. $size = @getimagesize($target_path); 
  2863. $image_type = $size ? $size['mime'] : 'image/jpeg'; 
  2864. $title = sanitize_file_name($image->alttext); 
  2865. $caption = sanitize_file_name($image->description); 
  2866. $attachment = array('post_title' => $title, 'post_content' => $caption, 'post_status' => 'attachment', 'post_parent' => 0, 'post_mime_type' => $image_type, 'guid' => $url); 
  2867. $attachment_id = wp_insert_attachment($attachment, $target_path); 
  2868. update_post_meta($attachment_id, '_ngg_image_id', $image->{$image->id_field}); 
  2869. wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata($attachment_id, $target_path)); 
  2870. return $attachment_id; 
  2871. /** 
  2872. * Copies (or moves) images into another gallery 
  2873. * 
  2874. * @param array $images 
  2875. * @param int|object $gallery 
  2876. * @param boolean $db optionally only copy the image files 
  2877. * @param boolean $move move the image instead of copying 
  2878. * @return mixed NULL on failure, array|image-ids on success 
  2879. */ 
  2880. function copy_images($images, $gallery, $db = TRUE, $move = FALSE) 
  2881. $new_image_pids = array(); 
  2882. // the return value 
  2883. // legacy requires passing just a numeric ID 
  2884. if (is_numeric($gallery)) { 
  2885. $gallery = $this->object->_gallery_mapper->find($gallery); 
  2886. // move_images() is a wrapper to this function so we implement both features here 
  2887. $func = $move ? 'rename' : 'copy'; 
  2888. // legacy allows for arrays of just the ID 
  2889. if (!is_array($images)) { 
  2890. $images = array($images); 
  2891. // Ensure we have a valid gallery 
  2892. $gallery_id = $this->object->_get_gallery_id($gallery); 
  2893. if (!$gallery_id) { 
  2894. return array(); 
  2895. $image_key = $this->object->_image_mapper->get_primary_key_column(); 
  2896. $gallery_abspath = $this->object->get_gallery_abspath($gallery); 
  2897. // Check for folder permission 
  2898. if (!is_dir($gallery_abspath) && !wp_mkdir_p($gallery_abspath)) { 
  2899. echo sprintf(__('Unable to create directory %s.', 'nggallery'), esc_html($gallery_abspath)); 
  2900. return $new_image_pids; 
  2901. if (!is_writable($gallery_abspath)) { 
  2902. echo sprintf(__('Unable to write to directory %s. Is this directory writable by the server?', 'nggallery'), esc_html($gallery_abspath)); 
  2903. return $new_image_pids; 
  2904. $old_gallery_ids = array(); 
  2905. $image_pid_map = array(); 
  2906. foreach ($images as $image) { 
  2907. if ($this->object->is_current_user_over_quota()) { 
  2908. throw new E_NoSpaceAvailableException(__('Sorry, you have used your space allocation. Please delete some files to upload more files.', 'nggallery')); 
  2909. // again legacy requires that it be able to pass just a numeric ID 
  2910. if (is_numeric($image)) { 
  2911. $image = $this->object->_image_mapper->find($image); 
  2912. $old_gallery_ids[] = $image->galleryid; 
  2913. $old_pid = $image->{$image_key}; 
  2914. // update the DB if requested 
  2915. $new_image = clone $image; 
  2916. $new_pid = $old_pid; 
  2917. if ($db) { 
  2918. unset($new_image->extras_post_id); 
  2919. $new_image->galleryid = $gallery_id; 
  2920. if (!$move) { 
  2921. $new_image->image_slug = nggdb::get_unique_slug(sanitize_title_with_dashes($image->alttext), 'image'); 
  2922. unset($new_image->{$image_key}); 
  2923. $new_pid = $this->object->_image_mapper->save($new_image); 
  2924. if (!$new_pid) { 
  2925. echo sprintf(__('Failed to copy database row for picture %s', 'nggallery'), $old_pid) . '<br />'; 
  2926. continue; 
  2927. // 'backup' is not included in get_image_sizes() 
  2928. $sizes = $this->object->get_image_sizes(); 
  2929. $sizes[] = 'backup'; 
  2930. // Copy each image size 
  2931. foreach ($sizes as $size) { 
  2932. // if backups are off there's no backup file to copy 
  2933. if (!C_NextGen_Settings::get_instance()->imgBackup && $size == 'backup') { 
  2934. continue; 
  2935. $orig_path = $this->object->get_image_abspath($image, $size, TRUE); 
  2936. if (!$orig_path || !@file_exists($orig_path)) { 
  2937. echo sprintf(__('Failed to get image path for %s', 'nggallery'), esc_html(M_I18n::mb_basename($orig_path))) . '<br/>'; 
  2938. continue; 
  2939. $new_path = $this->object->get_image_abspath($new_image, $size, FALSE); 
  2940. // Prevent duplicate filenames: check if the filename exists and begin appending '-#' 
  2941. if (!ini_get('safe_mode') && @file_exists($new_path)) { 
  2942. // prevent get_image_abspath() from using the thumbnail filename in metadata 
  2943. unset($new_image->meta_data['thumbnail']['filename']); 
  2944. $file_exists = TRUE; 
  2945. $i = 0; 
  2946. do { 
  2947. $i++; 
  2948. $parts = explode('.', $image->filename); 
  2949. $extension = array_pop($parts); 
  2950. $tmp_filename = implode('.', $parts) . '-' . $i . '.' . $extension; 
  2951. $new_image->filename = $tmp_filename; 
  2952. $tmp_path = $this->object->get_image_abspath($new_image, $size, FALSE); 
  2953. if (!@file_exists($tmp_path)) { 
  2954. $file_exists = FALSE; 
  2955. $new_path = $tmp_path; 
  2956. if ($db) { 
  2957. $this->object->_image_mapper->save($new_image); 
  2958. } while ($file_exists == TRUE); 
  2959. // Copy files 
  2960. if (!@$func($orig_path, $new_path)) { 
  2961. echo sprintf(__('Failed to copy image %1$s to %2$s', 'nggallery'), esc_html($orig_path), esc_html($new_path)) . '<br/>'; 
  2962. continue; 
  2963. // disabling: this is a bit too verbose 
  2964. // if (!empty($tmp_path)) 
  2965. // echo sprintf(__('Image %1$s (%2$s) copied as image %3$s (%4$s) » The file already existed in the destination gallery.', 'nggallery'), $old_pid, esc_html($orig_path), $new_pid, esc_html($new_path)) . '<br />'; 
  2966. // else 
  2967. // echo sprintf(__('Image %1$s (%2$s) copied as image %3$s (%4$s)', 'nggallery'), $old_pid, esc_html($orig_path), $new_pid, esc_html($new_path)) . '<br />'; 
  2968. // Copy tags 
  2969. if ($db) { 
  2970. $tags = wp_get_object_terms($old_pid, 'ngg_tag', 'fields=ids'); 
  2971. $tags = array_map('intval', $tags); 
  2972. wp_set_object_terms($new_pid, $tags, 'ngg_tag', true); 
  2973. $new_image_pids[] = $new_pid; 
  2974. $image_pid_map[$old_pid] = $new_pid; 
  2975. $old_gallery_ids = array_unique($old_gallery_ids); 
  2976. if ($move) { 
  2977. do_action('ngg_moved_images', $images, $old_gallery_ids, $gallery_id); 
  2978. } else { 
  2979. do_action('ngg_copied_images', $image_pid_map, $old_gallery_ids, $gallery_id); 
  2980. $title = '<a href="' . admin_url() . 'admin.php?page=nggallery-manage-gallery&mode=edit&gid=' . $gallery_id . '" >'; 
  2981. $title .= $gallery->title; 
  2982. $title .= '</a>'; 
  2983. echo '<hr/>' . sprintf(__('Copied %1$s picture(s) to gallery %2$s .', 'nggallery'), count($new_image_pids), $title); 
  2984. return $new_image_pids; 
  2985. /** 
  2986. * Recover image from backup copy and reprocess it 
  2987. * 
  2988. * @param int|stdClass|C_Image $image 
  2989. * @return string result code 
  2990. */ 
  2991. function recover_image($image) 
  2992. $retval = FALSE; 
  2993. if (is_numeric($image)) { 
  2994. $image = $this->object->_image_mapper->find($image); 
  2995. if ($image) { 
  2996. $full_abspath = $this->object->get_image_abspath($image); 
  2997. $backup_abspath = $this->object->get_image_abspath($image, 'backup'); 
  2998. if ($backup_abspath != $full_abspath && @file_exists($backup_abspath)) { 
  2999. if (is_writable($full_abspath) && is_writable(dirname($full_abspath))) { 
  3000. // Copy the backup 
  3001. if (@copy($backup_abspath, $full_abspath)) { 
  3002. // Re-create non-fullsize image sizes 
  3003. foreach ($this->object->get_image_sizes($image) as $named_size) { 
  3004. if ($named_size == 'full') { 
  3005. continue; 
  3006. $this->object->generate_image_clone($backup_abspath, $this->object->get_image_abspath($image, $named_size), $this->object->get_image_size_params($image, $named_size)); 
  3007. do_action('ngg_recovered_image', $image); 
  3008. // Reimport all metadata 
  3009. $retval = $this->object->_image_mapper->reimport_metadata($image); 
  3010. return $retval; 
  3011. /** 
  3012. * Class C_NggLegacy_GalleryStorage_Driver 
  3013. * @mixin Mixin_NggLegacy_GalleryStorage_Driver 
  3014. */ 
  3015. class C_NggLegacy_GalleryStorage_Driver extends C_GalleryStorage_Driver_Base 
  3016. function define($context = FALSE) 
  3017. parent::define($context); 
  3018. $this->add_mixin('Mixin_NggLegacy_GalleryStorage_Driver'); 
  3019. /** 
  3020. * gd.thumbnail.inc.php 
  3021. *  
  3022. * @author Ian Selby (ian@gen-x-design.com) 
  3023. * @copyright Copyright 2006-2011 
  3024. * @version 1.3.0 (based on 1.1.3) 
  3025. * @modded by Alex Rabe 
  3026. *  
  3027. */ 
  3028. /** 
  3029. * PHP class for dynamically resizing, cropping, and rotating images for thumbnail purposes and either displaying them on-the-fly or saving them. 
  3030. * 
  3031. */ 
  3032. class C_NggLegacy_Thumbnail 
  3033. /** 
  3034. * Error message to display, if any 
  3035. * 
  3036. * @var string 
  3037. */ 
  3038. var $errmsg; 
  3039. /** 
  3040. * Whether or not there is an error 
  3041. * 
  3042. * @var boolean 
  3043. */ 
  3044. var $error; 
  3045. /** 
  3046. * Format of the image file 
  3047. * 
  3048. * @var string 
  3049. */ 
  3050. var $format; 
  3051. /** 
  3052. * File name and path of the image file 
  3053. * 
  3054. * @var string 
  3055. */ 
  3056. var $fileName; 
  3057. /** 
  3058. * Current dimensions of working image 
  3059. * 
  3060. * @var array 
  3061. */ 
  3062. var $currentDimensions; 
  3063. /** 
  3064. * New dimensions of working image 
  3065. * 
  3066. * @var array 
  3067. */ 
  3068. var $newDimensions; 
  3069. /** 
  3070. * Image resource for newly manipulated image 
  3071. * 
  3072. * @var resource 
  3073. * @access private 
  3074. */ 
  3075. var $newImage; 
  3076. /** 
  3077. * Image resource for image before previous manipulation 
  3078. * 
  3079. * @var resource 
  3080. * @access private 
  3081. */ 
  3082. var $oldImage; 
  3083. /** 
  3084. * Image resource for image being currently manipulated 
  3085. * 
  3086. * @var resource 
  3087. * @access private 
  3088. */ 
  3089. var $workingImage; 
  3090. /** 
  3091. * Percentage to resize image by 
  3092. * 
  3093. * @var int 
  3094. * @access private 
  3095. */ 
  3096. var $percent; 
  3097. /** 
  3098. * Maximum width of image during resize 
  3099. * 
  3100. * @var int 
  3101. * @access private 
  3102. */ 
  3103. var $maxWidth; 
  3104. /** 
  3105. * Maximum height of image during resize 
  3106. * 
  3107. * @var int 
  3108. * @access private 
  3109. */ 
  3110. var $maxHeight; 
  3111. /** 
  3112. * Image for Watermark 
  3113. * 
  3114. * @var string 
  3115. *  
  3116. */ 
  3117. var $watermarkImgPath; 
  3118. /** 
  3119. * Text for Watermark 
  3120. * 
  3121. * @var string 
  3122. *  
  3123. */ 
  3124. var $watermarkText; 
  3125. /** 
  3126. * Image Resource ID for Watermark 
  3127. * 
  3128. * @var string 
  3129. *  
  3130. */ 
  3131. function __construct($fileName, $no_ErrorImage = false) 
  3132. //make sure the GD library is installed 
  3133. if (!function_exists("gd_info")) { 
  3134. echo 'You do not have the GD Library installed. This class requires the GD library to function properly.' . "\n"; 
  3135. echo 'visit http://us2.php.net/manual/en/ref.image.php for more information'; 
  3136. throw new E_No_Image_Library_Exception(); 
  3137. //initialize variables 
  3138. $this->errmsg = ''; 
  3139. $this->error = false; 
  3140. $this->currentDimensions = array(); 
  3141. $this->newDimensions = array(); 
  3142. $this->fileName = $fileName; 
  3143. $this->percent = 100; 
  3144. $this->maxWidth = 0; 
  3145. $this->maxHeight = 0; 
  3146. $this->watermarkImgPath = ''; 
  3147. $this->watermarkText = ''; 
  3148. //check to see if file exists 
  3149. if (!@file_exists($this->fileName)) { 
  3150. $this->errmsg = 'File not found'; 
  3151. $this->error = true; 
  3152. } elseif (!is_readable($this->fileName)) { 
  3153. $this->errmsg = 'File is not readable'; 
  3154. $this->error = true; 
  3155. //if there are no errors, determine the file format 
  3156. if ($this->error == false) { 
  3157. @ini_set('memory_limit', -1); 
  3158. $data = @getimagesize($this->fileName); 
  3159. if (isset($data) && is_array($data)) { 
  3160. $extensions = array('1' => 'GIF', '2' => 'JPG', '3' => 'PNG'); 
  3161. $extension = array_key_exists($data[2], $extensions) ? $extensions[$data[2]] : ''; 
  3162. if ($extension) { 
  3163. $this->format = $extension; 
  3164. } else { 
  3165. $this->errmsg = 'Unknown file format'; 
  3166. $this->error = true; 
  3167. } else { 
  3168. $this->errmsg = 'File is not an image'; 
  3169. $this->error = true; 
  3170. // increase memory-limit if possible, GD needs this for large images 
  3171. if (!extension_loaded('suhosin')) { 
  3172. @ini_set('memory_limit', '512M'); 
  3173. if ($this->error == false) { 
  3174. // Check memory consumption if file exists 
  3175. $this->checkMemoryForImage($this->fileName); 
  3176. //initialize resources if no errors 
  3177. if ($this->error == false) { 
  3178. $img_err = null; 
  3179. switch ($this->format) { 
  3180. case 'GIF': 
  3181. if (function_exists('ImageCreateFromGif')) { 
  3182. $this->oldImage = @ImageCreateFromGif($this->fileName); 
  3183. } else { 
  3184. $img_err = __('Support for GIF format is missing.', 'nggallery'); 
  3185. break; 
  3186. case 'JPG': 
  3187. if (function_exists('ImageCreateFromJpeg')) { 
  3188. $this->oldImage = @ImageCreateFromJpeg($this->fileName); 
  3189. } else { 
  3190. $img_err = __('Support for JPEG format is missing.', 'nggallery'); 
  3191. break; 
  3192. case 'PNG': 
  3193. if (function_exists('ImageCreateFromPng')) { 
  3194. $this->oldImage = @ImageCreateFromPng($this->fileName); 
  3195. } else { 
  3196. $img_err = __('Support for PNG format is missing.', 'nggallery'); 
  3197. break; 
  3198. if (!$this->oldImage) { 
  3199. if ($img_err == null) { 
  3200. $img_err = __('Check memory limit', 'nggallery'); 
  3201. $this->errmsg = sprintf(__('Create Image failed. %1$s', 'nggallery'), $img_err); 
  3202. $this->error = true; 
  3203. } else { 
  3204. $size = GetImageSize($this->fileName); 
  3205. $this->currentDimensions = array('width' => $size[0], 'height' => $size[1]); 
  3206. $this->newImage = $this->oldImage; 
  3207. if ($this->error == true) { 
  3208. if (!$no_ErrorImage) { 
  3209. $this->showErrorImage(); 
  3210. return; 
  3211. /** 
  3212. * Calculate the memory limit 
  3213. * 
  3214. */ 
  3215. function checkMemoryForImage($filename) 
  3216. if (function_exists('memory_get_usage') && ini_get('memory_limit')) { 
  3217. $imageInfo = getimagesize($filename); 
  3218. switch ($this->format) { 
  3219. case 'GIF': 
  3220. // measured factor 1 is better 
  3221. $CHANNEL = 1; 
  3222. break; 
  3223. case 'JPG': 
  3224. $CHANNEL = $imageInfo['channels']; 
  3225. break; 
  3226. case 'PNG': 
  3227. // didn't get the channel for png 
  3228. $CHANNEL = 3; 
  3229. break; 
  3230. $MB = 1048576; 
  3231. // number of bytes in 1M 
  3232. $K64 = 65536; 
  3233. // number of bytes in 64K 
  3234. $TWEAKFACTOR = 1.68; 
  3235. // Or whatever works for you 
  3236. $bits = !empty($imageInfo['bits']) ? $imageInfo['bits'] : 32; 
  3237. // imgInfo[bits] is not always available 
  3238. $memoryNeeded = round(($imageInfo[0] * $imageInfo[1] * $bits * $CHANNEL / 8 + $K64) * $TWEAKFACTOR); 
  3239. $memoryNeeded = memory_get_usage() + $memoryNeeded; 
  3240. // get memory limit 
  3241. $memory_limit = ini_get('memory_limit'); 
  3242. // PHP docs : Note that to have no memory limit, set this directive to -1. 
  3243. if ($memory_limit == -1) { 
  3244. return; 
  3245. // Just check megabyte limits, not higher 
  3246. if (strtolower(substr($memory_limit, -1)) == 'm') { 
  3247. if ($memory_limit != '') { 
  3248. $memory_limit = substr($memory_limit, 0, -1) * 1024 * 1024; 
  3249. if ($memoryNeeded > $memory_limit) { 
  3250. $memoryNeeded = round($memoryNeeded / 1024 / 1024, 2); 
  3251. $this->errmsg = 'Exceed Memory limit. Require : ' . $memoryNeeded . " MByte"; 
  3252. $this->error = true; 
  3253. return; 
  3254. function __destruct() 
  3255. $this->destruct(); 
  3256. /** 
  3257. * Must be called to free up allocated memory after all manipulations are done 
  3258. * 
  3259. */ 
  3260. function destruct() 
  3261. if (is_resource($this->newImage)) { 
  3262. @ImageDestroy($this->newImage); 
  3263. if (is_resource($this->oldImage)) { 
  3264. @ImageDestroy($this->oldImage); 
  3265. if (is_resource($this->workingImage)) { 
  3266. @ImageDestroy($this->workingImage); 
  3267. /** 
  3268. * Returns the current width of the image 
  3269. * 
  3270. * @return int 
  3271. */ 
  3272. function getCurrentWidth() 
  3273. return $this->currentDimensions['width']; 
  3274. /** 
  3275. * Returns the current height of the image 
  3276. * 
  3277. * @return int 
  3278. */ 
  3279. function getCurrentHeight() 
  3280. return $this->currentDimensions['height']; 
  3281. /** 
  3282. * Calculates new image width 
  3283. * 
  3284. * @param int $width 
  3285. * @param int $height 
  3286. * @return array 
  3287. */ 
  3288. function calcWidth($width, $height) 
  3289. $newWp = 100 * $this->maxWidth / $width; 
  3290. $newHeight = $height * $newWp / 100; 
  3291. if (intval($newHeight) == $this->maxHeight - 1) { 
  3292. $newHeight = $this->maxHeight; 
  3293. return array('newWidth' => intval($this->maxWidth), 'newHeight' => intval($newHeight)); 
  3294. /** 
  3295. * Calculates new image height 
  3296. * 
  3297. * @param int $width 
  3298. * @param int $height 
  3299. * @return array 
  3300. */ 
  3301. function calcHeight($width, $height) 
  3302. $newHp = 100 * $this->maxHeight / $height; 
  3303. $newWidth = $width * $newHp / 100; 
  3304. if (intval($newWidth) == $this->maxWidth - 1) { 
  3305. $newWidth = $this->maxWidth; 
  3306. return array('newWidth' => intval($newWidth), 'newHeight' => intval($this->maxHeight)); 
  3307. /** 
  3308. * Calculates new image size based on percentage 
  3309. * 
  3310. * @param int $width 
  3311. * @param int $height 
  3312. * @return array 
  3313. */ 
  3314. function calcPercent($width, $height) 
  3315. $newWidth = $width * $this->percent / 100; 
  3316. $newHeight = $height * $this->percent / 100; 
  3317. return array('newWidth' => intval($newWidth), 'newHeight' => intval($newHeight)); 
  3318. /** 
  3319. * Calculates new image size based on width and height, while constraining to maxWidth and maxHeight 
  3320. * 
  3321. * @param int $width 
  3322. * @param int $height 
  3323. */ 
  3324. function calcImageSize($width, $height) 
  3325. // $width and $height are the CURRENT image resolutions 
  3326. $ratio_w = $this->maxWidth / $width; 
  3327. $ratio_h = $this->maxHeight / $height; 
  3328. if ($ratio_w >= $ratio_h) { 
  3329. $width = $this->maxWidth; 
  3330. $height = (int) round($height * $ratio_h, 0); 
  3331. } else { 
  3332. $height = $this->maxHeight; 
  3333. $width = (int) round($width * $ratio_w, 0); 
  3334. $this->newDimensions = array('newWidth' => $width, 'newHeight' => $height); 
  3335. /** 
  3336. * Calculates new image size based percentage 
  3337. * 
  3338. * @param int $width 
  3339. * @param int $height 
  3340. */ 
  3341. function calcImageSizePercent($width, $height) 
  3342. if ($this->percent > 0) { 
  3343. $this->newDimensions = $this->calcPercent($width, $height); 
  3344. /** 
  3345. * Displays error image 
  3346. * 
  3347. */ 
  3348. function showErrorImage() 
  3349. header('Content-type: image/png'); 
  3350. $errImg = ImageCreate(220, 25); 
  3351. $bgColor = imagecolorallocate($errImg, 0, 0, 0); 
  3352. $fgColor1 = imagecolorallocate($errImg, 255, 255, 255); 
  3353. $fgColor2 = imagecolorallocate($errImg, 255, 0, 0); 
  3354. imagestring($errImg, 3, 6, 6, 'Error:', $fgColor2); 
  3355. imagestring($errImg, 3, 55, 6, $this->errmsg, $fgColor1); 
  3356. imagepng($errImg); 
  3357. imagedestroy($errImg); 
  3358. /** 
  3359. * Resizes image to fixed Width x Height 
  3360. *  
  3361. * @param int $Width 
  3362. * @param int $Height 
  3363. */ 
  3364. function resizeFix($Width = 0, $Height = 0, $deprecated = 3) 
  3365. $this->newWidth = $Width; 
  3366. $this->newHeight = $Height; 
  3367. if (function_exists("ImageCreateTrueColor")) { 
  3368. $this->workingImage = ImageCreateTrueColor($this->newWidth, $this->newHeight); 
  3369. } else { 
  3370. $this->workingImage = ImageCreate($this->newWidth, $this->newHeight); 
  3371. // ImageCopyResampled( 
  3372. $this->imagecopyresampled($this->workingImage, $this->oldImage, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->currentDimensions['width'], $this->currentDimensions['height']); 
  3373. $this->oldImage = $this->workingImage; 
  3374. $this->newImage = $this->workingImage; 
  3375. $this->currentDimensions['width'] = $this->newWidth; 
  3376. $this->currentDimensions['height'] = $this->newHeight; 
  3377. /** 
  3378. * Resizes image to maxWidth x maxHeight 
  3379. * 
  3380. * @param int $maxWidth 
  3381. * @param int $maxHeight 
  3382. */ 
  3383. function resize($maxWidth = 0, $maxHeight = 0, $deprecated = 3) 
  3384. $this->maxWidth = $maxWidth; 
  3385. $this->maxHeight = $maxHeight; 
  3386. $this->calcImageSize($this->currentDimensions['width'], $this->currentDimensions['height']); 
  3387. if (function_exists("ImageCreateTrueColor")) { 
  3388. $this->workingImage = ImageCreateTrueColor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); 
  3389. } else { 
  3390. $this->workingImage = ImageCreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); 
  3391. // ImageCopyResampled( 
  3392. $this->imagecopyresampled($this->workingImage, $this->oldImage, 0, 0, 0, 0, $this->newDimensions['newWidth'], $this->newDimensions['newHeight'], $this->currentDimensions['width'], $this->currentDimensions['height']); 
  3393. $this->oldImage = $this->workingImage; 
  3394. $this->newImage = $this->workingImage; 
  3395. $this->currentDimensions['width'] = $this->newDimensions['newWidth']; 
  3396. $this->currentDimensions['height'] = $this->newDimensions['newHeight']; 
  3397. /** 
  3398. * Resizes the image by $percent percent 
  3399. * 
  3400. * @param int $percent 
  3401. */ 
  3402. function resizePercent($percent = 0) 
  3403. $this->percent = $percent; 
  3404. $this->calcImageSizePercent($this->currentDimensions['width'], $this->currentDimensions['height']); 
  3405. if (function_exists("ImageCreateTrueColor")) { 
  3406. $this->workingImage = ImageCreateTrueColor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); 
  3407. } else { 
  3408. $this->workingImage = ImageCreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']); 
  3409. $this->ImageCopyResampled($this->workingImage, $this->oldImage, 0, 0, 0, 0, $this->newDimensions['newWidth'], $this->newDimensions['newHeight'], $this->currentDimensions['width'], $this->currentDimensions['height']); 
  3410. $this->oldImage = $this->workingImage; 
  3411. $this->newImage = $this->workingImage; 
  3412. $this->currentDimensions['width'] = $this->newDimensions['newWidth']; 
  3413. $this->currentDimensions['height'] = $this->newDimensions['newHeight']; 
  3414. /** 
  3415. * Crops the image from calculated center in a square of $cropSize pixels 
  3416. * 
  3417. * @param int $cropSize 
  3418. */ 
  3419. function cropFromCenter($cropSize) 
  3420. if ($cropSize > $this->currentDimensions['width']) { 
  3421. $cropSize = $this->currentDimensions['width']; 
  3422. if ($cropSize > $this->currentDimensions['height']) { 
  3423. $cropSize = $this->currentDimensions['height']; 
  3424. $cropX = intval(($this->currentDimensions['width'] - $cropSize) / 2); 
  3425. $cropY = intval(($this->currentDimensions['height'] - $cropSize) / 2); 
  3426. if (function_exists("ImageCreateTrueColor")) { 
  3427. $this->workingImage = ImageCreateTrueColor($cropSize, $cropSize); 
  3428. } else { 
  3429. $this->workingImage = ImageCreate($cropSize, $cropSize); 
  3430. $this->imagecopyresampled($this->workingImage, $this->oldImage, 0, 0, $cropX, $cropY, $cropSize, $cropSize, $cropSize, $cropSize); 
  3431. $this->oldImage = $this->workingImage; 
  3432. $this->newImage = $this->workingImage; 
  3433. $this->currentDimensions['width'] = $cropSize; 
  3434. $this->currentDimensions['height'] = $cropSize; 
  3435. /** 
  3436. * Advanced cropping function that crops an image using $startX and $startY as the upper-left hand corner. 
  3437. * 
  3438. * @param int $startX 
  3439. * @param int $startY 
  3440. * @param int $width 
  3441. * @param int $height 
  3442. */ 
  3443. function crop($startX, $startY, $width, $height) 
  3444. //make sure the cropped area is not greater than the size of the image 
  3445. if ($width > $this->currentDimensions['width']) { 
  3446. $width = $this->currentDimensions['width']; 
  3447. if ($height > $this->currentDimensions['height']) { 
  3448. $height = $this->currentDimensions['height']; 
  3449. //make sure not starting outside the image 
  3450. if ($startX + $width > $this->currentDimensions['width']) { 
  3451. $startX = $this->currentDimensions['width'] - $width; 
  3452. if ($startY + $height > $this->currentDimensions['height']) { 
  3453. $startY = $this->currentDimensions['height'] - $height; 
  3454. if ($startX < 0) { 
  3455. $startX = 0; 
  3456. if ($startY < 0) { 
  3457. $startY = 0; 
  3458. if (function_exists("ImageCreateTrueColor")) { 
  3459. $this->workingImage = ImageCreateTrueColor($width, $height); 
  3460. } else { 
  3461. $this->workingImage = ImageCreate($width, $height); 
  3462. $this->imagecopyresampled($this->workingImage, $this->oldImage, 0, 0, $startX, $startY, $width, $height, $width, $height); 
  3463. $this->oldImage = $this->workingImage; 
  3464. $this->newImage = $this->workingImage; 
  3465. $this->currentDimensions['width'] = $width; 
  3466. $this->currentDimensions['height'] = $height; 
  3467. /** 
  3468. * Outputs the image to the screen, or saves to $name if supplied. Quality of JPEG images can be controlled with the $quality variable 
  3469. * 
  3470. * @param int $quality 
  3471. * @param string $name 
  3472. */ 
  3473. function show($quality = 100, $name = '') 
  3474. switch ($this->format) { 
  3475. case 'GIF': 
  3476. if ($name != '') { 
  3477. @ImageGif($this->newImage, $name) or $this->error = true; 
  3478. } else { 
  3479. header('Content-type: image/gif'); 
  3480. ImageGif($this->newImage); 
  3481. break; 
  3482. case 'JPG': 
  3483. if ($name != '') { 
  3484. @ImageJpeg($this->newImage, $name, $quality) or $this->error = true; 
  3485. } else { 
  3486. header('Content-type: image/jpeg'); 
  3487. ImageJpeg($this->newImage, NULL, $quality); 
  3488. break; 
  3489. case 'PNG': 
  3490. if ($name != '') { 
  3491. @ImagePng($this->newImage, $name) or $this->error = true; 
  3492. } else { 
  3493. header('Content-type: image/png'); 
  3494. ImagePng($this->newImage); 
  3495. break; 
  3496. /** 
  3497. * Saves image as $name (can include file path), with quality of # percent if file is a jpeg 
  3498. * 
  3499. * @param string $name 
  3500. * @param int $quality 
  3501. * @return bool errorstate 
  3502. */ 
  3503. function save($name, $quality = 100) 
  3504. $this->show($quality, $name); 
  3505. if ($this->error == true) { 
  3506. $this->errmsg = 'Create Image failed. Check safe mode settings'; 
  3507. return false; 
  3508. if (function_exists('do_action')) { 
  3509. do_action('ngg_ajax_image_save', $name); 
  3510. return true; 
  3511. /** 
  3512. * Creates Apple-style reflection under image, optionally adding a border to main image 
  3513. * 
  3514. * @param int $percent 
  3515. * @param int $reflection 
  3516. * @param int $white 
  3517. * @param bool $border 
  3518. * @param string $borderColor 
  3519. */ 
  3520. function createReflection($percent, $reflection, $white, $border = true, $borderColor = '#a4a4a4') 
  3521. $width = $this->currentDimensions['width']; 
  3522. $height = $this->currentDimensions['height']; 
  3523. $reflectionHeight = intval($height * ($reflection / 100)); 
  3524. $newHeight = $height + $reflectionHeight; 
  3525. $reflectedPart = $height * ($percent / 100); 
  3526. $this->workingImage = ImageCreateTrueColor($width, $newHeight); 
  3527. ImageAlphaBlending($this->workingImage, true); 
  3528. $colorToPaint = ImageColorAllocateAlpha($this->workingImage, 255, 255, 255, 0); 
  3529. ImageFilledRectangle($this->workingImage, 0, 0, $width, $newHeight, $colorToPaint); 
  3530. imagecopyresampled($this->workingImage, $this->newImage, 0, 0, 0, $reflectedPart, $width, $reflectionHeight, $width, $height - $reflectedPart); 
  3531. $this->imageFlipVertical(); 
  3532. imagecopy($this->workingImage, $this->newImage, 0, 0, 0, 0, $width, $height); 
  3533. imagealphablending($this->workingImage, true); 
  3534. for ($i = 0; $i < $reflectionHeight; $i++) { 
  3535. $colorToPaint = imagecolorallocatealpha($this->workingImage, 255, 255, 255, ($i / $reflectionHeight * -1 + 1) * $white); 
  3536. imagefilledrectangle($this->workingImage, 0, $height + $i, $width, $height + $i, $colorToPaint); 
  3537. if ($border == true) { 
  3538. $rgb = $this->hex2rgb($borderColor, false); 
  3539. $colorToPaint = imagecolorallocate($this->workingImage, $rgb[0], $rgb[1], $rgb[2]); 
  3540. imageline($this->workingImage, 0, 0, $width, 0, $colorToPaint); 
  3541. //top line 
  3542. imageline($this->workingImage, 0, $height, $width, $height, $colorToPaint); 
  3543. //bottom line 
  3544. imageline($this->workingImage, 0, 0, 0, $height, $colorToPaint); 
  3545. //left line 
  3546. imageline($this->workingImage, $width - 1, 0, $width - 1, $height, $colorToPaint); 
  3547. //right line 
  3548. $this->oldImage = $this->workingImage; 
  3549. $this->newImage = $this->workingImage; 
  3550. $this->currentDimensions['width'] = $width; 
  3551. $this->currentDimensions['height'] = $newHeight; 
  3552. /** 
  3553. * Flip an image. 
  3554. * 
  3555. * @param bool $horz flip the image in horizontal mode 
  3556. * @param bool $vert flip the image in vertical mode 
  3557. */ 
  3558. function flipImage($horz = false, $vert = false) 
  3559. $sx = $vert ? $this->currentDimensions['width'] - 1 : 0; 
  3560. $sy = $horz ? $this->currentDimensions['height'] - 1 : 0; 
  3561. $sw = $vert ? -$this->currentDimensions['width'] : $this->currentDimensions['width']; 
  3562. $sh = $horz ? -$this->currentDimensions['height'] : $this->currentDimensions['height']; 
  3563. $this->workingImage = imagecreatetruecolor($this->currentDimensions['width'], $this->currentDimensions['height']); 
  3564. $this->imagecopyresampled($this->workingImage, $this->oldImage, 0, 0, $sx, $sy, $this->currentDimensions['width'], $this->currentDimensions['height'], $sw, $sh); 
  3565. $this->oldImage = $this->workingImage; 
  3566. $this->newImage = $this->workingImage; 
  3567. return true; 
  3568. /** 
  3569. * Rotate an image clockwise or counter clockwise 
  3570. * 
  3571. * @param string $direction could be CW or CCW 
  3572. */ 
  3573. function rotateImage($dir = 'CW') 
  3574. $angle = $dir == 'CW' ? 90 : -90; 
  3575. return $this->rotateImageAngle($angle); 
  3576. /** 
  3577. * Rotate an image clockwise or counter clockwise 
  3578. * 
  3579. * @param string $direction could be CW or CCW 
  3580. */ 
  3581. function rotateImageAngle($angle = 90) 
  3582. if (function_exists('imagerotate')) { 
  3583. $this->workingImage = imagerotate($this->oldImage, 360 - $angle, 0); 
  3584. // imagerotate() rotates CCW 
  3585. $this->currentDimensions['width'] = imagesx($this->workingImage); 
  3586. $this->currentDimensions['height'] = imagesy($this->workingImage); 
  3587. $this->oldImage = $this->workingImage; 
  3588. $this->newImage = $this->workingImage; 
  3589. return true; 
  3590. $this->workingImage = imagecreatetruecolor($this->currentDimensions['height'], $this->currentDimensions['width']); 
  3591. imagealphablending($this->workingImage, false); 
  3592. imagesavealpha($this->workingImage, true); 
  3593. switch ($angle) { 
  3594. case 90: 
  3595. for ($x = 0; $x < $this->currentDimensions['width']; $x++) { 
  3596. for ($y = 0; $y < $this->currentDimensions['height']; $y++) { 
  3597. if (!imagecopy($this->workingImage, $this->oldImage, $this->currentDimensions['height'] - $y - 1, $x, $x, $y, 1, 1)) { 
  3598. return false; 
  3599. break; 
  3600. case -90: 
  3601. for ($x = 0; $x < $this->currentDimensions['width']; $x++) { 
  3602. for ($y = 0; $y < $this->currentDimensions['height']; $y++) { 
  3603. if (!imagecopy($this->workingImage, $this->oldImage, $y, $this->currentDimensions['width'] - $x - 1, $x, $y, 1, 1)) { 
  3604. return false; 
  3605. break; 
  3606. default: 
  3607. return false; 
  3608. $this->currentDimensions['width'] = imagesx($this->workingImage); 
  3609. $this->currentDimensions['height'] = imagesy($this->workingImage); 
  3610. $this->oldImage = $this->workingImage; 
  3611. $this->newImage = $this->workingImage; 
  3612. return true; 
  3613. /** 
  3614. * Inverts working image, used by reflection function 
  3615. *  
  3616. * @access private 
  3617. */ 
  3618. function imageFlipVertical() 
  3619. $x_i = imagesx($this->workingImage); 
  3620. $y_i = imagesy($this->workingImage); 
  3621. for ($x = 0; $x < $x_i; $x++) { 
  3622. for ($y = 0; $y < $y_i; $y++) { 
  3623. imagecopy($this->workingImage, $this->workingImage, $x, $y_i - $y - 1, $x, $y, 1, 1); 
  3624. /** 
  3625. * Converts hexidecimal color value to rgb values and returns as array/string 
  3626. * 
  3627. * @param string $hex 
  3628. * @param bool $asString 
  3629. * @return array|string 
  3630. */ 
  3631. function hex2rgb($hex, $asString = false) 
  3632. // strip off any leading # 
  3633. if (0 === strpos($hex, '#')) { 
  3634. $hex = substr($hex, 1); 
  3635. } else { 
  3636. if (0 === strpos($hex, '&H')) { 
  3637. $hex = substr($hex, 2); 
  3638. // break into hex 3-tuple 
  3639. $cutpoint = ceil(strlen($hex) / 2) - 1; 
  3640. $rgb = explode(':', wordwrap($hex, $cutpoint, ':', $cutpoint), 3); 
  3641. // convert each tuple to decimal 
  3642. $rgb[0] = isset($rgb[0]) ? hexdec($rgb[0]) : 0; 
  3643. $rgb[1] = isset($rgb[1]) ? hexdec($rgb[1]) : 0; 
  3644. $rgb[2] = isset($rgb[2]) ? hexdec($rgb[2]) : 0; 
  3645. return $asString ? "{$rgb[0]} {$rgb[1]} {$rgb[2]}" : $rgb; 
  3646. /** 
  3647. * Based on the Watermark function by Marek Malcherek  
  3648. * http://www.malcherek.de 
  3649. * 
  3650. * @param string $color 
  3651. * @param string $wmFont 
  3652. * @param int $wmSize 
  3653. * @param int $wmOpaque 
  3654. */ 
  3655. function watermarkCreateText($color = '000000', $wmFont, $wmSize = 10, $wmOpaque = 90) 
  3656. // set font path 
  3657. $wmFontPath = NGGALLERY_ABSPATH . "fonts/" . $wmFont; 
  3658. if (!is_readable($wmFontPath)) { 
  3659. return; 
  3660. // This function requires both the GD library and the FreeType library. 
  3661. if (!function_exists('ImageTTFBBox')) { 
  3662. return; 
  3663. $words = preg_split('/ /', $this->watermarkText); 
  3664. $lines = array(); 
  3665. $line = ''; 
  3666. $watermark_image_width = 0; 
  3667. // attempt adding a new word until the width is too large; then start a new line and start again 
  3668. foreach ($words as $word) { 
  3669. // sanitize the text being input; imagettftext() can be sensitive 
  3670. $TextSize = $this->ImageTTFBBoxDimensions($wmSize, 0, $this->correct_gd_unc_path($wmFontPath), $line . preg_replace('~^(&([a-zA-Z0-9]);)~', htmlentities('${1}'), mb_convert_encoding($word, "HTML-ENTITIES", "UTF-8"))); 
  3671. if ($watermark_image_width == 0) { 
  3672. $watermark_image_width = $TextSize['width']; 
  3673. if ($TextSize['width'] > $this->newDimensions['newWidth']) { 
  3674. $lines[] = trim($line); 
  3675. $line = ''; 
  3676. } else { 
  3677. if ($TextSize['width'] > $watermark_image_width) { 
  3678. $watermark_image_width = $TextSize['width']; 
  3679. $line .= $word . ' '; 
  3680. $lines[] = trim($line); 
  3681. // use this string to determine our largest possible line height 
  3682. $line_dimensions = $this->ImageTTFBBoxDimensions($wmSize, 0, $this->correct_gd_unc_path($wmFontPath), 'MXQJALYmxqjabdfghjklpqry019`@$^&*(, !132'); 
  3683. $line_height = $line_dimensions['height'] * 1.05; 
  3684. // Create an image to apply our text to 
  3685. $this->workingImage = ImageCreateTrueColor($watermark_image_width, count($lines) * $line_height); 
  3686. ImageSaveAlpha($this->workingImage, true); 
  3687. ImageAlphaBlending($this->workingImage, false); 
  3688. $bgText = imagecolorallocatealpha($this->workingImage, 255, 255, 255, 127); 
  3689. imagefill($this->workingImage, 0, 0, $bgText); 
  3690. $wmTransp = 127 - $wmOpaque * 1.27; 
  3691. $rgb = $this->hex2rgb($color, false); 
  3692. $TextColor = imagecolorallocatealpha($this->workingImage, $rgb[0], $rgb[1], $rgb[2], $wmTransp); 
  3693. // Put text on the image, line-by-line 
  3694. $y_pos = $wmSize; 
  3695. foreach ($lines as $line) { 
  3696. imagettftext($this->workingImage, $wmSize, 0, 0, $y_pos, $TextColor, $this->correct_gd_unc_path($wmFontPath), $line); 
  3697. $y_pos += $line_height; 
  3698. $this->watermarkImgPath = $this->workingImage; 
  3699. return; 
  3700. /** 
  3701. * Returns a path that can be used with imagettftext() and ImageTTFBBox() 
  3702. * 
  3703. * imagettftext() and ImageTTFBBox() cannot load resources from Windows UNC paths 
  3704. * and require they be mangled to be like //server\filename instead of \\server\filename 
  3705. * @param string $path Absolute file path 
  3706. * @return string $path Mangled absolute file path 
  3707. */ 
  3708. public function correct_gd_unc_path($path) 
  3709. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && substr($path, 0, 2) === '\\\\') { 
  3710. $path = ltrim($path, '\\\\'); 
  3711. $path = '//' . $path; 
  3712. return $path; 
  3713. /** 
  3714. * Calculates the width & height dimensions of ImageTTFBBox(). 
  3715. * 
  3716. * Note: ImageTTFBBox() is unreliable with large font sizes 
  3717. * @param $wmSize 
  3718. * @param $fontAngle 
  3719. * @param $wmFontPath 
  3720. * @param $text 
  3721. * @return array 
  3722. */ 
  3723. function ImageTTFBBoxDimensions($wmSize, $fontAngle, $wmFontPath, $text) 
  3724. $box = @ImageTTFBBox($wmSize, $fontAngle, $this->correct_gd_unc_path($wmFontPath), $text); 
  3725. $max_x = max(array($box[0], $box[2], $box[4], $box[6])); 
  3726. $max_y = max(array($box[1], $box[3], $box[5], $box[7])); 
  3727. $min_x = min(array($box[0], $box[2], $box[4], $box[6])); 
  3728. $min_y = min(array($box[1], $box[3], $box[5], $box[7])); 
  3729. return array("width" => $max_x - $min_x, "height" => $max_y - $min_y); 
  3730. function applyFilter($filterType) 
  3731. $args = func_get_args(); 
  3732. array_unshift($args, $this->newImage); 
  3733. return call_user_func_array('imagefilter', $args); 
  3734. /** 
  3735. * Modfied Watermark function by Steve Peart  
  3736. * http://parasitehosting.com/ 
  3737. * 
  3738. * @param string $relPOS 
  3739. * @param int $xPOS 
  3740. * @param int $yPOS 
  3741. */ 
  3742. function watermarkImage($relPOS = 'botRight', $xPOS = 0, $yPOS = 0) 
  3743. // if it's a resource ID take it as watermark text image 
  3744. if (is_resource($this->watermarkImgPath)) { 
  3745. $this->workingImage = $this->watermarkImgPath; 
  3746. } else { 
  3747. // (possibly) search for the file from the document root 
  3748. if (!is_file($this->watermarkImgPath)) { 
  3749. $fs = C_Fs::get_instance(); 
  3750. if (is_file($fs->join_paths($fs->get_document_root('content'), $this->watermarkImgPath))) { 
  3751. $this->watermarkImgPath = $fs->get_document_root('content') . $this->watermarkImgPath; 
  3752. // Would you really want to use anything other than a png? 
  3753. $this->workingImage = @imagecreatefrompng($this->watermarkImgPath); 
  3754. // if it's not a valid file die... 
  3755. if (empty($this->workingImage) or !$this->workingImage) { 
  3756. return; 
  3757. imagealphablending($this->workingImage, false); 
  3758. imagesavealpha($this->workingImage, true); 
  3759. $sourcefile_width = imageSX($this->oldImage); 
  3760. $sourcefile_height = imageSY($this->oldImage); 
  3761. $watermarkfile_width = imageSX($this->workingImage); 
  3762. $watermarkfile_height = imageSY($this->workingImage); 
  3763. switch (substr($relPOS, 0, 3)) { 
  3764. case 'top': 
  3765. $dest_y = 0 + $yPOS; 
  3766. break; 
  3767. case 'mid': 
  3768. $dest_y = $sourcefile_height / 2 - $watermarkfile_height / 2; 
  3769. break; 
  3770. case 'bot': 
  3771. $dest_y = $sourcefile_height - $watermarkfile_height - $yPOS; 
  3772. break; 
  3773. default: 
  3774. $dest_y = 0; 
  3775. break; 
  3776. switch (substr($relPOS, 3)) { 
  3777. case 'Left': 
  3778. $dest_x = 0 + $xPOS; 
  3779. break; 
  3780. case 'Center': 
  3781. $dest_x = $sourcefile_width / 2 - $watermarkfile_width / 2; 
  3782. break; 
  3783. case 'Right': 
  3784. $dest_x = $sourcefile_width - $watermarkfile_width - $xPOS; 
  3785. break; 
  3786. default: 
  3787. $dest_x = 0; 
  3788. break; 
  3789. // debug 
  3790. // $this->errmsg = 'X '.$dest_x.' Y '.$dest_y; 
  3791. // $this->showErrorImage(); 
  3792. // if a gif, we have to upsample it to a truecolor image 
  3793. if ($this->format == 'GIF') { 
  3794. $tempimage = imagecreatetruecolor($sourcefile_width, $sourcefile_height); 
  3795. imagecopy($tempimage, $this->oldImage, 0, 0, 0, 0, $sourcefile_width, $sourcefile_height); 
  3796. $this->newImage = $tempimage; 
  3797. $this->imagecopymerge_alpha($this->newImage, $this->workingImage, $dest_x, $dest_y, 0, 0, $watermarkfile_width, $watermarkfile_height, 100); 
  3798. /** 
  3799. * Wrapper to imagecopymerge() that allows PNG transparency 
  3800. */ 
  3801. function imagecopymerge_alpha($destination_image, $source_image, $destination_x, $destination_y, $source_x, $source_y, $source_w, $source_h, $pct) 
  3802. $cut = imagecreatetruecolor($source_w, $source_h); 
  3803. imagecopy($cut, $destination_image, 0, 0, $destination_x, $destination_y, $source_w, $source_h); 
  3804. imagecopy($cut, $source_image, 0, 0, $source_x, $source_y, $source_w, $source_h); 
  3805. imagecopymerge($destination_image, $cut, $destination_x, $destination_y, 0, 0, $source_w, $source_h, $pct); 
  3806. /** 
  3807. * Modfied imagecopyresampled function to save transparent images 
  3808. * See : http://www.akemapa.com/2008/07/10/php-gd-resize-transparent-image-png-gif/ 
  3809. * @since 1.9.0 
  3810. *  
  3811. * @param resource $dst_image 
  3812. * @param resource $src_image 
  3813. * @param int $dst_x 
  3814. * @param int $dst_y 
  3815. * @param int $src_x 
  3816. * @param int $src_y 
  3817. * @param int $dst_w 
  3818. * @param int $dst_h 
  3819. * @param int $src_w 
  3820. * @param int $src_h 
  3821. * @return bool 
  3822. */ 
  3823. function imagecopyresampled(&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) 
  3824. // Check if this image is PNG or GIF, then set if Transparent 
  3825. if ($this->format == 'GIF' || $this->format == 'PNG') { 
  3826. imagealphablending($dst_image, false); 
  3827. imagesavealpha($dst_image, true); 
  3828. $transparent = imagecolorallocatealpha($dst_image, 255, 255, 255, 127); 
  3829. imagefilledrectangle($dst_image, 0, 0, $dst_w, $dst_h, $transparent); 
  3830. imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); 
  3831. return true; 
  3832. class Mixin_WordPress_GalleryStorage_Driver extends Mixin 
  3833. /** 
  3834. * Returns the named sizes available for images 
  3835. * @global array $_wp_additional_image_sizese 
  3836. * @return array 
  3837. */ 
  3838. function get_image_sizes() 
  3839. global $_wp_additional_image_sizes; 
  3840. $_wp_additional_image_sizes[] = 'full'; 
  3841. return $_wp_additional_image_sizes; 
  3842. /** 
  3843. * Gets the upload path for new images in this gallery 
  3844. * This will always be the date-based directory 
  3845. * @param type $gallery 
  3846. * @return type 
  3847. */ 
  3848. function get_upload_abspath($gallery = FALSE) 
  3849. // Gallery is used for this driver, as the upload path is 
  3850. // the same, regardless of what gallery is used 
  3851. $retval = FALSE; 
  3852. $dir = wp_upload_dir(time()); 
  3853. if ($dir) { 
  3854. $retval = $dir['path']; 
  3855. return $retval; 
  3856. /** 
  3857. * Will always return the same as get_upload_abspath(), as 
  3858. * WordPress storage is not organized by gallery but by date 
  3859. * @param int|object $gallery 
  3860. */ 
  3861. function get_gallery_abspath($gallery = FALSE) 
  3862. return $this->object->get_upload_abspath(); 
  3863. /** 
  3864. * Gets the url of a particular sized image 
  3865. * @param int|object $image 
  3866. * @param type $size 
  3867. * @return string 
  3868. */ 
  3869. function get_image_url($image = FALSE, $size = 'full') 
  3870. $retval = NULL; 
  3871. $image_key = C_Displayed_Gallery_Mapper::get_instance()->get_primary_key_column(); 
  3872. if ($image && ($image_id = $this->object->_get_image_id($image))) { 
  3873. $parts = wp_get_attachment_image_src($image->{$image_key}); 
  3874. if ($parts) { 
  3875. $retval = $parts['url']; 
  3876. return apply_filters('ngg_get_image_url', $retval, $image, $size); 
  3877. /** 
  3878. * Class C_WordPress_GalleryStorage_Driver 
  3879. * @mixin Mixin_WordPress_GalleryStorage_Driver 
  3880. */ 
  3881. class C_WordPress_GalleryStorage_Driver extends C_GalleryStorage_Driver_Base 
  3882. function define($context = FALSE) 
  3883. parent::define($context); 
  3884. $this->add_mixin('Mixin_WordPress_GalleryStorage_Driver'); 
  3885. /** 
  3886. * Class Mixin_NextGen_Table_Extras 
  3887. * @mixin C_CustomPost_DataMapper_Driver 
  3888. */ 
  3889. class Mixin_NextGen_Table_Extras extends Mixin 
  3890. const CUSTOM_POST_NAME = __CLASS__; 
  3891. function initialize() 
  3892. // Each record in a NextGEN Gallery table has an associated custom post in the wp_posts table 
  3893. $this->object->_custom_post_mapper = new C_CustomPost_DataMapper_Driver($this->object->get_object_name()); 
  3894. $this->object->_custom_post_mapper->set_model_factory_method('extra_fields'); 
  3895. /** 
  3896. * Defines a column for the mapper 
  3897. * @param $name 
  3898. * @param $data_type 
  3899. * @param null $default_value 
  3900. * @param bool $extra 
  3901. */ 
  3902. function define_column($name, $data_type, $default_value = NULL, $extra = FALSE) 
  3903. $this->call_parent('define_column', $name, $data_type, $default_value); 
  3904. if ($extra) { 
  3905. $this->object->_columns[$name]['extra'] = TRUE; 
  3906. } else { 
  3907. $this->object->_columns[$name]['extra'] = FALSE; 
  3908. /** 
  3909. * Gets a list of all the extra columns defined for this table 
  3910. * @return array 
  3911. */ 
  3912. function get_extra_columns() 
  3913. $retval = array(); 
  3914. foreach ($this->object->_columns as $key => $properties) { 
  3915. if ($properties['extra']) { 
  3916. $retval[] = $key; 
  3917. return $retval; 
  3918. /** 
  3919. * Adds a column to the database 
  3920. * @param $column_name 
  3921. * @param $datatype 
  3922. * @param null $default_value 
  3923. */ 
  3924. function _add_column($column_name, $datatype, $default_value = NULL) 
  3925. $skip = FALSE; 
  3926. if (isset($this->object->_columns[$column_name]) and $this->object->_columns[$column_name]['extra']) { 
  3927. $skip = TRUE; 
  3928. if (!$skip) { 
  3929. $this->call_parent('_add_column', $column_name, $datatype, $default_value); 
  3930. return !$skip; 
  3931. function create_custom_post_entity($entity) 
  3932. $custom_post_entity = new stdClass(); 
  3933. // If the custom post entity already exists then it needs 
  3934. // an ID 
  3935. if (isset($entity->extras_post_id)) { 
  3936. $custom_post_entity->ID = $entity->extras_post_id; 
  3937. // If a property isn't a column for the table, then 
  3938. // it belongs to the custom post record 
  3939. foreach (get_object_vars($entity) as $key => $value) { 
  3940. if (!$this->object->has_column($key)) { 
  3941. unset($entity->{$key}); 
  3942. if ($this->object->has_defined_column($key) && $key != $this->object->get_primary_key_column()) { 
  3943. $custom_post_entity->{$key} = $value; 
  3944. // Used to help find these type of records 
  3945. $custom_post_entity->post_name = self::CUSTOM_POST_NAME; 
  3946. return $custom_post_entity; 
  3947. /** 
  3948. * Creates a new record in the custom table, as well as a custom post record 
  3949. * @param $entity 
  3950. */ 
  3951. function _create($entity) 
  3952. $retval = FALSE; 
  3953. $custom_post_entity = $this->create_custom_post_entity($entity); 
  3954. // Try persisting the custom post type record first 
  3955. if ($custom_post_id = $this->object->_custom_post_mapper->save($custom_post_entity)) { 
  3956. // Try saving the custom table record. If that fails, then destroy the previously 
  3957. // created custom post type record 
  3958. if (!($retval = $this->call_parent('_create', $entity))) { 
  3959. $this->object->_custom_post_mapper->destroy($custom_post_id); 
  3960. } else { 
  3961. $entity->extras_post_id = $custom_post_id; 
  3962. return $retval; 
  3963. // Updates a custom table record and it's associated custom post type record in the database 
  3964. function _update($entity) 
  3965. $retval = FALSE; 
  3966. $custom_post_entity = $this->create_custom_post_entity($entity); 
  3967. $custom_post_id = $this->object->_custom_post_mapper->save($custom_post_entity); 
  3968. $entity->extras_post_id = $custom_post_id; 
  3969. $retval = $this->call_parent('_update', $entity); 
  3970. foreach ($this->get_extra_columns() as $key) { 
  3971. if (isset($custom_post_entity->{$key})) { 
  3972. $entity->{$key} = $custom_post_entity->{$key}; 
  3973. return $retval; 
  3974. function destroy($entity) 
  3975. if (isset($entity->extras_post_id)) { 
  3976. wp_delete_post($entity->extras_post_id, TRUE); 
  3977. return $this->call_parent('destroy', $entity); 
  3978. function _regex_replace($in) 
  3979. global $wpdb; 
  3980. $from = 'FROM `' . $this->object->get_table_name() . '`'; 
  3981. $out = str_replace('FROM', ", GROUP_CONCAT(CONCAT_WS('@@', meta_key, meta_value)) AS 'extras' FROM", $in); 
  3982. $out = str_replace($from, "{$from} LEFT OUTER JOIN `{$wpdb->postmeta}` ON `{$wpdb->postmeta}`.`post_id` = `extras_post_id` ", $out); 
  3983. return $out; 
  3984. /** 
  3985. * Gets the generated query 
  3986. */ 
  3987. function get_generated_query() 
  3988. // Add extras column 
  3989. if ($this->object->is_select_statement() && stripos($this->object->_select_clause, 'count(') === FALSE) { 
  3990. $table_name = $this->object->get_table_name(); 
  3991. $primary_key = "{$table_name}.{$this->object->get_primary_key_column()}"; 
  3992. if (stripos($this->object->_select_clause, 'DISTINCT') === FALSE) { 
  3993. $this->object->_select_clause = str_replace('SELECT', 'SELECT DISTINCT', $this->object->_select_clause); 
  3994. $this->object->group_by($primary_key); 
  3995. $sql = $this->call_parent('get_generated_query'); 
  3996. // Sections may be omitted by wrapping them in mysql/C style comments 
  3997. if (stripos($sql, '/*NGG_NO_EXTRAS_TABLE*/') !== FALSE) { 
  3998. $parts = explode('/*NGG_NO_EXTRAS_TABLE*/', $sql); 
  3999. foreach ($parts as $ndx => $row) { 
  4000. if ($ndx % 2 != 0) { 
  4001. continue; 
  4002. $parts[$ndx] = $this->_regex_replace($row); 
  4003. $sql = implode('', $parts); 
  4004. } else { 
  4005. $sql = $this->_regex_replace($sql); 
  4006. } else { 
  4007. $sql = $this->call_parent('get_generated_query'); 
  4008. return $sql; 
  4009. function _convert_to_entity($entity) 
  4010. // Add extra columns to entity 
  4011. if (isset($entity->extras)) { 
  4012. $extras = $entity->extras; 
  4013. unset($entity->extras); 
  4014. foreach (explode(', ', $extras) as $extra) { 
  4015. if ($extra) { 
  4016. list($key, $value) = explode('@@', $extra); 
  4017. if ($this->object->has_defined_column($key) && !isset($entity->key)) { 
  4018. $entity->{$key} = $value; 
  4019. // Cast custom_post_id as integer 
  4020. if (isset($entity->extras_post_id)) { 
  4021. $entity->extras_post_id = intval($entity->extras_post_id); 
  4022. } else { 
  4023. $entity->extras_post_id = 0; 
  4024. $retval = $this->call_parent('_convert_to_entity', $entity); 
  4025. return $entity; 
.