Просмотр исходного кода

#3068 Initial commit, pre-alpha version.

Alexander 6 лет назад
Родитель
Сommit
294273f4cf

+ 27 - 2
modules/boonex/files/classes/BxFilesConfig.php

@@ -30,6 +30,7 @@ class BxFilesConfig extends BxBaseModTextConfig
             'TABLE_ENTRIES' => $aModule['db_prefix'] . 'main',
             'TABLE_ENTRIES_FULLTEXT' => 'search_fields',
             'TABLE_FILES' => $aModule['db_prefix'] . 'files',
+            'TABLE_BOOKMARKS' => $aModule['db_prefix'] . 'bookmarks',
 
             // database fields
             'FIELD_ID' => 'id',
@@ -52,6 +53,9 @@ class BxFilesConfig extends BxBaseModTextConfig
             'FIELD_LOCATION_PREFIX' => 'location',
             'FIELD_LABELS' => 'labels',
             'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
+            'FIELD_BOOKMARKS_ID' => 'object_id',
+            'FIELD_BOOKMARKS_PROFILE' => 'profile_id',
+            'FIELD_MIME_TYPE' => 'mime_type',
 			
             // some params
             'PARAM_PER_PAGE_FOR_FAVORITES_LISTS' => 'bx_files_per_page_for_favorites_lists',
@@ -77,6 +81,7 @@ class BxFilesConfig extends BxBaseModTextConfig
             'PARAM_CHARS_SUMMARY_PLAIN' => '',
             'PARAM_NUM_RSS' => 'bx_files_rss_num',
             'PARAM_SEARCHABLE_FIELDS' => 'bx_files_searchable_fields',
+            'PARAM_LINK_TO_PREVIEW' => 'bx_files_show_link_to_preview',
 
             // objects
             'OBJECT_STORAGE' => 'bx_files_files',
@@ -146,11 +151,13 @@ class BxFilesConfig extends BxBaseModTextConfig
         );
 
         $this->_aJsClasses = array(
-        	'manage_tools' => 'BxFilesManageTools'
+        	'manage_tools' => 'BxFilesManageTools',
+            'toolbar_tools' => 'BxFilesBrowserTools',
         );
 
         $this->_aJsObjects = array(
-        	'manage_tools' => 'oBxFilesManageTools'
+        	'manage_tools' => 'oBxFilesManageTools',
+            'toolbar_tools' => 'oBxFilesBrowserTools',
         );
 
         $this->_aGridObjects = array(
@@ -159,6 +166,24 @@ class BxFilesConfig extends BxBaseModTextConfig
         	
         );
     }
+
+    public function getJsClass($mixedType)
+    {
+        if (is_array($mixedType)) {
+            return parent::getJsClass($mixedType['type']);
+        } else {
+            return parent::getJsClass($mixedType);
+        }
+    }
+
+    public function getJsObject($mixedType)
+    {
+        if (is_array($mixedType)) {
+            return parent::getJsObject($mixedType['type']).$mixedType['uniq'];
+        } else {
+            return parent::getJsObject($mixedType);
+        }
+    }
 }
 
 /** @} */

+ 40 - 0
modules/boonex/files/classes/BxFilesDb.php

@@ -34,6 +34,46 @@ class BxFilesDb extends BxBaseModFilesDb
         $sQuery = $this->prepare ("SELECT * FROM `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` WHERE `data_processed` = 0 ORDER BY `added` ASC LIMIT ?", $iLimit);
         return $this->getAll($sQuery);
     }
+
+    public function bookmarkFile($iContentId, $iProfileId) {
+        if ($this->isFileBookmarked($iContentId, $iProfileId)) {
+            $this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `object_id` = :id AND `profile_id` = :profile_id", [
+                'id' => $iContentId,
+                'profile_id' => $iProfileId
+            ]);
+        } else {
+            $this->query("INSERT INTO `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` (`object_id`, `profile_id`) VALUES (:id, :profile_id)", [
+                'id' => $iContentId,
+                'profile_id' => $iProfileId
+            ]);
+        }
+    }
+
+    public function deleteFileBookmarks($iContentId) {
+        $this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `object_id` = :id", [
+            'id' => $iContentId,
+        ]);
+    }
+
+    public function deleteProfileBookmarks($iProfileId) {
+        $this->query("DELETE FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `profile_id` = :profile_id", [
+            'profile_id' => $iProfileId,
+        ]);
+    }
+
+    public function isFileBookmarked($iContentId, $iProfileId) {
+        return $this->getOne("SELECT COUNT(*) FROM `" . $this->_oConfig->CNF['TABLE_BOOKMARKS'] . "` WHERE `object_id` = :id AND `profile_id` = :profile_id", [
+            'id' => $iContentId,
+            'profile_id' => $iProfileId
+        ]);
+    }
+
+    public function updateEntryTitle($iContentId, $sNewTitle) {
+        $this->query("UPDATE `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` SET `" . $this->_oConfig->CNF['FIELD_TITLE'] . "` = :title WHERE `" . $this->_oConfig->CNF['FIELD_ID'] . "` = :id", [
+            'id' => $iContentId,
+            'title' => $sNewTitle,
+        ]);
+    }
 }
 
 /** @} */

+ 5 - 0
modules/boonex/files/classes/BxFilesFormEntry.php

@@ -25,6 +25,11 @@ class BxFilesFormEntry extends BxBaseModFilesFormEntry
         parent::_associalFileWithContent($oStorage, $iFileId, $iProfileId, $iContentId, $sPictureField);
         $this->_oModule->_oDb->updateFileId($iContentId, $iFileId);
     }
+
+    public function delete ($iContentId, $aContentInfo = array()) {
+        $this->_oModule->_oDb->deleteFileBookmarks($iContentId);
+        return parent::delete ($iContentId, $aContentInfo);
+    }
 }
 
 /** @} */

+ 7 - 0
modules/boonex/files/classes/BxFilesMenuSnippetMeta.php

@@ -17,6 +17,13 @@ class BxFilesMenuSnippetMeta extends BxBaseModTextMenuSnippetMeta
 
         parent::__construct($aObject, $oTemplate);
     }
+
+    protected function _getMenuItemSize($aItem)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $aFile = $this->_oModule->getContentFile($this->_aContentInfo);
+        return $this->getUnitMetaItemText(_t_format_size($aFile['size']));
+    }
 }
 
 /** @} */

+ 132 - 0
modules/boonex/files/classes/BxFilesMenuViewActions.php

@@ -37,4 +37,136 @@ class BxFilesMenuViewActions extends BxBaseModTextMenuViewActions
     }
 }
 
+class BxFilesMenuViewActionsInline extends BxFilesMenuViewActions {
+    protected $_iMenuItemCounter;
+    protected $_sJsObjectMenuTools;
+    protected $_sBookmarkIcon;
+    protected $_bAllowEditOptions;
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+
+        $this->_bShowTitle = false;
+        $this->_iMenuItemCounter = 0;
+
+        $this->_iMoreAutoItemsStatic = 2;
+        $this->_bMoreAutoItemsStaticOnly = 1;
+
+        $this->_sJsObjectMenuTools = 'oMenuTools' . bx_gen_method_name($this->_sObject);
+
+        $this->addMarkers([
+            'js_object' => $this->_sJsObjectMenuTools,
+        ]);
+
+        $this->_sBookmarkIcon = 'far star';
+    }
+
+    public function setContentId($iContentId)
+    {
+        $iRandNumber = mt_rand();
+
+        $this->_iContentId = $iContentId;
+        $this->_sJsObjectMoreAuto = 'oMenuMoreAuto' . bx_gen_method_name($this->_sObject) . $this->_iContentId . $iRandNumber;
+
+        $sPrefix = str_replace('_', '-', $this->_sObject);
+        $this->_aHtmlIds = [
+            'main' => $sPrefix . $this->_iContentId . $iRandNumber,
+            'more_auto_popup' => $sPrefix . '-ma-popup' . $this->_iContentId . $iRandNumber,
+        ];
+
+        //erase cached data to force it to generate it again since this is going to be a menu for a new entry
+        $this->_oMenuAction = false;
+        $this->_oMenuActions = false;
+        $this->_oMenuActionsMore = false;
+        $this->_oMenuSocialSharing = false;
+        $this->_bMoreAuto = null;
+        unset($this->_aObject['menu_items']);
+
+        parent::setContentId($iContentId);
+    }
+
+    public function setBookmarked($bBookmarked) {
+        $this->_sBookmarkIcon = $bBookmarked ? 'fas star' : 'far star';
+    }
+
+    public function setAllowEditOptions($bAllow) {
+        $this->_bAllowEditOptions = $bAllow;
+    }
+
+    protected function _getMenuItem ($aItem) {
+        //first two items suppose to appear in a row without a title, the rest must go to more-auto along with a title
+        $this->_bShowTitle = $this->_iMenuItemCounter > $this->_iMoreAutoItemsStatic;
+
+        if ($aItem['name'] == 'bookmark')
+            $aItem['icon'] = $this->_sBookmarkIcon;
+
+        $bRes = parent::_getMenuItem ($aItem);
+
+        if ($bRes) $this->_iMenuItemCounter++;
+        return $bRes;
+    }
+
+    public function _getMenuItemBookmark($aItem) {
+        if (!isLogged()) return false;
+        return true;
+    }
+
+    public function _getMenuItemDeleteFileQuick($aItem) {
+        if (!$this->_bAllowEditOptions) return false;
+        return true;
+    }
+
+    public function _getMenuItemMoveTo($aItem) {
+        if (!$this->_bAllowEditOptions) return false;
+        return true;
+    }
+
+    public function _getMenuItemEditTitle($aItem) {
+        if (!$this->_bAllowEditOptions) return false;
+        return true;
+    }
+
+    public function getMenuItems () {
+        //reset the counter here because at the very first call the getMenuItems calls twice (the first is in BxBaseMenuMoreAuto::_isMoreAuto
+        $this->_iMenuItemCounter = 1;
+        return parent::getMenuItems();
+    }
+
+    protected function _getJsCodeMoreAuto()
+    {
+        $sJsObject = $this->_getJsObjectMoreAuto();
+        $aJsParams = array(
+            'sObject' => $this->_sObject,
+            'iItemsStatic' => $this->_iMoreAutoItemsStatic,
+            'bItemsStaticOnly' => $this->_bMoreAutoItemsStaticOnly ? 1 : 0,
+            'aHtmlIds' => $this->_getHtmlIds()
+        );
+
+        return $this->_oTemplate->_wrapInTagJsCode("if(!" . $sJsObject . ") {var " . $sJsObject . " = new BxDolMenuMoreAuto(" . json_encode($aJsParams) . "); " . $sJsObject . ".init();} else {" . $sJsObject . ".init();}");
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs('menu_tools.js');
+    }
+
+    protected function _getTemplateVars() {
+        $aResult = parent::_getTemplateVars();
+
+        $sJsObject = $this->_getJsObjectMenuTools();
+        $aJsParams = [
+            'sActionUrl' => BX_DOL_URL_ROOT.$this->_oTemplate->getModule()->_oConfig->getBaseUri(),
+        ];
+        $aResult['js_code'] .= $this->_oTemplate->_wrapInTagJsCode("if(!" . $sJsObject . ") {var " . $sJsObject . " = new BxFilesMenuTools(" . json_encode($aJsParams) . ");}");
+
+        return $aResult;
+    }
+
+    protected function _getJsObjectMenuTools() {
+        return $this->_sJsObjectMenuTools;
+    }
+}
+
 /** @} */

+ 124 - 0
modules/boonex/files/classes/BxFilesModule.php

@@ -198,6 +198,130 @@ class BxFilesModule extends BxBaseModTextModule
             array('id' => $aFile['id'], 'url' => $sUrl, 'src' => $sPhotoThumb),
         );
     }
+
+    public function actionBookmark($iContentId) {
+        if (!$this->isLogged()) return;
+
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+        if (!$aData) return;
+
+        $this->_oDb->bookmarkFile((int)$iContentId, bx_get_logged_profile_id());
+    }
+
+    public function actionEntryPreview($iContentId) {
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+
+        $sJsCss = '';
+
+        $sPreviewCode = $this->_serviceTemplateFunc ('entryFilePreview', $iContentId);
+
+        // various init scripts could be there, which fail to work properly
+        // because of a fact that it is inside a popup which is at the moment is hidden
+        // so we need a way to postpone that initialization
+        $aScripts = [];
+        $sPreviewCode = preg_replace_callback('/<script>(.*)<\/script>/s', function($aMatch) use (&$aScripts) {
+            $aScripts[] = ['code' => $aMatch[1]];
+            return '';
+        }, $sPreviewCode);
+
+        // if there is a script then most likely it is a text + codemirror initialization, so include it here.
+        if ($aScripts) {
+            $sJsCss .= $this->_oTemplate->addJs('codemirror/codemirror-ext.min.js', true);
+            $sJsCss .= $this->_oTemplate->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'codemirror/|codemirror.css', true);
+        }
+
+        $sJsCss .= $this->_oTemplate->addCss('file_handler.css', true);
+
+        $sPopupCode = $this->_oTemplate->parseHtmlByName('entry-popup.html', [
+            'content' => $sPreviewCode,
+            'bx_repeat:init_scripts' => $aScripts,
+        ]);
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', bx_process_output($aData['title']), $sJsCss.$sPopupCode),
+                'options' => [
+                    'onShow' => 'bx_files_popup_init()',
+                ],
+            ],
+        ]);
+    }
+
+    public function actionEntryInfo($iContentId) {
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', bx_process_output($aData['title']), $this->_oTemplate->entryInfoPopup($aData)),
+            ],
+        ]);
+    }
+
+    public function actionEntryEditTitle($iContentId) {
+        $iContentId = intval($iContentId);
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!$aData) {
+            echoJson(['message' => _t('_bx_files_txt_error')]);
+            exit;
+        }
+
+        if ($aData[$CNF['FIELD_AUTHOR']] != bx_get_logged_profile_id()) {
+            echoJson(['message' => _t('_bx_files_txt_permission_denied')]);
+            exit;
+        }
+
+        if (isset($_POST['title'])) {
+            if ($sNewTitle = bx_get('title')) {
+                $this->_oDb->updateEntryTitle($iContentId, $sNewTitle);
+                $aData = $this->_oDb->getContentInfoById($iContentId);
+                echo bx_process_output($aData[$CNF['FIELD_TITLE']]);
+            }
+            exit;
+        }
+
+        $sActionUrl = BX_DOL_URL_ROOT.$this->_oConfig->getBaseUri();
+        echoJson([
+            'eval' => "bx_prompt('"._t('_bx_files_form_entry_input_title')."', '".bx_js_string($aData[$CNF['FIELD_TITLE']])."', function(val) {                
+                $.post('{$sActionUrl}entry_edit_title/{$iContentId}', {title: val.getValue()}, function(sTitle) {
+                    if (sTitle)
+                        $('.bx-file-entry[file_id=".$aData[$CNF['FIELD_ID']]."] .bx-files-filename a').html(sTitle);
+                });                
+            })",
+        ]);
+    }
+
+    public function actionEntryDelete($iContentId) {
+        $iContentId = intval($iContentId);
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!$aData) {
+            echoJson(['message' => _t('_bx_files_txt_error')]);
+            exit;
+        }
+
+        if ($aData[$CNF['FIELD_AUTHOR']] != bx_get_logged_profile_id()) {
+            echoJson(['message' => _t('_bx_files_txt_permission_denied')]);
+            exit;
+        }
+
+        $this->serviceDeleteEntity($iContentId);
+
+        echoJson([
+            'eval' => "
+                $('.bx-file-entry[file_id={$iContentId}]').fadeOut();
+            ",
+        ]);
+    }
+
+    public function serviceDeleteEntitiesByAuthor ($iProfileId) {
+        $this->_oDb->deleteProfileBookmarks($iProfileId);
+        return parent::serviceDeleteEntitiesByAuthor ($iProfileId);
+    }
 }
 
 /** @} */

+ 249 - 1
modules/boonex/files/classes/BxFilesSearchResult.php

@@ -11,10 +11,49 @@
 
 class BxFilesSearchResult extends BxBaseModTextSearchResult
 {
+    protected $sOrderParamName = 'order';
+    protected $sBookmarksParamName = 'bookmarks';
+    protected $sSortingParamName = 'sorting';
+    protected $sCurrentView;
+    protected $bFileManagerMode = false;
+    protected $iFileManagerUploadTo = 0;
+
     function __construct($sMode = '', $aParams = array())
     {
+        $aParams['unit_views'] = [
+            'extended' => 'unit.html',
+            'gallery' => 'unit_gallery.html',
+            'full' => 'unit_full.html',
+            'showcase' => 'unit_showcase.html',
+            'table' => 'unit_simple_row.html',
+        ];
+
+        //Enble filemanager (toolbar, table files layout, dynamic menus, folders support) only for author/context blocks.
+        if (($sMode == 'author' || $sMode == 'context')) {
+            $this->bFileManagerMode = true;
+            $this->setUnitParams(['show_inline_menu' => true]);
+        }
+
         parent::__construct($sMode, $aParams);
 
+        $this->sCurrentView = array_flip($aParams['unit_views'])[$this->sUnitTemplate];
+
+        $this->aGetParams = array_merge($this->aGetParams, [
+            $this->sOrderParamName,
+            $this->sBookmarksParamName,
+            $this->sSortingParamName,
+            $this->sUnitViewParamName,
+        ]);
+
+
+        $this->aSortingOptions = [
+            'name',
+            'date',
+            'type',
+            'author',
+        ];
+
+
         $this->aCurrent = array(
             'name' => 'bx_files',
             'module_name' => 'bx_files',
@@ -121,8 +160,217 @@ class BxFilesSearchResult extends BxBaseModTextSearchResult
         }
 
         $this->processReplaceableMarkers($oProfileAuthor);
-
         $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+
+        if ($this->bFileManagerMode) {
+            if (bx_get($this->sBookmarksParamName))
+                $this->addConditionsForBookmarks($CNF);
+
+            $this->aCurrent['sorting'] = 'name';
+            if (bx_get($this->sSortingParamName)) $this->aCurrent['sorting'] = bx_get($this->sSortingParamName);
+
+            if ($this->aCurrent['sorting'] == 'type')
+                $this->addConditionsForMimeType($CNF);
+
+            $this->removeContainerClass('bx-def-margin-sec-lefttopright-neg');
+
+            $this->iFileManagerUploadTo = $oProfileAuthor->id();
+        }
+    }
+
+    protected function addConditionsForBookmarks(&$CNF) {
+        if(empty($this->aCurrent['join']) || !is_array($this->aCurrent['join'])) $this->aCurrent['join'] = [];
+
+        $this->aCurrent['join'] = array_merge($this->aCurrent['join'], [
+            'bookmarks' => [
+                'type' => 'INNER',
+                'table' => $CNF['TABLE_BOOKMARKS'],
+                'mainTable' => $CNF['TABLE_ENTRIES'],
+                'mainField' => $CNF['FIELD_ID'],
+                'onField' => $CNF['FIELD_BOOKMARKS_ID'],
+                'joinFields' => array($CNF['FIELD_BOOKMARKS_PROFILE']),
+            ],
+        ]);
+
+        $this->aCurrent['restriction']['bookmarks'] = [
+            'value' => bx_get_logged_profile_id(),
+            'field' => $CNF['FIELD_BOOKMARKS_PROFILE'],
+            'operator' => '=',
+            'table' => $CNF['TABLE_BOOKMARKS'],
+        ];
+    }
+
+    protected function addConditionsForMimeType(&$CNF) {
+        if(empty($this->aCurrent['join']) || !is_array($this->aCurrent['join'])) $this->aCurrent['join'] = [];
+
+        $this->aCurrent['join'] = array_merge($this->aCurrent['join'], [
+            'files' => [
+                'type' => 'INNER',
+                'table' => $CNF['TABLE_FILES'],
+                'mainTable' => $CNF['TABLE_ENTRIES'],
+                'mainField' => $CNF['FIELD_ID'],
+                'onField' => $CNF['FIELD_ID'],
+                'joinFields' => array($CNF['FIELD_MIME_TYPE']),
+            ],
+        ]);
+    }
+
+    public function addCustomParts() {
+        if ($this->_bLiveSearch) return;
+
+        if (!$this->bFileManagerMode) return;
+
+        return $this->showBrowseToolbar([
+            'aRequestParams' => [
+                'unit_view_param' => $this->sUnitViewParamName,
+                'bookmarks_param' => $this->sBookmarksParamName,
+                'sorting_param' => $this->sSortingParamName,
+                'keyword_param' => 'keyword',
+            ],
+
+            'layout' => $this->sCurrentView,
+            'bookmarks' => intval(bx_get($this->sBookmarksParamName)),
+            'sorting' => $this->aCurrent['sorting'],
+            'sorting_options' => $this->aSortingOptions,
+            'keyword' => bx_get('keyword') ? bx_get('keyword') : '',
+        ]);
+    }
+
+    function processing ()
+    {
+        $sCode = parent::processing();
+        if (!$this->aCurrent['paginate']['num'] && bx_is_dynamic_request() && $this->bFileManagerMode) {
+            //to show toolbar in case of empty results
+            $sCode = $this->displaySearchBox($this->addCustomParts().MsgBox(_t('_Empty')));
+        }
+        return $sCode;
+    }
+
+    function showPagination($bAdmin = false, $bChangePage = true, $bPageReload = true)
+    {
+        $aAdditionalParams = [
+            $this->sUnitViewParamName => $this->sCurrentView,
+            $this->sBookmarksParamName => intval(bx_get($this->sBookmarksParamName)),
+            $this->sSortingParamName => $this->aCurrent['sorting'],
+        ];
+        $sPageUrl = $this->getCurrentUrl($aAdditionalParams, false);
+        $sOnClick = $this->getCurrentOnclick($aAdditionalParams, false);
+
+        $oPaginate = new BxTemplPaginate([
+            'page_url' => $sPageUrl,
+            'on_change_page' => $sOnClick,
+            'num' => $this->aCurrent['paginate']['num'],
+            'per_page' => $this->aCurrent['paginate']['perPage'],
+            'start' => $this->aCurrent['paginate']['start'],
+        ]);
+
+        return $sOnClick ? $oPaginate->getSimplePaginate() : $oPaginate->getPaginate();
+    }
+
+    function getAlterOrder() {
+        if (in_array($this->aCurrent['sorting'], $this->aSortingOptions)) {
+            $CNF = &$this->oModule->_oConfig->CNF;
+
+            $aSql = array();
+            switch ($this->aCurrent['sorting']) {
+                case 'name':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+                case 'date':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_ID'].'` DESC';
+                    break;
+                case 'author':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_AUTHOR'].'` ASC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+                case 'type':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_FILES'].'`.`'.$CNF['FIELD_MIME_TYPE'].'` ASC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+            }
+            return $aSql;
+        } else {
+            return parent::getAlterOrder();
+        }
+
+    }
+
+    public function showBrowseToolbar($aParams) {
+        $aSortingOptions = [];
+        if (isset($aParams['sorting_options'])) {
+            foreach ($aParams['sorting_options'] as $sSorting) {
+                $aSortingOptions[$sSorting] = _t('_bx_files_toolbar_sorting_' . $sSorting);
+            }
+        }
+
+        $sUniqueIdent = mt_rand();
+        $aParams['unique_ident'] = $sUniqueIdent;
+
+        $this->oModule->_oTemplate->addJs('toolbar_tools.js');
+
+        $sJsCode = $this->oModule->_oTemplate->getJsCode(['type' => 'toolbar_tools', 'uniq' => $sUniqueIdent], $aParams);
+        $sJsObject = $this->oModule->_oConfig->getJsObject(['type' => 'toolbar_tools', 'uniq' => $sUniqueIdent]);
+
+        $oForm = new BxTemplFormView([], $this->oModule->_oTemplate);
+        $aInputSorting = [
+            'type' => 'select',
+            'name' => 'sorting',
+            'value' => $aParams['sorting'],
+            'values' => $aSortingOptions,
+            'attrs' => [
+                'onchange' => $sJsObject.'.setSorting(this.value);',
+            ],
+        ];
+
+        $aInputFilter = [
+            'type' => 'text',
+            'name' => 'keyword',
+            'value' => $aParams['keyword'],
+            'attrs' => [
+                'placeholder' => _t('_sys_search_placeholder'),
+                'onkeyup' => $sJsObject.'.onChangeFilter(this);',
+                'onpaste' => $sJsObject.'.onChangeFilter(this);',
+            ],
+        ];
+
+        return $this->oModule->_oTemplate->parseHtmlByName('files_browser_toolbar.html', [
+            'js_object' => $sJsObject,
+            'js_code' => $sJsCode,
+            'unique_ident' => $sUniqueIdent,
+            'sorting_dropdown' => $oForm->genRowStandard($aInputSorting),
+            'filter_box' => $oForm->genRowStandard($aInputFilter),
+            'bx_if:upload_visible' => [
+                'condition' => $this->oModule->serviceIsAllowedAddContentToProfile($this->iFileManagerUploadTo),
+                'content' => [],
+            ],
+            'bx_if:bookmarks_visible' => [
+                'condition' => isLogged(),
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'bookmark_enabled' => $aParams['bookmarks'] ? 'fas' : 'far',
+                ],
+            ],
+            'bx_if:table_layout_btn' => [
+                'condition' => $aParams['layout'] == 'table',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unit_view_param' => $aParams['aRequestParams']['unit_view_param'],
+                ],
+            ],
+            'bx_if:gallery_layout_btn' => [
+                'condition' => $aParams['layout'] == 'gallery',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unit_view_param' => $aParams['aRequestParams']['unit_view_param'],
+                ],
+            ],
+            'bx_if:select_all_checkbox' => [
+                'condition' => $aParams['layout'] == 'table',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unique_ident' => $sUniqueIdent,
+                ],
+            ],
+            'bx_repeat:sorting_options' => $aSortingOptions,
+        ]);
     }
 }
 

+ 76 - 2
modules/boonex/files/classes/BxFilesTemplate.php

@@ -53,8 +53,31 @@ class BxFilesTemplate extends BxBaseModTextTemplate
         $aParams['template_name'] = $sTemplateName;
         $aVars = $this->getUnit($aData, $aParams);
         $aVars['icon'] = $oStorage ? $oStorage->getFontIconNameByFileName($aFile['file_name']) : 'far file';
-        if($sTemplateName == 'unit_gallery.html') 
-            $aVars['bx_if:no_thumb']['content']['icon'] = $aVars['icon'];
+
+        $aVars['bx_if:no_thumb']['content']['icon'] = $aVars['icon'];
+
+
+        $aVars['bx_if:inline_menu']['condition'] = isset($aParams['show_inline_menu']) && $aParams['show_inline_menu'];
+        $aVars['bx_if:inline_menu']['content'] = [];
+        if ($aVars['bx_if:inline_menu']['condition']) {
+            bx_import('BxTemplMenu');
+            $oMenu = BxTemplMenu::getObjectInstance('bx_files_view_inline', $this);
+            if ($oMenu) {
+                $oMenu->setContentId($aData[$CNF['FIELD_ID']]);
+                $oMenu->setBookmarked($this->_oDb->isFileBookmarked($aData[$CNF['FIELD_ID']], bx_get_logged_profile_id()));
+                $oMenu->setAllowEditOptions($aData[$CNF['FIELD_AUTHOR']] == bx_get_logged_profile_id());
+                $aVars['bx_if:inline_menu']['content']['menu'] = $oMenu->getCode();
+            }
+        }
+
+        $sAltContentUrlHandler = '';
+        if ($oModule->_oDb->getParam($CNF['PARAM_LINK_TO_PREVIEW'])) {
+            $sAltContentUrlHandler = '$.get(\'' . BX_DOL_URL_ROOT . $oModule->_oConfig->getBaseUri() . 'entry_preview/' . $aData[$CNF['FIELD_ID']] . '\', processJsonData, \'json\'); return false;';
+        }
+        $aVars['alt_content_url_handler'] = $sAltContentUrlHandler;
+        $aVars['bx_if:no_thumb']['content']['alt_content_url_handler'] = $sAltContentUrlHandler;
+        $aVars['bx_if:thumb']['content']['alt_content_url_handler'] = $sAltContentUrlHandler;
+
 
         return $this->parseHtmlByName($sTemplateName, $aVars);
     }
@@ -82,6 +105,57 @@ class BxFilesTemplate extends BxBaseModTextTemplate
         return $this->parseHtmlByName($sTemplateName, $this->getTmplVarsText($aData));
     }
 
+    public function entryInfoPopup($aData) {
+        $CNF = &$this->getModule()->_oConfig->CNF;
+
+        $aFile = $this->getModule()->getContentFile($aData);
+
+        $oAuthor = BxDolProfile::getInstance($aData[$CNF['FIELD_AUTHOR']]);
+        if (!$oAuthor) $oAuthor = BxDolProfileUndefined::getInstance();
+
+        $aForm = [
+            'params' => [
+                'view_mode' => true,
+            ],
+            'inputs' => [
+                'size' => [
+                    'caption' => _t('_bx_files_entry_info_size'),
+                    'type' => 'value',
+                    'value' => _t_format_size($aFile['size']),
+                ],
+                'date' => [
+                    'caption' => _t('_bx_files_entry_info_date'),
+                    'type' => 'date_time',
+                    'value' => $aData[$CNF['FIELD_ADDED']],
+                ],
+                'author' => [
+                    'caption' => _t('_bx_files_entry_info_author'),
+                    'type' => 'textarea',
+                    'html' => 1,
+                    'value' => '<a href="'.$oAuthor->getUrl().'">'.$oAuthor->getDisplayName().'</a>',
+                ],
+            ],
+        ];
+
+        if ($aData[$CNF['FIELD_ALLOW_VIEW_TO']] < 0) {
+            $oProfile = BxDolProfile::getInstance(-$aData[$CNF['FIELD_ALLOW_VIEW_TO']]);
+            if ($oProfile) {
+                $aForm['inputs']['context'] = [
+                    'caption' => _t('_bx_files_entry_info_context'),
+                    'type' => 'textarea',
+                    'html' => 1,
+                    'value' => '<a href="' . $oProfile->getUrl() . '">' . $oProfile->getDisplayName() . '</a>',
+                ];
+            }
+        }
+
+        $oForm = new BxTemplFormView($aForm);
+        return $this->parseHtmlByName('entry-popup.html', [
+            'content' => $oForm->getCode(),
+            'bx_repeat:init_scripts' => [],
+        ]);
+    }
+
     public function getTmplVarsText($aData)
     {
         $aVars = parent::getTmplVarsText($aData);

+ 30 - 0
modules/boonex/files/install/langs/en.xml

@@ -243,4 +243,34 @@
 
     <string name="_bx_files_privacy_public"><![CDATA[Public]]></string>
     <string name="_bx_files_privacy_participants"><![CDATA[Participants]]></string>
+
+    <string name="_bx_files_menu_title_view_inline"><![CDATA[File inline menu]]></string>
+    <string name="_bx_files_menu_set_title_view_inline"><![CDATA[File inline menu]]></string>
+    <string name="_bx_files_menu_item_title_system_move_to_entry"><![CDATA[Move to...]]></string>
+    <string name="_bx_files_menu_item_title_move_to_entry"><![CDATA[Move to...]]></string>
+    <string name="_bx_files_menu_item_title_system_info_entry"><![CDATA[Info]]></string>
+    <string name="_bx_files_menu_item_title_info_entry"><![CDATA[Info]]></string>
+    <string name="_bx_files_menu_item_title_system_bookmark"><![CDATA[Bookmark]]></string>
+    <string name="_bx_files_menu_item_title_bookmark"><![CDATA[Bookmark]]></string>
+
+    <string name="_bx_files_menu_item_title_system_sm_size"><![CDATA[Size]]></string>
+    <string name="_bx_files_menu_item_title_sm_size"><![CDATA[Size]]></string>
+
+    <string name="_bx_files_option_link_to_preview"><![CDATA[Link file title to a preview popup instead of a file view page in context blocks]]></string>
+
+    <string name="_bx_files_entry_info_size"><![CDATA[Size]]></string>
+    <string name="_bx_files_entry_info_date"><![CDATA[Date]]></string>
+    <string name="_bx_files_entry_info_author"><![CDATA[By]]></string>
+    <string name="_bx_files_entry_info_context"><![CDATA[Uploaded to]]></string>
+
+    <string name="_bx_files_toolbar_view_table"><![CDATA[Table View]]></string>
+    <string name="_bx_files_toolbar_view_gallery"><![CDATA[Gallery View]]></string>
+    <string name="_bx_files_toolbar_view_bookmarks"><![CDATA[Bookmarked files]]></string>
+    <string name="_bx_files_toolbar_sorting_name"><![CDATA[Order by Name]]></string>
+    <string name="_bx_files_toolbar_sorting_date"><![CDATA[Order by Date]]></string>
+    <string name="_bx_files_toolbar_sorting_type"><![CDATA[Order by Type]]></string>
+    <string name="_bx_files_toolbar_sorting_author"><![CDATA[Order by Author]]></string>
+
+    <string name="_bx_files_txt_error"><![CDATA[File not found]]></string>
+    <string name="_bx_files_txt_permission_denied"><![CDATA[Permission denied]]></string>
 </resources>

+ 1 - 1
modules/boonex/files/install/sql/disable.sql

@@ -13,7 +13,7 @@ DELETE FROM `sys_pages_blocks` WHERE `module` = 'bx_files' OR `object` IN('bx_fi
 -- MENU
 DELETE FROM `sys_objects_menu` WHERE `module` = 'bx_files';
 DELETE FROM `sys_menu_sets` WHERE `module` = 'bx_files';
-DELETE FROM `sys_menu_items` WHERE `module` = 'bx_files' OR `set_name` IN('bx_files_view', 'bx_files_view_actions', 'bx_files_submenu', 'bx_files_view_submenu', 'bx_files_snippet_meta', 'bx_files_my', 'bx_files_menu_manage_tools');
+DELETE FROM `sys_menu_items` WHERE `module` = 'bx_files' OR `set_name` IN('bx_files_view', 'bx_files_view_actions', 'bx_files_submenu', 'bx_files_view_submenu', 'bx_files_snippet_meta', 'bx_files_my', 'bx_files_menu_manage_tools', 'bx_files_view_inline');
 
 -- PRIVACY 
 DELETE FROM `sys_objects_privacy` WHERE `object` IN ('bx_files_allow_view_to', 'bx_files_allow_view_favorite_list');

+ 27 - 5
modules/boonex/files/install/sql/enable.sql

@@ -12,6 +12,7 @@ SET @iCategId = LAST_INSERT_ID();
 
 INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
 ('bx_files_summary_chars', '700', @iCategId, '_bx_files_option_summary_chars', 'digit', '', '', '', 1),
+('bx_files_show_link_to_preview', '', @iCategId, '_bx_files_option_link_to_preview', 'checkbox', '', '', '', 5),
 ('bx_files_per_page_browse', '12', @iCategId, '_bx_files_option_per_page_browse', 'digit', '', '', '', 10),
 ('bx_files_per_page_profile', '6', @iCategId, '_bx_files_option_per_page_profile', 'digit', '', '', '', 12),
 ('bx_files_per_page_browse_showcase', '32', @iCategId, '_sys_option_per_page_browse_showcase', 'digit', '', '', '', 15),
@@ -250,6 +251,26 @@ INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `titl
 ('bx_files_view_actions', 'bx_files', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, 1, 0, 9999);
 
 
+-- MENU: actions menu for view inline entry
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_files_view_inline', '_bx_files_menu_title_view_inline', 'bx_files_view_inline', 'bx_files', 9, 0, 1, 'BxFilesMenuViewActionsInline', 'modules/boonex/files/classes/BxFilesMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
+('bx_files_view_inline', 'bx_files', '_bx_files_menu_set_title_view_inline', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('bx_files_view_inline', 'bx_files', 'bookmark', '_bx_files_menu_item_title_system_bookmark', '_bx_files_menu_item_title_bookmark', '', '{js_object}.bookmark({content_id}, this); return false;', '', 'far star', '', 2147483647, 1, 0, 1),
+('bx_files_view_inline', 'bx_files', 'download-file', '_bx_files_menu_item_title_system_download_file', '_bx_files_menu_item_title_download_file', 'modules/?r=files/download/{file_download_token}/{content_id}.{file_ext}', '', '_blank', 'download', '', 2147483647, 1, 0, 2),
+('bx_files_view_inline', 'bx_files', 'delete-file-quick', '_bx_files_menu_item_title_system_delete_entry', '_bx_files_menu_item_title_delete_entry', 'page.php?i=delete-file&id={content_id}', '{js_object}.delete({content_id}); return false;', '', 'remove', '', 2147483647, 1, 0, 3),
+('bx_files_view_inline', 'bx_files', 'move-to', '_bx_files_menu_item_title_system_move_to_entry', '_bx_files_menu_item_title_move_to_entry', '', 'bx_alert(\'TODO\');return false;', '', 'file-export', '', 2147483647, 1, 0, 4),
+('bx_files_view_inline', 'bx_files', 'edit-title', '_bx_files_menu_item_title_system_edit_entry', '_bx_files_menu_item_title_edit_entry', 'page.php?i=edit-file&id={content_id}', '{js_object}.edit({content_id}); return false;', '', 'pencil-alt', '', 2147483647, 1, 0, 5),
+('bx_files_view_inline', 'bx_files', 'preview', '_bx_files_menu_item_title_system_info_entry', '_bx_files_menu_item_title_info_entry', '', '{js_object}.info({content_id}); return false;', '', 'info', '', 2147483647, 1, 0, 6),
+('bx_files_view_inline', 'bx_files', 'report', '_sys_menu_item_title_system_va_report', '', '', '', '', '', '', 2147483647, 1, 0, 7),
+('bx_files_view_inline', 'bx_files', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', 2147483647, 1, 0, 9999);
+
+
+
 -- MENU: actions menu for my entries
 
 INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
@@ -298,11 +319,12 @@ INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES
 
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
 ('bx_files_snippet_meta', 'bx_files', 'date', '_sys_menu_item_title_system_sm_date', '_sys_menu_item_title_sm_date', '', '', '', '', '', 2147483647, 1, 0, 1, 1),
-('bx_files_snippet_meta', 'bx_files', 'author', '_sys_menu_item_title_system_sm_author', '_sys_menu_item_title_sm_author', '', '', '', '', '', 2147483647, 1, 0, 1, 2),
-('bx_files_snippet_meta', 'bx_files', 'category', '_sys_menu_item_title_system_sm_category', '_sys_menu_item_title_sm_category', '', '', '', '', '', 2147483647, 0, 0, 1, 3),
-('bx_files_snippet_meta', 'bx_files', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, 0, 0, 1, 4),
-('bx_files_snippet_meta', 'bx_files', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, 0, 0, 1, 5),
-('bx_files_snippet_meta', 'bx_files', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, 0, 0, 1, 6);
+('bx_files_snippet_meta', 'bx_files', 'size', '_bx_files_menu_item_title_system_sm_size', '_bx_files_menu_item_title_sm_size', '', '', '', '', '', 2147483647, 1, 0, 1, 2),
+('bx_files_snippet_meta', 'bx_files', 'author', '_sys_menu_item_title_system_sm_author', '_sys_menu_item_title_sm_author', '', '', '', '', '', 2147483647, 1, 0, 1, 3),
+('bx_files_snippet_meta', 'bx_files', 'category', '_sys_menu_item_title_system_sm_category', '_sys_menu_item_title_sm_category', '', '', '', '', '', 2147483647, 0, 0, 1, 4),
+('bx_files_snippet_meta', 'bx_files', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, 0, 0, 1, 5),
+('bx_files_snippet_meta', 'bx_files', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, 0, 0, 1, 6),
+('bx_files_snippet_meta', 'bx_files', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, 0, 0, 1, 7);
 
 -- MENU: profile stats
 SET @iNotifMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_profile_stats' AND `active` = 1 LIMIT 1);

+ 7 - 0
modules/boonex/files/install/sql/install.sql

@@ -87,6 +87,13 @@ CREATE TABLE IF NOT EXISTS `bx_files_cmts` (
   FULLTEXT KEY `search_fields` (`cmt_text`)
 );
 
+-- TABLE: bookmarks
+CREATE TABLE IF NOT EXISTS `bx_files_bookmarks` (
+  `object_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  PRIMARY KEY (`object_id`, `profile_id`)
+);
+
 -- TABLE: votes
 CREATE TABLE IF NOT EXISTS `bx_files_votes` (
   `object_id` int(11) NOT NULL default '0',

+ 1 - 1
modules/boonex/files/install/sql/uninstall.sql

@@ -1,6 +1,6 @@
 
 -- TABLES
-DROP TABLE IF EXISTS `bx_files_main`, `bx_files_files`, `bx_files_photos_resized`, `bx_files_cmts`, `bx_files_votes`, `bx_files_votes_track`, `bx_files_reactions`, `bx_files_reactions_track`, `bx_files_views_track`, `bx_files_meta_keywords`, `bx_files_meta_mentions`, `bx_files_reports`, `bx_files_reports_track`, `bx_files_favorites_track`, `bx_files_favorites_lists`, `bx_files_scores`, `bx_files_scores_track`;
+DROP TABLE IF EXISTS `bx_files_main`, `bx_files_files`, `bx_files_photos_resized`, `bx_files_cmts`, `bx_files_votes`, `bx_files_votes_track`, `bx_files_reactions`, `bx_files_reactions_track`, `bx_files_views_track`, `bx_files_meta_keywords`, `bx_files_meta_mentions`, `bx_files_reports`, `bx_files_reports_track`, `bx_files_favorites_track`, `bx_files_favorites_lists`, `bx_files_scores`, `bx_files_scores_track`, `bx_files_bookmarks`;
 
 -- STORAGES & TRANSCODERS
 DELETE FROM `sys_objects_storage` WHERE `object` = 'bx_files_files' OR `object` = 'bx_files_photos_resized';

+ 48 - 0
modules/boonex/files/js/menu_tools.js

@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxFilesMenuTools(oOptions) {
+	this._sActionsUrl = oOptions.sActionUrl;
+}
+
+BxFilesMenuTools.prototype.bookmark = function(iContentId, oMenuItem) {
+	$.post(this._sActionsUrl+'bookmark/'+iContentId);
+
+	if ($('i', oMenuItem).hasClass('far')) {
+		$('i', oMenuItem).removeClass('far').addClass('fas');
+	} else {
+		$('i', oMenuItem).removeClass('fas').addClass('far');
+	}
+};
+
+BxFilesMenuTools.prototype.preview = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	$.get(this._sActionsUrl+'entry_preview/'+iContentId, processJsonData, 'json');
+};
+
+BxFilesMenuTools.prototype.info = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	$.get(this._sActionsUrl+'entry_info/'+iContentId, processJsonData, 'json');
+};
+
+BxFilesMenuTools.prototype.edit = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	$.get(this._sActionsUrl+'entry_edit_title/'+iContentId, processJsonData, 'json');
+};
+
+BxFilesMenuTools.prototype.delete = function(iContentId) {
+	$('.bx-popup-wrapper:visible').dolPopupHide();
+	var $this = this;
+	bx_confirm(_t('_Are_you_sure'), function(){
+		$.get($this._sActionsUrl+'entry_delete/'+iContentId, processJsonData, 'json');
+	});
+};
+
+/** @} */

+ 57 - 0
modules/boonex/files/js/toolbar_tools.js

@@ -0,0 +1,57 @@
+function BxFilesBrowserTools(oOptions) {
+    this._sView = oOptions.layout;
+    this._bBookmarks = oOptions.bookmarks;
+    this._sSorting = oOptions.sorting;
+    this._sKeyword = oOptions.keyword;
+    this._sUniq = oOptions.unique_ident;
+    this._aRequestParams = oOptions.aRequestParams;
+    this._iSearchTimeoutId = 0;
+
+    this._oToolbarObject = $('#bx-files-browser-toolbar-' + this._sUniq).get(0);
+}
+
+BxFilesBrowserTools.prototype.reloadBlock = function() {
+    loadDynamicBlockAutoPaginate(this._oToolbarObject, '0', '0', this.getRequestUrl());
+}
+
+BxFilesBrowserTools.prototype.onChangeFilter = function(oFilter) {
+	var $this = this;
+
+	clearTimeout($this._iSearchTimeoutId);
+    $this._iSearchTimeoutId = setTimeout(function () {
+        $this._sKeyword = $(oFilter).val();
+        $this.reloadBlock();
+    }, 500);
+};
+
+BxFilesBrowserTools.prototype.setView = function(val) {
+    this._sView = val;
+    this.reloadBlock();
+}
+
+BxFilesBrowserTools.prototype.toggleBookmarks = function() {
+    this._bBookmarks = this._bBookmarks ? 0 : 1;
+    this.reloadBlock();
+}
+
+BxFilesBrowserTools.prototype.setSorting = function(val) {
+    this._sSorting = val;
+    this.reloadBlock();
+}
+
+BxFilesBrowserTools.prototype.getRequestUrl = function() {
+    return  this._aRequestParams.unit_view_param + '=' + this._sView + '&' +
+            this._aRequestParams.bookmarks_param + '=' + this._bBookmarks + '&' +
+            this._aRequestParams.sorting_param + '=' + this._sSorting + '&' +
+            this._aRequestParams.keyword_param + '=' + this._sKeyword;
+}
+
+BxFilesBrowserTools.prototype.onSelectAll = function(el) {
+    $(el).
+        closest('.bx-files-text-unit-simple-row').
+        siblings().
+        find('input[type=checkbox]').
+        attr('checked', $(el).attr('checked') == 'checked').change(function(){
+            $(el).attr('checked', false);
+        });
+};

+ 116 - 2
modules/boonex/files/template/css/main.css

@@ -9,12 +9,58 @@
     top:0;
     width:100%;
     position:absolute;
-    font-size:8rem; 
-    line-height:11.25rem;
+    font-size:10rem;
+    line-height:12.25rem;
     text-align:center;
     overflow:hidden;
 }
 
+.bx-files-text-unit-gallery .bx-files-filename,
+.bx-files-text-unit-simple-row .bx-files-filename{
+    overflow: hidden;
+    text-overflow: ellipsis;
+    line-height: 2rem;
+    margin: 0px;
+    font-weight: bold;
+    white-space: nowrap;
+}
+
+.bx-files-text-unit-gallery .bx-base-general-unit-meta ul.bx-menu-custom.bx-menu-hor {
+    align-items: center;
+    -webkit-align-items: center;
+    justify-content: center;
+    -webkit-justify-content: center;
+}
+
+.bx-files-text-unit-gallery .bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline li.bx-menu-item span.bx-base-general-entity-action {
+    margin: 0px;
+    vertical-align: top;
+}
+.bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline li.bx-menu-item {
+    white-space: nowrap;
+}
+.bx-files-text-unit-gallery .bx-menu-custom.bx-menu-hor.bx-menu-more-auto {
+    justify-content: center;
+}
+.bx-files-text-unit-gallery {
+    overflow: visible!important;
+}
+.bx-files-text-unit-gallery .bx-base-text-unit-thumb,
+.bx-files-text-unit-gallery .bx-base-text-unit-no-thumb,
+.bx-files-text-unit-gallery .bx-base-text-unit-text {
+    overflow: hidden;
+}
+.bx-media-desktop .bx-files-text-unit-gallery {
+    max-width: 33.25%;
+}
+.bx-media-tablet2 .bx-files-text-unit-gallery {
+    max-width: 49.8%;
+}
+.bx-media-phone2 .bx-files-text-unit-gallery {
+    max-width: 100%;
+}
+
+
 .bx-files-text-unit .bx-base-text-unit-text {
     position:relative;
 }
@@ -37,4 +83,72 @@
 .bx-files-entity-preview .bx-msg-box-container {
     margin: 0;
     padding: 0;
+}
+
+.bx-files-border-thick {
+    border-width: 3px;
+}
+
+/*--- Files Browser Table layout ---*/
+.bx-files-text-unit-simple-row {
+    text-align: left;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+.bx-files-text-unit-simple-row .bx-files-fileinfo {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    display: flex;
+    align-items: center;
+    -webkit-align-items: center;
+}
+.bx-files-text-unit-simple-row .bx-files-fileinfo-text {
+    display: inline-block;
+    overflow: hidden;
+}
+.bx-files-text-unit-simple-row .bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline {
+    float: right;
+}
+.bx-files-text-unit-simple-row .bx-files-inline-menu ul.bx-menu-object-bx_files_view_inline li.bx-menu-item span.bx-base-general-entity-action {
+    margin: 0px;
+    vertical-align: top;
+}
+.bx-files-text-unit-simple-row .bx-files-fileinfo i {
+    font-size: 2.3rem;
+}
+.bx-files-text-unit-simple-row .bx-base-general-unit-meta ul {
+    margin: 0px!important;
+    margin-top: 0.2rem;
+}
+.bx-files-text-unit-simple-row .bx-files-filename {
+    line-height: unset;
+}
+
+
+/*--- Toolbar ---*/
+.bx-files-browser-toolbar {
+    width: 100%;
+    box-sizing: border-box;
+    text-align: left;
+    padding-top: 0px;
+}
+.bx-files-browser-toolbar .bx-form-element-wrapper {
+    display: inline-block;
+    margin: 0px;
+}
+.bx-files-browser-toolbar .bx-btn {
+    float: none;
+    display: inline-block;
+}
+.bx-files-toolbar-controls-group {
+    display: inline-block;
+    margin-right: 10px;
+    white-space: nowrap;
+    margin-bottom: 1rem;
+}
+.bx-files-toolbar-spacer {
+    width: 100%;
+    margin-bottom: 0.5rem;
 }

+ 13 - 0
modules/boonex/files/template/entry-popup.html

@@ -0,0 +1,13 @@
+<div class="bx-def-margin-bottom">
+    __content__
+</div>
+<button class="bx-btn" onclick="$('#bx-files-popup').dolPopupHide();" style="float: none;"><bx_text:_sys_close /></button>
+
+<script>
+    function bx_files_popup_init() {
+        <bx_repeat:init_scripts>
+            __code__
+        </bx_repeat:init_scripts>
+        $(window).resize();
+    }
+</script>

+ 43 - 0
modules/boonex/files/template/files_browser_toolbar.html

@@ -0,0 +1,43 @@
+<div class="bx-def-padding-sec-leftright bx-def-border-bottom bx-files-border-thick bx-files-browser-toolbar bx-clearfix" id="bx-files-browser-toolbar-__unique_ident__">
+    <bx_if:upload_visible>
+    <div class="bx-files-toolbar-controls-group">
+        <button class="bx-btn" onclick="bx_alert('TODO'); return false;"><i class="sys-icon upload"></i>&nbsp;Upload</button>
+        <button class="bx-btn" onclick="bx_alert('TODO'); return false;"><i class="sys-icon plus"></i>&nbsp;Add Folder</button>
+    </div>
+    </bx_if:upload_visible>
+
+    <div class="bx-files-toolbar-controls-group">
+        <bx_if:table_layout_btn>
+            <button class="bx-btn bx-btn-disabled" title="<bx_text:_bx_files_toolbar_view_table />"><i class="sys-icon list"></i></button>
+            <button class="bx-btn" onclick="__js_object__.setView('gallery');" title="<bx_text:_bx_files_toolbar_view_gallery />"><i class="sys-icon th-large"></i></button>
+        </bx_if:table_layout_btn>
+        <bx_if:gallery_layout_btn>
+            <button class="bx-btn" onclick="__js_object__.setView('table');"  title="<bx_text:_bx_files_toolbar_view_table />"><i class="sys-icon list"></i></button>
+            <button class="bx-btn bx-btn-disabled" title="<bx_text:_bx_files_toolbar_view_gallery />"><i class="sys-icon th-large"></i></button>
+        </bx_if:gallery_layout_btn>
+    </div>
+
+    <bx_if:bookmarks_visible>
+    <div class="bx-files-toolbar-controls-group">
+        <button class="bx-btn" onclick="__js_object__.toggleBookmarks();" title="<bx_text:_bx_files_toolbar_view_bookmarks />"><i class="sys-icon __bookmark_enabled__ star"></i></button>
+    </div>
+    </bx_if:bookmarks_visible>
+
+    <div class="bx-files-toolbar-controls-group">
+        __sorting_dropdown__
+        __filter_box__
+    </div>
+</div>
+
+<div class="bx-files-toolbar-spacer"></div>
+
+<bx_if:select_all_checkbox>
+<div class="bx-def-padding-sec bx-files-text-unit-simple-row">
+    <div>
+        <input type="checkbox" class="bx-def-font-inputs bx-form-input-checkbox bx-def-margin-right" id="bx-files-browser-select-all-__unique_ident__" onchange="__js_object__.onSelectAll(this);" />
+        <label for="bx-files-browser-select-all-__unique_ident__"><bx_text:_Select_all /></label>
+    </div>
+</div>
+</bx_if:select_all_checkbox>
+
+__js_code__

+ 17 - 11
modules/boonex/files/template/unit_gallery.html

@@ -1,13 +1,7 @@
-<div class="__class__ bx-files-text-unit-gallery bx-base-text-unit bx-base-text-unit-gallery bx-def-gal-unit-size bx-def-padding-sec bx-def-margin-sec-bottom">
+<div class="__class__ bx-files-text-unit-gallery bx-base-text-unit bx-base-text-unit-gallery bx-def-gal-unit-size bx-def-padding-sec bx-def-margin-sec-bottom bx-file-entry" file_id="__id__">
 	<div class="bx-base-text-unit-cnt bx-def-box bx-def-round-corners bx-def-color-bg-box bx-def-box-sizing">
-		<div class="bx-base-text-unit-text bx-def-padding-leftright bx-def-padding-sec-topbottom bx-def-font-align-center bx-def-box-sizing">
-			<h2><a href="__content_url__">__title__</a></h2>
-		    <div class="bx-base-text-unit-info">
-                <bx_if:meta><div class="bx-base-general-unit-meta">__meta__</div></bx_if:meta>
-            </div>
-		</div>
-	    <bx_if:thumb>
-	        <a href="__content_url__" title="__summary_attr__">
+		<bx_if:thumb>
+	        <a href="__content_url__" title="__summary_attr__" onclick="__alt_content_url_handler__">
 	            <div class="bx-base-text-unit-thumb bx-def-gal-div-size bx-def-round-corners bx-def-font-middle" style="background-image:url(__gallery_url__)">
 	                <div class="bx-base-text-unit-strecher">__strecher__</div>
 	            </div>
@@ -15,12 +9,24 @@
 	    </bx_if:thumb>
 	    <bx_if:no_thumb>
 	        <div class="bx-def-gal-img-column">
-                <a href="__content_url__">
+                <a href="__content_url__" onclick="__alt_content_url_handler__">
     	            <div class="bx-base-text-unit-no-thumb bx-def-gal-div-size bx-def-font-middle bx-def-font-grayed bx-def-padding bx-def-box-sizing">
-                        <i class="sys-icon __icon__ "></i>
+                        <i class="sys-icon __icon__ sys-colored col-gray"></i>
     	            </div>
                 </a>
 	        </div>
 	    </bx_if:no_thumb>
+		<div class="bx-base-text-unit-text bx-def-padding-leftright bx-def-padding-sec-topbottom bx-def-font-align-center bx-def-box-sizing">
+			<div class="bx-files-filename bx-def-font-large"><a href="__content_url__" onclick="__alt_content_url_handler__">__title__</a></div>
+		    <div class="bx-base-text-unit-info">
+                <bx_if:meta><div class="bx-base-general-unit-meta">__meta__</div></bx_if:meta>
+            </div>
+		</div>
+
+		<bx_if:inline_menu>
+		<div class="bx-files-inline-menu bx-clearfix bx-def-margin-bottom">
+			__menu__
+		</div>
+		</bx_if:inline_menu>
 	</div>
 </div>

+ 15 - 0
modules/boonex/files/template/unit_simple_row.html

@@ -0,0 +1,15 @@
+<div class="__class__ bx-def-padding-sec bx-def-border-top bx-files-border-thick bx-files-text-unit-simple-row bx-file-entry" file_id="__id__">
+    <div class="bx-files-fileinfo">
+        <input type="checkbox" class="bx-def-font-inputs bx-form-input-checkbox bx-def-margin-right" name="file[]" value="__id__" />
+        <i class="sys-icon __icon__ sys-colored bx-def-font-large bx-def-padding-sec-right col-gray"></i>
+        <div class="bx-files-fileinfo-text">
+            <div class="bx-files-filename"><a href="__content_url__" onclick="__alt_content_url_handler__">__title__</a></div>
+            <bx_if:meta><div class="bx-base-general-unit-meta">__meta__</div></bx_if:meta>
+        </div>
+    </div>
+    <bx_if:inline_menu>
+    <div class="bx-files-inline-menu bx-clearfix">
+        __menu__
+    </div>
+    </bx_if:inline_menu>
+</div>