/products/photocrati_nextgen/modules/nextgen_gallery_display/module.nextgen_gallery_display.php

  1. <?php 
  2.  
  3. define('NGG_DISPLAY_SETTINGS_SLUG', 'ngg_display_settings'); 
  4. define('NGG_DISPLAY_PRIORITY_BASE', 10000); 
  5. define('NGG_DISPLAY_PRIORITY_STEP', 2000); 
  6. if (!defined('NGG_RENDERING_CACHE_TTL')) define('NGG_RENDERING_CACHE_TTL', PHOTOCRATI_CACHE_TTL); 
  7. if (!defined('NGG_DISPLAYED_GALLERY_CACHE_TTL')) define('NGG_DISPLAYED_GALLERY_CACHE_TTL', PHOTOCRATI_CACHE_TTL); 
  8. if (!defined('NGG_RENDERING_CACHE_ENABLED')) define('NGG_RENDERING_CACHE_ENABLED', PHOTOCRATI_CACHE); 
  9. if (!defined('NGG_SHOW_DISPLAYED_GALLERY_ERRORS')) define('NGG_SHOW_DISPLAYED_GALLERY_ERRORS', NGG_DEBUG); 
  10.  
  11. class M_Gallery_Display extends C_Base_Module 
  12. function define($id = 'pope-module',  
  13. $name = 'Pope Module',  
  14. $description = '',  
  15. $version = '',  
  16. $uri = '',  
  17. $author = '',  
  18. $author_uri = '',  
  19. $context = FALSE) 
  20. parent::define( 
  21. 'photocrati-nextgen_gallery_display',  
  22. 'Gallery Display',  
  23. 'Provides the ability to display gallery of images',  
  24. '0.17',  
  25. 'https://www.imagely.com/wordpress-gallery-plugin/nextgen-gallery/',  
  26. 'Imagely',  
  27. 'https://www.imagely.com' 
  28. ); 
  29.  
  30. C_Photocrati_Installer::add_handler($this->module_id, 'C_Display_Type_Installer'); 
  31.  
  32.  
  33. /** 
  34. * Register utilities required for this module 
  35. */ 
  36. function _register_utilities() 
  37. // Register frontend-only components 
  38. if (apply_filters('ngg_load_frontend_logic', TRUE, $this->module_id)) 
  39. // This utility provides a controller to render the settings form 
  40. // for a display type, or render the front-end of a display type 
  41. $this->get_registry()->add_utility( 
  42. 'I_Display_Type_Controller',  
  43. 'C_Display_Type_Controller' 
  44. ); 
  45.  
  46. // This utility provides the capabilities of rendering a display type 
  47. $this->get_registry()->add_utility( 
  48. 'I_Displayed_Gallery_Renderer',  
  49. 'C_Displayed_Gallery_Renderer' 
  50. ); 
  51.  
  52. // This utility provides a datamapper for Display Types 
  53. $this->get_registry()->add_utility( 
  54. 'I_Display_Type_Mapper',  
  55. 'C_Display_Type_Mapper' 
  56. ); 
  57.  
  58. // This utility provides a datamapper for Displayed Galleries. A 
  59. // displayed gallery is the association between some entities (images 
  60. //or galleries) and a display type 
  61. $this->get_registry()->add_utility( 
  62. 'I_Displayed_Gallery_Mapper',  
  63. 'C_Displayed_Gallery_Mapper' 
  64. ); 
  65.  
  66. /** 
  67. * Registers adapters required for this module 
  68. */ 
  69. function _register_adapters() 
  70. // Provides factory methods for creating display type and 
  71. // displayed gallery instances 
  72. $this->get_registry()->add_adapter( 
  73. 'I_Component_Factory', 'A_Gallery_Display_Factory' 
  74. ); 
  75.  
  76. if (is_admin()) { 
  77. $this->get_registry()->add_adapter( 
  78. 'I_Page_Manager',  
  79. 'A_Display_Settings_Page' 
  80. ); 
  81.  
  82. $this->get_registry()->add_adapter( 
  83. 'I_NextGen_Admin_Page',  
  84. 'A_Display_Settings_Controller',  
  85. NGG_DISPLAY_SETTINGS_SLUG 
  86. ); 
  87.  
  88. // Frontend-only components 
  89. if (apply_filters('ngg_load_frontend_logic', TRUE, $this->module_id)) 
  90. $this->get_registry()->add_adapter('I_MVC_View', 'A_Gallery_Display_View'); 
  91. $this->get_registry()->add_adapter('I_MVC_View', 'A_Displayed_Gallery_Trigger_Element'); 
  92. $this->get_registry()->add_adapter('I_Display_Type_Controller', 'A_Displayed_Gallery_Trigger_Resources'); 
  93.  
  94. /** 
  95. * Registers hooks for the WordPress framework 
  96. */ 
  97. function _register_hooks() 
  98. if (apply_filters('ngg_load_frontend_logic', TRUE, $this->module_id)) 
  99. C_NextGen_Shortcode_Manager::add('ngg_images', array(&$this, 'display_images')); 
  100. add_action('wp_enqueue_scripts', array(&$this, 'no_resources_mode'), PHP_INT_MAX-1); 
  101. add_filter('the_content', array($this, '_render_related_images')); 
  102.  
  103. add_action('init', array(&$this, 'register_resources'), 12); 
  104. add_action('admin_bar_menu', array(&$this, 'add_admin_bar_menu'), 100); 
  105. add_filter('run_ngg_resource_manager', array(&$this, 'no_resources_mode')); 
  106. add_action('init', array(&$this, 'serve_fontawesome'), 15); 
  107.  
  108. // Add hook to delete displayed galleries when removed from a post 
  109. add_action('pre_post_update', array(&$this, 'locate_stale_displayed_galleries')); 
  110. add_action('before_delete_post', array(&$this, 'locate_stale_displayed_galleries')); 
  111. add_action('post_updated', array(&$this, 'cleanup_displayed_galleries')); 
  112. add_action('after_delete_post', array(&$this, 'cleanup_displayed_galleries')); 
  113.  
  114. add_action('wp_print_styles', array($this, 'fix_nextgen_custom_css_order'), PHP_INT_MAX-1); 
  115.  
  116. /** 
  117. * This moves the NextGen custom CSS to the last of the queue 
  118. */ 
  119. function fix_nextgen_custom_css_order() 
  120. global $wp_styles; 
  121. if (in_array('nggallery', $wp_styles->queue)) 
  122. foreach ($wp_styles->queue as $ndx => $style) { 
  123. if ($style == 'nggallery') 
  124. unset($wp_styles->queue[$ndx]); 
  125. $wp_styles->queue[] = 'nggallery'; 
  126. break; 
  127.  
  128. /** 
  129. * Locates the ids of displayed galleries that have been 
  130. * removed from the post, and flags then for cleanup (deletion) 
  131. * @global array $displayed_galleries_to_cleanup 
  132. * @param int $post_id 
  133. */ 
  134. function locate_stale_displayed_galleries($post_id) 
  135. global $displayed_galleries_to_cleanup; 
  136. $displayed_galleries_to_cleanup = array(); 
  137. $post = get_post($post_id); 
  138. $gallery_preview_url = C_NextGen_Settings::get_instance()->get('gallery_preview_url'); 
  139. $preview_url = preg_quote($gallery_preview_url, '#'); 
  140. if (preg_match_all("#{$preview_url}/id--(\d+)#", html_entity_decode($post->post_content), $matches, PREG_SET_ORDER)) { 
  141. foreach ($matches as $match) { 
  142. $preview_url = preg_quote($match[0], '/'); 
  143. // The post was edited, and the displayed gallery placeholder was removed 
  144. if (isset($_REQUEST['post_content']) && (!preg_match("/{$preview_url}/", $_POST['post_content']))) { 
  145. $displayed_galleries_to_cleanup[] = intval($match[1]); 
  146. // The post was deleted 
  147. elseif (!isset($_REQUEST['action'])) { 
  148. $displayed_galleries_to_cleanup[] = intval($match[1]); 
  149.  
  150. /** 
  151. * Deletes any displayed galleries that are no longer associated with a post/page 
  152. * 
  153. * @global array $displayed_galleries_to_cleanup 
  154. * @param int $post_id 
  155. */ 
  156. function cleanup_displayed_galleries($post_id) 
  157. if (!apply_filters('ngg_cleanup_displayed_galleries', true, $post_id)) 
  158. return; 
  159.  
  160. global $displayed_galleries_to_cleanup; 
  161. $mapper = C_Displayed_Gallery_Mapper::get_instance(); 
  162. foreach ($displayed_galleries_to_cleanup as $id) { 
  163. $mapper->destroy($id); 
  164.  
  165. /** 
  166. * Serves the fontawesome woff file via PHP. We do this, as IIS won't serve .woff files. 
  167. * @throws E_Clean_Exit 
  168. */ 
  169. function serve_fontawesome() 
  170. if (isset($_REQUEST['ngg_serve_fontawesome_woff'])) { 
  171. $fs = C_Fs::get_instance(); 
  172. $abspath = $fs->find_static_abspath('photocrati-nextgen_gallery_display#fonts/fontawesome-webfont.woff'); 
  173. if ($abspath) { 
  174. header("Content-Type: application/x-font-woff"); 
  175. readfile($abspath); 
  176. throw new E_Clean_Exit(); 
  177. elseif (isset($_REQUEST['ngg_serve_fontawesome_css'])) { 
  178. $fs = C_Fs::get_instance(); 
  179. $abspath = $fs->find_static_abspath('photocrati-nextgen_gallery_display#fontawesome/font-awesome.css'); 
  180. if ($abspath) { 
  181. $router = C_Router::get_instance(); 
  182. $file_content = file_get_contents($abspath); 
  183. $file_content = str_replace('../fonts/fontawesome-webfont.eot', $router->get_static_url($this->module_id . '#fonts/fontawesome-webfont.eot'), $file_content); 
  184. $file_content = str_replace('../fonts/fontawesome-webfont.svg', $router->get_static_url($this->module_id . '#fonts/fontawesome-webfont.svg'), $file_content); 
  185. $file_content = str_replace('../fonts/fontawesome-webfont.ttf', $router->get_static_url($this->module_id . '#fonts/fontawesome-webfont.ttf'), $file_content); 
  186. $file_content = str_replace('../fonts/fontawesome-webfont.woff2', $router->get_static_url($this->module_id . '#fonts/fontawesome-webfont.woff2'), $file_content); 
  187. $file_content = str_replace('../fonts/fontawesome-webfont.woff', site_url('/?ngg_serve_fontawesome_woff=1'), $file_content); 
  188. header('Content-Type: text/css'); 
  189. echo $file_content; 
  190. throw new E_Clean_Exit(); 
  191.  
  192. /** 
  193. * Enqueues fontawesome. First checks to see if fontawesome is provided by another plugin or already enqueued,  
  194. * and if not, enqueues a version of fontawesome that will work with or without IIS 
  195. */ 
  196. static function enqueue_fontawesome() 
  197. if (!wp_style_is('fontawesome', 'registered')) 
  198. wp_enqueue_style( 
  199. 'fontawesome',  
  200. self::get_fontawesome_url(TRUE),  
  201. FALSE,  
  202. '4.6.1' 
  203. ); 
  204.  
  205. wp_enqueue_style('fontawesome'); 
  206.  
  207. /** 
  208. * Gets the src url for the registered fontawesome handler 
  209. * @param bool $ngg_provided_only 
  210. * @return null|string|void 
  211. */ 
  212. static function get_fontawesome_url($ngg_provided_only=FALSE) 
  213. $retval = NULL; 
  214.  
  215. if (wp_style_is('fontawesome', 'registered') && !$ngg_provided_only) { 
  216. $style = wp_styles()->registered['fontawesome']; 
  217. $retval = $style->src; 
  218. else { 
  219. if (strpos(strtolower($_SERVER['SERVER_SOFTWARE']), 'microsoft-iis') !== FALSE) { 
  220. $retval = site_url('/?ngg_serve_fontawesome_css=1'); 
  221. else { 
  222. $router = C_Router::get_instance(); 
  223. $retval = $router->get_static_url('photocrati-nextgen_gallery_display#fontawesome/font-awesome.css'); 
  224.  
  225. return $retval; 
  226.  
  227. function no_resources_mode($valid_request=TRUE) 
  228. if (isset($_REQUEST['ngg_no_resources'])) { 
  229. global $wp_scripts, $wp_styles; 
  230.  
  231. // Don't enqueue any stylesheets 
  232. if ($wp_scripts) 
  233. $wp_scripts->queue = $wp_styles->queue = array(); 
  234.  
  235. // Don't run the resource manager 
  236. $valid_request = FALSE; 
  237.  
  238. return $valid_request; 
  239.  
  240. static function _render_related_string($sluglist=array(), $maxImages=NULL, $type=NULL) 
  241. $settings = C_NextGen_Settings::get_instance(); 
  242. if (is_null($type)) $type = $settings->appendType; 
  243. if (is_null($maxImages)) $maxImages = $settings->maxImages; 
  244.  
  245. if (!$sluglist) { 
  246. switch ($type) { 
  247. case 'tags': 
  248. if (function_exists('get_the_tags')) 
  249. $taglist = get_the_tags(); 
  250. if (is_array($taglist)) { 
  251. foreach ($taglist as $tag) { 
  252. $sluglist[] = $tag->slug; 
  253. break; 
  254. case 'category': 
  255. $catlist = get_the_category(); 
  256. if (is_array($catlist)) 
  257. foreach ($catlist as $cat) { 
  258. $sluglist[] = $cat->category_nicename; 
  259. break; 
  260.  
  261. $taglist = implode(', ', $sluglist); 
  262.  
  263. if ($taglist === 'uncategorized' || empty($taglist)) 
  264. return; 
  265.  
  266. $renderer = C_Displayed_Gallery_Renderer::get_instance(); 
  267. $view = C_Component_Factory::get_instance()->create('mvc_view', ''); 
  268. $retval = $renderer->display_images(array( 
  269. 'source' => 'tags',  
  270. 'container_ids' => $taglist,  
  271. 'display_type' => NGG_BASIC_THUMBNAILS,  
  272. 'images_per_page' => $maxImages,  
  273. 'maximum_entity_count' => $maxImages,  
  274. 'template' => $view->get_template_abspath('photocrati-nextgen_gallery_display#related'),  
  275. 'show_all_in_lightbox' => FALSE,  
  276. 'show_slideshow_link' => FALSE,  
  277. 'disable_pagination' => TRUE,  
  278. 'display_no_images_error' => FALSE 
  279. )); 
  280.  
  281. if ($retval) wp_enqueue_style('nextgen_gallery_related_images'); 
  282.  
  283. return apply_filters('ngg_show_related_gallery_content', $retval, $taglist); 
  284.  
  285. function _render_related_images($content) 
  286. $settings = C_NextGen_Settings::get_instance(); 
  287.  
  288. if ($settings->get('activateTags')) { 
  289. $related = self::_render_related_string(); 
  290.  
  291. if ($related != null) { 
  292. $heading = $settings->relatedHeading; 
  293. $content .= $heading . $related; 
  294.  
  295. return $content; 
  296.  
  297. /** 
  298. * Adds menu item to the admin bar 
  299. */ 
  300. function add_admin_bar_menu() 
  301. global $wp_admin_bar; 
  302.  
  303. if ( current_user_can('NextGEN Change options') ) { 
  304. $wp_admin_bar->add_menu(array( 
  305. 'parent' => 'ngg-menu',  
  306. 'id' => 'ngg-menu-display_settings',  
  307. 'title' => __('Gallery Settings', 'nggallery'),  
  308. 'href' => admin_url('admin.php?page=ngg_display_settings') 
  309. )); 
  310.  
  311. /** 
  312. * Registers our static settings resources so the ATP module can find them later 
  313. */ 
  314. function register_resources() 
  315. // Register custom post types for compatibility 
  316. $types = array( 
  317. 'displayed_gallery' => 'NextGEN Gallery - Displayed Gallery',  
  318. 'display_type' => 'NextGEN Gallery - Display Type',  
  319. 'gal_display_source' => 'NextGEN Gallery - Displayed Gallery Source' 
  320. ); 
  321. foreach ($types as $type => $label) { 
  322. register_post_type($type, array( 
  323. 'label' => $label,  
  324. 'publicly_queryable' => FALSE,  
  325. 'exclude_from_search' => TRUE,  
  326. )); 
  327. $router = C_Router::get_instance(); 
  328.  
  329. wp_register_script( 
  330. 'nextgen_gallery_display_settings',  
  331. $router->get_static_url('photocrati-nextgen_gallery_display#nextgen_gallery_display_settings.js'),  
  332. array('jquery-ui-accordion', 'jquery-ui-tooltip'),  
  333. NGG_SCRIPT_VERSION 
  334. ); 
  335.  
  336. wp_register_style( 
  337. 'nextgen_gallery_display_settings',  
  338. $router->get_static_url('photocrati-nextgen_gallery_display#nextgen_gallery_display_settings.css'),  
  339. FALSE,  
  340. NGG_SCRIPT_VERSION 
  341. ); 
  342.  
  343. if (apply_filters('ngg_load_frontend_logic', TRUE, $this->module_id)) 
  344. wp_register_style( 
  345. 'nextgen_gallery_related_images',  
  346. $router->get_static_url('photocrati-nextgen_gallery_display#nextgen_gallery_related_images.css'),  
  347. FALSE,  
  348. NGG_SCRIPT_VERSION 
  349. ); 
  350. wp_register_script( 
  351. 'ngg_common',  
  352. $router->get_static_url('photocrati-nextgen_gallery_display#common.js'),  
  353. array('jquery', 'photocrati_ajax'),  
  354. NGG_SCRIPT_VERSION,  
  355. TRUE 
  356. ); 
  357. wp_register_style( 
  358. 'ngg_trigger_buttons',  
  359. $router->get_static_url('photocrati-nextgen_gallery_display#trigger_buttons.css'),  
  360. FALSE,  
  361. NGG_SCRIPT_VERSION 
  362. ); 
  363.  
  364.  
  365. /** 
  366. * Adds the display settings page to wp-admin 
  367. */ 
  368. function add_display_settings_page() 
  369. add_submenu_page( 
  370. NGGFOLDER,  
  371. __('NextGEN Gallery & Album Settings', 'nggallery'),  
  372. __('Gallery Settings', 'nggallery'),  
  373. 'NextGEN Change options',  
  374. NGG_DISPLAY_SETTINGS_SLUG,  
  375. array(&$this->controller, 'index_action') 
  376. ); 
  377.  
  378. /** 
  379. * Provides the [display_images] shortcode 
  380. * @param array $params 
  381. * @param string $inner_content 
  382. * @return string 
  383. */ 
  384. function display_images($params, $inner_content=NULL) 
  385. $renderer = C_Displayed_Gallery_Renderer::get_instance(); 
  386. return $renderer->display_images($params, $inner_content); 
  387.  
  388. /** 
  389. * Gets a value from the parameter array, and if not available, uses the default value 
  390. * 
  391. * @param string $name 
  392. * @param mixed $default 
  393. * @param array $params 
  394. * @return mixed 
  395. */ 
  396. function _get_param($name, $default, $params) 
  397. return (isset($params[$name])) ? $params[$name] : $default; 
  398.  
  399. function get_type_list() 
  400. return array( 
  401. 'C_Displayed_Gallery_Trigger' => 'class.displayed_gallery_trigger.php',  
  402. 'C_Displayed_Gallery_Trigger_Manager' => 'class.displayed_gallery_trigger_manager.php',  
  403. 'A_Displayed_Gallery_Trigger_Element' => 'adapter.displayed_gallery_trigger_element.php',  
  404. 'A_Displayed_Gallery_Trigger_Resources' => 'adapter.displayed_gallery_trigger_resources.php',  
  405. 'A_Display_Settings_Controller' => 'adapter.display_settings_controller.php',  
  406. 'A_Display_Settings_Page' => 'adapter.display_settings_page.php',  
  407. 'A_Gallery_Display_Factory' => 'adapter.gallery_display_factory.php',  
  408. 'C_Display_Type_Installer' => 'class.gallery_display_installer.php',  
  409. 'A_Gallery_Display_View' => 'adapter.gallery_display_view.php',  
  410. 'C_Displayed_Gallery' => 'class.displayed_gallery.php',  
  411. 'C_Displayed_Gallery_Mapper' => 'class.displayed_gallery_mapper.php',  
  412. 'C_Displayed_Gallery_Renderer' => 'class.displayed_gallery_renderer.php',  
  413. 'C_Displayed_Gallery_Source_Manager' => 'class.displayed_gallery_source_manager.php',  
  414. 'C_Display_Type' => 'class.display_type.php',  
  415. 'C_Display_Type_Controller' => 'class.display_type_controller.php',  
  416. 'C_Display_Type_Mapper' => 'class.display_type_mapper.php',  
  417. 'Hook_Propagate_Thumbnail_Dimensions_To_Settings' => 'hook.propagate_thumbnail_dimensions_to_settings.php',  
  418. 'Mixin_Display_Type_Form' => 'mixin.display_type_form.php' 
  419. ); 
  420.  
  421. /** 
  422. * Gets a list of directories in which display type template might be stored 
  423. * 
  424. * @param C_Display_Type $display_type 
  425. * @return array 
  426. */ 
  427. static function get_display_type_view_dirs($display_type) 
  428. if (!is_object($display_type)) $display_type = C_Display_Type_Mapper::get_instance()->find_by_name($display_type); 
  429.  
  430. /** Create array of directories to scan */ 
  431. $dirs = array( 
  432. 'default' => C_Component_Registry::get_instance()->get_module_dir($display_type->name) . DIRECTORY_SEPARATOR . 'templates',  
  433. 'custom' => WP_CONTENT_DIR . DIRECTORY_SEPARATOR . 'ngg' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $display_type->name . DIRECTORY_SEPARATOR . 'templates',  
  434. ); 
  435.  
  436. /** Apply filters so third party devs can add directories for their templates */ 
  437. $dirs = apply_filters('ngg_display_type_template_dirs', $dirs, $display_type); 
  438. $dirs = apply_filters('ngg_' . $display_type->name . '_template_dirs', $dirs, $display_type); 
  439. foreach ($display_type->aliases as $alias) { 
  440. $dirs = apply_filters("ngg_{$alias}_template_dirs", $dirs, $display_type); 
  441.  
  442. return $dirs; 
  443.  
  444.  
  445. class C_Display_Type_Installer 
  446. function get_registry() 
  447. return C_Component_Registry::get_instance(); 
  448.  
  449. /** 
  450. * Installs a display type 
  451. * @param string $name 
  452. * @param array $properties 
  453. */ 
  454. function install_display_type($name, $properties=array()) 
  455. // Try to find the existing entity. If it doesn't exist, we'll create 
  456. $fs = C_Fs::get_instance(); 
  457. $mapper = C_Display_Type_Mapper::get_instance(); 
  458. $display_type = $mapper->find_by_name($name); 
  459. if (!$display_type) $display_type = new stdClass; 
  460.  
  461. // Update the properties of the display type 
  462. $properties['name'] = $name; 
  463. $properties['installed_at_version'] = NGG_PLUGIN_VERSION; 
  464. foreach ($properties as $key=>$val) { 
  465. if ($key == 'preview_image_relpath') { 
  466. $val = $fs->find_static_abspath($val, FALSE, TRUE); 
  467. $display_type->$key = $val; 
  468.  
  469. // Save the entity 
  470. $retval = $mapper->save($display_type); 
  471. return $retval; 
  472.  
  473. /** 
  474. * Deletes all displayed galleries 
  475. */ 
  476. function uninstall_displayed_galleries() 
  477. $mapper = C_Displayed_Gallery_Mapper::get_instance(); 
  478. $mapper->delete()->run_query(); 
  479.  
  480. /** 
  481. * Uninstalls all display types 
  482. */ 
  483. function uninstall_display_types() 
  484. $mapper = C_Display_Type_Mapper::get_instance(); 
  485. $mapper->delete()->run_query(); 
  486.  
  487. /** 
  488. * Installs displayed gallery sources 
  489. */ 
  490. function install($reset=FALSE) 
  491. // Display types are registered in other modules 
  492.  
  493. /** 
  494. * Uninstalls this module 
  495. */ 
  496. function uninstall($hard = FALSE) 
  497. C_Photocrati_Transient_Manager::flush(); 
  498.  
  499. $this->uninstall_display_types(); 
  500.  
  501. // TODO temporary Don't remove galleries on uninstall 
  502. //if ($hard) $this->uninstall_displayed_galleries(); 
  503.  
  504. /** 
  505. * Show related images for a post/page. Ngglegacy function 
  506. * @param $taglist 
  507. * @param int $maxImages 
  508. */ 
  509. function nggShowRelatedGallery($taglist, $maxImages = 0) 
  510. return M_Gallery_Display::_render_related_string($taglist, $maxImages, $type=NULL); 
  511.  
  512. function nggShowRelatedImages($type=NULL, $maxImages=0) 
  513. return M_Gallery_Display::_render_related_string(NULL, $maxImages, $type); 
  514.  
  515. function the_related_images($type = 'tags', $maxNumbers = 7) 
  516. echo nggShowRelatedImages($type, $maxNumbers); 
  517.  
  518.  
  519. new M_Gallery_Display(); 
.