/app/rule/media/class-ms-rule-media-model.php

  1. <?php 
  2. /** 
  3. * @copyright Incsub (http://incsub.com/) 
  4. * 
  5. * @license http://opensource.org/licenses/GPL-2.0 GNU General Public License, version 2 (GPL-2.0) 
  6. * 
  7. * This program is free software; you can redistribute it and/or modify 
  8. * it under the terms of the GNU General Public License, version 2, as 
  9. * published by the Free Software Foundation. 
  10. * 
  11. * This program is distributed in the hope that it will be useful,  
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  14. * GNU General Public License for more details. 
  15. * 
  16. * You should have received a copy of the GNU General Public License 
  17. * along with this program; if not, write to the Free Software 
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,  
  19. * MA 02110-1301 USA 
  20. * 
  21. */ 
  22.  
  23. /** 
  24. * Membership Media Rule class. 
  25. * 
  26. * Persisted by Membership class. 
  27. * 
  28. * @since 1.0.0 
  29. * 
  30. * @package Membership2 
  31. * @subpackage Model 
  32. */ 
  33. class MS_Rule_Media_Model extends MS_Rule { 
  34.  
  35. /** 
  36. * Rule type. 
  37. * 
  38. * @since 1.0.0 
  39. * 
  40. * @var string $rule_type 
  41. */ 
  42. protected $rule_type = MS_Rule_Media::RULE_ID; 
  43.  
  44. /** 
  45. * Media protection type constants. 
  46. * 
  47. * @since 1.0.0 
  48. * 
  49. * @var string $protection_type 
  50. */ 
  51. const PROTECTION_TYPE_BASIC = 'protection_type_basic'; 
  52. const PROTECTION_TYPE_COMPLETE = 'protection_type_complete'; 
  53. const PROTECTION_TYPE_HYBRID = 'protection_type_hybrid'; 
  54.  
  55. /** 
  56. * Media protection file change prefix. 
  57. * 
  58. * @since 1.0.0 
  59. * 
  60. * @var string 
  61. */ 
  62. const FILE_PROTECTION_PREFIX = 'ms_'; 
  63.  
  64. /** 
  65. * Media protection file seed/token. 
  66. * 
  67. * @since 1.0.0 
  68. * 
  69. * @var string 
  70. */ 
  71. const FILE_PROTECTION_INCREMENT = 2771; 
  72.  
  73. /** 
  74. * Returns the active flag for a specific rule. 
  75. * State depends on Add-on 
  76. * 
  77. * @since 1.1.0 
  78. * @return bool 
  79. */ 
  80. static public function is_active() { 
  81. return MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_MEDIA ); 
  82.  
  83.  
  84. /** 
  85. * Verify access to the current content. 
  86. * 
  87. * This rule will return NULL (not relevant), because the media-item is 
  88. * protected via the download URL and not by protecting the current page. 
  89. * 
  90. * @since 1.0.0 
  91. * 
  92. * @param string $id The content id to verify access. 
  93. * @return bool|null True if has access, false otherwise. 
  94. * Null means: Rule not relevant for current page. 
  95. */ 
  96. public function has_access( $id, $admin_has_access = true ) { 
  97. return null; 
  98.  
  99. /** 
  100. * Get the protection type available. 
  101. * 
  102. * @since 1.0.0 
  103. * 
  104. * @return array { 
  105. * The protection type => Description. 
  106. * @type string $protection_type The media protection type. 
  107. * @type string $description The media protection description. 
  108. * } 
  109. */ 
  110. public static function get_protection_types() { 
  111. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  112. $mask = $settings->downloads['masked_url']; 
  113. $example1 = home_url( $mask . date( '/Y/m/' ) . 'my-image.jpg' ); 
  114. $example2 = home_url( $mask . '/ms_12345.jpg' ); 
  115. $example3 = home_url( $mask . '/?ms_file=ms_12345.jpg' ); 
  116. $example1 = '<br /><small>' . __( 'Example:', MS_TEXT_DOMAIN ) . ' ' . $example1 . '</small>'; 
  117. $example2 = '<br /><small>' . __( 'Example:', MS_TEXT_DOMAIN ) . ' ' . $example2 . '</small>'; 
  118. $example3 = '<br /><small>' . __( 'Example:', MS_TEXT_DOMAIN ) . ' ' . $example3 . '</small>'; 
  119.  
  120. $protection_types = array( 
  121. self::PROTECTION_TYPE_BASIC => __( 'Basic protection (default)', MS_TEXT_DOMAIN ) . $example1,  
  122. self::PROTECTION_TYPE_COMPLETE => __( 'Complete protection', MS_TEXT_DOMAIN ) . $example2,  
  123. self::PROTECTION_TYPE_HYBRID => __( 'Hybrid protection', MS_TEXT_DOMAIN ) . $example3,  
  124. ); 
  125.  
  126. return apply_filters( 
  127. 'ms_rule_media_model_get_protection_types',  
  128. $protection_types 
  129. ); 
  130.  
  131. /** 
  132. * Validate protection type. 
  133. * 
  134. * @since 1.0.0 
  135. * 
  136. * @param string $type The protection type to validate. 
  137. * @return boolean True if is valid. 
  138. */ 
  139. public static function is_valid_protection_type( $type ) { 
  140. $valid = false; 
  141. $types = self::get_protection_types(); 
  142.  
  143. if ( array_key_exists( $type, $types ) ) { 
  144. $valid = true; 
  145.  
  146. return apply_filters( 
  147. 'ms_rule_media_model_is_valid_protection_type',  
  148. $valid 
  149. ); 
  150.  
  151. /** 
  152. * Starts the output buffering to replace all links in the final HTML 
  153. * document. 
  154. * 
  155. * Related filter: 
  156. * - init 
  157. * 
  158. * @since 1.1.1.4 
  159. */ 
  160. public function buffer_start() { 
  161. ob_start( array( $this, 'protect_download_content' ) ); 
  162.  
  163. /** 
  164. * Ends the output buffering and calls the output_callback function. 
  165. * 
  166. * Related filter: 
  167. * - shutdown 
  168. * 
  169. * @since 1.1.1.4 
  170. */ 
  171. public function buffer_end() { 
  172. if ( ob_get_level() ) { 
  173. ob_end_flush(); 
  174.  
  175. /** 
  176. * Set initial protection. 
  177. * 
  178. * @since 1.0.0 
  179. */ 
  180. public function initialize() { 
  181. parent::protect_content(); 
  182.  
  183. if ( MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_MEDIA ) ) { 
  184. // Start buffering during init action, though output should only 
  185. // happen a lot later... This way we're save. 
  186. $this->add_action( 'init', 'buffer_start' ); 
  187.  
  188. // Process the buffer right in the end. 
  189. $this->add_action( 'shutdown', 'buffer_end' ); 
  190.  
  191. $this->add_action( 'parse_request', 'handle_download_protection', 3 ); 
  192.  
  193. /** 
  194. * Protect media file. 
  195. * 
  196. * Search content and mask media filename and path. 
  197. * 
  198. * This function is called as output_callback for ob_start() - as a result 
  199. * we cannot echo/output anything in this function! The return value of the 
  200. * function will be displayed to the user. 
  201. * 
  202. * @since 1.0.0 
  203. * 
  204. * @param string $the_content The content before filter. 
  205. * @return string The content with masked media url. 
  206. */ 
  207. public function protect_download_content( $the_content ) { 
  208. do_action( 
  209. 'ms_rule_media_model_protect_download_content_before',  
  210. $the_content,  
  211. $this 
  212. ); 
  213.  
  214. $download_settings = MS_Plugin::instance()->settings->downloads; 
  215.  
  216. if ( ! MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_MEDIA ) ) { 
  217. return $the_content; 
  218.  
  219. $upload_dir = wp_upload_dir(); 
  220. $original_url = trailingslashit( $upload_dir['baseurl'] ); 
  221. $new_path = trailingslashit( 
  222. trailingslashit( get_option( 'home' ) ) . 
  223. $download_settings['masked_url'] 
  224. ); 
  225.  
  226. /** 
  227. * Find all the urls in the post and then we'll check if they are protected 
  228. * Regex from http://blog.mattheworiordan.com/post/13174566389/url-regular-expression-for-links-with-or-without-the 
  229. */ 
  230. $url_exp = '/((([A-Za-z]{3, 9}:(?:\/\/)?)' . 
  231. '(?:[-;:&=\+\$, \w]+@)?'. 
  232. '[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$, \w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?' . 
  233. '\??(?:[-\+=&;%@.\w_]*)#?(?:[.\!\/\\w]*))?)/'; 
  234.  
  235. $matches = array(); 
  236. if ( preg_match_all( $url_exp, $the_content, $matches ) ) { 
  237. $home = untrailingslashit( get_option( 'home' ) ); 
  238.  
  239. /** 
  240. * $matches[0] .. Full link 'http://example.com/blog/img.png?ver=1' 
  241. * $matches[1] .. Full link 'http://example.com/blog/img.png?ver=1' 
  242. * $matches[2] .. Domain only 'http://example.com' 
  243. * $matches[3] .. Protocol 'http://' 
  244. * $matches[4] .. File path '/blog/img.png?ver=1' 
  245. */ 
  246. if ( ! empty( $matches ) && ! empty( $matches[2] ) ) { 
  247. $links = (array) $matches[0]; 
  248. $paths = (array) $matches[4]; 
  249.  
  250. foreach ( $links as $key => $link ) { 
  251. // Ignore all external links 
  252. if ( 0 !== strpos( $link, $home ) ) { continue; } 
  253.  
  254. // The file is on local site - is it a valid attachment? 
  255. $file = basename( $paths[ $key ] ); 
  256. $post_id = $this->get_attachment_id( $link ); 
  257.  
  258. // Ignore links that have no relevant wp_posts entry. 
  259. if ( empty( $post_id ) ) { continue; } 
  260. $f_info = $this->extract_file_info( $file ); 
  261.  
  262. // We have a protected file - so we'll mask it! 
  263. switch ( $download_settings['protection_type'] ) { 
  264. case self::PROTECTION_TYPE_COMPLETE: 
  265. $protected_filename = self::FILE_PROTECTION_PREFIX . 
  266. ( $post_id + (int) self::FILE_PROTECTION_INCREMENT ) . 
  267. $f_info->size_extension . 
  268. '.' . pathinfo( $f_info->filename, PATHINFO_EXTENSION ); 
  269.  
  270. $the_content = str_replace( 
  271. $link,  
  272. $new_path . $protected_filename,  
  273. $the_content 
  274. ); 
  275. break; 
  276.  
  277. case self::PROTECTION_TYPE_HYBRID: 
  278. $protected_filename = self::FILE_PROTECTION_PREFIX . 
  279. ($post_id + (int) self::FILE_PROTECTION_INCREMENT ) . 
  280. $f_info->size_extension . 
  281. '.' . pathinfo( $f_info->filename, PATHINFO_EXTENSION ); 
  282.  
  283. $the_content = str_replace( 
  284. $link,  
  285. $new_path . '?ms_file=' . $protected_filename,  
  286. $the_content 
  287. ); 
  288. break; 
  289.  
  290. case self::PROTECTION_TYPE_BASIC: 
  291. default: 
  292. $the_content = str_replace( 
  293. $link,  
  294. str_replace( 
  295. $original_url,  
  296. $new_path,  
  297. $link 
  298. ),  
  299. $the_content 
  300. ); 
  301. break; 
  302.  
  303. return apply_filters( 
  304. 'ms_rule_media_model_protect_download_content',  
  305. $the_content,  
  306. $this 
  307. ); 
  308.  
  309. /** 
  310. * Extract filename and size extension info. 
  311. * 
  312. * @since 1.0.0 
  313. * 
  314. * @param string $file The filename to extract info from. 
  315. * @return array { 
  316. * @type string $filename The filename. 
  317. * @type string $size_extension The wordpress thumbnail size extension. Default empty. 
  318. * } 
  319. */ 
  320. public function extract_file_info( $file ) { 
  321. // See if the filename has a size extension and if so, strip it out 
  322. $filename_exp_full = '/(.+)\-(\d+[x]\d+)\.(.+)$/'; 
  323. $filename_exp_min = '/(.+)\.(.+)$/'; 
  324. $filematch = array(); 
  325.  
  326. if ( preg_match( $filename_exp_full, $file, $filematch ) ) { 
  327. // Image with an image size attached 
  328. $type = strtolower( $filematch[3] ); 
  329. $filename = $filematch[1] . '.' . $type; 
  330. $size_extension = '-' . $filematch[2]; 
  331. } elseif ( preg_match( $filename_exp_min, $file, $filematch ) ) { 
  332. // Image without an image size definition 
  333. $type = strtolower( $filematch[2] ); 
  334. $filename = $filematch[1] . '.' . $type; 
  335. $size_extension = ''; 
  336. } else { 
  337. // Image without an extension. 
  338. $type = ''; 
  339. $filename = $file; 
  340. $size_extension = ''; 
  341.  
  342. $info = (object) array( 
  343. 'filename' => $filename,  
  344. 'size_extension' => $size_extension,  
  345. 'type' => $type,  
  346. ); 
  347.  
  348. return apply_filters( 
  349. 'ms_rule_media_model_extract_file_info',  
  350. $info,  
  351. $file,  
  352. $this 
  353. ); 
  354.  
  355. /** 
  356. * Get attachment post_id using the filename. 
  357. * 
  358. * @since 1.0.0 
  359. * 
  360. * @param string $filename The filename to obtain the post_id. 
  361. * @return int The post ID or 0 if not found. 
  362. */ 
  363. public function get_attachment_id( $url ) { 
  364. static $Uploads_Url = null; 
  365. static $Uploads_Url_Len = 0; 
  366. global $wpdb; 
  367.  
  368. // First let WordPress try to find the Attachment ID. 
  369. $id = url_to_postid( $url ); 
  370.  
  371. if ( $id ) { 
  372. // Make sure the result ID is a valid attachment ID. 
  373. if ( 'attachment' != get_post_type( $id ) ) { 
  374. $id = 0; 
  375. } else { 
  376. // Manual attempt: Get the filename from the URL and use a custom query. 
  377.  
  378. if ( null === $Uploads_Url ) { 
  379. $uploads = wp_upload_dir(); 
  380. $Uploads_Url = trailingslashit( $uploads['baseurl'] ); 
  381. $Uploads_Url_Len = strlen( $Uploads_Url ); 
  382.  
  383. if ( false !== strpos( $url, $Uploads_Url ) ) { 
  384. $url = substr( $url, $Uploads_Url_Len ); 
  385.  
  386. // See if we cached that URL already. 
  387. $id = wp_cache_get( $url, 'ms_attachment_id' ); 
  388.  
  389. if ( empty( $id ) ) { 
  390. $sql = " 
  391. SELECT wposts.ID 
  392. FROM $wpdb->posts wposts 
  393. INNER JOIN $wpdb->postmeta wpostmeta ON wposts.ID = wpostmeta.post_id 
  394. WHERE 
  395. wposts.post_type = 'attachment' 
  396. AND wpostmeta.meta_key = '_wp_attached_file' 
  397. AND wpostmeta.meta_value = %s 
  398. "; 
  399. $sql = $wpdb->prepare( $sql, $url ); 
  400. $id = $wpdb->get_var( $sql ); 
  401.  
  402. wp_cache_set( $url, $id, 'ms_attachment_id' ); 
  403.  
  404. return apply_filters( 
  405. 'ms_rule_get_attachment_id',  
  406. absint( $id ),  
  407. $url,  
  408. $this 
  409. ); 
  410.  
  411. /** 
  412. * Handle protected media access. 
  413. * 
  414. * Search for masked file and show the proper content, or no access image if don't have access. 
  415. * 
  416. * Realted Action Hooks: 
  417. * - parse_request 
  418. * 
  419. * @since 1.0.0 
  420. * 
  421. * @param WP_Query $query The WP_Query object to filter. 
  422. */ 
  423. public function handle_download_protection( $query ) { 
  424. do_action( 
  425. 'ms_rule_media_model_handle_download_protection_before',  
  426. $query,  
  427. $this 
  428. ); 
  429.  
  430. $the_file = false; 
  431. $requested_item = false; 
  432. $download_settings = MS_Plugin::instance()->settings->downloads; 
  433. $protection_type = $download_settings['protection_type']; 
  434.  
  435. if ( ! MS_Model_Addon::is_enabled( MS_Model_Addon::ADDON_MEDIA ) ) { 
  436. return; 
  437.  
  438. if ( ! empty( $query->query_vars['protectedfile'] ) ) { 
  439. $requested_item = explode( '/', $query->query_vars['protectedfile'] ); 
  440. $requested_item = array_pop( $requested_item ); 
  441. } elseif ( ! empty( $_GET['ms_file'] ) 
  442. && self::PROTECTION_TYPE_HYBRID == $protection_type 
  443. ) { 
  444. $requested_item = $_GET['ms_file']; 
  445.  
  446. if ( ! empty( $requested_item ) ) { 
  447. // At this point we know that the requested post is an attachment. 
  448. $f_info = $this->extract_file_info( $requested_item ); 
  449.  
  450. switch ( $protection_type ) { 
  451. case self::PROTECTION_TYPE_COMPLETE: 
  452. case self::PROTECTION_TYPE_HYBRID: 
  453. // Work out the post_id again 
  454. $attachment_id = preg_replace( 
  455. '/^' . self::FILE_PROTECTION_PREFIX . '/',  
  456. '',  
  457. $f_info->filename 
  458. ); 
  459. $attachment_id -= (int) self::FILE_PROTECTION_INCREMENT; 
  460.  
  461. $the_file = $this->restore_filename( $attachment_id, $f_info->size_extension ); 
  462. break; 
  463.  
  464. default: 
  465. case self::PROTECTION_TYPE_BASIC: 
  466. $home = untrailingslashit( get_option( 'home' ) ); 
  467. $attachment_id = $this->get_attachment_id( $home . $f_info->filename ); 
  468. $the_file = $this->restore_filename( $attachment_id, $f_info->size_extension ); 
  469. break; 
  470.  
  471. if ( ! empty( $the_file ) 
  472. && ! empty( $attachment_id ) 
  473. && is_numeric( $attachment_id ) 
  474. ) { 
  475. if ( $this->can_access_file( $attachment_id ) ) { 
  476. $upload_dir = wp_upload_dir(); 
  477. $file = trailingslashit( $upload_dir['basedir'] ) . $the_file; 
  478. $this->output_file( $file ); 
  479. } else { 
  480. $this->show_no_access_image(); 
  481.  
  482. do_action( 
  483. 'ms_rule_media_model_handle_download_protection_after',  
  484. $query,  
  485. $this 
  486. ); 
  487.  
  488. /** 
  489. * Checks if the current user can access the specified attachment. 
  490. * 
  491. * @since 1.1.1.2 
  492. * @param int $attachment_id 
  493. * @return bool 
  494. */ 
  495. public function can_access_file( $attachment_id ) { 
  496. $access = false; 
  497.  
  498. if ( MS_Model_Member::is_normal_admin() ) { 
  499. return true; 
  500.  
  501. /** 
  502. * Default protection mode: 
  503. * Protect Attachments based on the parent post. 
  504. */ 
  505. $parent_id = get_post_field( 'post_parent', $attachment_id ); 
  506.  
  507. if ( ! $parent_id ) { 
  508. $access = true; 
  509. } else { 
  510. $member = MS_Model_Member::get_current_member(); 
  511. foreach ( $member->subscriptions as $subscription ) { 
  512. $membership = $subscription->get_membership(); 
  513. $access = $membership->has_access_to_post( $parent_id ); 
  514. if ( $access ) { break; } 
  515.  
  516. return apply_filters( 
  517. 'ms_rule_media_can_access_file',  
  518. $access,  
  519. $attachment_id 
  520. ); 
  521.  
  522. /** 
  523. * Restore filename from post_id. 
  524. * 
  525. * @since 1.0.0 
  526. * 
  527. * @todo refactory hack to get extension. 
  528. * 
  529. * @param int $post_id The attachment post_id. 
  530. * @param string $size_extension The image size extension. 
  531. * @return string The attachment filename. 
  532. */ 
  533. public function restore_filename( $post_id, $size_extension ) { 
  534. $img_filename = null; 
  535.  
  536. if ( ! empty( $post_id ) && is_numeric( $post_id ) ) { 
  537. $img_filename = get_post_meta( $post_id, '_wp_attached_file', true ); 
  538. if ( ! empty( $size_extension ) ) { 
  539. // Add back in a size extension if we need to 
  540. $img_filename = str_replace( 
  541. '.' . pathinfo( $img_filename, PATHINFO_EXTENSION ),  
  542. $size_extension . '.' . pathinfo( $img_filename, PATHINFO_EXTENSION ),  
  543. $img_filename 
  544. ); 
  545.  
  546. // hack to remove any double extensions :/ need to change when work out a neater way 
  547. $img_filename = str_replace( 
  548. $size_extension . $size_extension,  
  549. $size_extension,  
  550. $img_filename 
  551. ); 
  552.  
  553. return apply_filters( 
  554. 'ms_rule_restore_filename',  
  555. $img_filename,  
  556. $post_id,  
  557. $this 
  558. ); 
  559.  
  560. /** 
  561. * Output file to the browser. 
  562. * 
  563. * @since 1.0.0 
  564. * 
  565. * @param string $file The complete path to the file. 
  566. */ 
  567. private function output_file( $file ) { 
  568. do_action( 'ms_rule_media_model_output_file_before', $file, $this ); 
  569.  
  570. if ( ! is_file( $file ) ) { 
  571. status_header( 404 ); 
  572. die( '404 — File not found.' ); 
  573.  
  574. $mime = wp_check_filetype( $file ); 
  575. if ( empty( $mime['type'] ) && function_exists( 'mime_content_type' ) ) { 
  576. $mime['type'] = mime_content_type( $file ); 
  577.  
  578. if ( $mime['type'] ) { 
  579. $mimetype = $mime['type']; 
  580. } else { 
  581. $mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 ); 
  582.  
  583. header( 'Content-type: ' . $mimetype ); 
  584. if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) ) { 
  585. header( 'Content-Length: ' . filesize( $file ) ); 
  586.  
  587. $last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) ); 
  588. $etag = '"' . md5( $last_modified ) . '"'; 
  589. header( "Last-Modified: $last_modified GMT" ); 
  590. header( 'ETag: ' . $etag ); 
  591. header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' ); 
  592.  
  593. // Support for Conditional GET 
  594. if ( isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ) { 
  595. $client_etag = stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ); 
  596. } else { 
  597. $client_etag = false; 
  598.  
  599. if ( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { 
  600. $_SERVER['HTTP_IF_MODIFIED_SINCE'] = false; 
  601.  
  602. $client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); 
  603. // If string is empty, return 0. If not, attempt to parse into a timestamp 
  604. $client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0; 
  605.  
  606. // Make a timestamp for our most recent modification... 
  607. $modified_timestamp = strtotime( $last_modified ); 
  608.  
  609. if ( $client_last_modified && $client_etag ) { 
  610. $valid_etag = ( $client_modified_timestamp >= $modified_timestamp ) 
  611. && ( $client_etag === $etag ); 
  612. } else { 
  613. $valid_etag = ( $client_modified_timestamp >= $modified_timestamp ) 
  614. || ( $client_etag === $etag ); 
  615.  
  616. if ( $valid_etag ) { 
  617. status_header( 304 ); 
  618. exit; 
  619.  
  620. // If we made it this far, just serve the file 
  621. readfile( $file ); 
  622.  
  623. do_action( 'ms_rule_media_model_output_file_after', $file, $this ); 
  624.  
  625. die(); 
  626.  
  627. /** 
  628. * Show no access image. 
  629. * 
  630. * @since 1.0.0 
  631. */ 
  632. private function show_no_access_image() { 
  633. $no_access_file = apply_filters( 
  634. 'ms_rule_media_model_show_no_access_image_path',  
  635. MS_Plugin::instance()->dir . 'app/assets/images/no-access.png' 
  636. ); 
  637.  
  638. $this->output_file( $no_access_file ); 
  639.  
  640. do_action( 'ms_rule_show_no_access_image_after', $this ); 
  641.  
  642. /** 
  643. * Get content to protect. 
  644. * 
  645. * @since 1.0.0 
  646. * @param $args The query post args 
  647. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  648. * @return array The contents array. 
  649. */ 
  650. public function get_contents( $args = null ) { 
  651. $args = self::get_query_args( $args ); 
  652. $posts = get_posts( $args ); 
  653.  
  654. $contents = array(); 
  655. foreach ( $posts as $content ) { 
  656. $content->id = $content->ID; 
  657. $content->type = MS_Rule_Media::RULE_ID; 
  658. $content->name = $content->post_name; 
  659. $content->access = $this->can_access_file( $content->id ); 
  660.  
  661. $contents[ $content->id ] = $content; 
  662.  
  663. return apply_filters( 
  664. 'ms_rule_media_model_get_contents',  
  665. $contents,  
  666. $args,  
  667. $this 
  668. ); 
  669.  
  670. /** 
  671. * Get the total content count. 
  672. * 
  673. * @since 1.0.0 
  674. * 
  675. * @param $args The query post args 
  676. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  677. * @return int The total content count. 
  678. */ 
  679. public function get_content_count( $args = null ) { 
  680. $args = self::get_query_args( $args ); 
  681. $query = new WP_Query( $args ); 
  682.  
  683. $count = $query->found_posts; 
  684.  
  685. return apply_filters( 
  686. 'ms_rule_media_model_get_content_count',  
  687. $count,  
  688. $args 
  689. ); 
  690.  
  691. /** 
  692. * Get the default query args. 
  693. * 
  694. * @since 1.0.0 
  695. * 
  696. * @param string $args The query post args. 
  697. * @see @link http://codex.wordpress.org/Class_Reference/WP_Query 
  698. * @return array The parsed args. 
  699. */ 
  700. public function get_query_args( $args = null ) { 
  701. $defaults = array( 
  702. 'orderby' => 'post_date',  
  703. 'order' => 'DESC',  
  704. 'post_type' => 'attachment',  
  705. 'post_status' => 'any',  
  706. ); 
  707. $args = wp_parse_args( $args, $defaults ); 
  708.  
  709. return parent::prepare_query_args( $args, 'get_posts' ); 
.