Помощь в расширении пользовательского порядка перетаскивания страниц на экране списка страниц администратора


Я использую следующий код, который преобразует любую иерархическую страницу или пользовательский тип записи с возможностью изменения порядка страниц с помощью простого перетаскивания. (кроме того, этот код также добавляет новый фильтр отображения на иерархические страницы, чтобы упростить задачу).

Проблема, с которой мне нужна помощь, которая в настоящее время не работает, заключается в возможности перетаскивания одного дерева страниц в другое дерево страниц или из существующего дерева страниц.

Не мог бы кто-нибудь, пожалуйста, предоставить код изменения, необходимые для расширения этой возможности?

Если вы знаете лучший способ достижения тех же целей с помощью другого кода вместо расширения кода, приведенного ниже, пожалуйста, также опубликуйте его.

ОБНОВЛЕНО: Я также разместил код здесь: https://gist.github.com/812204

ПРИМЕЧАНИЕ: для всех тех, кто хотел бы проверить это, просто сделайте следующее.

1-я копия/мимо кода ниже в ваших темах functions.php " файл.

///////////////////////////////////////////////////////////////////////////////////////////
// CODE TO ADD POST PER PAGE FILTER
///////////////////////////////////////////////////////////////////////////////////////////    
    add_filter( 'edit_posts_per_page', 'reorder_edit_posts_per_page', 10, 2 );
    function reorder_edit_posts_per_page( $per_page, $post_type ) {

        // CHECK USER PERMISSIONS
        if ( !current_user_can('edit_others_pages') )
            return;
        $post_type_object = get_post_type_object( $post_type );

        // ONLY APPLY TO HIERARCHICAL POST TYPE
        if ( !$post_type_object->hierarchical )
            return;

        // ADD POST PER PAGE DROP DOWN UI
        add_action( 'restrict_manage_posts', 'reorder_posts_per_page_filter' );

        // ADD SPECIAL STYLES (MOVE CURSOR & SPINNING LOADER AFTER REORDER)
        wp_enqueue_script( 'page-ordering', get_bloginfo('stylesheet_directory') . '/custom/js/page-resorting.js', array('jquery-ui-sortable'), '0.8.4', true );
        add_action( 'admin_print_styles', 'reorder_admin_styles' );

        if ( isset( $_GET['spo'] ) && is_numeric( $_GET['spo'] ) && ( $_GET['spo'] == -1 || ($_GET['spo']%10) == 0 ) ) :
            global $edit_per_page, $user_ID;
            $per_page = $_GET['spo'];
            if ( $per_page == -1 )
                $per_page = 99999;
            update_user_option( $user_ID, $edit_per_page, $per_page );
        endif;
        return $per_page;
    }


    // STYLING CSS FOR THE AJAX
       function reorder_admin_styles() {
        echo '<style type="text/css">table.widefat tbody th, table.widefat tbody td { cursor: move; }</style>';
       }


    // FUNCTION TO CREATE THE NUMBER OF POSTS PER PAGE DROPDOWN UI
       function reorder_posts_per_page_filter() {
        global $per_page;       
        $spo = isset($_GET['spo']) ? (int)$_GET['spo'] : $per_page;
       ?>
        Display:<select name="spo" style="width: 100px;">
            <option<?php selected( $spo, -1 ); ?> value="-1"><?php _e('All Results'); ?></option>
            <?php for( $i=10;$i<=100;$i+=10 ) : ?>
            <option<?php selected( $spo, $i ); ?> value="<?php echo $i ?>"><?php echo $i; ?> <?php _e('Results'); ?></option>
            <?php endfor; ?>        
        </select>
       <?php
       } 



    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // ACTUAL AJAX REQUEST FOR SORTING PAGES 
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////

    add_action( 'wp_ajax_simple_page_ordering', 'reorder_do_page_ordering' );
    function reorder_do_page_ordering() {

        // RECHECK PERMISSIONS
        if ( !current_user_can('edit_others_pages') || !isset($_POST['id']) || empty($_POST['id']) || ( !isset($_POST['previd']) && !isset($_POST['nextid']) ) )
            die(-1);

        // IS IT A REAL POST?
        if ( !$post = get_post( $_POST['id'] ) )
            die(-1);
        $previd = isset($_POST['previd']) ? $_POST['previd'] : false;
        $nextid = isset($_POST['nextid']) ? $_POST['nextid'] : false;
        if ( $previd ) {

            // FETCH ALL THE SIBLINGS (RELATIVE ORDERING)
            $siblings = get_posts(array( 'depth' => 1, 'numberposts' => -1, 'post_type' => $post->post_type, 'post_status' => 'publish,pending,draft,future,private', 'post_parent' => $post->post_parent, 'orderby' => 'menu_order', 'order' => 'ASC', 'exclude' => $post->ID ));
            foreach( $siblings as $sibling ) :

                // BEGIN UPDATING MENU ORDERS
                if ( $sibling->ID == $previd ) {
                    $menu_order = $sibling->menu_order + 1;
                    // UPDATE THE ACTUAL MOVED POST TO 1 AFTER PREV
                    wp_update_post(array( 'ID' => $post->ID, 'menu_order' => $menu_order ));
                    continue;
                }

                // NOTHING LEFT TO DO - NUMBERS CORRECTLY PADDED
                if ( isset($menu_order) && $menu_order < $sibling->menu_order )
                    break; 

                // NEED TO UPDATE THE SIBLINGS MENU ORDER AS WELL
                if ( isset($menu_order) ) {
                    $menu_order++;
                    // UPDATE THE ACTUAL MOVED POST TO 1 AFTER PREV
                    wp_update_post(array( 'ID' => $sibling->ID, 'menu_order' => $menu_order )); 
                }       
            endforeach;
        }

        if ( !isset($menu_order) && $nextid ) {

            // FETCH ALL THE SIBLINGS (RELATIVE ORDERING)
            $siblings = get_posts(array( 'depth' => 1, 'numberposts' => -1, 'post_type' => $post->post_type, 'post_status' => 'publish,pending,draft,future,private', 'post_parent' => $post->post_parent, 'orderby' => 'menu_order', 'order' => 'DESC', 'exclude' => $post->ID ));
            foreach( $siblings as $sibling ) :

                // START UPDATING MENU ORDERS
                if ( $sibling->ID == $nextid ) {
                    $menu_order = $sibling->menu_order - 1;
                    // UPDATE THE ACTUAL MOVED POST TO 1 AFTER PREV
                    wp_update_post(array( 'ID' => $post->ID, 'menu_order' => $menu_order ));
                    continue;
                }

                // NOTHING LEFT TO DO - NUMBER ALREADY PADDED
                if ( isset($menu_order) && $menu_order > $sibling->menu_order )
                    break; 

                // NEED TO UPDATE THE SIBLING MENU ORDER
                if ( isset($menu_order) ) {
                    $menu_order--;
                    // UPDATE THE ACTUAL MOVED POST TO 1 AFTER PREV
                    wp_update_post(array( 'ID' => $sibling->ID, 'menu_order' => $menu_order )); 
                }       
            endforeach;
        }

        // FETCH ALL THE SIBLINGS WITH RELATIVE ORDERING AND IF THE MOVED POST HAS CHILDREN REFRESH THE PAGE
        $children = get_posts(array( 'depth' => 1, 'numberposts' => 1, 'post_type' => $post->post_type, 'post_status' => 'publish,pending,draft,future,private', 'post_parent' => $post->ID ));
        if ( !empty($children) )
            die('children');
        die();
    }

2-й шаг Поскольку мы в приведенном ниже сценарии вызываем пользовательский файл js, нам необходимо создать и включить этот файл. Если вы не хотите изменять приведенный выше сценарий, перейдите и создайте папку в корневом каталоге папки вашей темы и назовите ее "пользовательской". Затем в этой папке создайте другую папку под названием "js". Затем создайте новый файл в этой папке под названием"page-resorting.js "и вставьте следующий код в этот файл. После того, как это будет сделано, вы должны быть возможность изменить порядок с помощью перетаскивания всех страниц в области администрирования.

/////////////////////////////////////////////////////////////////////////////////////////////////////
// THIS SCRIPT APPLIES TO THE CUSTOM SCRIPT MODIFICATION ALLOWING HIERARCHICAL PAGES TO BE REORDERED
/////////////////////////////////////////////////////////////////////////////////////////////////////


jQuery("table.widefat tbody").sortable({  
    cursor: 'move',
    axis: 'y',
    containment: 'table.widefat',
    scrollSensitivity: 40,
    helper: function(e, ui) {                   
        ui.children().each(function() { jQuery(this).width(jQuery(this).width()); });
        return ui;
    },
    start: function(event, ui) {
        if ( ! ui.item.hasClass('alternate') ) ui.item.css( 'background-color', '#ffffff' );
        ui.item.children('td, th').css('border','none');
        ui.item.css( 'outline', '1px solid #dfdfdf' );
    },
    stop: function(event, ui) {     
        ui.item.removeAttr('style');
        ui.item.children('td, th').removeAttr('style');
    },
    update: function(event, ui) {   
        if ( ui.item.hasClass('inline-editor') ) {
            jQuery("table.widefat tbody").sortable('cancel');
            alert( 'Please close the quick editor before reordering this item.' );
            return;
        }

        var postid = ui.item.find('.check-column input').val(); // THIS POST ID
        var postparent = ui.item.find('.post_parent').html();   // POST PARENT

        var prevpostid = ui.item.prev().find('.check-column input').val();
        var nextpostid = ui.item.next().find('.check-column input').val();

        // can only sort in same tree

        var prevpostparent = undefined;
        if ( prevpostid != undefined ) {
            var prevpostparent = ui.item.prev().find('.post_parent').html()
            if ( prevpostparent != postparent) prevpostid = undefined;
        }

        var nextpostparent = undefined;
        if ( nextpostid != undefined ) {
            nextpostparent = ui.item.next().find('.post_parent').html();
            if ( nextpostparent != postparent) nextpostid = undefined;
        }   

        // DISPLAY AN ALERT MESSAGE IF ANY OF THE FOLLOWING TAKES PLACE
        // IF PREVIOUS AND NEXT ARE NOT AT THE SAME TREE LEVEL OR NOT AT THE SAME TREE LEVEL AND THE PREVIOUS PAGE IS THE PARENT OF THE NEXT OR JUST MOVED BENEATH ITS OWN CHILDREN                     
        if ( ( prevpostid == undefined && nextpostid == undefined ) || ( nextpostid == undefined && nextpostparent == prevpostid ) || ( nextpostid != undefined && prevpostparent == postid ) ) {
            jQuery("table.widefat tbody").sortable('cancel');
            alert( "SORRY, THIS ACTION IS NOT POSSIBLE!\n\n>>> WHY THIS DOES NOT WORK:\nDrag-and-Drop capabilities only work for items within their current tree.\n\n>>> HERE IS HOW YOU CAN MOVE IT:\nIn order to move this item to the location you specified you simply need to use the \"Quick Edit\" link and modify the associated \"Parent\" page.\n\n>>> LOCATING THE QUICK EDIT LINK:\nOn the post you want to move, just hover over the post title and click on the \"Quick Edit\" link which appears below the title." );
            return;
        }

        // SHOW AJAX SPINNING SAVE ELEMENT
        ui.item.find('.check-column input').hide().after('<img alt="processing" src="images/wpspin_light.gif" class="waiting" style="margin-left: 6px;" />');

        // EXECUTE THE SORTING VIA AJAX
        jQuery.post( ajaxurl, { action: 'simple_page_ordering', id: postid, previd: prevpostid, nextid: nextpostid }, function(response){           
            if ( response == 'children' ) window.location.reload();
            else ui.item.find('.check-column input').show().siblings('img').remove();
        });

        // FIX CELL COLORS
        jQuery( 'table.widefat tbody tr' ).each(function(){
            var i = jQuery('table.widefat tbody tr').index(this);
            if ( i%2 == 0 ) jQuery(this).addClass('alternate');
            else jQuery(this).removeClass('alternate');
        });
    }
}).disableSelection();
Author: NetConstructor.com, 2010-10-11

1 answers

На самом деле это не ответ, но, возможно, ответ кроется где-то в этом. Я распаковал сценарий navmenu js и удалил то, что казалось кодом, который делает возможным перетаскивание сортируемых вложенных элементов навигации. Это не очень красиво.

Я не думаю, что перетаскиваемые/сортируемые модули пользовательского интерфейса jQuery поддерживают вложенные элементы, и это, скорее всего, то, что заставит или сломает вас, если вы заставите его работать таким образом. Сценарий navmenu имеет довольно глубокий и трудный для расшифровки набор объекты и функции, которые используются для вычисления внутренней и внешней ширины или глубины. Я бы попытался разобраться в этом, но у меня есть своя проблема с моими вложениями в посты ajax, загружающими динамический плагин metabox и все еще заставляющими его работать правильно.

Возможно, взглянув на это, вы получите некоторое представление о том, что вам нужно сделать в своем коде, если вы даже можете использовать jqUI.

Вот весь nav-menu.js файл распакован, https://gist.github.com/820633 Есть кусочки возможно, вам придется посмотреть, что связывает эти вещи воедино, чтобы понять их смысл, который я не включил.

depthToPx: function (c) {
    return c * a.options.menuItemDepthPerLevel
},
pxToDepth: function (c) {
    return Math.floor(c / a.options.menuItemDepthPerLevel)
}

menuItemDepth: function () {
    var c = a.isRTL ? this.eq(0).css("margin-right") : this.eq(0).css("margin-left");
    return a.pxToDepth(c && -1 != c.indexOf("px") ? c.slice(0, -2) : 0)
},
updateDepthClass: function (d, c) {
    return this.each(function () {
        var e = b(this);
        c = c || e.menuItemDepth();
        b(this).removeClass("menu-item-depth-" + c).addClass("menu-item-depth-" + d)
    })
},
shiftDepthClass: function (c) {
    return this.each(function () {
        var d = b(this),
            e = d.menuItemDepth();
        b(this).removeClass("menu-item-depth-" + e).addClass("menu-item-depth-" + (e + c))
    })
},
childMenuItems: function () {
    var c = b();
    this.each(function () {
        var d = b(this),
            f = d.menuItemDepth(),
            e = d.next();
        while (e.length && e.menuItemDepth() > f) {
            c = c.add(e);
            e = e.next()
        }
    });
    return c
},
updateParentMenuItemDBId: function () {
    return this.each(function () {
        var e = b(this),
            c = e.find(".menu-item-data-parent-id"),
            f = e.menuItemDepth(),
            d = e.prev();
        if (f == 0) {
            c.val(0)
        } else {
            while (!d[0] || !d[0].className || -1 == d[0].className.indexOf("menu-item") || (d.menuItemDepth() != f - 1)) {
                d = d.prev()
            }
            c.val(d.find(".menu-item-data-db-id").val())
        }
    })
}

Это метод инициализации sortables, который делает некоторые сумасшедшие алгебраические вещи, похожие на E=Mc2, lol

initSortables: function () {
    var p = 0,
        e, t, d, l, o, f, c, i, s, m = a.menuList.offset().left,
        h = b("body"),
        q, n = r();
    m += a.isRTL ? a.menuList.width() : 0;
    a.menuList.sortable({
        handle: ".menu-item-handle",
        placeholder: "sortable-placeholder",
        start: function (A, z) {
            var u, x, w, v, y;
            if (a.isRTL) {
                z.item[0].style.right = "auto"
            }
            s = z.item.children(".menu-item-transport");
            e = z.item.menuItemDepth();
            j(z, e);
            w = (z.item.next()[0] == z.placeholder[0]) ? z.item.next() : z.item;
            v = w.childMenuItems();
            s.append(v);
            u = s.outerHeight();
            u += (u > 0) ? (z.placeholder.css("margin-top").slice(0, -2) * 1) : 0;
            u += z.helper.outerHeight();
            i = u;
            u -= 2;
            z.placeholder.height(u);
            q = e;
            v.each(function () {
                var B = b(this).menuItemDepth();
                q = (B > q) ? B : q
            });
            x = z.helper.find(".menu-item-handle").outerWidth();
            x += a.depthToPx(q - e);
            x -= 2;
            z.placeholder.width(x);
            y = z.placeholder.next();
            y.css("margin-top", i + "px");
            z.placeholder.detach();
            b(this).sortable("refresh");
            z.item.after(z.placeholder);
            y.css("margin-top", 0);
            k(z)
        },
        stop: function (x, w) {
            var v, u = p - e;
            v = s.children().insertAfter(w.item);
            if (u != 0) {
                w.item.updateDepthClass(p);
                v.shiftDepthClass(u);
                g(u)
            }
            a.registerChange();
            w.item.updateParentMenuItemDBId();
            w.item[0].style.top = 0;
            if (a.isRTL) {
                w.item[0].style.left = "auto";
                w.item[0].style.right = 0
            }
            a.refreshMenuTabs(true)
        },
        change: function (v, u) {
            if (!u.placeholder.parent().hasClass("menu")) {
                (l.length) ? l.after(u.placeholder) : a.menuList.prepend(u.placeholder)
            }
            k(u)
        },
        sort: function (w, v) {
            var y = v.helper.offset(),
                u = a.isRTL ? y.left + v.helper.width() : y.left,
                x = a.negateIfRTL * a.pxToDepth(u - m);
            if (x > d || y.top < f) {
                x = d
            } else {
                if (x < t) {
                    x = t
                }
            }
            if (x != p) {
                j(v, x)
            }
            if (c && y.top + i > c) {
                o.after(v.placeholder);
                k(v);
                b(this).sortable("refreshPositions")
            }
        }
    });

    function k(u) {
        var v;
        l = u.placeholder.prev();
        o = u.placeholder.next();
        if (l[0] == u.item[0]) {
            l = l.prev()
        }
        if (o[0] == u.item[0]) {
            o = o.next()
        }
        f = (l.length) ? l.offset().top + l.height() : 0;
        c = (o.length) ? o.offset().top + o.height() / 3 : 0;
        t = (o.length) ? o.menuItemDepth() : 0;
        if (l.length) {
            d = ((v = l.menuItemDepth() + 1) > a.options.globalMaxDepth) ? a.options.globalMaxDepth : v
        } else {
            d = 0
        }
    }
    function j(u, v) {
        u.placeholder.updateDepthClass(v, p);
        p = v
    }
    function r() {
        if (!h[0].className) {
            return 0
        }
        var u = h[0].className.match(/menu-max-depth-(\d+)/);
        return u && u[1] ? parseInt(u[1]) : 0
    }
    function g(u) {
        var v, w = n;
        if (u === 0) {
            return
        } else {
            if (u > 0) {
                v = q + u;
                if (v > n) {
                    w = v
                }
            } else {
                if (u < 0 && q == n) {
                    while (!b(".menu-item-depth-" + w, a.menuList).length && w > 0) {
                        w--
                    }
                }
            }
        }
        h.removeClass("menu-max-depth-" + n).addClass("menu-max-depth-" + w);
        n = w
    }
}

Было бы неплохо, если бы у них была версия этого файла для разработчиков, как у других сценариев в WP. Уменьшенные однобуквенные переменные - это слишком много для меня прямо сейчас. Это просто алфавитный суп.

 2
Author: jaredwilli, 2011-02-10 14:51:27