Add media dialog pagination
authorMatthias Schmidt <gravatronics@live.com>
Fri, 31 Mar 2017 17:24:41 +0000 (19:24 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Fri, 31 Mar 2017 17:24:49 +0000 (19:24 +0200)
See #2224

com.woltlab.wcf/templates/mediaManager.tpl
wcfsetup/install/files/acp/templates/mediaManager.tpl
wcfsetup/install/files/js/WoltLabSuite/Core/Media/Manager/Base.js
wcfsetup/install/files/js/WoltLabSuite/Core/Media/Manager/Search.js
wcfsetup/install/files/js/WoltLabSuite/Core/Media/Upload.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Pagination.js
wcfsetup/install/files/lib/data/media/MediaAction.class.php

index 4850e5571d460c97b91dd1d432b5ccfc7f881fce..2dc7d4828ca242601524d949a99ba58fbfc07ea4 100644 (file)
@@ -41,3 +41,5 @@
                {include file='mediaListItems'}
        </ul>
 </div>
+
+<div class="paginationBottom jsPagination"></div>
index 4850e5571d460c97b91dd1d432b5ccfc7f881fce..2dc7d4828ca242601524d949a99ba58fbfc07ea4 100644 (file)
@@ -41,3 +41,5 @@
                {include file='mediaListItems'}
        </ul>
 </div>
+
+<div class="paginationBottom jsPagination"></div>
index ae07ecd1645e1f7c441fed0ee1783895eb660962..c0f95d6b19b6e13ba57e1e4176e415d39fce3d96 100644 (file)
@@ -11,13 +11,15 @@ define(
                'Core',                     'Dictionary',               'Dom/ChangeListener',              'Dom/Traverse',
                'Dom/Util',                 'EventHandler',             'Language',                        'List',
                'Permission',               'Ui/Dialog',                'Ui/Notification',                 'WoltLabSuite/Core/Controller/Clipboard',
-               'WoltLabSuite/Core/Media/Editor', 'WoltLabSuite/Core/Media/Upload', 'WoltLabSuite/Core/Media/Manager/Search', 'StringUtil'
+               'WoltLabSuite/Core/Media/Editor', 'WoltLabSuite/Core/Media/Upload', 'WoltLabSuite/Core/Media/Manager/Search', 'StringUtil',
+               'WoltLabSuite/Core/Ui/Pagination'
        ],
        function(
                Core,                        Dictionary,                 DomChangeListener,                 DomTraverse,
                DomUtil,                     EventHandler,               Language,                          List,
                Permission,                  UiDialog,                   UiNotification,                    Clipboard,
-               MediaEditor,                 MediaUpload,                MediaManagerSearch,                StringUtil
+               MediaEditor,                 MediaUpload,                MediaManagerSearch,                StringUtil,
+               UiPagination
        )
 {
        "use strict";
@@ -37,12 +39,12 @@ define(
                this._id = 'mediaManager' + _mediaManagerCounter++;
                this._listItems = new Dictionary();
                this._media = new Dictionary();
-               this._mediaCache = null;
                this._mediaManagerMediaList = null;
                this._search = null;
                this._upload = null;
                this._forceClipboard = false;
                this._hadInitiallyMarkedItems = false;
+               this._pagination = null;
                
                if (Permission.get('admin.content.cms.canManageMedia')) {
                        this._mediaEditor = new MediaEditor(this);
@@ -131,6 +133,8 @@ define(
                                }
                        }
                        
+                       this._initPagination(~~data.returnValues.pageCount);
+                       
                        this._hadInitiallyMarkedItems = data.returnValues.hasMarkedItems;
                },
                
@@ -191,7 +195,7 @@ define(
                                        
                                        var deleteAction = new WCF.Action.Delete('wcf\\data\\media\\MediaAction', '.mediaFile');
                                        deleteAction._didTriggerEffect = function(element) {
-                                               this.removeMedia(elData(element[0], 'object-id'), true);
+                                               this.removeMedia(elData(element[0], 'object-id'));
                                        }.bind(this);
                                }
                                
@@ -278,6 +282,31 @@ define(
                        }
                },
                
+               /**
+                * Initializes the dialog pagination.
+                *
+                * @param       {integer}       pageCount
+                * @param       {integer}       pageNo
+                */
+               _initPagination: function(pageCount, pageNo) {
+                       if (pageNo === undefined) pageNo = 1;
+                       
+                       if (pageCount > 1) {
+                               var newPagination = elCreate('div');
+                               newPagination.className = 'paginationBottom jsPagination';
+                               DomUtil.replaceElement(elBySel('.jsPagination', UiDialog.getDialog(this).content), newPagination);
+                               
+                               this._pagination = new UiPagination(newPagination, {
+                                       activePage: pageNo,
+                                       callbackSwitch: this._search.search.bind(this._search),
+                                       maxPage: pageCount
+                               });
+                       }
+                       else if (this._pagination) {
+                               elHide(this._pagination.getElement());
+                       }
+               },
+               
                /**
                 * Removes all media clipboard checkboxes.
                 */
@@ -407,9 +436,8 @@ define(
                 * Removes a media file.
                 *
                 * @param       {int}                   mediaId         id of the removed media file
-                * @param       {boolean|undefined}     checkCache      media file will also be removed from the local cache if true
                 */
-               removeMedia: function(mediaId, checkCache) {
+               removeMedia: function(mediaId) {
                        if (this._listItems.has(mediaId)) {
                                // remove list item
                                try {
@@ -422,26 +450,14 @@ define(
                                this._listItems.delete(mediaId);
                                this._media.delete(mediaId);
                        }
-                       
-                       if (checkCache && this._mediaCache && this._mediaCache.has(mediaId)) {
-                               this._mediaCache.delete(mediaId);
-                       }
                },
                
                /**
                 * Changes the displayed media to the previously displayed media.
                 */
                resetMedia: function() {
-                       if (this._mediaCache !== null) {
-                               this._setMedia(this._mediaCache);
-                               
-                               this._mediaCache = null;
-                               
-                               this._search.resetSearch();
-                               if (this._mediaCategorySelect) {
-                                       this._mediaCategorySelect.value = 0;
-                               }
-                       }
+                       // calling WoltLabSuite/Core/Media/Manager/Search.search() reloads the first page of the dialog
+                       this._search.search();
                },
                
                /**
@@ -449,12 +465,9 @@ define(
                 * 
                 * @param       {object}        media           media data
                 * @param       {string}        template        
+                * @param       {object}        additionalData
                 */
-               setMedia: function(media, template) {
-                       if (!this._mediaCache) {
-                               this._mediaCache = this._media;
-                       }
-                       
+               setMedia: function(media, template, additionalData) {
                        var hasMedia = false;
                        for (var mediaId in media) {
                                if (objOwns(media, mediaId)) {
@@ -478,6 +491,8 @@ define(
                                }
                        }
                        
+                       this._initPagination(additionalData.pageCount, additionalData.pageNo);
+                       
                        this._setMedia(media);
                },
                
index 94bfd6351138c764882204b38348faef82ac18ed..39b4ad5b261778f44df146c6c44ed44f2d995c6e 100644 (file)
@@ -45,7 +45,12 @@ define(['Ajax', 'Core', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language', 'Ui/
                 * @param       {object}        data    response data
                 */
                _ajaxSuccess: function(data) {
-                       this._mediaManager.setMedia(data.returnValues.media || { }, data.returnValues.template || '');
+                       this._mediaManager.setMedia(data.returnValues.media || { }, data.returnValues.template || '', {
+                               pageCount: data.returnValues.pageCount || 0,
+                               pageNo: data.returnValues.pageNo || 0
+                       });
+                       
+                       elByClass('dialogContent', this._mediaManager.getDialog())[0].scrollTop = 0;
                },
                
                /**
@@ -55,8 +60,8 @@ define(['Ajax', 'Core', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language', 'Ui/
                        if (this._searchMode) {
                                this._searchMode = false;
                                
-                               this._mediaManager.resetMedia();
                                this.resetSearch();
+                               this._mediaManager.resetMedia();
                        }
                },
                
@@ -130,8 +135,14 @@ define(['Ajax', 'Core', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language', 'Ui/
                
                /**
                 * Sends an AJAX request to fetch search results.
+                * 
+                * @param       {integer}       pageNo
                 */
-               search: function() {
+               search: function(pageNo) {
+                       if (typeof pageNo !== "number") {
+                               pageNo = 1;
+                       }
+                       
                        var searchString = this._input.value;
                        if (searchString && this._input.value.length < this._mediaManager.getOption('minSearchLength')) {
                                this._showStringThresholdError();
@@ -149,6 +160,7 @@ define(['Ajax', 'Core', 'Dom/Traverse', 'Dom/Util', 'EventKey', 'Language', 'Ui/
                                        categoryID: this._mediaManager.getCategoryId(),
                                        imagesOnly: this._mediaManager.getOption('imagesOnly'),
                                        mode: this._mediaManager.getMode(),
+                                       pageNo: pageNo,
                                        searchString: searchString
                                }
                        });
index 10b24159f4eb7a7f29a673fdf4da00c1a9db063b..78052d13eabcf20a78229f10cd3174a5a97e5b73 100644 (file)
@@ -87,10 +87,9 @@ define(
                                        imagesOnly: this._mediaManager.getOption('imagesOnly')
                                };
                                
-                               if (this._categoryId) {
-                                       parameters.categoryID = this._categoryId;
-                                       
-                                       this._categoryId = null;
+                               var categoryId = this._mediaManager.getCategoryId();
+                               if (categoryId) {
+                                       parameters.categoryID = categoryId;
                                }
                                
                                return Core.extend(MediaUpload._super.prototype._getParameters.call(this), parameters);
@@ -178,12 +177,6 @@ define(
                 * @see WoltLabSuite/Core/Upload#_uploadFiles
                 */
                _uploadFiles: function(files, blob) {
-                       // reset media (search) before uploading
-                       if (this._mediaManager) {
-                               this._categoryId = this._mediaManager.getCategoryId();
-                               this._mediaManager.resetMedia();
-                       }
-                       
                        return MediaUpload._super.prototype._uploadFiles.call(this, files, blob);
                }
        });
index acea0fa498c67740a2faac0d1c8b647216dcff31..43e943d7ebfc581b887c7cb537c790a555ad9070 100644 (file)
@@ -203,6 +203,33 @@ define(['Core', 'Language', 'ObjectMap', 'StringUtil', 'WoltLabSuite/Core/Ui/Pag
                        return listItem;
                },
                
+               /**
+                * Returns the active page.
+                *
+                * @return      {integer}
+                */
+               getActivePage: function() {
+                       return this._options.activePage;
+               },
+               
+               /**
+                * Returns the pagination Ui element.
+                * 
+                * @return      {HTMLElement}
+                */
+               getElement: function() {
+                       return this._element;
+               },
+               
+               /**
+                * Returns the maximum page.
+                * 
+                * @return      {integer}
+                */
+               getMaxPage: function() {
+                       return this._options.maxPage;
+               },
+               
                /**
                 * Switches to given page number.
                 * 
index 46157d5451f2a867a73f41ca56aee87983466979..c1be63eab5388714e265b65cbbc7e8ad18056ff2 100644 (file)
@@ -33,6 +33,11 @@ use wcf\util\FileUtil;
  * @method     MediaEditor     getSingleObject()
  */
 class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction, IUploadAction {
+       /**
+        * number of media files per media manager dialog page
+        */
+       const ITEMS_PER_MANAGER_DIALOG_PAGE = 50;
+       
        /**
         * @inheritDoc
         */
@@ -188,8 +193,8 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                if ($this->parameters['imagesOnly']) {
                        $mediaList->getConditionBuilder()->add('media.isImage = ?', [1]);
                }
-               $mediaList->sqlOrderBy = 'media.uploadTime DESC';
-               $mediaList->sqlLimit = 50;
+               $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
+               $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
                $mediaList->readObjects();
                
                $categoryList = (new CategoryNodeTree('com.woltlab.wcf.media.category'))->getIterator();
@@ -198,6 +203,7 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                return [
                        'hasMarkedItems' => ClipboardHandler::getInstance()->hasMarkedItems(ClipboardHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.media')),
                        'media' => $this->getI18nMediaData($mediaList),
+                       'pageCount' => ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
                        'template' => WCF::getTPL()->fetch('mediaManager', 'wcf', [
                                'categoryList' => $categoryList,
                                'mediaList' => $mediaList,
@@ -440,6 +446,9 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                if ($this->parameters['mode'] != 'editor' && $this->parameters['mode'] != 'select') {
                        throw new UserInputException('mode');
                }
+               
+               $this->readInteger('pageNo', true);
+               if (!$this->parameters['pageNo']) $this->parameters['pageNo'] = 1;
        }
        
        /**
@@ -454,11 +463,22 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                if ($this->parameters['categoryID']) {
                        $mediaList->getConditionBuilder()->add('media.categoryID = ?', [$this->parameters['categoryID']]);
                }
-               $mediaList->sqlOrderBy = 'media.uploadTime DESC';
-               $mediaList->sqlLimit = 50;
+               $mediaList->sqlOrderBy = 'media.uploadTime DESC, media.mediaID DESC';
+               $mediaList->sqlLimit = static::ITEMS_PER_MANAGER_DIALOG_PAGE;
+               $mediaList->sqlOffset = ($this->parameters['pageNo'] - 1) * static::ITEMS_PER_MANAGER_DIALOG_PAGE;
                $mediaList->readObjectIDs();
                
                if (empty($mediaList->getObjectIDs())) {
+                       // check if page is requested that might have existed but does not exist anymore due to deleted
+                       // media files
+                       if ($this->parameters['pageNo'] > 1 && $this->parameters['searchString'] === '' && !$this->parameters['categoryID']) {
+                               // request media dialog page with highest page number 
+                               $parameters = $this->parameters;
+                               $parameters['pageNo'] = ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE);
+                               
+                               return (new MediaAction($this->objects, 'getSearchResultList', $parameters))->executeAction()['returnValues'];
+                       }
+                       
                        return [
                                'template' => WCF::getLanguage()->getDynamicVariable('wcf.media.search.noResults')
                        ];
@@ -470,6 +490,8 @@ class MediaAction extends AbstractDatabaseObjectAction implements ISearchAction,
                
                return [
                        'media' => $this->getI18nMediaData($viewableMediaList),
+                       'pageCount' => ceil($mediaList->countObjects() / static::ITEMS_PER_MANAGER_DIALOG_PAGE),
+                       'pageNo' => $this->parameters['pageNo'],
                        'template' => WCF::getTPL()->fetch('mediaListItems', 'wcf', [
                                'mediaList' => $viewableMediaList,
                                'mode' => $this->parameters['mode']