PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/transcribe/metabox/transcripts.php

https://github.com/mgsisk/webcomic
PHP | 481 lines | 371 code | 40 blank | 70 comment | 14 complexity | c6cdc359124549d93b4dc12bc27b20b4 MD5 | raw file
  1. <?php
  2. /**
  3. * Transcripts metabox functionality
  4. *
  5. * @package Webcomic
  6. */
  7. namespace Mgsisk\Webcomic\Transcribe\MetaBox;
  8. /**
  9. * Add transcripts metabox hooks.
  10. *
  11. * @return void
  12. */
  13. function transcripts() {
  14. add_filter( 'admin_init', __NAMESPACE__ . '\hook_register_transcripts_styles' );
  15. add_filter( 'admin_init', __NAMESPACE__ . '\hook_register_transcripts_scripts' );
  16. add_filter( 'admin_enqueue_scripts', __NAMESPACE__ . '\hook_localize_transcripts_scripts' );
  17. add_filter( 'admin_enqueue_scripts', __NAMESPACE__ . '\hook_enqueue_transcripts_styles' );
  18. add_filter( 'admin_enqueue_scripts', __NAMESPACE__ . '\hook_enqueue_transcripts_scripts' );
  19. add_filter( 'admin_enqueue_scripts', __NAMESPACE__ . '\hook_enqueue_quick_transcripts_scripts' );
  20. add_filter( 'admin_head', __NAMESPACE__ . '\hook_add_box_transcripts_help' );
  21. add_filter( 'add_meta_boxes', __NAMESPACE__ . '\hook_add_box_transcripts' );
  22. add_filter( 'bulk_edit_custom_box', __NAMESPACE__ . '\hook_add_quick_edit_transcripts', 10, 2 );
  23. add_filter( 'quick_edit_custom_box', __NAMESPACE__ . '\hook_add_quick_edit_transcripts', 10, 2 );
  24. add_filter( 'get_post_metadata', __NAMESPACE__ . '\hook_get_post_meta_default_transcripts', 10, 4 );
  25. add_filter( 'wp_ajax_webcomic_transcribe_quick_edit', __NAMESPACE__ . '\hook_quick_edit_transcripts' );
  26. add_filter( 'wp_ajax_webcomic_transcribe_form', __NAMESPACE__ . '\hook_get_transcript_form' );
  27. add_filter( 'wp_ajax_webcomic_transcribe_submit', __NAMESPACE__ . '\hook_submit_transcript' );
  28. add_filter( 'wp_ajax_webcomic_transcribe_trash', __NAMESPACE__ . '\hook_trash_transcript' );
  29. add_filter( 'wp_ajax_webcomic_transcribe_untrash', __NAMESPACE__ . '\hook_untrash_transcript' );
  30. add_filter( 'wp_ajax_webcomic_transcribe_row', __NAMESPACE__ . '\hook_get_transcript_row' );
  31. foreach ( webcomic( 'option.collections' ) as $collection ) {
  32. add_filter( "save_post_{$collection}", __NAMESPACE__ . '\hook_update_post_meta_transcribe' );
  33. }
  34. }
  35. /**
  36. * Register transcripts stylesheets.
  37. *
  38. * @return void
  39. */
  40. function hook_register_transcripts_styles() {
  41. wp_register_style(
  42. __NAMESPACE__ . 'TranscriptsCSS',
  43. plugins_url( 'srv/transcribe/metabox-transcripts.css', webcomic( 'file' ) ),
  44. [],
  45. webcomic( 'option.version' )
  46. );
  47. wp_register_style(
  48. __NAMESPACE__ . 'TranscriptsCSSrtl',
  49. plugins_url( 'srv/transcribe/metabox-transcripts-rtl.css', webcomic( 'file' ) ),
  50. [],
  51. webcomic( 'option.version' )
  52. );
  53. }
  54. /**
  55. * Register transcripts scripts.
  56. *
  57. * @return void
  58. */
  59. function hook_register_transcripts_scripts() {
  60. wp_register_script(
  61. __NAMESPACE__ . 'TranscriptsJS',
  62. plugins_url( 'srv/transcribe/metabox-transcripts.js', webcomic( 'file' ) ),
  63. [ 'quicktags' ],
  64. webcomic( 'option.version' ),
  65. true
  66. );
  67. wp_register_script(
  68. __NAMESPACE__ . 'TranscriptsQuickEditJS',
  69. plugins_url( 'srv/transcribe/quick-edit-transcripts.js', webcomic( 'file' ) ),
  70. [],
  71. webcomic( 'option.version' ),
  72. true
  73. );
  74. }
  75. /**
  76. * Localize transcripts scripts.
  77. *
  78. * @return void
  79. */
  80. function hook_localize_transcripts_scripts() {
  81. wp_localize_script(
  82. __NAMESPACE__ . 'TranscriptsJS',
  83. 'webcomicTranscriptL10n',
  84. [
  85. 'add' => __( 'Add transcript', 'webcomic' ),
  86. 'warn' => __( "Are you sure you want to do this?\nThe transcript changes you made will be lost.", 'webcomic' ),
  87. 'comic' => __( 'comic', 'webcomic' ),
  88. ]
  89. );
  90. }
  91. /**
  92. * Enqueue transcripts meta box stylesheets.
  93. *
  94. * @return void
  95. */
  96. function hook_enqueue_transcripts_styles() {
  97. if ( ! webcomic_collection_exists( get_current_screen()->id ) ) {
  98. return;
  99. }
  100. wp_enqueue_style( __NAMESPACE__ . 'TranscriptsCSS' );
  101. if ( is_rtl() ) {
  102. wp_enqueue_style( __NAMESPACE__ . 'TranscriptsCSSrtl' );
  103. }
  104. }
  105. /**
  106. * Enqueue transcripts meta box scripts.
  107. *
  108. * @return void
  109. */
  110. function hook_enqueue_transcripts_scripts() {
  111. if ( ! webcomic_collection_exists( get_current_screen()->id ) || 'auto-draft' === get_post_status() ) {
  112. return;
  113. }
  114. wp_enqueue_script( __NAMESPACE__ . 'TranscriptsJS' );
  115. }
  116. /**
  117. * Enqueue transcripts quick edit scripts.
  118. *
  119. * @return void
  120. */
  121. function hook_enqueue_quick_transcripts_scripts() {
  122. if ( ! preg_match( '/^edit-webcomic\d+$/', get_current_screen()->id ) ) {
  123. return;
  124. }
  125. wp_enqueue_script( __NAMESPACE__ . 'TranscriptsQuickEditJS' );
  126. }
  127. /**
  128. * Add the help tab.
  129. *
  130. * @return void
  131. */
  132. function hook_add_box_transcripts_help() {
  133. $screen = get_current_screen();
  134. if ( ! webcomic_collection_exists( $screen->id ) ) {
  135. return;
  136. }
  137. $screen->add_help_tab(
  138. [
  139. 'id' => 'webcomic_transcripts',
  140. 'title' => __( 'Webcomic Transcripts', 'webcomic' ),
  141. 'callback' => function() {
  142. require __DIR__ . '/transcripts-inc-help.php';
  143. },
  144. ]
  145. );
  146. }
  147. /**
  148. * Display the transcripts meta box.
  149. *
  150. * @param string $type The current post type.
  151. * @return void
  152. */
  153. function hook_add_box_transcripts( string $type ) {
  154. if ( ! webcomic_collection_exists( $type ) ) {
  155. return;
  156. }
  157. add_meta_box(
  158. str_replace( '\\', '_', __NAMESPACE__ ) . 'Transcripts',
  159. __( 'Webcomic Transcripts', 'webcomic' ),
  160. function( $post ) {
  161. $args = [
  162. 'file' => __DIR__ . '/transcripts-inc-box.php',
  163. 'nonce' => __NAMESPACE__ . 'TranscriptsNonce',
  164. 'option' => (bool) get_post_meta( $post->ID, 'webcomic_transcribe', true ),
  165. 'trash_nonce' => __NAMESPACE__ . 'TrashTranscriptNonce',
  166. 'transcripts' => get_posts(
  167. [
  168. 'post_type' => 'webcomic_transcript',
  169. 'post_parent' => $post->ID,
  170. 'post_status' => 'any',
  171. 'posts_per_page' => -1,
  172. ]
  173. ),
  174. ];
  175. require $args['file'];
  176. },
  177. $type
  178. );
  179. }
  180. /**
  181. * Add the transcripts quick edit field.
  182. *
  183. * @param string $column The current column.
  184. * @param string $type The current post type.
  185. * @return void
  186. */
  187. function hook_add_quick_edit_transcripts( string $column, string $type ) {
  188. if ( 'webcomic_media' !== $column || ! webcomic_collection_exists( $type ) ) {
  189. return;
  190. }
  191. $args = [
  192. 'file' => __DIR__ . '/transcripts-inc-quick-edit.php',
  193. 'bulk' => false !== strpos( current_filter(), 'bulk' ),
  194. 'nonce' => __NAMESPACE__ . 'TranscriptsNonce',
  195. ];
  196. require $args['file'];
  197. }
  198. /**
  199. * Get the default transcripts setting for new posts.
  200. *
  201. * @param mixed $meta The meta value.
  202. * @param int $id The post ID.
  203. * @param string $key The meta key.
  204. * @param bool $single Whether to return only the first $key value.
  205. * @return mixed
  206. */
  207. function hook_get_post_meta_default_transcripts( $meta, $id, $key, $single ) {
  208. if ( 'webcomic_transcribe' !== $key || 'auto-draft' !== get_post_status( $id ) ) {
  209. return $meta;
  210. }
  211. $post_type = get_post_type( $id );
  212. return webcomic( "option.{$post_type}.transcribe_comic" );
  213. }
  214. /**
  215. * Handle quick edit requests.
  216. *
  217. * @return void
  218. */
  219. function hook_quick_edit_transcripts() {
  220. if ( ! webcomic( 'GLOBALS._REQUEST.post' ) ) {
  221. wp_die();
  222. }
  223. $output = (bool) get_post_meta( abs( (int) webcomic( 'GLOBALS._REQUEST.post' ) ), 'webcomic_transcribe', true );
  224. wp_send_json( $output );
  225. }
  226. /**
  227. * Handle new transcript form requests.
  228. *
  229. * @return void
  230. * @suppress PhanAccessMethodInternal - get_terms() incorrectly triggers this.
  231. */
  232. function hook_get_transcript_form() {
  233. if ( ! webcomic( 'GLOBALS._REQUEST.parent' ) ) {
  234. wp_die();
  235. }
  236. $transcript = get_webcomic_transcript( abs( (int) webcomic( 'GLOBALS._REQUEST.post' ) ) );
  237. $parent = abs( (int) webcomic( 'GLOBALS._REQUEST.parent' ) );
  238. $args = [
  239. 'file' => __DIR__ . '/transcripts-inc-form.php',
  240. 'nonce' => __NAMESPACE__ . 'SubmitTranscriptNonce',
  241. 'post' => 0,
  242. 'post_status' => 'publish',
  243. 'post_content' => '',
  244. 'post_parent' => $parent,
  245. 'post_media' => get_webcomic_media( 'full', $parent ),
  246. 'post_languages' => [],
  247. 'form_title' => __( 'Add new Transcript', 'webcomic' ),
  248. 'form_submit' => __( 'Add Transcript', 'webcomic' ),
  249. 'languages' => get_terms(
  250. [
  251. 'taxonomy' => 'webcomic_transcript_language',
  252. 'hide_empty' => false,
  253. ]
  254. ),
  255. ];
  256. if ( $transcript ) {
  257. $args['post'] = $transcript->ID;
  258. $args['post_status'] = $transcript->post_status;
  259. $args['post_content'] = $transcript->post_content;
  260. $args['post_languages'] = wp_get_object_terms(
  261. $transcript->ID, 'webcomic_transcript_language', [
  262. 'fields' => 'ids',
  263. ]
  264. );
  265. $args['form_title'] = __( 'Edit Transcript', 'webcomic' );
  266. $args['form_submit'] = __( 'Save Transcript', 'webcomic' );
  267. }
  268. require $args['file'];
  269. wp_die();
  270. }
  271. /**
  272. * Handle new transcript submission requests.
  273. *
  274. * @return void
  275. * @suppress PhanTypeInvalidDimOffset - $args keys incorrectly trigger this.
  276. */
  277. function hook_submit_transcript() {
  278. check_admin_referer( __NAMESPACE__ . 'SubmitTranscriptNonce', __NAMESPACE__ . 'SubmitTranscriptNonce' );
  279. $post_data = [
  280. 'ID' => abs( (int) webcomic( 'GLOBALS._REQUEST.post' ) ),
  281. 'post_type' => 'webcomic_transcript',
  282. 'post_parent' => abs( (int) webcomic( 'GLOBALS._REQUEST.post_parent' ) ),
  283. 'post_status' => sanitize_key( webcomic( 'GLOBALS._REQUEST.post_status' ) ),
  284. 'post_content' => wp_kses_post( webcomic( 'GLOBALS._REQUEST.post_content' ) ),
  285. 'tax_input' => [
  286. 'webcomic_transcript_language' => array_map( 'intval', explode( ',', webcomic( 'GLOBALS._REQUEST.post_languages' ) ) ),
  287. ],
  288. ];
  289. $error = util_validate_admin_transcript_data( $post_data );
  290. if ( $error ) {
  291. wp_die( esc_html( $error ) );
  292. } elseif ( ! $post_data['ID'] ) {
  293. unset( $post_data['ID'] );
  294. $user = wp_get_current_user();
  295. $post_data['meta_input']['webcomic_transcript_authors'] = [
  296. 'name' => $user->display_name,
  297. 'email' => $user->user_email,
  298. 'url' => $user->user_url,
  299. 'time' => current_time( 'mysql' ),
  300. 'ip' => webcomic( 'GLOBALS._SERVER.REMOTE_ADDR' ),
  301. ];
  302. }
  303. $post = wp_insert_post( $post_data, true );
  304. if ( is_wp_error( $post ) ) {
  305. // Translators: WP_Error error message.
  306. wp_die( sprintf( esc_html__( 'ERROR: %s', 'webcomic' ), esc_html( $post->get_error_message() ) ) );
  307. }
  308. wp_send_json( $post );
  309. }
  310. /**
  311. * Handle transcript trash requests.
  312. *
  313. * @return void
  314. */
  315. function hook_trash_transcript() {
  316. check_admin_referer();
  317. $transcript = abs( (int) webcomic( 'GLOBALS._REQUEST.post' ) );
  318. if ( ! is_a_webcomic_transcript( $transcript ) ) {
  319. wp_die( esc_html__( 'ERROR: Invalid post.', 'webcomic' ) );
  320. } elseif ( ! current_user_can( 'delete_post', $transcript ) ) {
  321. wp_die( esc_html__( "ERROR: You don't have permission to trash this comic transcript.", 'webcomic' ) );
  322. } elseif ( ! wp_trash_post( $transcript ) ) {
  323. wp_die( esc_html__( 'ERROR: An unknown error occurred while trying to trash the comic transcript.', 'webcomic' ) );
  324. }
  325. $url = esc_url( wp_nonce_url( '#' ) );
  326. echo '<td colspan="3">';
  327. esc_html_e( 'Transcript moved to trash.', 'webcomic' );
  328. printf( " <a href='{$url}'>%s</a>", esc_html__( 'Undo', 'webcomic' ) ); // WPCS: xss ok.
  329. echo ' <span class="js-error"></span></td>';
  330. wp_die();
  331. }
  332. /**
  333. * Handle transcript untrash requests.
  334. *
  335. * @return void
  336. */
  337. function hook_untrash_transcript() {
  338. check_admin_referer();
  339. $transcript = get_webcomic_transcript( abs( (int) webcomic( 'GLOBALS._REQUEST.post' ) ) );
  340. if ( ! $transcript ) {
  341. wp_die( esc_html__( 'ERROR: Invalid post.', 'webcomic' ) );
  342. } elseif ( ! current_user_can( 'delete_post', $transcript ) ) {
  343. wp_die( esc_html__( "ERROR: You don't have permission to untrash this comic transcript.", 'webcomic' ) );
  344. } elseif ( ! wp_untrash_post( $transcript->ID ) ) {
  345. wp_die( esc_html__( 'ERROR: An unknown error occurred while trying to trash the comic transcript.', 'webcomic' ) );
  346. }
  347. require __DIR__ . '/transcripts-inc-row.php';
  348. wp_die();
  349. }
  350. /**
  351. * Get a transcript table row.
  352. *
  353. * @return void
  354. */
  355. function hook_get_transcript_row() {
  356. if ( ! webcomic( 'GLOBALS._REQUEST.post' ) ) {
  357. wp_die();
  358. }
  359. $transcript = get_webcomic_transcript( abs( (int) webcomic( 'GLOBALS._REQUEST.post' ) ) );
  360. if ( ! $transcript ) {
  361. wp_die( esc_html__( 'ERROR: Invalid post.', 'webcomic' ) );
  362. } elseif ( ! current_user_can( 'read_post', $transcript ) ) {
  363. wp_die( esc_html__( "ERROR: You don't have permission to view this comic transcript.", 'webcomic' ) );
  364. }
  365. require __DIR__ . '/transcripts-inc-row.php';
  366. wp_die();
  367. }
  368. /* ===== Collection Hooks =================================================== */
  369. /**
  370. * Update webcomic_transcribe meta data.
  371. *
  372. * @param int $id The post to update.
  373. * @return void
  374. */
  375. function hook_update_post_meta_transcribe( int $id ) {
  376. if ( ! wp_verify_nonce( sanitize_key( webcomic( 'GLOBALS._REQUEST.' . __NAMESPACE__ . 'TranscriptsNonce' ) ), __NAMESPACE__ . 'TranscriptsNonce' ) ) {
  377. return;
  378. } elseif ( ! webcomic( 'GLOBALS._REQUEST.webcomic_transcribe' ) ) {
  379. delete_post_meta( $id, 'webcomic_transcribe' );
  380. return;
  381. }
  382. $transcribe = abs( (int) webcomic( 'GLOBALS._REQUEST.webcomic_transcribe' ) );
  383. if ( ! $transcribe ) {
  384. return;
  385. }
  386. update_post_meta( $id, 'webcomic_transcribe', true );
  387. }
  388. /* ===== Utility Functions ================================================== */
  389. /**
  390. * Validate submitted transcript data.
  391. *
  392. * @param array $transcript The transcript data to validate.
  393. * @return string
  394. */
  395. function util_validate_admin_transcript_data( array $transcript ) : string {
  396. if ( $transcript['ID'] && ! is_a_webcomic_transcript( $transcript['ID'] ) ) {
  397. return __( 'ERROR: Invalid comic transcript.', 'webcomic' );
  398. } elseif ( $transcript['ID'] && ! current_user_can( 'edit_post', $transcript['ID'] ) ) {
  399. return __( "ERROR: You don't have permission to edit this comic transcript.", 'webcomic' );
  400. } elseif ( ! $transcript['post_parent'] || ! is_a_webcomic( $transcript['post_parent'] ) ) {
  401. return __( 'ERROR: Invalid comic.', 'webcomic' );
  402. } elseif ( 'publish' === $transcript['post_status'] && ! current_user_can( 'publish_posts' ) ) {
  403. return __( "ERROR: You don't have permission to publish comic transcripts.", 'webcomic' );
  404. } elseif ( ! in_array( $transcript['post_status'], [ 'private', 'publish', 'pending', 'draft' ], true ) ) {
  405. return __( 'ERROR: Invalid post status.', 'webcomic' );
  406. } elseif ( ! $transcript['post_content'] ) {
  407. return __( 'ERROR: Please type a transcript.', 'webcomic' );
  408. }
  409. return '';
  410. }