diff --git a/addons/publisher/README.md b/addons/publisher/README.md new file mode 100644 index 00000000..9f1cb415 --- /dev/null +++ b/addons/publisher/README.md @@ -0,0 +1,4 @@ +organize-series-publisher +========================= + +An ADD-ON for the PublishPress Series WordPress Plugin that enables easy bulk publishing of all posts in a series at once. \ No newline at end of file diff --git a/addons/publisher/css/series_im_sort_articles.css b/addons/publisher/css/series_im_sort_articles.css new file mode 100644 index 00000000..22870019 --- /dev/null +++ b/addons/publisher/css/series_im_sort_articles.css @@ -0,0 +1,28 @@ +.pp-series-publisher-wrap .tablenav.top { + display: none; +} + +.pp-series-publisher-wrap .inner-sidebar #side-sortables { + width: 300px; +} + +.pp-series-publisher-wrap .has-right-sidebar .inner-sidebar { + width: 301px; +} + +.pp-series-publisher-wrap .has-right-sidebar #post-body-content { + max-width: 910px; +} + +.pp-series-publisher-wrap table.series-parts tbody tr { + cursor: move; +} + +@media screen and (max-width: 782px){ + + .pp-series-publisher-wrap .has-right-sidebar #post-body-content, + .pp-series-publisher-wrap .has-right-sidebar .inner-sidebar, + .pp-series-publisher-wrap .inner-sidebar #side-sortables { + width: 100%; + } +} \ No newline at end of file diff --git a/addons/publisher/info.json b/addons/publisher/info.json new file mode 100644 index 00000000..632e4f6c --- /dev/null +++ b/addons/publisher/info.json @@ -0,0 +1,37 @@ +{ + "versionFile":"series_issue_manager.php", + "slug":"organize-series-publisher", + "textDomain": "organize-series-publisher", + "wpOrgSlug":"organize-series-publisher", + "wpOrgMainFileSlug": "organize-series-publisher", + "wpOrgUser": "nerrad", + "wpOrgRelease": "2.2.4", + "wpOrgPluginName": "PublishPress Series Publisher", + "wpOrgPluginUrl": "http://organizeseries.com", + "name": "PublishPress Series Publisher", + "releaseFilesRemove": [ + "src/tests/**", + "src/acceptance_tests/**", + "src/docs/**", + "src/info.json", + "src/readme.txt", + "src/wp-assets/**", + "src/.travis.yml", + "src/ISSUE_TEMPLATE.md", + "src/PULL_REQUEST_TEMPLATE.md", + "src/assets/src/**" + ], + "decafFilesRemove": [ + "src/tests/**", + "src/acceptance_tests/**", + "src/docs/**", + "src/info.json", + "src/.travis.yml", + "src/caffeinated", + "src/wp-assets/**", + "src/ISSUE_TEMPLATE.md", + "src/PULL_REQUEST_TEMPLATE.md", + "src/assets/src/**", + "src/README.md" + ] +} \ No newline at end of file diff --git a/addons/publisher/js/series_im_sort_articles.js b/addons/publisher/js/series_im_sort_articles.js new file mode 100644 index 00000000..297b56a5 --- /dev/null +++ b/addons/publisher/js/series_im_sort_articles.js @@ -0,0 +1,19 @@ +jQuery(document).ready( function($) { + /*im_update_post_order();*/ + $(".im_article_list").sortable({ + axis: "y", + }); + + $(".pp-series-publisher-wrap table.series-parts tbody").sortable({ + axis: "y", + }); + + function im_update_post_order() { + var im_post_IDs = new Array(); + jQuery(".im_article_list tr").each( function() { + im_post_IDs.push(jQuery(this).attr('id').substring(5)); + }); + jQuery("#im_publish_posts").val(im_post_IDs.join(',')); +} + +}); \ No newline at end of file diff --git a/addons/publisher/lang/organize-series-publisher.po b/addons/publisher/lang/organize-series-publisher.po new file mode 100644 index 00000000..818e12f8 --- /dev/null +++ b/addons/publisher/lang/organize-series-publisher.po @@ -0,0 +1,110 @@ +msgid "" +msgstr "" +"Project-Id-Version: PublishPress Series Publisher\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-09-09 21:12-0500\n" +"PO-Revision-Date: 2012-09-09 21:13-0500\n" +"Last-Translator: Darren Ethier \n" +"Language-Team: Darren Ethier \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-KeywordsList: _e;__;_ngettext:1;2;_x;_n\n" +"X-Poedit-Basepath: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/" +"orgseries-testing/wp-content/plugins/organize-series-publisher\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-SearchPath-0: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/" +"orgseries-testing/wp-content/plugins/organize-series-publisher\n" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:2 +msgid "Manage Series to Publish" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:7 +msgid "Series Name" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:25 +#, php-format +msgid "Edit the status of %1$s" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:27 +msgid "Published" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:28 +msgid "Publish" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:31 +msgid "Unpublished" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:32 +msgid "Unpublish" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:35 +msgid "Ignored" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_admin_main.php:36 +msgid "Ignore" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:26 +msgid "Publishing Series:" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:32 +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:75 +msgid "Publish Issue" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:44 +msgid "Publication Date/Time:" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:85 +msgid "" +"Drag the post names into the order you want them to be in the series, from " +"the first part to the last part. Keep in mind that any Draft posts that are a part of this series will not show up in this list " +"and will not be published." +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:99 +#, php-format +msgid "No pending posts in %1$s" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_im_article_list.php:101 +#, php-format +msgid "Series %1$s does not exist" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_issue_manager.php:34 +msgid "" +"PublishPress Series Publisher requires the PublishPress Series plugin to be " +"installed and activated in order to work. Plugin won't activate until this " +"condition is met." +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_issue_manager.php:40 +msgid "Manage Series Issues" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_issue_manager.php:40 +msgid "Publish Series" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_issue_manager.php:211 +msgid "Create as unpublished:" +msgstr "" + +#: /Users/drenobi/Documents/Dropbox/WebProjects/htdocs/orgseries-testing/wp-content/plugins/organize-series-publisher/series_issue_manager.php:214 +msgid "" +"When checked, all posts you assign to this series will remain unpublished " +"until you publish the entire series." +msgstr "" diff --git a/addons/publisher/readme.txt b/addons/publisher/readme.txt new file mode 100644 index 00000000..17b48d0e --- /dev/null +++ b/addons/publisher/readme.txt @@ -0,0 +1,91 @@ +=== PublishPress Series Publisher === +Contributors: nerrad +Donate link:https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7871313 +Tags: category, series, organize, mass publish, post, taxonomy, issues, magazine +Requires at least: 3.7 +Tested up to: 3.7 +Stable Tag: 2.2.4 +License: GPLv2 or later + +An ADD-ON for the PublishPress Series Plugin that enables easy bulk publishing of all posts in a series at once. + +== Description == +**Important** PublishPress Series Publisher is an ADD-ON for the [PublishPress Series Plugin](http://wordpress.org/extend/plugins/organize-series) and thus REQUIRES PublishPress Series. + +**NOTE: PublishPress Series Publisher 2.2.4 has a bug fix in it for WP3.7. In order to use PublishPress Series Publisher 2.2.4 your site MUST be running WordPress 3.7** + +This plugin used to be bundled with PublishPress Series but it makes it easier for maintenance and updatings with it being on it's own. + +PublishPress Series Publisher is the result of some custom work I did for Amanda Giles who graciously agreed to make this available to all PublishPress Series users. This plug-in allows you to quickly publish all posts linked to a Series. The plug-in works by adding a Status to each Series. If the status for a Series is "Unpublished", all posts tied to that Series can be individually published, but those posted will be "held" until the Series itself is published. Once the Series is "Published", all previously published posts in that series will be immediately published and visible on your site. Any posts in a Series still in the Draft status will be left as such and will not be published (until individually published). If you have a large number of articles, this saves time and provides a clean "publish" with no accidental broken links from unpublished posts. + +== Installation == + +Be sure the [PublishPress Series plug-in](http://wordpress.org/extend/plugins/organize-series) is activated first, then activate the PublishPress Series Publisher plug-in. If you have any Series already created, they will default to the "Ignored" state which means all posts are left in their current post status (Draft, Published). If you want to remove all posts in a Series from your site, you can "Unpublish" the Series through the Settings > Publish Series. Likewise, if you want them to reappear on your site, you can “Publish” the series from the same screen. + +== Frequently Asked Questions == + +**Why would I want to use this plugin?** +The main feature is the ability for a editor to easily publish a series of posts all at once rather than holding back a series from publishing and then having to publish each post individually when it's time to publish. + +== Support == + +Here are the different options for support of this plugin: + +1. Post any support questions on the WordPress Forums at [wordpress.org](http://wordpress.org/support). I will keep an eye on the forums for bug reports but will not be actively responding to usage/installation help. + +1. Subscribe to the Paid Basic Support Plan I am offering for PublishPress Series and all add-ons at [organizeseries.com](http://organizeseries.com). This will give access to the private support forums that I will be active on. + +== Usage == + +The following are some basic usage instructions + += Using the Plug-In for the First Time = + +Be sure the PublishPress Series plug-in is activated first, then activate the PublishPress Series Publisher plug-in. If you have any Series already created, they will default to the "Ignored" state which is means all posts are left in their current post status (Draft, Published). If you want to remove all posts in a Series from your site, you can "Unpublish" the Series through the Settings > Publish Series. Likewise, if you want them to reappear on your site, you can "Publish" the series from the same screen. + += Creating a Series for Later Publishing = + +With this plug-in activated, you will have an additional check box to allow creation of a Series in an "Unpublished" state. This will allow you to create posts tied to Series without the posts actually being published to your site yet. See below. + += Creating a new Series = + +* Go to Posts > Manage Series +* Begin typing on left side to create a new series +* Decide whether the Series should be "Unpublished" (defaults to Ignored [unchecked]) +* Click Add Series to create the Series + +Note: If you create a Series on the fly via the Add New or Edit Posts screen, you will not have the option of creating an "Unpublished" series. This means your posts will appear on your website as soon as they are individually published - unless you "Unpublish" Your Series immediately (see below). + += Publish/Unpublish a Series = + +A Series, unless specified as "Unpublished" starts in an "Ignored" state. This is essentially the same as "Published" as posts which are published will appear on your site normally (if the posts have been individually published). You can change the status of a Series by going to the Posts menu and selecting Publish Series. On this screen you will see all Series listed and you have the ability to Publish or Unpublish an issue by clicking on the text link to the right of the Series name. + +When you "Publish" a Series, only posts already marked for publishing are now actually published and visible on the site; posts in draft status will stay as [unpublished] drafts although they still can be published at a later time. If you wish to Publish, there is an additional step. Clicking Publish will take you to the Publish Series screen which will allow you a chance to reorder the posts. Reordering posts sets the series_part custom field value which is used for sorting within some PublishPress Series template tags. You must then click "Publish Issue" on the right side of the screen to complete the process. Once you do this all posts in the Series will be published no matter what their previous status (draft, pending review, or published). + +== Changelog == + += 2.2.4 = + +**NOTE: PublishPress Series Publisher 2.2.4 has a bug fix in it for WP3.7. In order to use PublishPress Series Publisher 2.2.4 your site MUST be running WordPress 3.7** + + += 2.2.3 = + +* ensure full compatibility with WordPress 3.4.2 +* fixes some php warnings/notices +* made sure language domain is a concrete string instead of variable reference in all translated strings. + += 2.2.2 = + +* ensure full compatibility with WordPress 3.2 + += 2.2.1 = +bugfixes + +* removed/updated deprecated functions and general cleanup. + += 2.2 = + +- PublishPress Series Publisher is removed from being bundled with PublishPress Series and is released as it's own download on WordPress.org. +- modifications made to the plugin for hosting on WordPress.org. +- modifications made for working with PublishPress Series 2.2 \ No newline at end of file diff --git a/addons/publisher/series-part-post-table.php b/addons/publisher/series-part-post-table.php new file mode 100644 index 00000000..f1d4dbf9 --- /dev/null +++ b/addons/publisher/series-part-post-table.php @@ -0,0 +1,429 @@ + 'series-part', //singular name of the listed records + 'plural' => 'series-parts', //plural name of the listed records + 'ajax' => true //does this table support ajax? + ]); + } + + /** + * Get a list of CSS classes for the WP_List_Table table tag. + * + * @return array List of CSS classes for the table tag. + */ + protected function get_table_classes() + { + + return parent::get_table_classes(); + } + + /** + * Show single row item + * + * @param array $item + */ + public function single_row($item) + { + $class = ['series-part-tr']; + + echo sprintf('', 'post-' . esc_attr($item->ID), esc_attr(implode(' ', $class))); + $this->single_row_columns($item); + echo ''; + } + + /** + * Get list table columns + * + * @return array + */ + public function get_columns() + { + $columns = [ + 'cb' => '', //Render a checkbox instead of text + 'title' => esc_html__('Title', 'organize-series'), + 'author' => esc_html__('Author', 'organize-series'), + 'categories' => esc_html__('Categories', 'organize-series'), + 'tags' => esc_html__('Tags', 'organize-series'), + 'date' => esc_html__('Date', 'organize-series'), + 'part' => esc_html__('Current Part', 'organize-series'), + ]; + + return $columns; + } + + public function get_table_data(){ + + $series_id = isset($_GET['series_ID'])? (int)$_GET['series_ID'] : false; + $meta_key = SERIES_PART_KEY; + $series_posts = []; + + if ($series_id) { + $arg = array( + 'post_status' => 'any', + 'posts_per_page' => -1, + 'no_found_rows' => true, + 'tax_query' => array( + 'relation' => 'AND', + array( + 'taxonomy' => ppseries_get_series_slug(), + 'field' => 'id', + 'terms' => array($series_id) + ) + ), + 'meta_query' => array( + "relation" => "or", + 'part_field_sort_value' => array( + 'key' => $meta_key, + 'type'=> 'NUMERIC' + ), + 'part_field_sort' => array( + 'key' => $meta_key, + 'compare' => 'NOT EXISTS', + 'type'=> 'NUMERIC' + ), + ), + 'orderby' => array( + 'part_field_sort' => 'ASC' + ), + ); + $series_query = new WP_Query($arg); + $series_posts = $series_query->posts; + } + + return $series_posts; + } + + /** + * Generates and display row actions links for the list table. + * + * @param object $item The item being acted upon. + * @param string $column_name Current column name. + * @param string $primary Primary column name. + * + * @return string The row actions HTML, or an empty string if the current column is the primary column. + */ + protected function handle_row_actions($item, $column_name, $primary) + { + $series_id = isset($_GET['series_ID'])? (int)$_GET['series_ID'] : false; + + $actions['edit'] = sprintf( + '%s', + esc_url( + add_query_arg( + [ + 'post' => $item->ID, + 'action' => 'edit', + ], + admin_url('post.php') + ) + ), + esc_html__('Edit', 'organize-series') + ); + + $actions = array_merge($actions, [ + 'delete' => sprintf( + '%s', + add_query_arg([ + 'page' => 'manage-issues', + 'action' => 'part', + 'part_action' => 'pps-publisher-delete-posts', + 'series_ID' => esc_attr($series_id), + 'series_post' => esc_attr($item->ID), + '_wpnonce' => wp_create_nonce('bulk-series-parts') + ], + admin_url('edit.php')), + esc_html__('Trash', 'organize-series') + ), + ]); + + return $column_name === $primary ? $this->row_actions($actions, false) : ''; + } + + /** + * Add default + * + * @param object $item + * @param string $column_name + * + * @return mixed|string|void + */ + protected function column_default($item, $column_name) + { + return !empty($item->$column_name) ? $item->$column_name : '—'; + } + + /** + * The checkbox column + * + * @param object $item + * + * @return string|void + */ + protected function column_cb($item) + { + $out = sprintf('', 'series_post', (int)$item->ID); + + return $out; + } + + /** + * The title column + * + * @param $item + * + * @return string + */ + protected function column_title($item) + { + return sprintf( + '%2$s', + esc_url( + add_query_arg( + [ + 'post' => $item->ID, + 'action' => 'edit', + ], + admin_url('post.php') + ) + ), + esc_html($item->post_title) + ); + } + + /** + * Handles the post author column output. + * + */ + protected function column_author($item) + { + return get_the_author_meta('display_name', $item->post_author); + } + + /** + * Handles the post category column output. + * + */ + protected function column_categories($item) + { + $terms = get_the_terms($item->ID, 'category'); + $term_html = ''; + + if ( is_array( $terms ) ) { + $term_links = []; + foreach ( $terms as $t ) { + $term_links[] = ' ' . esc_html($t->name) . ' '; + } + $term_html = implode(', ', $term_links); + } else { + $term_html = '—'; + } + + return $term_html; + } + + /** + * Handles the post tags column output. + * + */ + protected function column_tags($item) + { + $terms = get_the_terms($item->ID, 'post_tag'); + $term_html = ''; + + if ( is_array( $terms ) ) { + $term_links = []; + foreach ( $terms as $t ) { + $term_links[] = ' ' . esc_html($t->name) . ' '; + } + $term_html = implode(', ', $term_links); + } else { + $term_html = '—'; + } + + return $term_html; + } + + /** + * Handles the post date column output. + * + */ + public function column_date( $post ) { + global $mode; + + if ( '0000-00-00 00:00:00' === $post->post_date ) { + $t_time = __( 'Unpublished' ); + $time_diff = 0; + } else { + $t_time = sprintf( + /* translators: 1: Post date, 2: Post time. */ + __( '%1$s at %2$s' ), + /* translators: Post date format. See https://www.php.net/manual/datetime.format.php */ + get_the_time( __( 'Y/m/d' ), $post ), + /* translators: Post time format. See https://www.php.net/manual/datetime.format.php */ + get_the_time( __( 'g:i a' ), $post ) + ); + + $time = get_post_timestamp( $post ); + $time_diff = time() - $time; + } + + if ( 'publish' === $post->post_status ) { + $status = __( 'Published' ); + } elseif ( 'future' === $post->post_status ) { + if ( $time_diff > 0 ) { + $status = '' . __( 'Missed schedule' ) . ''; + } else { + $status = __( 'Scheduled' ); + } + } else { + $status = __( 'Last Modified' ); + } + + /** + * Filters the status text of the post. + * + * @since 4.8.0 + * + * @param string $status The status text. + * @param WP_Post $post Post object. + * @param string $column_name The column name. + * @param string $mode The list display mode ('excerpt' or 'list'). + */ + $status = apply_filters( 'post_date_column_status', $status, $post, 'date', $mode ); + + $date_html = ''; + if ( $status ) { + $date_html .= $status . '
'; + } + + /** + * Filters the published time of the post. + * + * @since 2.5.1 + * @since 5.5.0 Removed the difference between 'excerpt' and 'list' modes. + * The published time and date are both displayed now, + * which is equivalent to the previous 'excerpt' mode. + * + * @param string $t_time The published time. + * @param WP_Post $post Post object. + * @param string $column_name The column name. + * @param string $mode The list display mode ('excerpt' or 'list'). + */ + $date_html .= apply_filters( 'post_date_column_time', $t_time, $post, 'date', $mode ); + + return $date_html; + } + + /** + * The part column + * + * @param $item + * + * @return string + */ + protected function column_part($item) + { + $series_id = isset($_GET['series_ID'])? (int)$_GET['series_ID'] : false; + $series_part = get_post_meta($item->ID, SERIES_PART_KEY, true); + + if(empty(trim($series_part))){ + $series_part_output = esc_html__('(Currently has no Part number)', 'organize-series'); + }else{ + $series_part_output = $series_part; + } + + return $series_part_output; + } + + /** + * Get the bulk actions to show in the top page dropdown + * + * @return array + */ + protected function get_bulk_actions() + { + $actions = [ + 'pps-publisher-delete-posts' => esc_html__('Move to Trash', 'organize-series') + ]; + + return $actions; + } + + /** + * Sets up the items to list. + */ + public function prepare_items() + { + /** + * First, lets decide how many records per page to show + */ + $per_page = $this->get_items_per_page(str_replace('-', '_', $this->screen->id . '_per_page'), 999); + + + /** + * Fetch the data + */ + $data = $this->get_table_data(); + + /** + * Pagination. + */ + $current_page = $this->get_pagenum(); + $total_items = count($data); + + + /** + * The WP_List_Table class does not handle pagination for us, so we need + * to ensure that the data is trimmed to only the current page. We can use + * array_slice() to + */ + $data = array_slice($data, (($current_page - 1) * $per_page), $per_page); + + /** + * Now we can add the data to the items property, where it can be used by the rest of the class. + */ + $this->items = $data; + + /** + * We also have to register our pagination options & calculations. + */ + $this->set_pagination_args([ + 'total_items' => $total_items, //calculate the total number of items + 'per_page' => $per_page, //determine how many items to show on a page + 'total_pages' => ceil($total_items / $per_page) //calculate the total number of pages + ]); + } + + /** + * Display the list table. + * + * @access public + * @return void + */ + public function display() { + + $this->views(); + + parent::display(); + } +} \ No newline at end of file diff --git a/addons/publisher/series-publish-post-table.php b/addons/publisher/series-publish-post-table.php new file mode 100644 index 00000000..34a65c05 --- /dev/null +++ b/addons/publisher/series-publish-post-table.php @@ -0,0 +1,410 @@ + 'series-part', //singular name of the listed records + 'plural' => 'series-parts', //plural name of the listed records + 'ajax' => true //does this table support ajax? + ]); + } + + /** + * Get a list of CSS classes for the WP_List_Table table tag. + * + * @return array List of CSS classes for the table tag. + */ + protected function get_table_classes() + { + + return parent::get_table_classes(); + } + + /** + * Show single row item + * + * @param array $item + */ + public function single_row($item) + { + $class = ['series-part-tr']; + + echo sprintf('', 'post-' . esc_attr($item->ID), esc_attr(implode(' ', $class))); + $this->single_row_columns($item); + echo ''; + } + + /** + * Get list table columns + * + * @return array + */ + public function get_columns() + { + $columns = [ + 'cb' => '', //Render a checkbox instead of text + 'title' => esc_html__('Title', 'organize-series'), + 'author' => esc_html__('Author', 'organize-series'), + 'categories' => esc_html__('Categories', 'organize-series'), + 'tags' => esc_html__('Tags', 'organize-series'), + 'date' => esc_html__('Date', 'organize-series'), + 'series_preview' => esc_html__('Preview', 'organize-series'), + ]; + + return $columns; + } + + public function get_table_data(){ + + $series_id = isset($_GET['series_ID'])? (int)$_GET['series_ID'] : false; + $meta_key = SERIES_PART_KEY; + $series_posts = []; + + if ($series_id) { + $arg = array( + 'post_status' => ['future', 'draft', 'pending'], + 'posts_per_page' => -1, + 'no_found_rows' => true, + 'tax_query' => array( + 'relation' => 'AND', + array( + 'taxonomy' => ppseries_get_series_slug(), + 'field' => 'id', + 'terms' => array($series_id) + ) + ), + ); + $series_query = new WP_Query($arg); + $series_posts = $series_query->posts; + } + + return $series_posts; + } + + /** + * Generates and display row actions links for the list table. + * + * @param object $item The item being acted upon. + * @param string $column_name Current column name. + * @param string $primary Primary column name. + * + * @return string The row actions HTML, or an empty string if the current column is the primary column. + */ + protected function handle_row_actions($item, $column_name, $primary) + { + $series_id = isset($_GET['series_ID'])? (int)$_GET['series_ID'] : false; + + $actions['edit'] = sprintf( + '%s', + esc_url( + add_query_arg( + [ + 'post' => $item->ID, + 'action' => 'edit', + ], + admin_url('post.php') + ) + ), + esc_html__('Edit', 'organize-series') + ); + + $actions = array_merge($actions, [ + 'delete' => sprintf( + '%s', + add_query_arg([ + 'page' => 'manage-issues', + 'action' => 'list', + 'part_action' => 'pps-publisher-delete-posts', + 'series_ID' => esc_attr($series_id), + 'series_post' => esc_attr($item->ID), + '_wpnonce' => wp_create_nonce('bulk-series-parts') + ], + admin_url('edit.php')), + esc_html__('Trash', 'organize-series') + ), + ]); + + return $column_name === $primary ? $this->row_actions($actions, false) : ''; + } + + /** + * Add default + * + * @param object $item + * @param string $column_name + * + * @return mixed|string|void + */ + protected function column_default($item, $column_name) + { + return !empty($item->$column_name) ? $item->$column_name : '—'; + } + + /** + * The checkbox column + * + * @param object $item + * + * @return string|void + */ + protected function column_cb($item) + { + $out = sprintf('', 'series_post', (int)$item->ID); + + return $out; + } + + /** + * The title column + * + * @param $item + * + * @return string + */ + protected function column_title($item) + { + + return sprintf( + '%2$s', + esc_url( + add_query_arg( + [ + 'post' => $item->ID, + 'action' => 'edit', + ], + admin_url('post.php') + ) + ), + esc_html($item->post_title) + ); + } + + /** + * Handles the post author column output. + * + */ + protected function column_author($item) + { + return get_the_author_meta('display_name', $item->post_author); + } + + /** + * Handles the post category column output. + * + */ + protected function column_categories($item) + { + $terms = get_the_terms($item->ID, 'category'); + $term_html = ''; + + if ( is_array( $terms ) ) { + $term_links = []; + foreach ( $terms as $t ) { + $term_links[] = ' ' . esc_html($t->name) . ' '; + } + $term_html = implode(', ', $term_links); + } else { + $term_html = '—'; + } + + return $term_html; + } + + /** + * Handles the post tags column output. + * + */ + protected function column_tags($item) + { + $terms = get_the_terms($item->ID, 'post_tag'); + $term_html = ''; + + if ( is_array( $terms ) ) { + $term_links = []; + foreach ( $terms as $t ) { + $term_links[] = ' ' . esc_html($t->name) . ' '; + } + $term_html = implode(', ', $term_links); + } else { + $term_html = '—'; + } + + return $term_html; + } + + /** + * Handles the post date column output. + * + */ + public function column_date( $post ) { + global $mode; + + if ( '0000-00-00 00:00:00' === $post->post_date ) { + $t_time = __( 'Unpublished' ); + $time_diff = 0; + } else { + $t_time = sprintf( + /* translators: 1: Post date, 2: Post time. */ + __( '%1$s at %2$s' ), + /* translators: Post date format. See https://www.php.net/manual/datetime.format.php */ + get_the_time( __( 'Y/m/d' ), $post ), + /* translators: Post time format. See https://www.php.net/manual/datetime.format.php */ + get_the_time( __( 'g:i a' ), $post ) + ); + + $time = get_post_timestamp( $post ); + $time_diff = time() - $time; + } + + if ( 'publish' === $post->post_status ) { + $status = __( 'Published' ); + } elseif ( 'future' === $post->post_status ) { + if ( $time_diff > 0 ) { + $status = '' . __( 'Missed schedule' ) . ''; + } else { + $status = __( 'Scheduled' ); + } + } else { + $status = __( 'Last Modified' ); + } + + /** + * Filters the status text of the post. + * + * @since 4.8.0 + * + * @param string $status The status text. + * @param WP_Post $post Post object. + * @param string $column_name The column name. + * @param string $mode The list display mode ('excerpt' or 'list'). + */ + $status = apply_filters( 'post_date_column_status', $status, $post, 'date', $mode ); + + $date_html = ''; + if ( $status ) { + $date_html .= $status . '
'; + } + + /** + * Filters the published time of the post. + * + * @since 2.5.1 + * @since 5.5.0 Removed the difference between 'excerpt' and 'list' modes. + * The published time and date are both displayed now, + * which is equivalent to the previous 'excerpt' mode. + * + * @param string $t_time The published time. + * @param WP_Post $post Post object. + * @param string $column_name The column name. + * @param string $mode The list display mode ('excerpt' or 'list'). + */ + $date_html .= apply_filters( 'post_date_column_time', $t_time, $post, 'date', $mode ); + + return $date_html; + } + + /** + * The preview column + * + * @param $item + * + * @return string + */ + protected function column_series_preview($item) + { + return sprintf( + '%2$s', + esc_url(home_url('?p='.$item->ID.'&preview=true')), + esc_html__('Preview', 'organize-series'), + ); + } + + /** + * Get the bulk actions to show in the top page dropdown + * + * @return array + */ + protected function get_bulk_actions() + { + $actions = [ + 'pps-publisher-delete-posts' => esc_html__('Move to Trash', 'organize-series') + ]; + + return $actions; + } + + /** + * Sets up the items to list. + */ + public function prepare_items() + { + /** + * First, lets decide how many records per page to show + */ + $per_page = $this->get_items_per_page(str_replace('-', '_', $this->screen->id . '_per_page'), 999); + + + /** + * Fetch the data + */ + $data = $this->get_table_data(); + + /** + * Pagination. + */ + $current_page = $this->get_pagenum(); + $total_items = count($data); + + + /** + * The WP_List_Table class does not handle pagination for us, so we need + * to ensure that the data is trimmed to only the current page. We can use + * array_slice() to + */ + $data = array_slice($data, (($current_page - 1) * $per_page), $per_page); + + /** + * Now we can add the data to the items property, where it can be used by the rest of the class. + */ + $this->items = $data; + + /** + * We also have to register our pagination options & calculations. + */ + $this->set_pagination_args([ + 'total_items' => $total_items, //calculate the total number of items + 'per_page' => $per_page, //determine how many items to show on a page + 'total_pages' => ceil($total_items / $per_page) //calculate the total number of pages + ]); + } + + /** + * Display the list table. + * + * @access public + * @return void + */ + public function display() { + + $this->views(); + + parent::display(); + } +} \ No newline at end of file diff --git a/addons/publisher/series_im_admin_main.php b/addons/publisher/series_im_admin_main.php new file mode 100644 index 00000000..84e53dfb --- /dev/null +++ b/addons/publisher/series_im_admin_main.php @@ -0,0 +1,105 @@ +
+

+ + + + + + + + + + + + + + + + + + + 'publish', + 'paged' => 1, + 'posts_per_page' => 1, + 'tax_query' => array( + 'relation' => 'AND', + array( + 'taxonomy' => ppseries_get_series_slug(), + 'field' => 'id', + 'terms' => array($ser->term_id) + ) + ), + ); + $series_posts = new WP_Query($series_posts_arg); + $published_posts_counts = $series_posts->found_posts; + + //count unpublished posts + $series_posts_arg = array( + 'post_status' => ['draft', 'pending'], + 'paged' => 1, + 'posts_per_page' => 1, + 'tax_query' => array( + 'relation' => 'AND', + array( + 'taxonomy' => ppseries_get_series_slug(), + 'field' => 'id', + 'terms' => array($ser->term_id) + ) + ), + ); + $series_posts = new WP_Query($series_posts_arg); + $unpublished_posts_counts = $series_posts->found_posts; + + //count scheduled posts + $series_posts_arg = array( + 'post_status' => 'future', + 'paged' => 1, + 'posts_per_page' => 1, + 'tax_query' => array( + 'relation' => 'AND', + array( + 'taxonomy' => ppseries_get_series_slug(), + 'field' => 'id', + 'terms' => array($ser->term_id) + ) + ), + ); + $series_posts = new WP_Query($series_posts_arg); + $scheduled_posts_counts = $series_posts->found_posts; + + ?> + > + + + + + + + + + + + + + +
name); ?> term_id.'')) ."'>". esc_html__('Update order', 'organize-series').""; + ?>term_id.'')) ."'>". esc_html__('Publish all', 'organize-series').""; + ?>term_id.'')) ."'>". esc_html__('Unpublish all', 'organize-series').""; + ?>slug.'')) ."'>". esc_html__('View series in admin','organize-series').""; + ?>term_id)) ."'>". esc_html__('View series in frontend','organize-series').""; + ?>
+ + +
+ +
diff --git a/addons/publisher/series_issue_manager.php b/addons/publisher/series_issue_manager.php new file mode 100644 index 00000000..341cad85 --- /dev/null +++ b/addons/publisher/series_issue_manager.php @@ -0,0 +1,803 @@ +Jonathan Brinley for his original Issue Manage plugin because all I did was modify it for use with series rather than categories. Also NOTE that this REQUIRES PublishPress Series to be installed. +Version: 2.2.5.rc.000 +Author: Darren Ethier +Author URI: http://organizeseries.com +*/ + +if (!defined('OS_PUBLISHER_VERSION')) { + define('OS_PUBLISHER_VERSION', '2.2.5.rc.000'); +} + +if (!function_exists('series_issue_manager_part')) { + function series_issue_manager_part($series_ID, $post_IDs) + { + + + //delete all series part + foreach (explode(',', $post_IDs) as $post_ID) { + $part_key = apply_filters('orgseries_part_key', SERIES_PART_KEY, $series_ID); + delete_post_meta($post_ID, $part_key); + add_post_meta($post_ID, $part_key, ''); + } + + // $post_IDs should have all pending posts' IDs in the series + $counter = 0; + $current_sn = 0; + foreach (explode(',', $post_IDs) as $post_ID) { + $current_sn++; + $post_ID = (int)$post_ID; + $post = get_post($post_ID); + publisher_wp_set_post_series($post, true, $post_ID, $series_ID, false); + $counter++; + } + } +} + +if (!function_exists('series_issue_manager_publish')) { + function series_issue_manager_publish($series_ID, $post_IDs, $pub_time, &$published, &$unpublished) + { + + // take the series out of the unpublished list + $key = array_search($series_ID, $unpublished); + if (false !== $key) { + array_splice($unpublished, $key, 1); + update_option('im_unpublished_series', $unpublished); + } + //if ( !in_array( $series_ID, $published ) ) + { + // add to the published list + $published[] = $series_ID; + sort($published); + update_option('im_published_series', $published); + + // see if we have a valid publication date/time + $publish_at = strtotime($pub_time['aa'].'-'.$pub_time['mm'].'-'.$pub_time['jj'].' '.$pub_time['hh'].':'.$pub_time['mn']); + + if (!$publish_at) { + $publish_at = strtotime(current_time('mysql')); + } + + // $post_IDs should have all pending posts' IDs in the series + $counter = 0; + $current_sn = 0; + foreach (explode(',', $post_IDs) as $post_ID) { + $current_sn++; + $post_ID = (int)$post_ID; + $post = get_post($post_ID); + // set the date to about the appropriate time, keeping a small gap so posts stay in order + wp_update_post( + array( + 'ID' => $post->ID, + 'post_date' => date('Y-m-d H:i:s', $publish_at-($counter+1)), + 'post_date_gmt' => '', + 'post_status' => 'publish' + ) + ); + + if ($publish_at > strtotime(current_time('mysql'))) { + // scheduled + publisher_wp_set_post_series($post, true, $post_ID, $series_ID, false, $current_sn); + } else { + publisher_wp_set_post_series($post, true, $post_ID, $series_ID, false); + } + $counter++; + } + } + } +} + +if (!function_exists('series_issue_manager_unpublish')) { + function series_issue_manager_unpublish($series_ID, &$published, &$unpublished) + { + // take the series out of the published list + $key = array_search($series_ID, $published); + if (false !== $key) { + array_splice($published, $key, 1); + update_option('im_published_series', $published); + } + //if ( !in_array( $series_ID, $unpublished ) ) + { + // add to the unpublished list + $unpublished[] = $series_ID; + sort($unpublished); + update_option('im_unpublished_series', $unpublished); + + // change all published posts in the series to pending + $posts = get_objects_in_term($series_ID, 'series'); + foreach ($posts as $post) { + wp_update_post( + array( + 'ID' => $post, + 'post_status' => 'pending' + ) + ); + publisher_wp_set_post_series($post, true, $post, $series_ID, true); + } + } + } +} + + +if (!function_exists('publisher_wp_set_post_series')) { + function publisher_wp_set_post_series($post, $update, $post_ID = 0, $series_id = array(), $remove_part = false, $part = false) + { + $post_ID = (int) $post_ID; + $old_series = wp_get_post_series($post_ID); + $old_series = is_array($old_series) ? $old_series : array(); + $post_series = is_array($series_id) ? $series_id : array($series_id); + $post_series = os_strarr_to_intarr($post_series); + + if (empty($post_series) || (count($post_series) >= count($old_series))) { + $match = false; + } else { + $match = array_diff($old_series, $post_series); + } + + if (empty($post_series) || (count($post_series) == 1 && $post_series[0] == 0)) { + $post_series = array(); + } + + $p_ser_edit = $post_series; + + if (empty($post_series)) { + foreach ($old_series as $o_ser) { + $part_key = apply_filters('orgseries_part_key', SERIES_PART_KEY, $o_ser); + delete_post_meta($post_ID, $part_key); + } + } + + foreach ($old_series as $os_id) { + if (!in_array($os_id, $post_series)) { + wp_delete_post_series_relationship($post_ID, $os_id); + } + } + + if (!empty($match) && $match) { + foreach ($match as $part_reset_id) { + wp_reset_series_order_meta_cache($post_ID, $part_reset_id); + } + } + + $success = wp_set_object_terms($post_ID, $post_series, ppseries_get_series_slug()); + + if (empty($p_ser_edit)) { + return; //let's get out we've done everything we need to do. + } + + if ($success) { + foreach ($p_ser_edit as $ser_id) { + if ($remove_part) { + $s_pt = ''; + } else { + $s_pt = wp_series_part($post_ID, $ser_id); + } + + if ($part) { + $series_part_key = apply_filters('orgseries_part_key', SERIES_PART_KEY, $ser_id); + $s_pt = $part; + delete_post_meta($post_ID, $series_part_key); + add_post_meta($post_ID, $series_part_key, $s_pt); + } else { + if ($remove_part) { + $series_part_key = apply_filters('orgseries_part_key', SERIES_PART_KEY, $ser_id); + delete_post_meta($post_ID, $series_part_key); + } else { + set_series_order($ser_id, $post_ID, $s_pt, true); + } + } + } + + return; + } else { + return false; + } + } +} + +if (!function_exists('series_issue_manager_publish_intercept')) { + function series_issue_manager_publish_intercept($post_ID, $post) + { + /* + $unpublished = get_option( 'im_unpublished_series' ); + $publishable = TRUE; + // check if post is in an unpublished series + + foreach ( get_the_series($post_ID) as $series ) { + if ( in_array( $series->term_id, $unpublished ) ) { + $publishable = FALSE; + break; + } + } + // if post is in an unpublished series, change its status to 'pending' instead of 'publish' + if ( !$publishable ) { + if ($post->post_status != 'publish') return; + + wp_update_post( array( + 'ID' => $post_ID, + 'post_status' => 'pending' + ) ); + } + return;*/ + } +} + +if (!function_exists('series_issue_manager_add_series_form')) { + function series_issue_manager_add_series_form() + { + $published = get_option('im_published_series'); + $unpublished = get_option('im_unpublished_series'); ?> +
+ +
+

'; + + $messagewrapend = '

'; + + $action = ''; + + /** + * Filters the custom admin notice for pps_publisher. + * + * @param string $value Complete HTML output for notice. + * @param string $action Action whose message is being generated. + * @param string $message The message to be displayed. + * @param string $messagewrapstart Beginning wrap HTML. + * @param string $messagewrapend Ending wrap HTML. + */ + return apply_filters( + 'pps_publisher_admin_notice', + $messagewrapstart . $message . $messagewrapend, + $action, + $message, + $messagewrapstart, + $messagewrapend + ); +} + + +function pps_publisher_unpublished_success_message_admin_notice() +{ + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo pps_publisher_admin_notices_helper( + esc_html__('The posts in your series were successfully unpublished.', 'organize-series') + ); +} + + +function pps_publisher_order_success_message_admin_notice() +{ + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo pps_publisher_admin_notices_helper( + esc_html__('Congratulations. Your series order was updated successfully.', 'organize-series') + ); +} + + +function pps_publisher_delete_success_message_admin_notice() +{ + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo pps_publisher_admin_notices_helper( + esc_html__('Post moved to the Trash.', 'organize-series') + ); +} + + +function pps_publisher_published_success_message_admin_notice() +{ + $pub_time['mm'] = isset($_GET['mm'])?sanitize_text_field($_GET['mm']):null; + $pub_time['jj'] = isset($_GET['jj'])?sanitize_text_field($_GET['jj']):null; + $pub_time['aa'] = isset($_GET['aa'])?sanitize_text_field($_GET['aa']):null; + $pub_time['hh'] = isset($_GET['hh'])?sanitize_text_field($_GET['hh']):null; + $pub_time['mn'] = isset($_GET['mn'])?sanitize_text_field($_GET['mn']):null; + + // see if we have a valid publication date/time + $publish_at = strtotime($pub_time['aa'].'-'.$pub_time['mm'].'-'.$pub_time['jj'].' '.$pub_time['hh'].':'.$pub_time['mn']); + + if (!$publish_at) { + $publish_at = strtotime(current_time('mysql')); + } + + if ($publish_at > strtotime(current_time('mysql'))) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo pps_publisher_admin_notices_helper( + esc_html__('Congratulations. Your series was scheduled successfully.', 'organize-series') + ); + } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo pps_publisher_admin_notices_helper( + esc_html__('Congratulations. Your series was published successfully.', 'organize-series') + ); + } +} + +function pps_publisher_filter_removable_query_args_unpublish(array $args) +{ + return array_merge( + $args, + [ + 'action', + 'series_ID', + ] + ); +} + +function pps_publisher_filter_removable_query_args_delete(array $args) +{ + return array_merge( + $args, + [ + 'part_action', + 'series_post', + '_wpnonce', + ] + ); +} + +function pps_publisher_filter_removable_query_args_order(array $args) +{ + return array_merge( + $args, + [ + 'posts', + 'publish', + '_wpnonce', + ] + ); +} + +function pps_publisher_filter_removable_query_args_publish(array $args) +{ + return array_merge( + $args, + [ + 'action', + 'series_ID', + 'posts', + 'mm', + 'jj', + 'aa', + 'hh', + 'mn', + 'publish' + ] + ); +} + +function ppseries_publisher_admin_init() +{ + if (isset($_GET['page']) && $_GET['page'] === 'manage-issues' && isset($_GET['action']) && $_GET['action'] === 'unpublish') { + add_action('admin_notices', "pps_publisher_unpublished_success_message_admin_notice"); + add_filter('removable_query_args', 'pps_publisher_filter_removable_query_args_unpublish'); + } elseif (isset($_GET['page']) && $_GET['page'] === 'manage-issues' && isset($_GET['action']) && $_GET['action'] === 'publish') { + add_action('admin_notices', "pps_publisher_published_success_message_admin_notice"); + add_filter('removable_query_args', 'pps_publisher_filter_removable_query_args_publish'); + } elseif (isset($_GET['posts']) && isset($_GET['page']) && $_GET['page'] === 'manage-issues' && isset($_GET['action']) && $_GET['action'] === 'order') { + add_action('admin_notices', "pps_publisher_order_success_message_admin_notice"); + add_filter('removable_query_args', 'pps_publisher_filter_removable_query_args_order'); + } elseif (isset($_REQUEST['page']) && $_REQUEST['page'] === 'manage-issues' + && ( + isset($_REQUEST['action']) && $_REQUEST['action'] === 'pps-publisher-delete-posts' + || isset($_REQUEST['part_action']) && $_REQUEST['part_action'] === 'pps-publisher-delete-posts' + ) + && isset($_REQUEST['series_post']) + && isset($_REQUEST['_wpnonce']) + ) { + $nonce = sanitize_text_field($_REQUEST['_wpnonce']); + $post_ids = is_array($_REQUEST['series_post']) ? array_map('sanitize_text_field', $_REQUEST['series_post']) : (array)sanitize_text_field($_REQUEST['series_post']); + if (wp_verify_nonce($nonce, 'bulk-series-parts')) { + foreach($post_ids as $post_id){ + wp_trash_post($post_id); + } + add_action('admin_notices', "pps_publisher_delete_success_message_admin_notice"); + add_filter('removable_query_args', 'pps_publisher_filter_removable_query_args_delete'); + } + } +} + + add_action(''.ppseries_get_series_slug().'_add_form_fields', 'series_issue_manager_add_series_form'); + //add_filter('save_post', 'series_issue_manager_publish_intercept',3,2); + add_action('created_'.ppseries_get_series_slug().'', 'series_issue_set_publish_status', 2, 2); + add_action('admin_init', 'ppseries_publisher_admin_init'); + + + +class PPS_Publisher_Admin +{ + + // class instance + public static $instance; + + // WP_List_Table object + public $series_part_table; + public $series_publish_table; + + /** + * Constructor + * + * @return void + * @author Olatechpro + */ + public function __construct() + { + add_action('admin_menu', [$this, 'admin_menu']); + } + + /** + * Singleton instance + */ + public static function get_instance() + { + if (!isset(self::$instance)) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Add WP admin menu for Tags + * + * @return void + * @author Olatechpro + */ + public function admin_menu() + { + $page = add_submenu_page( + 'edit.php', + __('Manage Series Issues', 'organize-series'), + __('Publish Series', 'organize-series'), + 'publish_posts', + 'manage-issues', + [$this, 'series_issue_manager_admin'] + ); + add_action("admin_print_scripts-$page", [$this, 'series_issue_manager_scripts']); + + add_action("load-$page", [$this, 'screen_option']); + } + + public function series_issue_manager_scripts() + { + wp_enqueue_script("series_im_sort_articles", plugin_dir_url(__FILE__)."/js/series_im_sort_articles.js", array( 'jquery-ui-sortable' ), ORG_SERIES_VERSION, true); + wp_enqueue_style('series_im_sort_articles', plugin_dir_url(__FILE__) . '/css/series_im_sort_articles.css', array(), ORG_SERIES_VERSION, 'all'); + } + + /** + * Screen options + */ + public function screen_option() + { + if(isset($_GET['action']) && $_GET['action'] === 'list'){ + include_once 'series-publish-post-table.php'; + $this->series_publish_table = new PPS_Publisher_Post_Publish_Table(); + + } + if(isset($_GET['action']) && ($_GET['action'] === 'part' || $_GET['action'] === 'order')){ + include_once 'series-part-post-table.php'; + $this->series_part_table = new PPS_Publisher_Post_Part_Table(); + } + } + + public function series_issue_manager_admin() + { + $published = get_option('im_published_series'); + $unpublished = get_option('im_unpublished_series'); + $series = get_series('orderby=name&hide_empty=0'); + + // Make sure the options exist + if ($published === false) { + $published = array(); + update_option('im_published_series', $published); + } + if ($unpublished === false) { + $unpublished = array(); + update_option('im_unpublished_series', $unpublished); + } + + // See if we have GET parameters + $series_ID = isset($_GET['series_ID'])? (int)$_GET['series_ID']:null; + $action = isset($_GET['action'])? sanitize_text_field($_GET['action']):null; + + if ($series_ID) { + $series_ID = (int)$series_ID; + switch ($action) { + case "part": + $this->ppseries_publisher_part_output($series_ID); + break; + case "order": + $post_IDs = isset($_GET['posts']) ? sanitize_text_field($_GET['posts']) : null; + if ($post_IDs) { + series_issue_manager_part($series_ID, $post_IDs); + } + $this->ppseries_publisher_part_output($series_ID); + break; + case "list": + $this->ppseries_publisher_publish_output($series_ID); + break; + case "publish": + $post_IDs = isset($_GET['posts'])?sanitize_text_field($_GET['posts']):null; + $pub_time['mm'] = isset($_GET['mm'])?sanitize_text_field($_GET['mm']):null; + $pub_time['jj'] = isset($_GET['jj'])?sanitize_text_field($_GET['jj']):null; + $pub_time['aa'] = isset($_GET['aa'])?sanitize_text_field($_GET['aa']):null; + $pub_time['hh'] = isset($_GET['hh'])?sanitize_text_field($_GET['hh']):null; + $pub_time['mn'] = isset($_GET['mn'])?sanitize_text_field($_GET['mn']):null; + if ($post_IDs) { + series_issue_manager_publish($series_ID, $post_IDs, $pub_time, $published, $unpublished); + } + include_once 'series_im_admin_main.php'; + break; + case "unpublish": + series_issue_manager_unpublish($series_ID, $published, $unpublished); + include_once 'series_im_admin_main.php'; + break; + case "ignore": + // stop tracking the series_ID + $key = array_search($series_ID, $published); + if (false !== $key) { + array_splice($published, $key, 1); + update_option('im_published_series', $published); + } + $key = array_search($series_ID, $unpublished); + if (false !== $key) { + array_splice($unpublished, $key, 1); + update_option('im_unpublished_series', $unpublished); + } + include_once 'series_im_admin_main.php'; + break; + default: + include_once 'series_im_admin_main.php'; + break; + } + } else { + include_once 'series_im_admin_main.php'; + } + } + + public function ppseries_publisher_publish_output($series_ID) + { + $series = get_term($series_ID); + $this->series_publish_table->prepare_items(); + ?> + +
+ +

+ name); ?> +

+

+

+
+
+ +
+
+ '; + } + if (!empty($_REQUEST['order'])) { + echo ''; + } + if (!empty($_REQUEST['page'])) { + echo ''; + } + ?> + series_publish_table->display(); //Display the table ?> +
+
+

+
+
+ +
+
+
+
+

+

+
+
+
+ + + + +
+
+
+
+
+

+
+ \n"; + for ( $i = 1; $i < 13; $i = $i +1 ) { + $publish_month .= "\t\t\t" . '\n"; + } + $publish_month .= ''; + $publish_day = ''; + $publish_year = ''; + $hour = ''; + $minute = ''; + printf(__('%1$s%2$s, %3$s @ %4$s : %5$s'), $publish_month, $publish_day, $publish_year, $hour, $minute);// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> +
+
+
+
+
+
+
+
+
+ +
+ +
+
+ +
+ +
+
+ +
+ + series_part_table->prepare_items(); + ?> + +
+ +

+ name); ?> +

+

+

+
+
+ +
+
+ '; + } + if (!empty($_REQUEST['order'])) { + echo ''; + } + if (!empty($_REQUEST['page'])) { + echo ''; + } + ?> + series_part_table->display(); //Display the table ?> +
+
+

+
+
+ +
+
+
+
+

+

+
+
+
+ + + + +
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+ +
+
+ +
+ + = 7" - }, - "require-dev": { - "phpunit/phpunit": "4.*|5.*", - "vimeo/psalm": "^1" - }, - "suggest": { - "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." - }, - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paragon Initiative Enterprises", - "email": "security@paragonie.com", - "homepage": "https://paragonie.com" - } - ], - "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": [ - "csprng", - "polyfill", - "pseudorandom", - "random" - ], - "support": { - "email": "info@paragonie.com", - "issues": "https://github.com/paragonie/random_compat/issues", - "source": "https://github.com/paragonie/random_compat" - }, - "time": "2020-10-15T08:29:30+00:00" + "time": "2021-12-12T09:50:45+00:00" }, { "name": "psr/event-dispatcher", @@ -1649,22 +1602,22 @@ }, { "name": "publishpress/publishpress-plugin-builder", - "version": "v1.3.5", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/publishpress/PublishPress-Plugin-Builder.git", - "reference": "843c2bd811e2307fbca99dfa193b6922a97b5e61" + "reference": "75708e8b1b093988dfd160f50b95803bbff725f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/publishpress/PublishPress-Plugin-Builder/zipball/843c2bd811e2307fbca99dfa193b6922a97b5e61", - "reference": "843c2bd811e2307fbca99dfa193b6922a97b5e61", + "url": "https://api.github.com/repos/publishpress/PublishPress-Plugin-Builder/zipball/75708e8b1b093988dfd160f50b95803bbff725f1", + "reference": "75708e8b1b093988dfd160f50b95803bbff725f1", "shasum": "" }, "require": { "consolidation/robo": "^3.0", - "nelexa/zip": "^3.3", - "php": "~7.3", + "nelexa/zip": "^3.3|^4.0", + "php": "^7.3|^8.0", "symfony/yaml": "^5" }, "require-dev": { @@ -1696,9 +1649,9 @@ "description": "Robo tasks for building WordPress plugins", "support": { "issues": "https://github.com/publishpress/PublishPress-Plugin-Builder/issues", - "source": "https://github.com/publishpress/PublishPress-Plugin-Builder/tree/v1.3.5" + "source": "https://github.com/publishpress/PublishPress-Plugin-Builder/tree/v1.4.0" }, - "time": "2022-01-19T22:09:53+00:00" + "time": "2022-04-14T15:14:49+00:00" }, { "name": "sirbrillig/phpcs-variable-analysis", diff --git a/includes-core/PPSeriesCoreAdmin.php b/includes-core/PPSeriesCoreAdmin.php index 4fc68cbe..082555a9 100644 --- a/includes-core/PPSeriesCoreAdmin.php +++ b/includes-core/PPSeriesCoreAdmin.php @@ -15,7 +15,8 @@ function ($settings) { 'screens' => [ ['base' => 'toplevel_page_orgseries_options_page', 'id' => 'toplevel_page_orgseries_options_page'], ['base' => 'edit-tags', 'id' => 'edit-series', 'taxonomy' => ppseries_get_series_slug() ], - ['base' => 'term', 'id' => 'edit-series', 'taxonomy' => ppseries_get_series_slug() ] + ['base' => 'term', 'id' => 'edit-series', 'taxonomy' => ppseries_get_series_slug() ], + ['base' => 'posts_page_manage-issues', 'id' => 'posts_page_manage-issues' ] ] ]; @@ -25,7 +26,7 @@ function ($settings) { add_filter( \PPVersionNotices\Module\MenuLink\Module::SETTINGS_FILTER, function ($settings) { - $settings['publishpress-taxopress'] = [ + $settings['publishpress-series'] = [ 'parent' => 'orgseries_options_page', 'label' => 'Upgrade to Pro', 'link' => 'https://publishpress.com/links/series-banner', @@ -37,36 +38,19 @@ function ($settings) { } add_action('publishpress_series_admin_menu_page', [$this, 'publishpress_series_admin_menu_page']); + add_action('publishpress_series_admin_after_sidebar', [$this, 'publishpress_series_advertising_sidebar_banner']); } public function publishpress_series_admin_menu_page(){ - - add_submenu_page( - 'orgseries_options_page', - esc_html__('Publish Series', 'organize-series'), - esc_html__('Publish Series', 'organize-series'), - 'manage_publishpress_series', - 'pp-series-pro-placeholders-publish-series', - [$this, 'placeholderPagePublishSeries'], - 12 - ); - add_submenu_page( 'orgseries_options_page', - esc_html__('Series Group', 'organize-series'), - esc_html__('Series Group', 'organize-series'), + esc_html__('Series Groups', 'organize-series'), + esc_html__('Series Groups', 'organize-series'), 'manage_publishpress_series', 'pp-series-pro-placeholders-series-group', [$this, 'placeholderPageSeriesGroup'], 12 ); - - } - - public function placeholderPagePublishSeries(){ - wp_register_style('pps-pro-placeholder-css', SERIES_PATH_URL . 'includes-core/pro-placeholder/assets/css/placeholder.css', [], ORG_SERIES_VERSION); - wp_enqueue_style( 'pps-pro-placeholder-css' ); - include_once __DIR__ . '/pro-placeholder/views/publish-series-placeholder.php'; } public function placeholderPageSeriesGroup(){ @@ -74,5 +58,74 @@ public function placeholderPageSeriesGroup(){ wp_enqueue_style( 'pps-pro-placeholder-css' ); include_once __DIR__ . '/pro-placeholder/views/series-group-placeholder.php'; } + + public function publishpress_series_advertising_sidebar_banner(){ + ?> + +
+
+
+ + +
+
+
+ + -

-
-
-
-
- - -
-

- -

-

- - - -

-
-
-
-
-
diff --git a/orgSeries-admin.css b/orgSeries-admin.css index 21449ad1..945996ca 100644 --- a/orgSeries-admin.css +++ b/orgSeries-admin.css @@ -1,3 +1,23 @@ +.publishpress-series-permalink-error { + background: #fff; + border: 1px solid #c3c4c7; + border-left-width: 4px; + box-shadow: 0 1px 1px rgb(0 0 0 / 4%); + border-left-color: #d63638; + margin: 5px 0 15px; + padding: 5px 5px; +} + +.ppseries-settings-tab-content .stuffbox > h3, +.ppseries-settings-tab-content .postbox > h3, +.ppseries-settings-tab-content h3.handle, +.ppseries-settings-tab-content h2.handle { + font-size: 14px; + padding: 8px 12px; + margin: 0; + line-height: 1.4; +} + .ppseries-settings-tab-content .description { font-weight: initial; } @@ -225,6 +245,154 @@ input #seriesadd { font-size: 13px !important; } +.ppseries-advertisement-right-sidebar .upgrade-btn a { + background: #FCB223; + color: #000 !important; + font-weight: normal; + text-decoration: none; + padding: 9px 12px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-sizing: border-box; + border: 1px solid #fca871; + break-inside: avoid; + white-space: nowrap; +} + +.ppseries-advertisement-right-sidebar .upgrade-btn a:hover { + background: #fcca46; + color: #000 !important; +} + +.ppseries-advertisement-right-sidebar h3.hndle { + font-size: 14px; + padding: 8px 12px; + margin: 0; + line-height: 1.4; +} + +.ppseries-token-right-sidebar h3.hndle { + font-size: 14px; + padding: 8px 12px; + margin: 0; + line-height: 1.4; +} + +.ppseries-advertisement-right-sidebar h3.hndle { + font-size: 14px; + padding: 8px 12px; + margin: 0; + line-height: 1.4; +} + +.ppseries-advertisement-right-sidebar .postbox-container .inside ul { + margin-bottom: 20px; +} + +.ppseries-advertisement-right-sidebar .postbox-container .inside ul li { + position: relative; + padding-left: 22px; + font-weight: 600; + font-size: .9em; +} + +.ppseries-advertisement-right-sidebar .postbox-container .inside ul li:before { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 0; + background-color: #3C50FF; + mask-image: url("data:image/svg+xml;utf8,"); + -webkit-mask-image: url("data:image/svg+xml;utf8,"); + mask-size: 16px; + -webkit-mask-size: 16px; + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-position: left; + -webkit-mask-position: left; +} + +.ppseries-advertisement-right-sidebar a.advert-link, +.ppseries-content-promo-box a.advert-link { + display: block; + margin-top: 10px; + font-size: 1em; +} + +.ppseries-advertisement-right-sidebar .advertisement-box-header, +.ppseries-content-promo-box .advertisement-box-header { + background: #655897; + color: #ffffff; +} + +.ppseries-advertisement-right-sidebar .advertisement-box-content, +.ppseries-content-promo-box.advertisement-box-content { + border: 1px solid #655897; +} + + +.ppseries-content-promo-box .upgrade-btn { + margin-top: 20px; +} + +.ppseries-content-promo-box .upgrade-btn a { + background: #FCB223; + color: #000 !important; + font-weight: normal; + text-decoration: none; + padding: 9px 12px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-sizing: border-box; + border: 1px solid #fca871; + break-inside: avoid; + white-space: nowrap; +} + +.ppseries-content-promo-box .upgrade-btn a:hover { + background: #fcca46; + color: #000 !important; +} + +.ppseries-content-promo-box.postbox.upgrade-pro .inside-content { + position: relative; + margin: 11px 0; + padding: 0 12px 12px; + line-height: 1.4; + font-size: 13px; +} + +@media only screen and (min-width: 1075px) { + .ppseries-advertisement-right-sidebar-message, + .upgrade-btn { + display: inline-block; + } + + .ppseries-advertisement-right-sidebar-message { + margin-right: 25px; + } +} + +@media only screen and (max-width: 1074px) { + .ppseries-advertisement-right-sidebar-message, + .upgrade-btn { + display: block; + } + + .upgrade-btn { + margin-top: 20px; + } + + .upgrade-btn a { + max-width: 170px; + } +} + /* Mobile styles */ @media only screen and (max-width: 782px) { .ppseries-settings-tab-content .form-table th { diff --git a/orgSeries-options.php b/orgSeries-options.php index a106e9b5..2257a206 100644 --- a/orgSeries-options.php +++ b/orgSeries-options.php @@ -264,7 +264,7 @@ function orgseries_option_page() { if(apply_filters('ppseries_settings_'.$settings_tab_key.'_tabbed', false)){ $tabbled_class = 'series-tab-content'; }else{ - $tabbled_class = ''; + $tabbled_class = !defined('SERIES_PRO_VERSION') ? 'series-tab-content' : ''; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''.$settings_tab_label.''; @@ -275,61 +275,73 @@ function orgseries_option_page() {
-
+
+ +
+
+ +
+ +

+
+



    • '; + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo '

      '. htmlentities ($html_list) .'

      '; + ?> +
-
+

+

+
+

+ %series_icon%
+

+ %series_icon_linked%
+

+ %series_list%
+

+ %series_title%
+

+ %series_title_linked%
+

+ %post_title_list%
+

+ %post_title_list_short%
+

+ %post_title%
+

+ %post_title_linked%
+

+ %previous_post%
+

+ %next_post%
+

+ %first_post%
+

+ %postcontent%
+

+ %series_part%
+

+ %total_posts_in_series%
+

+ %series_description%
+ + + +
+ +
+ +
+ +
+ +
+
+
-

-
-



    • '; - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo '

      '. htmlentities ($html_list) .'

      '; - ?> -
-

-

-
-

- %series_icon%
-

- %series_icon_linked%
-

- %series_list%
-

- %series_title%
-

- %series_title_linked%
-

- %post_title_list%
-

- %post_title_list_short%
-

- %post_title%
-

- %post_title_linked%
-

- %previous_post%
-

- %next_post%
-

- %first_post%
-

- %postcontent%
-

- %series_part%
-

- %total_posts_in_series%
-

- %series_description%
- - - -
-
- -
@@ -536,6 +548,26 @@ function series_automation_core_fieldset() { / + + permalink_structure ) ) { + ?> + + +