This is an example page. It's different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:
Hi there! I'm a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin' caught in the rain.)
...or something like this:
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!
]]>Suggested text: Our website address is: http://127.0.0.1:8000.
Suggested text: When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.
An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.
Suggested text: If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.
Suggested text: If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.
If you visit our login page, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.
When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.
If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.
Suggested text: Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.
These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.
Suggested text: If you request a password reset, your IP address will be included in the reset email.
Suggested text: If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.
For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.
Suggested text: If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.
Suggested text: Visitor comments may be checked through an automated spam detection service.
]]>
The rest of this paragraph is filler for the sake of seeing the text wrap around the 150x150 image, which is left aligned.
As you can see there should be some space above, below, and to the right of the image. The text should not be creeping on the image. Creeping is just not right. Images need breathing room too. Let them speak like you words. Let them do their jobs without any hassle from the text. In about one more sentence here, we'll see that the text moves from the right of the image down below the image in seamless transition. Again, letting the do it's thang. Mission accomplished!
And now for a massively large image. It also has no alignment.
The image above, though 1200px wide, should not overflow the content area. It should remain contained with no visible disruption to the flow of content.
And we try the large image again, with the center alignment since that sometimes is a problem. The image above, though 1200px wide, should not overflow the content area. It should remain contained with no visible disruption to the flow of content.
And now we're going to shift things to the right align. Again, there should be plenty of room above, below, and to the left of the image. Just look at him there... Hey guy! Way to rock that right side. I don't care what the left aligned image says, you look great. Don't let anyone else tell you differently.
In just a bit here, you should see the text start to wrap below the right aligned image and settle in nicely. There should still be plenty of room and everything should be sitting pretty. Yeah... Just like that. It never felt so good to be right.
And just when you thought we were done, we're going to do them all over again with captions!
[caption id="attachment_906" align="aligncenter" width="580"]
Look at 580x300 getting some caption love.[/caption]
The image above happens to be centered. The caption also has a link in it, just to see if it does anything funky.
[caption id="attachment_904" align="alignleft" width="150"]
Bigger caption than the image usually is.[/caption]
The rest of this paragraph is filler for the sake of seeing the text wrap around the 150x150 image, which is left aligned.
As you can see the should be some space above, below, and to the right of the image. The text should not be creeping on the image. Creeping is just not right. Images need breathing room too. Let them speak like you words. Let them do their jobs without any hassle from the text. In about one more sentence here, we'll see that the text moves from the right of the image down below the image in seamless transition. Again, letting the do it's thang. Mission accomplished!
And now for a massively large image. It also has no alignment.
[caption id="attachment_907" align="alignnone" width="1200"]
Comment for massive image for your eyeballs.[/caption]
The image above, though 1200px wide, should not overflow the content area. It should remain contained with no visible disruption to the flow of content.
[caption id="attachment_907" align="aligncenter" width="1200"]
This massive image is centered.[/caption]
And again with the big image centered. The image above, though 1200px wide, should not overflow the content area. It should remain contained with no visible disruption to the flow of content.
[caption id="attachment_905" align="alignright" width="300"]
Feels good to be right all the time.[/caption]
And now we're going to shift things to the right align. Again, there should be plenty of room above, below, and to the left of the image. Just look at him there... Hey guy! Way to rock that right side. I don't care what the left aligned image says, you look great. Don't let anyone else tell you differently.
In just a bit here, you should see the text start to wrap below the right aligned image and settle in nicely. There should still be plenty of room and everything should be sitting pretty. Yeah... Just like that. It never felt so good to be right.
And that's a wrap, yo! You survived the tumultuous waters of alignment. Image alignment achievement unlocked! Last thing is a small image aligned right. Whatever follows should be unaffected.
]]>Stay hungry. Stay foolish.Multi line blockquote with a cite reference:
multiple contributors - MDN HTML element reference - blockquoteThe HTML
<blockquote>Element (or HTML Block Quotation Element) indicates that the enclosed text is an extended quotation. Usually, this is rendered visually by indentation (see Notes for how to change it). A URL for the source of the quotation may be given using the cite attribute, while a text representation of the source can be given using the<cite>element.
| Employee | Salary | |
|---|---|---|
| Jane | $1 | Because that's all Steve Jobs needed for a salary. |
| John | $100K | For all the blogging he does. |
| Jane | $100M | Pictures are worth a thousand words, right? So Tom x 1,000. |
| Jane | $100B | With hair like that?! Enough said... |
.post-title {
margin: 0 0 5px;
font-weight: bold;
font-size: 38px;
line-height: 1.2;
and here's a line of some really, really, really, really long text, just to see how it is handled and to find out how it overflows;
}
You will learn later on in these tests that word-wrap: break-word; will be your best friend.
Delete Tag
This tag will let you <s> instead).
Emphasize Tag
The emphasize tag should italicize text.
Horizontal Rule Tag
<hr /> tag.
Insert Tag
This tag should denote inserted text.
Keyboard Tag
This scarcely known tag emulates keyboard text, which is usually styled like the <code> tag.
Preformatted Tag
This tag is for preserving whitespace as typed, such as in poetry or ASCII art.
Robert Frost
Two roads diverged in a yellow wood,
And sorry I could not travel both (\_/)
And be one traveler, long I stood (='.'=)
And looked down one as far as I could (")_(")
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim, |\_/|
Because it was grassy and wanted wear; / @ @ \
Though as for that the passing there ( > º < )
Had worn them really about the same, `>>x<<´
/ O \
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
and here's a line of some really, really, really, really long text, just to see how it is handled and to find out how it overflows;
Quote Tag for short, inline quotes
Developers, developers, developers...--Steve Ballmer Strike Tag (deprecated in HTML5) and S Tag This tag shows
<code> tag.
Underline Tag deprecated in HTML 4, re-introduced in HTML5 with other semantics
This tag shows underlined text.
Variable Tag
This allows you to denote variables.]]>Πάντα να είναι περίεργος.Πολλές γραμμέ με αναφορά Multi line blockquote with a cite reference:
Το HTMLmultiple contributors - MDN HTML element reference - blockquote<blockquote>Element (ή HTML Block Quotation Element) καταδεικνύει ότι το κείμενο έχει μια παράθεση. Συνήθως οπτικοποιείται με εσοχή (δείτε Σημειώσεις για το πως να το αλλάξετε. Ίσως να δίνεται και URL πηγής με την χρήση του cite attribute, μπλα, μπλα<cite>.
| Υπάλληλος Employee | Μισθός Salary | |
|---|---|---|
| Τάδε κάποιος | $1 | Γιατί τόσα χρειάζεται για να ζήσει |
| Jane Doe | $100K | For all the blogging she does. |
| Fred Bloggs | $100M | Pictures are worth a thousand words, right? So Jane x 1,000. |
| Jane Bloggs | $100B | With hair like that?! Enough said... |
.post-title {
margin: 0 0 5px;
font-weight: bold;
font-size: 38px;
line-height: 1.2;
και μία γραμμή με πολύ πάρα πολύ υπερβολικά πάρα πολύ μεγάλο κείμενο που πρέπει να δούμε πως το χειρίζεται η γραμματοσειρά και αν ξεχειλίζει από τις γραμμές και δημιουργεί πρόβλημα;
}
Διαγραφή Delete Tag
Μπορείτε να <κώδικα code> tag.
Προδιαμορφωμένο Preformatted Tag
Robert Frost
Δυο δρόμοι διασταυρώθηκαν σ' ένα χρυσαφένιο δάσος ,
Και προς λύπη μου και τους δυο τα πόδια μου να ταξιδέψουν δεν μπορούσαν
Κι επί μακρόν εστάθηκα , καθώς ένας ήμουν ταξιδευτής μονάχος ,
κι έστρεψα το βλέμμα μου στον πρώτο όσο να χαθεί στο βάθος
μέχρι εκεί που χάνονταν στα άγρια χόρτα που βλαστούσαν.
Two roads diverged in a yellow wood,
And sorry I could not travel both (\_/)
And be one traveler, long I stood (='.'=)
And looked down one as far as I could (")_(")
To where it bent in the undergrowth;
Then took the other, as just as fair,
And having perhaps the better claim, |\_/|
Because it was grassy and wanted wear; / @ @ \
Though as for that the passing there ( > º < )
Had worn them really about the same, `>>x<<´
/ O \
And both that morning equally lay
In leaves no step had trodden black.
Oh, I kept the first for another day!
Yet knowing how way leads on to way,
I doubted if I should ever come back.
I shall be telling this with a sigh
Somewhere ages and ages hence:
Two roads diverged in a wood, and I—
I took the one less traveled by,
And that has made all the difference.
και μία μακριά, πάρα πολύ μακριά, υπερβολικά μακροσκελής δίχως νόημα πρόταση για να δούμε πως το χειρίζεται το θέμα εμφάνισης και αν αναδιπλώνεται, κρύβεται ή ξεχειλίζει;
Quote Tag for short, inline quotes
Προγραμματιστές, προγραμματιστές, developers...--Steve Ballmer Strike Tag (deprecated in HTML5) and S Tag Αυτή η ετικέτα είναι με διαγράμμιση strike-through
<κώδικα code> tag.
Underline Tag deprecated in HTML 4, re-introduced in HTML5 with other semantics
Αυτή η ετικέτα δείχνει υπογράμμιση underlined text.
Variable Tag
Αυτή η ετικέτα δείχνει παράμετροι variables.]]>
================================================
FILE: assets/css/scss/simple-page-ordering.scss
================================================
.wp-list-table {
.ui-sortable {
tr {
cursor: move;
&.inline-editor {
cursor: default;
}
}
}
.spo-updating {
tr {
cursor: default;
}
}
.ui-sortable-placeholder {
outline: 1px dashed #bbb;
background: #F1F1F1;
visibility: visible !important;
}
.ui-sortable-helper {
background-color: #fff;
outline: 1px solid #e1e1e1;
}
}
.spo-updating-row {
.check-column {
display: table-cell; // reset from core .spinner
float: none; // reset from core .spinner
margin: 0; // reset from core .spinner
background-position: 9px 9px;
input {
visibility: hidden;
}
}
}
================================================
FILE: assets/js/src/simple-page-ordering.js
================================================
import '../../css/scss/simple-page-ordering.scss';
import { decodeEntities } from '@wordpress/html-entities';
// eslint-disable-next-line import/no-unresolved
import 'jquery-ui-sortable';
const sortable_post_table = jQuery('.wp-list-table tbody');
function update_simple_ordering_callback(response) {
if (response === 'children') {
window.location.reload();
return;
}
const changes = jQuery.parseJSON(response);
const { new_pos } = changes;
// eslint-disable-next-line no-restricted-syntax
for (const key in new_pos) {
if (key === 'next') {
// eslint-disable-next-line no-continue
continue;
}
const inline_key = document.getElementById(`inline_${key}`);
// eslint-disable-next-line no-prototype-builtins
if (inline_key !== null && new_pos.hasOwnProperty(key)) {
const dom_menu_order = inline_key.querySelector('.menu_order');
if (undefined !== new_pos[key].menu_order) {
if (dom_menu_order !== null) {
dom_menu_order.textContent = new_pos[key].menu_order;
}
const dom_post_parent = inline_key.querySelector('.post_parent');
if (dom_post_parent !== null) {
dom_post_parent.textContent = new_pos[key].post_parent;
}
let post_title = null;
const dom_post_title = inline_key.querySelector('.post_title');
if (dom_post_title !== null) {
post_title = dom_post_title.innerHTML;
// Convert emoji img tags back to Unicode emoji characters
// See: https://github.com/10up/simple-page-ordering/issues/205
post_title = post_title.replace(
/Simple Page Ordering. Please run npm i; npm run build to create assets.', 'simple-page-ordering' ) ); ?>
%s
%s', esc_html__( 'To reposition an item, simply drag and drop the row by "clicking and holding" it anywhere (outside of the links and form controls) and moving it to its new position.', 'simple-page-ordering' ), esc_attr( get_query_var( 'post_type' ) ), /* translators: %1$s is replaced with the post type name */ sprintf( esc_html__( 'Reset %1$s order', 'simple-page-ordering' ), $post_type ) ), ) ); } /** * Modify the row actions for hierarchical post types. * * This adds the actions to change the parent/child relationships. * * @param array $actions An array of row action links. * @param WP_Post $post The post object. */ public static function page_row_actions( $actions, $post ) { $post = get_post( $post ); if ( ! $post ) { return $actions; } if ( ! current_user_can( 'edit_post', $post->ID ) ) { return $actions; } /** * Allow or disallow new row actions. * * @since 2.7.5 * * @param boolean $should_add_actions Whether to add the new row actions. * @param array $actions An array of row action links. * @param WP_Post $post The post object. */ $should_add_actions = apply_filters( 'simple_page_ordering_allow_row_actions', true, $actions, $post ); if ( ! $should_add_actions ) { return $actions; } list( 'top_level_pages' => $top_level_pages, 'children_pages' => $children_pages ) = self::get_walked_pages( $post->post_type ); $edit_link = get_edit_post_link( $post->ID, 'raw' ); $move_under_grandparent_link = add_query_arg( array( 'action' => 'spo-move-under-grandparent', 'spo_nonce' => wp_create_nonce( "simple-page-ordering-nonce-move-{$post->ID}" ), 'post_type' => $post->post_type, ), $edit_link ); $move_under_sibling_link = add_query_arg( array( 'action' => 'spo-move-under-sibling', 'spo_nonce' => wp_create_nonce( "simple-page-ordering-nonce-move-{$post->ID}" ), 'post_type' => $post->post_type, ), $edit_link ); $parent_id = $post->post_parent; if ( $parent_id ) { $actions['spo-move-under-grandparent'] = sprintf( '%s', esc_url( $move_under_grandparent_link ), sprintf( /* translators: %s: parent page/post title */ __( 'Move out from under %s', 'simple-page-ordering' ), get_the_title( $parent_id ) ) ); } // Get the relevant siblings. if ( 0 === $post->post_parent ) { $siblings = $top_level_pages; } else { $siblings = $children_pages[ $post->post_parent ] ?? array(); } // Assume no sibling. $sibling = 0; // Check if the post being moved is a top level page. $filtered_siblings = wp_list_filter( $siblings, array( 'ID' => $post->ID ) ); if ( ! empty( $filtered_siblings ) ) { // Find the previous page in the sibling tree $key = array_key_first( $filtered_siblings ); if ( 0 === $key ) { // It's the first page, can't do anything. $sibling = 0; } else { $previous_page = $siblings[ $key - 1 ]; $sibling = $previous_page->ID; } } if ( $sibling ) { $actions['spo-move-under-sibling'] = sprintf( '%s', esc_url( $move_under_sibling_link ), sprintf( /* translators: %s: sibling page/post title */ __( 'Move under %s', 'simple-page-ordering' ), get_the_title( $sibling ) ) ); } return $actions; } /** * Page ordering ajax callback * * @return void */ public static function ajax_simple_page_ordering() { // check and make sure we have what we need if ( empty( $_POST['id'] ) || ( ! isset( $_POST['previd'] ) && ! isset( $_POST['nextid'] ) ) ) { die( - 1 ); } $nonce = isset( $_POST['_wpnonce'] ) ? sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'simple-page-ordering-nonce' ) ) { die( -1 ); } $post_id = empty( $_POST['id'] ) ? false : (int) $_POST['id']; $previd = empty( $_POST['previd'] ) ? false : (int) $_POST['previd']; $nextid = empty( $_POST['nextid'] ) ? false : (int) $_POST['nextid']; $start = empty( $_POST['start'] ) ? 1 : (int) $_POST['start']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized after json_decode. $excluded = empty( $_POST['excluded'] ) ? array( $post_id ) : array_filter( json_decode( wp_unslash( $_POST['excluded'] ), true ), 'intval' ); // real post? $post = empty( $post_id ) ? false : get_post( (int) $post_id ); if ( ! $post ) { die( - 1 ); } // does user have the right to manage these post objects? if ( ! self::check_edit_others_caps( $post->post_type ) ) { die( - 1 ); } $result = self::page_ordering( $post_id, $previd, $nextid, $start, $excluded ); if ( is_wp_error( $result ) ) { die( -1 ); } die( wp_json_encode( $result ) ); } /** * Page ordering reset ajax callback * * @return void */ public static function ajax_reset_simple_page_ordering() { global $wpdb; $nonce = isset( $_POST['_wpnonce'] ) ? sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'simple-page-ordering-nonce' ) ) { die( -1 ); } // check and make sure we have what we need $post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( wp_unslash( $_POST['post_type'] ) ) : ''; if ( empty( $post_type ) ) { die( -1 ); } // does user have the right to manage these post objects? if ( ! self::check_edit_others_caps( $post_type ) ) { die( -1 ); } /* * Reset the order of all posts of given post type. * * Doing this manually via a direct query for speed in order to bypass the overhead * of multiple calls to `wp_update_post()`. */ // phpcs:ignore WordPress.DB.DirectDatabaseQuery $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = %s AND menu_order != 0", $post_type ) ); $post_ids = array_map( 'intval', $post_ids ); // Required for cache keys. // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->query( $wpdb->prepare( sprintf( "UPDATE $wpdb->posts SET menu_order = 0 WHERE ID IN (%s)", implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) ), $post_ids ) ); /* * Clear the post caches. * * `clean_post_cache()` is not used here as it will clear the post, post meta, terms and * other related caches. This is much more expensive than necessary for clearing the menu * order cache. */ if ( empty( $_wp_suspend_cache_invalidation ) ) { // Clear the post caches. wp_cache_delete_multiple( $post_ids, 'posts' ); wp_cache_set_posts_last_changed(); } die( 0 ); } /** * Page ordering function * * @param int $post_id The post ID. * @param int $previd The previous post ID. * @param int $nextid The next post ID. * @param int $start The start index. * @param array $excluded Array of post IDs. * * @return object|WP_Error|"children" */ public static function page_ordering( $post_id, $previd, $nextid, $start, $excluded ) { // real post? $post = empty( $post_id ) ? false : get_post( (int) $post_id ); if ( ! $post ) { return new WP_Error( 'invalid', __( 'Missing mandatory parameters.', 'simple-page-ordering' ) ); } // Badly written plug-in hooks for save post can break things. if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting, WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting -- Intentionally suppressing errors from third-party plugins during ordering. error_reporting( 0 ); } global $wp_version; $previd = empty( $previd ) ? false : (int) $previd; $nextid = empty( $nextid ) ? false : (int) $nextid; $start = empty( $start ) ? 1 : (int) $start; $excluded = empty( $excluded ) ? array( $post_id ) : array_filter( (array) $excluded, 'intval' ); $new_pos = array(); // store new positions for ajax $return_data = new stdClass(); do_action( 'simple_page_ordering_pre_order_posts', $post, $start ); // attempt to get the intended parent... if either sibling has a matching parent ID, use that $parent_id = $post->post_parent; $next_post_parent = $nextid ? wp_get_post_parent_id( $nextid ) : false; if ( $previd === $next_post_parent ) { // if the preceding post is the parent of the next post, move it inside $parent_id = $next_post_parent; } elseif ( $next_post_parent !== $parent_id ) { // otherwise, if the next post's parent isn't the same as our parent, we need to study $prev_post_parent = $previd ? wp_get_post_parent_id( $previd ) : false; if ( $prev_post_parent !== $parent_id ) { // if the previous post is not our parent now, make it so! $parent_id = ( false !== $prev_post_parent ) ? $prev_post_parent : $next_post_parent; } } // if the next post's parent isn't our parent, it might as well be false (irrelevant to our query) if ( $next_post_parent !== $parent_id ) { $nextid = false; } $max_sortable_posts = (int) apply_filters( 'simple_page_ordering_limit', 50 ); // should reliably be able to do about 50 at a time if ( $max_sortable_posts < 5 ) { // don't be ridiculous! $max_sortable_posts = 50; } // we need to handle all post stati, except trash (in case of custom stati) $post_stati = get_post_stati( array( 'show_in_admin_all_list' => true, ) ); $siblings_query = array( 'depth' => 1, 'posts_per_page' => $max_sortable_posts, 'post_type' => $post->post_type, 'post_status' => $post_stati, 'post_parent' => $parent_id, 'post__not_in' => $excluded, // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Likely faster via the DB than PHP. 'orderby' => array( 'menu_order' => 'ASC', 'title' => 'ASC', ), 'update_post_term_cache' => false, 'update_post_meta_cache' => false, 'suppress_filters' => true, // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters 'ignore_sticky_posts' => true, ); if ( version_compare( $wp_version, '4.0', '<' ) ) { $siblings_query['orderby'] = 'menu_order title'; $siblings_query['order'] = 'ASC'; } $siblings = new WP_Query( $siblings_query ); // fetch all the siblings (relative ordering) // don't waste overhead of revisions on a menu order change (especially since they can't *all* be rolled back at once) remove_action( 'post_updated', 'wp_save_post_revision' ); foreach ( $siblings->posts as $sibling ) : // don't handle the actual post if ( $sibling->ID === $post->ID ) { continue; } // if this is the post that comes after our repositioned post, set our repositioned post position and increment menu order if ( $nextid === $sibling->ID ) { wp_update_post( array( 'ID' => $post->ID, 'menu_order' => $start, 'post_parent' => $parent_id, ) ); $ancestors = get_post_ancestors( $post->ID ); $new_pos[ $post->ID ] = array( 'menu_order' => $start, 'post_parent' => $parent_id, 'depth' => count( $ancestors ), ); ++$start; } // if repositioned post has been set, and new items are already in the right order, we can stop if ( isset( $new_pos[ $post->ID ] ) && $sibling->menu_order >= $start ) { $return_data->next = false; break; } // set the menu order of the current sibling and increment the menu order if ( $sibling->menu_order !== $start ) { wp_update_post( array( 'ID' => $sibling->ID, 'menu_order' => $start, ) ); } $new_pos[ $sibling->ID ] = $start; ++$start; if ( ! $nextid && $previd === $sibling->ID ) { wp_update_post( array( 'ID' => $post->ID, 'menu_order' => $start, 'post_parent' => $parent_id, ) ); $ancestors = get_post_ancestors( $post->ID ); $new_pos[ $post->ID ] = array( 'menu_order' => $start, 'post_parent' => $parent_id, 'depth' => count( $ancestors ), ); ++$start; } endforeach; // max per request if ( ! isset( $return_data->next ) && $siblings->max_num_pages > 1 ) { $return_data->next = array( 'id' => $post->ID, 'previd' => $previd, 'nextid' => $nextid, 'start' => $start, 'excluded' => array_merge( array_keys( $new_pos ), $excluded ), ); } else { $return_data->next = false; } do_action( 'simple_page_ordering_ordered_posts', $post, $new_pos ); if ( ! $return_data->next ) { // if the moved post has children, we need to refresh the page (unless we're continuing) $children = new WP_Query( array( 'posts_per_page' => 1, 'post_type' => $post->post_type, 'post_status' => $post_stati, 'post_parent' => $post->ID, 'fields' => 'ids', 'update_post_term_cache' => false, 'update_post_meta_cache' => false, 'ignore_sticky' => true, 'no_found_rows' => true, ) ); if ( $children->have_posts() ) { return 'children'; } } $return_data->new_pos = $new_pos; return $return_data; } /** * Append a sort by order link to the post actions * * @param array $views An array of available list table views. * * @return array */ public static function sort_by_order_link( $views ) { $class = ( get_query_var( 'orderby' ) === 'menu_order title' ) ? 'current' : ''; $query_string = remove_query_arg( array( 'orderby', 'order' ) ); if ( ! is_post_type_hierarchical( get_post_type() ) ) { $query_string = add_query_arg( 'orderby', 'menu_order title', $query_string ); $query_string = add_query_arg( 'order', 'asc', $query_string ); $query_string = add_query_arg( 'id', 'simple-page-ordering', $query_string ); } $views['byorder'] = sprintf( '%s', esc_url( $query_string ), $class, __( 'Sort by Order', 'simple-page-ordering' ) ); return $views; } /** * Checks to see if the current user has the capability to "edit others" for a post type * * @param string $post_type Post type name * * @return bool True or false */ private static function check_edit_others_caps( $post_type ) { $post_type_object = get_post_type_object( $post_type ); $edit_others_cap = empty( $post_type_object ) ? 'edit_others_' . $post_type . 's' : $post_type_object->cap->edit_others_posts; return apply_filters( 'simple_page_ordering_edit_rights', current_user_can( $edit_others_cap ), $post_type ); } /** * Registers the API endpoint for sorting from the REST endpoint */ public static function rest_api_init() { register_rest_route( 'simple-page-ordering/v1', 'page_ordering', array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'rest_page_ordering' ), 'permission_callback' => array( __CLASS__, 'rest_page_ordering_permissions_check' ), 'args' => array( 'id' => array( 'description' => __( 'ID of item we want to sort', 'simple-page-ordering' ), 'required' => true, 'type' => 'integer', 'minimum' => 1, ), 'previd' => array( 'description' => __( 'ID of item we want to be previous to after sorting', 'simple-page-ordering' ), 'required' => true, 'type' => array( 'boolean', 'integer' ), ), 'nextid' => array( 'description' => __( 'ID of item we want to be next to after sorting', 'simple-page-ordering' ), 'required' => true, 'type' => array( 'boolean', 'integer' ), ), 'start' => array( 'default' => 1, 'description' => __( 'Index we start with when sorting', 'simple-page-ordering' ), 'required' => false, 'type' => 'integer', ), // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- false positive. 'exclude' => array( 'default' => array(), 'description' => __( 'Array of IDs we want to exclude', 'simple-page-ordering' ), 'required' => false, 'type' => 'array', 'items' => array( 'type' => 'integer', ), ), ), ) ); } /** * Check if a given request has access to reorder content. * * This check ensures the current user making the request has * proper permissions to edit the item, that the post type * is allowed in REST requests and the post type is sortable. * * @since 2.5.1 * * @param WP_REST_Request $request Full data about the request. * @return bool|WP_Error */ public static function rest_page_ordering_permissions_check( \WP_REST_Request $request ) { $post_id = $request->get_param( 'id' ); // Ensure we have a logged in user that can edit the item. if ( ! current_user_can( 'edit_post', $post_id ) ) { return false; } $post_type = get_post_type( $post_id ); $post_type_obj = get_post_type_object( $post_type ); // Ensure the post type is allowed in REST endpoints. if ( ! $post_type || empty( $post_type_obj ) || empty( $post_type_obj->show_in_rest ) ) { return false; } // Ensure this post type is sortable. if ( ! self::is_post_type_sortable( $post_type ) ) { return new WP_Error( 'not_enabled', esc_html__( 'This post type is not sortable.', 'simple-page-ordering' ) ); } return true; } /** * Handle REST page sorting * * @param WP_REST_Request $request The REST request object. */ public static function rest_page_ordering( \WP_REST_Request $request ) { $post_id = empty( $request->get_param( 'id' ) ) ? false : (int) $request->get_param( 'id' ); $previd = empty( $request->get_param( 'previd' ) ) ? false : (int) $request->get_param( 'previd' ); $nextid = empty( $request->get_param( 'nextid' ) ) ? false : (int) $request->get_param( 'nextid' ); $start = empty( $request->get_param( 'start' ) ) ? 1 : (int) $request->get_param( 'start' ); $excluded = empty( $request->get_param( 'excluded' ) ) ? array( $request->get_param( 'id' ) ) : array_filter( (array) json_decode( $request->get_param( 'excluded' ) ), 'intval' ); // Check and make sure we have what we need. if ( false === $post_id || ( false === $previd && false === $nextid ) ) { return new WP_Error( 'invalid', __( 'Missing mandatory parameters.', 'simple-page-ordering' ) ); } $page_ordering = self::page_ordering( $post_id, $previd, $nextid, $start, $excluded ); if ( is_wp_error( $page_ordering ) ) { return $page_ordering; } return new WP_REST_Response( array( 'status' => 200, 'response' => 'success', 'body_response' => $page_ordering, ) ); } } Simple_Page_Ordering::get_instance(); endif; ================================================ FILE: composer.json ================================================ { "name": "10up/simple-page-ordering", "description": "Order your pages and other hierarchical post types with simple drag and drop right from the standard page list.", "type": "wordpress-plugin", "keywords": [ "wordpress", "10up" ], "homepage": "https://github.com/10up/simple-page-ordering", "license": "GPLv2", "authors": [ { "name": "10up", "homepage": "https://10up.com/" } ], "support": { "issues": "https://github.com/10up/simple-page-ordering/issues", "source": "https://github.com/10up/simple-page-ordering" }, "minimum-stability": "dev", "require": { "10up/wp-compat-validation-tool": "0.4.0" }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "composer/installers": true }, "platform-check": false }, "scripts": { "post-install-cmd": [ "./10up-lib/wp-compat-validation-tool/replace-namespace.sh Simple_Page_Ordering_Validator simple-page-ordering" ], "post-update-cmd": [ "./10up-lib/wp-compat-validation-tool/replace-namespace.sh Simple_Page_Ordering_Validator simple-page-ordering" ] }, "extra": { "installer-paths": { "./{$name}/": ["10up/wp-compat-validation-tool"] } }, "require-dev": { "wp-coding-standards/wpcs": "^3.0", "phpcompatibility/phpcompatibility-wp": "^3.0@dev", "automattic/vipwpcs": "^3.0" } } ================================================ FILE: package.json ================================================ { "name": "simple-page-ordering", "description": "Order your pages and other hierarchical post types with simple drag and drop right from the standard page list.", "version": "2.8.0", "author": "10up