core.php 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. <?php
  2. class Meow_WR2X_Core {
  3. public $admin = null;
  4. public function __construct( $admin ) {
  5. $this->admin = $admin;
  6. add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
  7. add_action( 'admin_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
  8. add_filter( 'wp_generate_attachment_metadata', array( $this, 'wp_generate_attachment_metadata' ) );
  9. add_action( 'delete_attachment', array( $this, 'delete_attachment' ) );
  10. add_filter( 'generate_rewrite_rules', array( 'Meow_WR2X_Admin', 'generate_rewrite_rules' ) );
  11. add_filter( 'retina_validate_src', array( $this, 'validate_src' ) );
  12. add_filter( 'wp_calculate_image_srcset', array( $this, 'calculate_image_srcset' ), 1000, 3 );
  13. add_action( 'init', array( $this, 'init' ) );
  14. include( __DIR__ . '/api.php' );
  15. if ( is_admin() ) {
  16. include( __DIR__ . '/ajax.php' );
  17. new Meow_WR2X_Ajax( $this );
  18. if ( !get_option( "wr2x_hide_retina_dashboard" ) ) {
  19. include( __DIR__ . '/dashboard.php' );
  20. new Meow_WR2X_Dashboard( $this );
  21. }
  22. if ( !get_option( "wr2x_hide_retina_column" ) ) {
  23. include( __DIR__ . '/media-library.php' );
  24. new Meow_WR2X_MediaLibrary( $this );
  25. }
  26. }
  27. }
  28. function init() {
  29. //load_plugin_textdomain( 'wp-retina-2x', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
  30. if ( get_option( 'wr2x_disable_medium_large' ) ) {
  31. remove_image_size( 'medium_large' );
  32. add_filter( 'image_size_names_choose', array( $this, 'unset_medium_large' ) );
  33. add_filter( 'intermediate_image_sizes_advanced', array( $this, 'unset_medium_large' ) );
  34. }
  35. if ( is_admin() ) {
  36. wp_register_style( 'wr2x-admin-css', plugins_url( '/wr2x_admin.css', __FILE__ ) );
  37. wp_enqueue_style( 'wr2x-admin-css' );
  38. if ( !get_option( "wr2x_retina_admin" ) )
  39. return;
  40. }
  41. $method = get_option( "wr2x_method" );
  42. if ( $method == "Picturefill" ) {
  43. add_action( 'wp_head', array( $this, 'picture_buffer_start' ) );
  44. add_action( 'wp_footer', array( $this, 'picture_buffer_end' ) );
  45. }
  46. else if ( $method == 'HTML Rewrite' ) {
  47. $is_retina = false;
  48. if ( isset( $_COOKIE['devicePixelRatio'] ) ) {
  49. $is_retina = ceil( floatval( $_COOKIE['devicePixelRatio'] ) ) > 1;
  50. }
  51. if ( $is_retina || $this->is_debug() ) {
  52. add_action( 'wp_head', array( $this, 'buffer_start' ) );
  53. add_action( 'wp_footer', array( $this, 'buffer_end' ) );
  54. }
  55. }
  56. }
  57. function unset_medium_large( $sizes ) {
  58. unset( $sizes['medium_large'] );
  59. return $sizes;
  60. }
  61. /**
  62. *
  63. * PICTURE METHOD
  64. *
  65. */
  66. function is_supported_image( $url ) {
  67. $wr2x_supported_image = array( 'jpg', 'jpeg', 'png', 'gif' );
  68. $ext = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
  69. if ( !in_array( $ext, $wr2x_supported_image ) ) {
  70. $this->log( "Extension (" . $ext . ") is not " . implode( ', ', $wr2x_supported_image ) . "." );
  71. return false;
  72. }
  73. return true;
  74. }
  75. function picture_buffer_start() {
  76. ob_start( array( $this, "picture_rewrite" ) );
  77. $this->log( "* HTML REWRITE FOR PICTUREFILL" );
  78. }
  79. function picture_buffer_end() {
  80. @ob_end_flush();
  81. }
  82. // Replace the IMG tags by PICTURE tags with SRCSET
  83. function picture_rewrite( $buffer ) {
  84. if ( !isset( $buffer ) || trim( $buffer ) === '' )
  85. return $buffer;
  86. if ( !function_exists( "str_get_html" ) )
  87. include( __DIR__ . '/inc/simple_html_dom.php' );
  88. $lazysize = get_option( "wr2x_picturefill_lazysizes" ) && $this->admin->is_registered();
  89. $killsrc = !get_option( "wr2x_picturefill_keep_src" );
  90. $nodes_count = 0;
  91. $nodes_replaced = 0;
  92. $html = str_get_html( $buffer );
  93. if ( !$html ) {
  94. $this->log( "The HTML buffer is null, another plugin might block the process." );
  95. return $buffer;
  96. }
  97. // IMG TAGS
  98. foreach( $html->find( 'img' ) as $element ) {
  99. $nodes_count++;
  100. $parent = $element->parent();
  101. if ( $parent->tag == "picture" ) {
  102. $this->log("The img tag is inside a picture tag. Tag ignored.");
  103. continue;
  104. }
  105. else {
  106. $valid = apply_filters( "wr2x_validate_src", $element->src );
  107. if ( empty( $valid ) ) {
  108. $nodes_count--;
  109. continue;
  110. }
  111. // Original HTML
  112. $from = substr( $element, 0 );
  113. // SRC-SET already exists, let's check if LazySize is used
  114. if ( !empty( $element->srcset ) ) {
  115. if ( $lazysize ) {
  116. $this->log( "The src-set has already been created but it will be modifid to data-srcset for lazyload." );
  117. $element->class = $element->class . ' lazyload';
  118. $element->{'data-srcset'} = $element->srcset;
  119. $element->srcset = null;
  120. if ( $killsrc )
  121. $element->src = null;
  122. $to = $element;
  123. $buffer = str_replace( trim( $from, "</> "), trim( $to, "</> " ), $buffer );
  124. $this->log( "The img tag '$from' was rewritten to '$to'" );
  125. $nodes_replaced++;
  126. }
  127. else {
  128. $this->log( "The src-set has already been created. Tag ignored." );
  129. }
  130. continue;
  131. }
  132. // Process of SRC-SET creation
  133. if ( !$this->is_supported_image( $element->src ) ) {
  134. $nodes_count--;
  135. continue;
  136. }
  137. $retina_url = $this->get_retina_from_url( $element->src );
  138. $retina_url = apply_filters( 'wr2x_img_retina_url', $retina_url );
  139. if ( $retina_url != null ) {
  140. $retina_url = $this->cdn_this( $retina_url );
  141. $img_url = $this->cdn_this( $element->src );
  142. $img_url = apply_filters( 'wr2x_img_url', $img_url );
  143. if ( $lazysize ) {
  144. $element->class = $element->class . ' lazyload';
  145. $element->{'data-srcset'} = "$img_url, $retina_url 2x";
  146. }
  147. else
  148. $element->srcset = "$img_url, $retina_url 2x";
  149. if ( $killsrc )
  150. $element->src = null;
  151. else {
  152. $img_src = apply_filters( 'wr2x_img_src', $element->src );
  153. $element->src = $this->cdn_this( $img_src );
  154. }
  155. $to = $element;
  156. $buffer = str_replace( trim( $from, "</> "), trim( $to, "</> " ), $buffer );
  157. $this->log( "The img tag '$from' was rewritten to '$to'" );
  158. $nodes_replaced++;
  159. }
  160. else {
  161. $this->log( "The img tag was not rewritten. No retina for '" . $element->src . "'." );
  162. }
  163. }
  164. }
  165. $this->log( "$nodes_replaced/$nodes_count img tags were replaced." );
  166. // INLINE CSS BACKGROUND
  167. if ( get_option( 'wr2x_picturefill_css_background', false ) && $this->admin->is_registered() ) {
  168. preg_match_all( "/url(?:\(['\"]?)(.*?)(?:['\"]?\))/", $buffer, $matches );
  169. $match_css = $matches[0];
  170. $match_url = $matches[1];
  171. if ( count( $matches ) != 2 )
  172. return $buffer;
  173. $nodes_count = 0;
  174. $nodes_replaced = 0;
  175. for ( $c = 0; $c < count( $matches[0] ); $c++ ) {
  176. $css = $match_css[$c];
  177. $url = $match_url[$c];
  178. if ( !$this->is_supported_image( $url ) )
  179. continue;
  180. $nodes_count++;
  181. $retina_url = $this->get_retina_from_url( $url );
  182. $retina_url = apply_filters( 'wr2x_img_retina_url', $retina_url );
  183. if ( $retina_url != null ) {
  184. $retina_url = $this->cdn_this( $retina_url );
  185. $minibuffer = str_replace( $url, $retina_url, $css );
  186. $buffer = str_replace( $css, $minibuffer, $buffer );
  187. $this->log( "The background src '$css' was rewritten to '$minibuffer'" );
  188. $nodes_replaced++;
  189. }
  190. else {
  191. $this->log( "The background src was not rewritten. No retina for '" . $url . "'." );
  192. }
  193. }
  194. $this->log( "$nodes_replaced/$nodes_count background src were replaced." );
  195. }
  196. return $buffer;
  197. }
  198. /**
  199. *
  200. * HTML REWRITE METHOD
  201. *
  202. */
  203. function buffer_start () {
  204. ob_start( array( $this, "html_rewrite" ) );
  205. $this->log( "* HTML REWRITE" );
  206. }
  207. function buffer_end () {
  208. @ob_end_flush();
  209. }
  210. // Replace the images by retina images (if available)
  211. function html_rewrite( $buffer ) {
  212. if ( !isset( $buffer ) || trim( $buffer ) === '' )
  213. return $buffer;
  214. $nodes_count = 0;
  215. $nodes_replaced = 0;
  216. $doc = new DOMDocument();
  217. @$doc->loadHTML( $buffer ); // = ($doc->strictErrorChecking = false;)
  218. $imageTags = $doc->getElementsByTagName('img');
  219. foreach ( $imageTags as $tag ) {
  220. $nodes_count++;
  221. $img_pathinfo = $this->get_pathinfo_from_image_src( $tag->getAttribute('src') );
  222. $filepath = trailingslashit( $this->get_upload_root() ) . $img_pathinfo;
  223. $system_retina = $this->get_retina( $filepath );
  224. if ( $system_retina != null ) {
  225. $retina_pathinfo = $this->cdn_this( ltrim( str_replace( $this->get_upload_root(), "", $system_retina ), '/' ) );
  226. $buffer = str_replace( $img_pathinfo, $retina_pathinfo, $buffer );
  227. $this->log( "The img src '$img_pathinfo' was replaced by '$retina_pathinfo'" );
  228. $nodes_replaced++;
  229. }
  230. else {
  231. $this->log( "The file '$system_retina' was not found. Tag not modified." );
  232. }
  233. }
  234. $this->log( "$nodes_replaced/$nodes_count were replaced." );
  235. return $buffer;
  236. }
  237. // Converts PHP INI size type (e.g. 24M) to int
  238. function parse_ini_size( $size ) {
  239. $unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
  240. $size = preg_replace('/[^0-9\.]/', '', $size);
  241. if ( $unit )
  242. return round( $size * pow( 1024, stripos( 'bkmgtpezy', $unit[0] ) ) );
  243. else
  244. round( $size );
  245. }
  246. function get_max_filesize() {
  247. if ( defined ('HHVM_VERSION' ) ) {
  248. $post_max_size = ini_get( 'post_max_size' ) ? (int)$this->parse_ini_size( ini_get( 'post_max_size' ) ) : (int)ini_get( 'hhvm.server.max_post_size' );
  249. $upload_max_filesize = ini_get( 'upload_max_filesize' ) ? (int)$this->parse_ini_size( ini_get( 'upload_max_filesize' ) ) :
  250. (int)ini_get( 'hhvm.server.upload.upload_max_file_size' );
  251. }
  252. else {
  253. $post_max_size = (int)$this->parse_ini_size( ini_get( 'post_max_size' ) );
  254. $upload_max_filesize = (int)$this->parse_ini_size( ini_get( 'upload_max_filesize' ) );
  255. }
  256. $max = min( $post_max_size, $upload_max_filesize );
  257. return $max > 0 ? $max : 66600000;
  258. }
  259. /**
  260. *
  261. * RESPONSIVE IMAGES METHOD
  262. *
  263. */
  264. function calculate_image_srcset( $srcset, $size ) {
  265. if ( get_option( "wr2x_disable_responsive" ) )
  266. return null;
  267. $method = get_option( "wr2x_method" );
  268. if ( $method == "none" )
  269. return $srcset;
  270. $count = 0;
  271. $total = 0;
  272. $retinized_srcset = $srcset;
  273. if ( empty( $srcset ) )
  274. return $srcset;
  275. foreach ( $srcset as $s => $cfg ) {
  276. $total++;
  277. $retina = $this->cdn_this( $this->get_retina_from_url( $cfg['url'] ) );
  278. if ( !empty( $retina ) ) {
  279. $count++;
  280. $retinized_srcset[(int)$s * 2] = array(
  281. 'url' => $retina,
  282. 'descriptor' => 'w',
  283. 'value' => (int)$s * 2 );
  284. }
  285. }
  286. $this->log( "WP's srcset: " . $count . " retina files added out of " . $total . " image sizes" );
  287. return $retinized_srcset;
  288. }
  289. /**
  290. *
  291. * ISSUES CALCULATION AND FUNCTIONS
  292. *
  293. */
  294. // Compares two images dimensions (resolutions) against each while accepting an margin error
  295. function are_dimensions_ok( $width, $height, $retina_width, $retina_height ) {
  296. $w_margin = $width - $retina_width;
  297. $h_margin = $height - $retina_height;
  298. return ( $w_margin >= -2 && $h_margin >= -2 );
  299. }
  300. // UPDATE THE ISSUE STATUS OF THIS ATTACHMENT
  301. function update_issue_status( $attachmentId, $issues = null, $info = null ) {
  302. if ( $this->is_ignore( $attachmentId ) )
  303. return;
  304. if ( $issues == null )
  305. $issues = $this->get_issues();
  306. if ( $info == null )
  307. $info = $this->retina_info( $attachmentId );
  308. $consideredIssue = in_array( $attachmentId, $issues );
  309. $realIssue = $this->info_has_issues( $info );
  310. if ( $consideredIssue && !$realIssue )
  311. $this->remove_issue( $attachmentId );
  312. else if ( !$consideredIssue && $realIssue )
  313. $this->add_issue( $attachmentId );
  314. return $realIssue;
  315. }
  316. function get_issues() {
  317. $issues = get_transient( 'wr2x_issues' );
  318. if ( !$issues || !is_array( $issues ) ) {
  319. $issues = array();
  320. set_transient( 'wr2x_issues', $issues );
  321. }
  322. return $issues;
  323. }
  324. // CHECK IF THE 'INFO' OBJECT CONTAINS ISSUE (RETURN TRUE OR FALSE)
  325. function info_has_issues( $info ) {
  326. foreach ( $info as $aindex => $aval ) {
  327. if ( is_array( $aval ) || $aval == 'PENDING' )
  328. return true;
  329. }
  330. return false;
  331. }
  332. function calculate_issues() {
  333. global $wpdb;
  334. $postids = $wpdb->get_col( "
  335. SELECT p.ID FROM $wpdb->posts p
  336. WHERE post_status = 'inherit'
  337. AND post_type = 'attachment'" . $this->create_sql_if_wpml_original() . "
  338. AND ( post_mime_type = 'image/jpeg' OR
  339. post_mime_type = 'image/jpg' OR
  340. post_mime_type = 'image/png' OR
  341. post_mime_type = 'image/gif' )
  342. " );
  343. $issues = array();
  344. foreach ( $postids as $id ) {
  345. $info = $this->retina_info( $id );
  346. if ( $this->info_has_issues( $info ) )
  347. array_push( $issues, $id );
  348. }
  349. set_transient( 'wr2x_ignores', array() );
  350. set_transient( 'wr2x_issues', $issues );
  351. }
  352. function add_issue( $attachmentId ) {
  353. if ( $this->is_ignore( $attachmentId ) )
  354. return;
  355. $issues = $this->get_issues();
  356. if ( !in_array( $attachmentId, $issues ) ) {
  357. array_push( $issues, $attachmentId );
  358. set_transient( 'wr2x_issues', $issues );
  359. }
  360. return $issues;
  361. }
  362. function remove_issue( $attachmentId, $onlyIgnore = false ) {
  363. $issues = array_diff( $this->get_issues(), array( $attachmentId ) );
  364. set_transient( 'wr2x_issues', $issues );
  365. if ( !$onlyIgnore )
  366. $this->remove_ignore( $attachmentId );
  367. return $issues;
  368. }
  369. // IGNORE
  370. function get_ignores( $force = false ) {
  371. $ignores = get_transient( 'wr2x_ignores' );
  372. if ( !$ignores || !is_array( $ignores ) ) {
  373. $ignores = array();
  374. set_transient( 'wr2x_ignores', $ignores );
  375. }
  376. return $ignores;
  377. }
  378. function is_ignore( $attachmentId ) {
  379. $ignores = $this->get_ignores();
  380. return in_array( $attachmentId, $this->get_ignores() );
  381. }
  382. function remove_ignore( $attachmentId ) {
  383. $ignores = $this->get_ignores();
  384. $ignores = array_diff( $ignores, array( $attachmentId ) );
  385. set_transient( 'wr2x_ignores', $ignores );
  386. return $ignores;
  387. }
  388. function add_ignore( $attachmentId ) {
  389. $ignores = $this->get_ignores();
  390. if ( !in_array( $attachmentId, $ignores ) ) {
  391. array_push( $ignores, $attachmentId );
  392. set_transient( 'wr2x_ignores', $ignores );
  393. }
  394. $this->remove_issue( $attachmentId, true );
  395. return $ignores;
  396. }
  397. /**
  398. *
  399. * INFORMATION ABOUT THE RETINA IMAGE IN HTML
  400. *
  401. */
  402. function html_get_basic_retina_info_full( $attachmentId, $retina_info ) {
  403. $status = ( isset( $retina_info ) && isset( $retina_info['full-size'] ) ) ? $retina_info['full-size'] : 'IGNORED';
  404. if ( $status == 'EXISTS' ) {
  405. return '<ul class="meow-sized-images"><li class="meow-bk-blue" title="full-size"></li></ul>';
  406. }
  407. else if ( is_array( $status ) ) {
  408. return '<ul class="meow-sized-images"><li class="meow-bk-orange" title="full-size"></li></ul>';
  409. }
  410. else if ( $status == 'IGNORED' ) {
  411. return __( "N/A", "wp-retina-2x" );
  412. }
  413. return $status;
  414. }
  415. function format_title( $i, $size ) {
  416. return $i . ' (' . ( $size['width'] * 2 ) . 'x' . ( $size['height'] * 2 ) . ')';
  417. }
  418. // Information for the 'Media Sizes Retina-ized' Column in the Retina Dashboard
  419. function html_get_basic_retina_info( $attachmentId, $retina_info ) {
  420. $sizes = $this->get_active_image_sizes();
  421. $result = '<ul class="meow-sized-images" postid="' . ( is_integer( $attachmentId ) ? $attachmentId : $attachmentId->ID ) . '">';
  422. foreach ( $sizes as $i => $size ) {
  423. $status = ( isset( $retina_info ) && isset( $retina_info[$i] ) ) ? $retina_info[$i] : null;
  424. if ( is_array( $status ) )
  425. $result .= '<li class="meow-bk-red" title="' . $this->format_title( $i, $size ) . '">'
  426. . MeowApps_Admin::size_shortname( $i ) . '</li>';
  427. else if ( $status == 'EXISTS' )
  428. $result .= '<li class="meow-bk-blue" title="' . $this->format_title( $i, $size ) . '">'
  429. . MeowApps_Admin::size_shortname( $i ) . '</li>';
  430. else if ( $status == 'PENDING' )
  431. $result .= '<li class="meow-bk-orange" title="' . $this->format_title( $i, $size ) . '">'
  432. . MeowApps_Admin::size_shortname( $i ) . '</li>';
  433. else if ( $status == 'MISSING' )
  434. $result .= '<li class="meow-bk-red" title="' . $this->format_title( $i, $size ) . '">'
  435. . MeowApps_Admin::size_shortname( $i ) . '</li>';
  436. else if ( $status == 'IGNORED' )
  437. $result .= '<li class="meow-bk-gray" title="' . $this->format_title( $i, $size ) . '">'
  438. . MeowApps_Admin::size_shortname( $i ) . '</li>';
  439. else {
  440. error_log( "Retina: This status is not recognized: " . $status );
  441. }
  442. }
  443. $result .= '</ul>';
  444. return $result;
  445. }
  446. // Information for Details in the Retina Dashboard
  447. function html_get_details_retina_info( $post, $retina_info ) {
  448. if ( !$this->admin->is_registered() ) {
  449. return __( "PRO VERSION ONLY", 'wp-retina-2x' );
  450. }
  451. $sizes = $this->get_image_sizes();
  452. $total = 0; $possible = 0; $issue = 0; $ignored = 0; $retina = 0;
  453. $postinfo = get_post( $post, OBJECT );
  454. $meta = wp_get_attachment_metadata( $post );
  455. $fullsize_file = get_attached_file( $post );
  456. $pathinfo_system = pathinfo( $fullsize_file );
  457. $pathinfo = pathinfo( $meta['file'] );
  458. $uploads = wp_upload_dir();
  459. $basepath_url = trailingslashit( $uploads['baseurl'] ) . $pathinfo['dirname'];
  460. if ( get_option( "wr2x_full_size" ) ) {
  461. $sizes['full-size']['file'] = $pathinfo['basename'];
  462. $sizes['full-size']['width'] = $meta['width'];
  463. $sizes['full-size']['height'] = $meta['height'];
  464. $meta['sizes']['full-size']['file'] = $pathinfo['basename'];
  465. $meta['sizes']['full-size']['width'] = $meta['width'];
  466. $meta['sizes']['full-size']['height'] = $meta['height'];
  467. }
  468. $result = "<p>This screen displays all the image sizes set-up by your WordPress configuration with the Retina details.</p>";
  469. $result .= "<br /><a target='_blank' href='" . trailingslashit( $uploads['baseurl'] ) . $meta['file'] . "'><img src='" . trailingslashit( $uploads['baseurl'] ) . $meta['file'] . "' height='100px' style='float: left; margin-right: 10px;' /></a><div class='base-info'>";
  470. $result .= "Title: <b>" . ( $postinfo->post_title ? $postinfo->post_title : '<i>Untitled</i>' ) . "</b><br />";
  471. $result .= "Full-size: <b>" . $meta['width'] . "×" . $meta['height'] . "</b><br />";
  472. $result .= "Image URL: <a target='_blank' href='" . trailingslashit( $uploads['baseurl'] ) . $meta['file'] . "'>" . trailingslashit( $uploads['baseurl'] ) . $meta['file'] . "</a><br />";
  473. $result .= "Image Path: " . $fullsize_file . "<br />";
  474. $result .= "</div><div style='clear: both;'></div><br />";
  475. $result .= "<div class='scrollable-info'>";
  476. foreach ( $sizes as $i => $sizemeta ) {
  477. $total++;
  478. $normal_file_system = ""; $retina_file_system = "";
  479. $normal_file = ""; $retina_file = ""; $width = ""; $height = "";
  480. if ( isset( $retina_info[$i] ) && $retina_info[$i] == 'IGNORED' ) {
  481. $status = "IGNORED";
  482. }
  483. else if ( !isset( $meta['sizes'] ) ) {
  484. $statusText = __( "The metadata is broken! This is not related to the retina plugin. You should probably use a plugin to re-generate the missing metadata and images.", 'wp-retina-2x' );
  485. $status = "MISSING";
  486. }
  487. else if ( !isset( $meta['sizes'][$i] ) ) {
  488. $statusText = sprintf( __( "The image size '%s' could not be found. You probably changed your image sizes but this specific image was not re-build. This is not related to the retina plugin. You should probably use a plugin to re-generate the missing metadata and images.", 'wp-retina-2x' ), $i );
  489. $status = "MISSING";
  490. }
  491. else {
  492. $normal_file_system = trailingslashit( $pathinfo_system['dirname'] ) . $meta['sizes'][$i]['file'];
  493. $retina_file_system = $this->get_retina( $normal_file_system );
  494. $normal_file = trailingslashit( $basepath_url ) . $meta['sizes'][$i]['file'];
  495. $retina_file = $this->get_retina_from_url( $normal_file );
  496. $status = ( isset( $retina_info ) && isset( $retina_info[$i] ) ) ? $retina_info[$i] : null;
  497. $width = $meta['sizes'][$i]['width'];
  498. $height = $meta['sizes'][$i]['height'];
  499. }
  500. $result .= "<h3>";
  501. // Status Icon
  502. if ( is_array( $status ) && $i == 'full-size' ) {
  503. $result .= '<div class="meow-sized-image meow-bk-red"></div>';
  504. $statusText = sprintf( __( "The retina version of the Full-Size image is missing.<br />Full Size Retina has been checked in the Settings and this image is therefore required.<br />Please drag & drop an image of at least <b>%dx%d</b> in the <b>Full-Size Retina Upload</b> column.", 'wp-retina-2x' ), $status['width'], $status['height'] );
  505. }
  506. else if ( is_array( $status ) ) {
  507. $result .= '<div class="meow-sized-image meow-bk-red"></div>';
  508. $statusText = sprintf( __( "The Full-Size image is too small (<b>%dx%d</b>) and this size cannot be generated.<br />Please upload an image of at least <b>%dx%d</b>.", 'wp-retina-2x' ), $meta['width'], $meta['height'], $status['width'], $status['height'] );
  509. $issue++;
  510. }
  511. else if ( $status == 'EXISTS' ) {
  512. $result .= '<div class="meow-sized-image meow-bk-blue"></div>';
  513. $statusText = "";
  514. $retina++;
  515. }
  516. else if ( $status == 'PENDING' ) {
  517. $result .= '<div class="meow-sized-image meow-bk-orange"></div>';
  518. $statusText = __( "The retina image can be created. Please use the 'GENERATE' button.", 'wp-retina-2x' );
  519. $possible++;
  520. }
  521. else if ( $status == 'MISSING' ) {
  522. $result .= '<div class="meow-sized-image meow-bk-gray"></div>';
  523. $statusText = __( "The standard image normally created by WordPress is missing.", 'wp-retina-2x' );
  524. $total--;
  525. }
  526. else if ( $status == 'IGNORED' ) {
  527. $result .= '<div class="meow-sized-image meow-bk-gray"></div>';
  528. $statusText = __( "This size is ignored by your retina settings.", 'wp-retina-2x' );
  529. $ignored++;
  530. $total--;
  531. }
  532. $result .= "&nbsp;Size: $i</h3><p>$statusText</p>";
  533. if ( !is_array( $status ) && $status !== 'IGNORED' && $status !== 'MISSING' ) {
  534. $result .= "<table><tr><th>Normal (" . $width . "×" . $height. ")</th><th>Retina 2x (" . $width * 2 . "×" . $height * 2 . ")</th></tr><tr><td><a target='_blank' href='$normal_file'><img src='$normal_file' width='100'></a></td><td><a target='_blank' href='$retina_file'><img src='$retina_file' width='100'></a></td></tr></table>";
  535. $result .= "<p><small>";
  536. $result .= "Image URL: <a target='_blank' href='$normal_file'>$normal_file</a><br />";
  537. $result .= "Retina URL: <a target='_blank' href='$retina_file'>$retina_file</a><br />";
  538. $result .= "Image Path: $normal_file_system<br />";
  539. $result .= "Retina Path: $retina_file_system<br />";
  540. $result .= "</small></p>";
  541. }
  542. }
  543. $result .= "</table>";
  544. $result .= "</div>";
  545. return $result;
  546. }
  547. /**
  548. *
  549. * WP RETINA 2X CORE
  550. *
  551. */
  552. // Get WordPress upload directory
  553. function get_upload_root() {
  554. $uploads = wp_upload_dir();
  555. return $uploads['basedir'];
  556. }
  557. function get_upload_root_url() {
  558. $uploads = wp_upload_dir();
  559. return $uploads['baseurl'];
  560. }
  561. // Get WordPress directory
  562. function get_wordpress_root() {
  563. return ABSPATH;
  564. }
  565. // Resize the image
  566. function resize( $file_path, $width, $height, $crop, $newfile, $customCrop = false ) {
  567. $crop_params = $crop == '1' ? true : $crop;
  568. $orig_size = getimagesize( $file_path );
  569. $image_src[0] = $file_path;
  570. $image_src[1] = $orig_size[0];
  571. $image_src[2] = $orig_size[1];
  572. $file_info = pathinfo( $file_path );
  573. $newfile_info = pathinfo( $newfile );
  574. $extension = '.' . $newfile_info['extension'];
  575. $no_ext_path = $file_info['dirname'] . '/' . $file_info['filename'];
  576. $cropped_img_path = $no_ext_path . '-' . $width . 'x' . $height . "-tmp" . $extension;
  577. $image = wp_get_image_editor( $file_path );
  578. if ( is_wp_error( $image ) ) {
  579. $this->log( "Resize failure: " . $image->get_error_message() );
  580. error_log( "Resize failure: " . $image->get_error_message() );
  581. return null;
  582. }
  583. // Resize or use Custom Crop
  584. if ( !$customCrop )
  585. $image->resize( $width, $height, $crop_params );
  586. else
  587. $image->crop( $customCrop['x'] * $customCrop['scale'], $customCrop['y'] * $customCrop['scale'], $customCrop['w'] * $customCrop['scale'], $customCrop['h'] * $customCrop['scale'], $width, $height, false );
  588. // Quality
  589. $quality = get_option( 'wr2x_quality', 90 );
  590. $image->set_quality( $quality );
  591. $saved = $image->save( $cropped_img_path );
  592. if ( is_wp_error( $saved ) ) {
  593. $error = $saved->get_error_message();
  594. trigger_error( "Retina: Could not create/resize image " . $file_path . " to " . $newfile . ": " . $error , E_WARNING );
  595. error_log( "Retina: Could not create/resize image " . $file_path . " to " . $newfile . ":" . $error );
  596. return null;
  597. }
  598. if ( rename( $saved['path'], $newfile ) )
  599. $cropped_img_path = $newfile;
  600. else {
  601. trigger_error( "Retina: Could not move " . $saved['path'] . " to " . $newfile . "." , E_WARNING );
  602. error_log( "Retina: Could not move " . $saved['path'] . " to " . $newfile . "." );
  603. return null;
  604. }
  605. $new_img_size = getimagesize( $cropped_img_path );
  606. $new_img = str_replace( basename( $image_src[0] ), basename( $cropped_img_path ), $image_src[0] );
  607. $vt_image = array ( 'url' => $new_img, 'width' => $new_img_size[0], 'height' => $new_img_size[1] );
  608. return $vt_image;
  609. }
  610. // Return the retina file if there is any (system path)
  611. function get_retina( $file ) {
  612. $pathinfo = pathinfo( $file ) ;
  613. if ( empty( $pathinfo ) || !isset( $pathinfo['dirname'] ) ) {
  614. if ( empty( $file ) ) {
  615. $this->log( "An empty filename was given to $this->get_retina()." );
  616. error_log( "An empty filename was given to $this->get_retina()." );
  617. }
  618. else {
  619. $this->log( "Pathinfo is null for " . $file . "." );
  620. error_log( "Pathinfo is null for " . $file . "." );
  621. }
  622. return null;
  623. }
  624. $retina_file = trailingslashit( $pathinfo['dirname'] ) . $pathinfo['filename'] .
  625. $this->retina_extension() . ( isset( $pathinfo['extension'] ) ? $pathinfo['extension'] : "" );
  626. if ( file_exists( $retina_file ) )
  627. return $retina_file;
  628. $this->log( "Retina file at '{$retina_file}' does not exist." );
  629. return null;
  630. }
  631. function get_retina_from_remote_url( $url ) {
  632. $over_http = get_option( 'wr2x_over_http_check', false ) && $this->admin->is_registered();
  633. if ( !$over_http )
  634. return null;
  635. $potential_retina_url = $this->rewrite_url_to_retina( $url );
  636. $response = wp_remote_head( $potential_retina_url, array(
  637. 'user-agent' => "MeowApps-Retina",
  638. 'sslverify' => false,
  639. 'timeout' => 10
  640. ));
  641. if ( is_array( $response ) && is_array( $response['response'] ) && isset( $response['response']['code'] ) ) {
  642. if ( $response['response']['code'] == 200 ) {
  643. $this->log( "Retina URL: " . $potential_retina_url, true);
  644. return $potential_retina_url;
  645. }
  646. else
  647. $this->log( "Remote head failed with code " . $response['response']['code'] . "." );
  648. }
  649. $this->log( "Retina URL couldn't be found (URL -> Retina URL).", true);
  650. }
  651. // Return retina URL from the image URL
  652. function get_retina_from_url( $url ) {
  653. $this->log( "Standard URL: " . $url, true);
  654. $over_http = get_option( 'wr2x_over_http_check', false ) && $this->admin->is_registered();
  655. $filepath = $this->from_url_to_system( $url );
  656. if ( empty ( $filepath ) )
  657. return $this->get_retina_from_remote_url( $url );
  658. $this->log( "Standard PATH: " . $filepath, true);
  659. $system_retina = $this->get_retina( $filepath );
  660. if ( empty ( $system_retina ) )
  661. return $this->get_retina_from_remote_url( $url );
  662. $this->log( "Retina PATH: " . $system_retina, true);
  663. $retina_url = $this->rewrite_url_to_retina( $url );
  664. $this->log( "Retina URL: " . $retina_url, true);
  665. return $retina_url;
  666. }
  667. // Get the filepath from the URL
  668. function from_url_to_system( $url ) {
  669. $img_pathinfo = $this->get_pathinfo_from_image_src( $url );
  670. $filepath = trailingslashit( $this->get_wordpress_root() ) . $img_pathinfo;
  671. if ( file_exists( $filepath ) )
  672. return $filepath;
  673. $filepath = trailingslashit( $this->get_upload_root() ) . $img_pathinfo;
  674. if ( file_exists( $filepath ) )
  675. return $filepath;
  676. $this->log( "Standard PATH couldn't be found (URL -> System).", true);
  677. return null;
  678. }
  679. function rewrite_url_to_retina( $url ) {
  680. $whereisdot = strrpos( $url, '.' );
  681. $url = substr( $url, 0, $whereisdot ) . $this->retina_extension() . substr( $url, $whereisdot + 1 );
  682. return $url;
  683. }
  684. // Clean the PathInfo of the IMG SRC.
  685. // IMPORTANT: This function STRIPS THE UPLOAD FOLDER if it's found
  686. // REASON: The reason is that on some installs the uploads folder is linked to a different "unlogical" physical folder
  687. // http://wordpress.org/support/topic/cant-find-retina-file-with-custom-uploads-constant?replies=3#post-5078892
  688. function get_pathinfo_from_image_src( $image_src ) {
  689. $uploads_url = trailingslashit( $this->get_upload_root_url() );
  690. if ( strpos( $image_src, $uploads_url ) === 0 )
  691. return ltrim( substr( $image_src, strlen( $uploads_url ) ), '/');
  692. else if ( strpos( $image_src, wp_make_link_relative( $uploads_url ) ) === 0 )
  693. return ltrim( substr( $image_src, strlen( wp_make_link_relative( $uploads_url ) ) ), '/');
  694. $img_info = parse_url( $image_src );
  695. return ltrim( $img_info['path'], '/' );
  696. }
  697. // Rename this filename with CDN
  698. function cdn_this( $url ) {
  699. $cdn_domain = "";
  700. if ( $this->admin->is_registered() )
  701. $cdn_domain = get_option( "wr2x_cdn_domain" );
  702. if ( empty( $cdn_domain ) )
  703. return $url;
  704. $home_url = parse_url( home_url() );
  705. $uploads_url = trailingslashit( $this->get_upload_root_url() );
  706. $uploads_url_cdn = str_replace( $home_url['host'], $cdn_domain, $uploads_url );
  707. // Perform additional CDN check (Issue #1631 by Martin)
  708. if ( strpos( $url, $uploads_url_cdn ) === 0 ) {
  709. $this->log( "URL already has CDN: $url" );
  710. return $url;
  711. }
  712. $this->log( "URL before CDN: $url" );
  713. $site_url = preg_replace( '#^https?://#', '', rtrim( get_site_url(), '/' ) );
  714. $new_url = str_replace( $site_url, $cdn_domain, $url );
  715. $this->log( "URL with CDN: $new_url" );
  716. return $new_url;
  717. }
  718. // function admin_menu() {
  719. // add_options_page( 'Retina', 'Retina', 'manage_options', 'wr2x_settings', 'wr2x_settings_page' );
  720. // }
  721. function get_image_sizes() {
  722. $sizes = array();
  723. global $_wp_additional_image_sizes;
  724. foreach ( get_intermediate_image_sizes() as $s ) {
  725. $crop = false;
  726. if ( isset( $_wp_additional_image_sizes[$s] ) ) {
  727. $width = intval($_wp_additional_image_sizes[$s]['width']);
  728. $height = intval($_wp_additional_image_sizes[$s]['height']);
  729. $crop = $_wp_additional_image_sizes[$s]['crop'];
  730. } else {
  731. $width = get_option( $s . '_size_w' );
  732. $height = get_option( $s . '_size_h' );
  733. $crop = get_option( $s . '_crop' );
  734. }
  735. $sizes[$s] = array( 'width' => $width, 'height' => $height, 'crop' => $crop );
  736. }
  737. if ( get_option( 'wr2x_disable_medium_large' ) )
  738. unset( $sizes['medium_large'] );
  739. return $sizes;
  740. }
  741. function get_active_image_sizes() {
  742. $sizes = $this->get_image_sizes();
  743. $active_sizes = array();
  744. $ignore = get_option( "wr2x_ignore_sizes", array() );
  745. if ( empty( $ignore ) )
  746. $ignore = array();
  747. $ignore = array_keys( $ignore );
  748. foreach ( $sizes as $name => $attr ) {
  749. $validSize = !empty( $attr['width'] ) || !empty( $attr['height'] );
  750. if ( $validSize && !in_array( $name, $ignore ) ) {
  751. $active_sizes[$name] = $attr;
  752. }
  753. }
  754. return $active_sizes;
  755. }
  756. function is_wpml_installed() {
  757. return function_exists( 'icl_object_id' ) && !class_exists( 'Polylang' );
  758. }
  759. // SQL Query if WPML with an AND to check if the p.ID (p is attachment) is indeed an original
  760. // That is to limit the SQL that queries all the attachments
  761. function create_sql_if_wpml_original() {
  762. $whereIsOriginal = "";
  763. if ( $this->is_wpml_installed() ) {
  764. global $wpdb;
  765. global $sitepress;
  766. $tbl_wpml = $wpdb->prefix . "icl_translations";
  767. $language = $sitepress->get_default_language();
  768. $whereIsOriginal = " AND p.ID IN (SELECT element_id FROM $tbl_wpml WHERE element_type = 'post_attachment' AND language_code = '$language') ";
  769. }
  770. return $whereIsOriginal;
  771. }
  772. function is_debug() {
  773. static $debug = -1;
  774. if ( $debug == -1 ) {
  775. $debug = get_option( "wr2x_debug" );
  776. }
  777. return !!$debug;
  778. }
  779. function log( $data, $isExtra = false ) {
  780. if ( !$this->is_debug() )
  781. return;
  782. $fh = fopen( trailingslashit( dirname(__FILE__) ) . 'wp-retina-2x.log', 'a' );
  783. $date = date( "Y-m-d H:i:s" );
  784. fwrite( $fh, "$date: {$data}\n" );
  785. fclose( $fh );
  786. }
  787. // Based on http://wordpress.stackexchange.com/questions/6645/turn-a-url-into-an-attachment-post-id
  788. function get_attachment_id( $file ) {
  789. $query = array(
  790. 'post_type' => 'attachment',
  791. 'meta_query' => array(
  792. array(
  793. 'key' => '_wp_attached_file',
  794. 'value' => ltrim( $file, '/' )
  795. )
  796. )
  797. );
  798. $posts = get_posts( $query );
  799. foreach( $posts as $post )
  800. return $post->ID;
  801. return false;
  802. }
  803. // Return the retina extension followed by a dot
  804. function retina_extension() {
  805. return '@2x.';
  806. }
  807. function is_image_meta( $meta ) {
  808. if ( !isset( $meta ) )
  809. return false;
  810. if ( !isset( $meta['sizes'] ) )
  811. return false;
  812. if ( !isset( $meta['width'], $meta['height'] ) )
  813. return false;
  814. return true;
  815. }
  816. function retina_info( $id ) {
  817. $result = array();
  818. $meta = wp_get_attachment_metadata( $id );
  819. if ( !$this->is_image_meta( $meta ) )
  820. return $result;
  821. $original_width = $meta['width'];
  822. $original_height = $meta['height'];
  823. $sizes = $this->get_image_sizes();
  824. $required_files = true;
  825. $originalfile = get_attached_file( $id );
  826. $pathinfo = pathinfo( $originalfile );
  827. $basepath = $pathinfo['dirname'];
  828. $ignore = get_option( "wr2x_ignore_sizes", array() );
  829. if ( empty( $ignore ) )
  830. $ignore = array();
  831. $ignore = array_keys( $ignore );
  832. // Full-Size (if required in the settings)
  833. $fullsize_required = get_option( "wr2x_full_size" ) && $this->admin->is_registered();
  834. $retina_file = trailingslashit( $pathinfo['dirname'] ) . $pathinfo['filename'] . $this->retina_extension() . $pathinfo['extension'];
  835. if ( $retina_file && file_exists( $retina_file ) )
  836. $result['full-size'] = 'EXISTS';
  837. else if ( $fullsize_required && $retina_file )
  838. $result['full-size'] = array( 'width' => $original_width * 2, 'height' => $original_height * 2 );
  839. //}
  840. if ( $sizes ) {
  841. foreach ( $sizes as $name => $attr ) {
  842. $validSize = !empty( $attr['width'] ) || !empty( $attr['height'] );
  843. if ( !$validSize || in_array( $name, $ignore ) ) {
  844. $result[$name] = 'IGNORED';
  845. continue;
  846. }
  847. // Check if the file related to this size is present
  848. $pathinfo = null;
  849. $retina_file = null;
  850. if ( isset( $meta['sizes'][$name]['width'] ) && isset( $meta['sizes'][$name]['height']) && isset($meta['sizes'][$name]) && isset($meta['sizes'][$name]['file']) && file_exists( trailingslashit( $basepath ) . $meta['sizes'][$name]['file'] ) ) {
  851. $normal_file = trailingslashit( $basepath ) . $meta['sizes'][$name]['file'];
  852. $pathinfo = pathinfo( $normal_file ) ;
  853. $retina_file = trailingslashit( $pathinfo['dirname'] ) . $pathinfo['filename'] . $this->retina_extension() . $pathinfo['extension'];
  854. }
  855. // None of the file exist
  856. else {
  857. $result[$name] = 'MISSING';
  858. $required_files = false;
  859. continue;
  860. }
  861. // The retina file exists
  862. if ( $retina_file && file_exists( $retina_file ) ) {
  863. $result[$name] = 'EXISTS';
  864. continue;
  865. }
  866. // The size file exists
  867. else if ( $retina_file )
  868. $result[$name] = 'PENDING';
  869. // The retina file exists
  870. $required_width = $meta['sizes'][$name]['width'] * 2;
  871. $required_height = $meta['sizes'][$name]['height'] * 2;
  872. if ( !$this->are_dimensions_ok( $original_width, $original_height, $required_width, $required_height ) ) {
  873. $result[$name] = array( 'width' => $required_width, 'height' => $required_height );
  874. }
  875. }
  876. }
  877. return $result;
  878. }
  879. function delete_attachment( $attach_id, $deleteFullSize = true ) {
  880. $meta = wp_get_attachment_metadata( $attach_id );
  881. $this->delete_images( $meta, $deleteFullSize );
  882. $this->remove_issue( $attach_id );
  883. }
  884. function wp_generate_attachment_metadata( $meta ) {
  885. if ( get_option( "wr2x_auto_generate" ) == true )
  886. if ( $this->is_image_meta( $meta ) )
  887. $this->generate_images( $meta );
  888. return $meta;
  889. }
  890. function generate_images( $meta ) {
  891. global $_wp_additional_image_sizes;
  892. $sizes = $this->get_image_sizes();
  893. if ( !isset( $meta['file'] ) )
  894. return;
  895. $originalfile = $meta['file'];
  896. $uploads = wp_upload_dir();
  897. $pathinfo = pathinfo( $originalfile );
  898. $original_basename = $pathinfo['basename'];
  899. $basepath = trailingslashit( $uploads['basedir'] ) . $pathinfo['dirname'];
  900. $ignore = get_option( "wr2x_ignore_sizes" );
  901. if ( empty( $ignore ) )
  902. $ignore = array();
  903. $ignore = array_keys( $ignore );
  904. $issue = false;
  905. $id = $this->get_attachment_id( $meta['file'] );
  906. /**
  907. * @param $id ID of the attachment whose retina image is to be generated
  908. */
  909. do_action( 'wr2x_before_generate_retina', $id );
  910. $this->log("* GENERATE RETINA FOR ATTACHMENT '{$meta['file']}'");
  911. $this->log( "Full-Size is {$original_basename}." );
  912. foreach ( $sizes as $name => $attr ) {
  913. $normal_file = "";
  914. if ( in_array( $name, $ignore ) ) {
  915. $this->log( "Retina for {$name} ignored (settings)." );
  916. continue;
  917. }
  918. // Is the file related to this size there?
  919. $pathinfo = null;
  920. $retina_file = null;
  921. if ( isset( $meta['sizes'][$name] ) && isset( $meta['sizes'][$name]['file'] ) ) {
  922. $normal_file = trailingslashit( $basepath ) . $meta['sizes'][$name]['file'];
  923. $pathinfo = pathinfo( $normal_file ) ;
  924. $retina_file = trailingslashit( $pathinfo['dirname'] ) . $pathinfo['filename'] . $this->retina_extension() . $pathinfo['extension'];
  925. }
  926. if ( $retina_file && file_exists( $retina_file ) ) {
  927. $this->log( "Base for {$name} is '{$normal_file }'." );
  928. $this->log( "Retina for {$name} already exists: '$retina_file'." );
  929. continue;
  930. }
  931. if ( $retina_file ) {
  932. $originalfile = trailingslashit( $pathinfo['dirname'] ) . $original_basename;
  933. if ( !file_exists( $originalfile ) ) {
  934. $this->log( "[ERROR] Original file '{$originalfile}' cannot be found." );
  935. return $meta;
  936. }
  937. // Maybe that new image is exactly the size of the original image.
  938. // In that case, let's make a copy of it.
  939. if ( $meta['sizes'][$name]['width'] * 2 == $meta['width'] && $meta['sizes'][$name]['height'] * 2 == $meta['height'] ) {
  940. copy ( $originalfile, $retina_file );
  941. $this->log( "Retina for {$name} created: '{$retina_file}' (as a copy of the full-size)." );
  942. }
  943. // Otherwise let's resize (if the original size is big enough).
  944. else if ( $this->are_dimensions_ok( $meta['width'], $meta['height'], $meta['sizes'][$name]['width'] * 2, $meta['sizes'][$name]['height'] * 2 ) ) {
  945. // Change proposed by Nicscott01, slighlty modified by Jordy (+isset)
  946. // (https://wordpress.org/support/topic/issue-with-crop-position?replies=4#post-6200271)
  947. $crop = isset( $_wp_additional_image_sizes[$name] ) ? $_wp_additional_image_sizes[$name]['crop'] : true;
  948. $customCrop = null;
  949. // Support for Manual Image Crop
  950. // If the size of the image was manually cropped, let's keep it.
  951. if ( class_exists( 'ManualImageCrop' ) && isset( $meta['micSelectedArea'] ) && isset( $meta['micSelectedArea'][$name] ) && isset( $meta['micSelectedArea'][$name]['scale'] ) ) {
  952. $customCrop = $meta['micSelectedArea'][$name];
  953. }
  954. $image = $this->resize( $originalfile, $meta['sizes'][$name]['width'] * 2,
  955. $meta['sizes'][$name]['height'] * 2, $crop, $retina_file, $customCrop );
  956. }
  957. if ( !file_exists( $retina_file ) ) {
  958. $is_ok = apply_filters( "wr2x_last_chance_generate", false, $id, $retina_file,
  959. $meta['sizes'][$name]['width'] * 2, $meta['sizes'][$name]['height'] * 2 );
  960. if ( !$is_ok ) {
  961. $this->log( "[ERROR] Retina for {$name} could not be created. Full-Size is " . $meta['width'] . "x" . $meta['height'] . " but Retina requires a file of at least " . $meta['sizes'][$name]['width'] * 2 . "x" . $meta['sizes'][$name]['height'] * 2 . "." );
  962. $issue = true;
  963. }
  964. }
  965. else {
  966. do_action( 'wr2x_retina_file_added', $id, $retina_file, $name );
  967. $this->log( "Retina for {$name} created: '{$retina_file}'." );
  968. }
  969. } else {
  970. if ( empty( $normal_file ) )
  971. $this->log( "[ERROR] Base file for '{$name}' does not exist." );
  972. else
  973. $this->log( "[ERROR] Base file for '{$name}' cannot be found here: '{$normal_file}'." );
  974. }
  975. }
  976. // Checks attachment ID + issues
  977. if ( !$id )
  978. return $meta;
  979. if ( $issue )
  980. $this->add_issue( $id );
  981. else
  982. $this->remove_issue( $id );
  983. /**
  984. * @param $id ID of the attachment whose retina image has been generated
  985. */
  986. do_action( 'wr2x_generate_retina', $id );
  987. return $meta;
  988. }
  989. function delete_images( $meta, $deleteFullSize = false ) {
  990. if ( !$this->is_image_meta( $meta ) )
  991. return $meta;
  992. $sizes = $meta['sizes'];
  993. if ( !$sizes || !is_array( $sizes ) )
  994. return $meta;
  995. $this->log("* DELETE RETINA FOR ATTACHMENT '{$meta['file']}'");
  996. $originalfile = $meta['file'];
  997. $id = $this->get_attachment_id( $originalfile );
  998. $pathinfo = pathinfo( $originalfile );
  999. $uploads = wp_upload_dir();
  1000. $basepath = trailingslashit( $uploads['basedir'] ) . $pathinfo['dirname'];
  1001. foreach ( $sizes as $name => $attr ) {
  1002. $pathinfo = pathinfo( $attr['file'] );
  1003. $retina_file = $pathinfo['filename'] . $this->retina_extension() . $pathinfo['extension'];
  1004. if ( file_exists( trailingslashit( $basepath ) . $retina_file ) ) {
  1005. $fullpath = trailingslashit( $basepath ) . $retina_file;
  1006. unlink( $fullpath );
  1007. do_action( 'wr2x_retina_file_removed', $id, $retina_file );
  1008. $this->log("Deleted '$fullpath'.");
  1009. }
  1010. }
  1011. // Remove full-size if there is any
  1012. if ( $deleteFullSize ) {
  1013. $pathinfo = pathinfo( $originalfile );
  1014. $retina_file = $pathinfo[ 'filename' ] . $this->retina_extension() . $pathinfo[ 'extension' ];
  1015. if ( file_exists( trailingslashit( $basepath ) . $retina_file ) ) {
  1016. $fullpath = trailingslashit( $basepath ) . $retina_file;
  1017. unlink( $fullpath );
  1018. do_action( 'wr2x_retina_file_removed', $id, $retina_file );
  1019. $this->log( "Deleted '$fullpath'." );
  1020. }
  1021. }
  1022. return $meta;
  1023. }
  1024. /**
  1025. *
  1026. * FILTERS
  1027. *
  1028. */
  1029. function validate_src( $src ) {
  1030. if ( preg_match( "/^data:/i", $src ) )
  1031. return null;
  1032. return $src;
  1033. }
  1034. /**
  1035. *
  1036. * LOAD SCRIPTS IF REQUIRED
  1037. *
  1038. */
  1039. function wp_enqueue_scripts () {
  1040. global $wr2x_version, $wr2x_retinajs, $wr2x_retina_image, $wr2x_picturefill, $wr2x_lazysizes;
  1041. $method = get_option( "wr2x_method" );
  1042. if ( is_admin() && !get_option( "wr2x_retina_admin" ) ) {
  1043. wp_enqueue_script( 'wr2x-admin', plugins_url( '/js/admin.js', __FILE__ ), array(), $wr2x_version, false );
  1044. $nonce = array (
  1045. 'wr2x_generate' => null,
  1046. 'wr2x_delete' => null,
  1047. 'wr2x_delete_full' => null,
  1048. 'wr2x_list_all' => null,
  1049. 'wr2x_replace' => null,
  1050. 'wr2x_upload' => null,
  1051. 'wr2x_retina_upload' => null,
  1052. 'wr2x_retina_details' => null
  1053. );
  1054. foreach ( array_keys( $nonce ) as $action )
  1055. $nonce[$action] = wp_create_nonce( $action );
  1056. wp_localize_script( 'wr2x-admin', 'wr2x_admin_server', array (
  1057. 'maxFileSize' => $this->get_max_filesize(),
  1058. 'nonce' => $nonce,
  1059. 'i18n' => array (
  1060. 'Refresh' => __( "<a href='?page=wp-retina-2x&view=issues&refresh=true'>Refresh</a> this page.", 'wp-retina-2x' ),
  1061. 'Wait' => __( "Wait...", 'wp-retina-2x' ),
  1062. 'Nothing_to_do' => __( "Nothing to do ;)", 'wp-retina-2x' ),
  1063. 'Generate' => __( "GENERATE", 'wp-retina-2x' )
  1064. )
  1065. ) );
  1066. }
  1067. // Picturefill
  1068. if ( $method == "Picturefill" ) {
  1069. if ( $this->is_debug() )
  1070. wp_enqueue_script( 'wr2x-debug', plugins_url( '/js/debug.js', __FILE__ ), array(), $wr2x_version, false );
  1071. // Picturefill
  1072. if ( !get_option( "wr2x_picturefill_noscript" ) )
  1073. wp_enqueue_script( 'picturefill', plugins_url( '/js/picturefill.min.js', __FILE__ ), array(), $wr2x_picturefill, false );
  1074. // Lazysizes
  1075. if ( get_option( "wr2x_picturefill_lazysizes" ) && $this->admin->is_registered() )
  1076. wp_enqueue_script( 'lazysizes', plugins_url( '/js/lazysizes.min.js', __FILE__ ), array(), $wr2x_lazysizes, false );
  1077. return;
  1078. }
  1079. // Debug + HTML Rewrite = No JS!
  1080. if ( $this->is_debug() && $method == "HTML Rewrite" ) {
  1081. return;
  1082. }
  1083. // Debug mode, we force the devicePixelRatio to be Retina
  1084. if ( $this->is_debug() )
  1085. wp_enqueue_script( 'wr2x-debug', plugins_url( '/js/debug.js', __FILE__ ), array(), $wr2x_version, false );
  1086. // Retina-Images and HTML Rewrite both need the devicePixelRatio cookie on the server-side
  1087. if ( $method == "Retina-Images" || $method == "HTML Rewrite" )
  1088. wp_enqueue_script( 'retina-images', plugins_url( '/js/retina-cookie.js', __FILE__ ), array(), $wr2x_retina_image, false );
  1089. // Retina.js only needs itself
  1090. if ($method == "retina.js")
  1091. wp_enqueue_script( 'retinajs', plugins_url( '/js/retina.min.js', __FILE__ ), array(), $wr2x_retinajs, true );
  1092. }
  1093. }
  1094. // Used by WP Rocket (and maybe by other plugins)
  1095. function wr2x_is_registered() {
  1096. global $wr2x_core;
  1097. return $wr2x_core->admin->is_registered();
  1098. }
  1099. ?>