ItSpiderman has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/311676

Change subject: Added FileRepo Grid - extended filelist, added page_link, 
file_link fields to BSApiFileBackendStore
......................................................................

Added FileRepo Grid - extended filelist, added page_link, file_link fields to 
BSApiFileBackendStore

Change-Id: I36bfbc5897562a035f5c2dac4c0d4340760b5722
---
M extension.json
A i18n/filerepo/en.json
A i18n/filerepo/qqq.json
A includes/JsonLicenses.php
M includes/api/BSApiFileBackendStore.php
A includes/api/BSApiUploadLicenseStore.php
M resources/Resources.php
A resources/bluespice.extjs/BS/grid/FileRepo.js
M resources/bluespice.extjs/BS/model/File.js
A resources/bluespice.extjs/BS/panel/Upload.js
A resources/bluespice.extjs/BS/panel/UploadPanel.js
11 files changed, 1,087 insertions(+), 6 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/BlueSpiceFoundation 
refs/changes/76/311676/1

diff --git a/extension.json b/extension.json
old mode 100644
new mode 100755
index e0a8151..9b698f6
--- a/extension.json
+++ b/extension.json
@@ -1,6 +1,6 @@
 {
        "name": "BlueSpice",
-       "version": "2.27.0-beta",
+       "version": "2.27.0",
        "author": [
                "[http://www.hallowelt.com Hallo Welt! GmbH]"
        ],
@@ -24,7 +24,8 @@
                "bs-wikipage-store": "BSApiWikiPageStore",
                "bs-wikisubpage-treestore": "BSApiWikiSubPageTreeStore",
                "bs-titlequery-store": "BSApiTitleQueryStore",
-               "bs-ping-tasks": "BSApiPingTasks"
+               "bs-ping-tasks": "BSApiPingTasks",
+               "bs-upload-license-store": "BSApiUploadLicenseStore"
        },
        "MessagesDirs": {
                "BlueSpice": [
@@ -53,6 +54,12 @@
                ],
                "BlueSpice.API": [
                        "i18n/api"
+               ],
+               "BlueSpice.Upload": [
+                       "i18n/upload"
+               ],
+               "BlueSpice.FileRepo": [
+                       "i18n/filerepo"
                ]
        },
        "ExtensionMessagesFiles": {
@@ -80,8 +87,8 @@
        "config": {
                "BlueSpiceExtInfo": {
                        "name": "BlueSpice",
-                       "version": "2.27.0-beta",
-                       "status": "beta",
+                       "version": "2.27.0",
+                       "status": "alpha",
                        "package": "BlueSpice Free",
                        "url": "http://bluespice.com";,
                        "desc": "Makes MediaWiki enterprise ready.",
@@ -153,6 +160,8 @@
                "BSApiAdminUserStore": "includes/api/BSApiAdminUserStore.php",
                "BSApiGroupStore": "includes/api/BSApiGroupStore.php",
                "BSApiInterwikiStore": "includes/api/BSApiInterwikiStore.php",
+               "JsonLicenses": "includes/JsonLicenses.php",
+               "BSApiUploadLicenseStore": 
"includes/api/BSApiUploadLicenseStore.php",
                "BSApiWikiPageTasks": "includes/api/BSApiWikiPageTasks.php",
                "BSApiWikiPageStore": "includes/api/BSApiWikiPageStore.php",
                "BSApiWikiSubPageTreeStore": 
"includes/api/BSApiWikiSubPageTreeStore.php",
diff --git a/i18n/filerepo/en.json b/i18n/filerepo/en.json
new file mode 100644
index 0000000..22b8b6e
--- /dev/null
+++ b/i18n/filerepo/en.json
@@ -0,0 +1,38 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Dejan Savuljesku"
+               ]
+       },
+       "bs-filerepo-desc": "Extended filelist description - TODO",
+       "bs-filerepo-headerfilename": "Filename",
+       "bs-filerepo-headerfilesize": "File size",
+       "bs-filerepo-labelupload": "Upload",
+       "bs-filerepo-labelfilter": "Filter",
+       "bs-filerepo-uploaddialogtitle": "Upload file",
+       "bs-filerepo-headerpageid": "Page ID",
+       "bs-filerepo-headerpagetitle": "Page title",
+       "bs-filerepo-headerpagelatest": "Page latest change",
+       "bs-filerepo-headerpagenamespace": "Page namespace",
+       "bs-filerepo-headerpageisredirect": "Is page redirect?",
+       "bs-filerepo-headerpageisnew": "Is page new?",
+       "bs-filerepo-headerpagetouched": "Page touched",
+       "bs-filerepo-headerpagelink": "Page link",
+       "bs-filerepo-headerpagecategories": "Page categories",
+       "bs-filerepo-headerfileurl": "File URL",
+       "bs-filerepo-headerfilebits": "File bits",
+       "bs-filerepo-headerfileuser": "User ID",
+       "bs-filerepo-headerfilewidth": "File width",
+       "bs-filerepo-headerfileheight": "File height",
+       "bs-filerepo-headerfilemimetype": "File mimetype",
+       "bs-filerepo-headerfileusertext": "User",
+       "bs-filerepo-headerfileextension": "File extension",
+       "bs-filerepo-headerfiletimestamp": "Uploaded",
+       "bs-filerepo-headerfilemediatype": "File media type",
+       "bs-filerepo-headerfiledescription": "Description",
+       "bs-filerepo-headerfiledisplaytext": "Display text",
+       "bs-filerepo-headerfilethumbnail": "Preview",
+       "bs-filerepo-yes": "Yes",
+       "bs-filerepo-no": "No",
+       "bs-filerepo-headerfilelink": "File link"
+}
\ No newline at end of file
diff --git a/i18n/filerepo/qqq.json b/i18n/filerepo/qqq.json
new file mode 100644
index 0000000..22b8b6e
--- /dev/null
+++ b/i18n/filerepo/qqq.json
@@ -0,0 +1,38 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Dejan Savuljesku"
+               ]
+       },
+       "bs-filerepo-desc": "Extended filelist description - TODO",
+       "bs-filerepo-headerfilename": "Filename",
+       "bs-filerepo-headerfilesize": "File size",
+       "bs-filerepo-labelupload": "Upload",
+       "bs-filerepo-labelfilter": "Filter",
+       "bs-filerepo-uploaddialogtitle": "Upload file",
+       "bs-filerepo-headerpageid": "Page ID",
+       "bs-filerepo-headerpagetitle": "Page title",
+       "bs-filerepo-headerpagelatest": "Page latest change",
+       "bs-filerepo-headerpagenamespace": "Page namespace",
+       "bs-filerepo-headerpageisredirect": "Is page redirect?",
+       "bs-filerepo-headerpageisnew": "Is page new?",
+       "bs-filerepo-headerpagetouched": "Page touched",
+       "bs-filerepo-headerpagelink": "Page link",
+       "bs-filerepo-headerpagecategories": "Page categories",
+       "bs-filerepo-headerfileurl": "File URL",
+       "bs-filerepo-headerfilebits": "File bits",
+       "bs-filerepo-headerfileuser": "User ID",
+       "bs-filerepo-headerfilewidth": "File width",
+       "bs-filerepo-headerfileheight": "File height",
+       "bs-filerepo-headerfilemimetype": "File mimetype",
+       "bs-filerepo-headerfileusertext": "User",
+       "bs-filerepo-headerfileextension": "File extension",
+       "bs-filerepo-headerfiletimestamp": "Uploaded",
+       "bs-filerepo-headerfilemediatype": "File media type",
+       "bs-filerepo-headerfiledescription": "Description",
+       "bs-filerepo-headerfiledisplaytext": "Display text",
+       "bs-filerepo-headerfilethumbnail": "Preview",
+       "bs-filerepo-yes": "Yes",
+       "bs-filerepo-no": "No",
+       "bs-filerepo-headerfilelink": "File link"
+}
\ No newline at end of file
diff --git a/includes/JsonLicenses.php b/includes/JsonLicenses.php
new file mode 100755
index 0000000..197e37e
--- /dev/null
+++ b/includes/JsonLicenses.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * JsonLicenses
+ *
+ * Extends the mediawiki licenses class to put the available licenses out in a 
json format for ExtJS combobox.
+ *
+ * @author Sebastian Ulbricht
+ */
+class JsonLicenses extends Licenses {
+
+       protected $json;
+
+       public function __construct() {
+               parent::__construct( array( 'fieldname' => 'JsonLicenses' ) );
+       }
+
+       public function getJsonOutput() {
+               $this->json[] = $this->outputJsonOption( wfMessage( 'nolicense' 
)->text(), '' );
+               $this->makeJson( $this->getLicenses() );
+               return json_encode(array('items' => $this->json));
+       }
+
+       protected function outputJsonOption( $text, $value, $depth = 0 ) {
+               return array(
+                       'text' => $text,
+                       'value' => "\n\n==".  wfMessage( 'license-header' 
)->inContentLanguage()->text()."==\n{{".$value."}}",
+                       'indent'=> $depth
+               );
+       }
+
+       protected function makeJson( $tagset, $depth = 0 ) {
+               foreach ( $tagset as $key => $val ) {
+                       if ( is_array( $val ) ) {
+                               $this->json[] = $this->outputJsonOption( $key, 
'', $depth );
+                               $this->makeJson( $val, $depth + 1 );
+                       }
+                       else {
+                               $this->json[] = $this->outputJsonOption( 
$val->text, $val->template, $depth );
+                       }
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/includes/api/BSApiFileBackendStore.php 
b/includes/api/BSApiFileBackendStore.php
index 58cc951..b2b5cd9 100644
--- a/includes/api/BSApiFileBackendStore.php
+++ b/includes/api/BSApiFileBackendStore.php
@@ -69,9 +69,17 @@
                                $sThumb = html_entity_decode( 
SecureFileStore::secureStuff( $sThumb, true ) );
                                $sUrl = html_entity_decode( 
SecureFileStore::secureStuff( $sUrl, true ) );
                        }
+                       $oFileLink = Html::element('a', array(
+                                       'href' => $sUrl,
+                                       'title' => $oImg->getName(),
+                                       'data-bs-title' => $oImg->getName(),
+                               ), $oImg->getName());
+
+                       $oFileName = Title::makeTitle( NS_FILE , 
$oImg->getName());
 
                        $aReturn[ $oRow->page_id ] = (object) array(
                                'file_url' => $sUrl,
+                               'file_link' => $oFileLink,
                                'file_name' => $oImg->getName(),
                                'file_size' => $oImg->getSize(),
                                'file_bits' => $oImg->getBitDepth(),
@@ -86,6 +94,7 @@
                                'file_description' => $oImg->getDescription(),
                                'file_display_text' => $oImg->getName(),
                                'file_thumbnail_url' => $sThumb,
+                               'page_link' => Linker::link($oFileName, 
$oImg->getName()),
                                'page_id' => $oTitle->getArticleID(),
                                'page_title' => $oTitle->getText(),
                                'page_latest' => $oTitle->getLatestRevID(),
diff --git a/includes/api/BSApiUploadLicenseStore.php 
b/includes/api/BSApiUploadLicenseStore.php
new file mode 100755
index 0000000..6e78922
--- /dev/null
+++ b/includes/api/BSApiUploadLicenseStore.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * This class serves as a backend for the license store of the InsertFile
+ * extension
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * This file is part of BlueSpice for MediaWiki
+ * For further information visit http://www.blue-spice.org
+ *
+ * @author     Patric Wirth <wi...@hallowelt.com>
+ * @package    Bluespice_Extensions
+ * @copyright  Copyright (C) 2016 Hallo Welt! GmbH, All rights reserved.
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License v2 or 
later
+ *
+ * Example request parameters of an ExtJS store
+ */
+class BSApiUploadLicenseStore extends BSApiExtJSStoreBase {
+       /**
+        * @param string $sQuery Potential query provided by ExtJS component.
+        * This is some kind of preliminary filtering. Subclass has to decide if
+        * and how to process it
+        * @return array - Full list of of data objects. Filters, paging, 
sorting
+        * will be done by the base class
+        */
+       protected function makeData( $sQuery = '' ) {
+               //There is an api action "licences" in mw 2.24, but it is 
described as
+               //"Get media license dropdown HTML."
+               $oLicenses = new JsonLicenses();
+               $aData = FormatJson::decode(
+                       $oLicenses->getJsonOutput()
+               );
+               return $aData->items;
+       }
+}
\ No newline at end of file
diff --git a/resources/Resources.php b/resources/Resources.php
old mode 100644
new mode 100755
index 97f7fd4..da25508
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -41,10 +41,56 @@
                'bs-two-units-ago',
                'bs-one-unit-ago',
                'bs-now',
-               'blanknamespace', //MediaWiki
+               'blanknamespace',
+               'bs-upload-uploadfilefieldlabel',
+               'bs-upload-uploadbuttontext',
+               'bs-upload-uploadfileemptytext',
+               'bs-upload-uploaddestfilelabel',
+               'bs-upload-descfilelabel',
+               'bs-upload-license',
+               'bs-upload-uploadwatchthislabel',
+               'bs-upload-uploadignorewarningslabel',
+               'bs-upload-categories',
+               'bs-upload-upload-waitmessage',
+               'bs-upload-error',
+               'bs-upload-error-long',
+               'bs-upload-filetypenotsupported',
+               'bs-filerepo-headerfilename',
+               'bs-filerepo-headerfilesize',
+               'bs-filerepo-labelfilter',
+               'bs-filerepo-labelupload',
+               'bs-filerepo-uploaddialogtitle',
+               'bs-filerepo-headerpageid',
+               'bs-filerepo-headerpagetitle',
+               'bs-filerepo-headerpagelatest',
+               'bs-filerepo-headerpagenamespace',
+               'bs-filerepo-headerpageisredirect',
+               'bs-filerepo-headerpageisnew',
+               'bs-filerepo-headerpagetouched',
+               'bs-filerepo-headerpagelink',
+               'bs-filerepo-headerpagecategories',
+               'bs-filerepo-headerfileurl',
+               'bs-filerepo-headerfilebits',
+               'bs-filerepo-headerfileuser',
+               'bs-filerepo-headerfilewidth',
+               'bs-filerepo-headerfileheight',
+               'bs-filerepo-headerfilemimetype',
+               'bs-filerepo-headerfileusertext',
+               'bs-filerepo-headerfileextension',
+               'bs-filerepo-headerfiletimestamp',
+               'bs-filerepo-headerfilemediatype',
+               'bs-filerepo-headerfiledescription',
+               'bs-filerepo-headerfiledisplaytext',
+               'bs-filerepo-headerfilethumbnail',
+               'bs-filerepo-yes',
+               'bs-filerepo-no',
+               'bs-filerepo-headerfilelink'
+
        )
 ) + $aResourceModuleTemplate;
 
+
+
 $wgResourceModules['ext.bluespice.styles'] = array(
        'styles' => array(
                'bluespice/bluespice.css',
diff --git a/resources/bluespice.extjs/BS/grid/FileRepo.js 
b/resources/bluespice.extjs/BS/grid/FileRepo.js
new file mode 100755
index 0000000..5b983a9
--- /dev/null
+++ b/resources/bluespice.extjs/BS/grid/FileRepo.js
@@ -0,0 +1,357 @@
+Ext.define( 'BS.grid.FileRepo', {
+       extend: 'Ext.grid.Panel',
+       requires: ['BS.store.BSApi', 'Ext.ux.form.SearchField', 'BS.model.File',
+                               'Ext.Button', 'BS.panel.Upload'],
+       id: 'bs-filerepo-panel',
+       features: [],
+       initComponent: function() {
+               var pageSize = 10;
+
+               this.store = new BS.store.BSApi({
+                       apiAction: 'bs-filebackend-store',
+                       model: 'BS.model.File',
+                       proxy:{
+                               extraParams: {
+                                       limit: pageSize
+                               }
+                       },
+                       pageSize: pageSize
+               });
+
+
+               this.colFileThumb = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filethumb',
+                       sortable: false,
+                       filterable: false,
+                       dataIndex: 'file_thumbnail_url',
+                       renderer: this.renderThumb,
+                       width: 64,
+                       header: 
mw.message('bs-filerepo-headerfilethumbnail').plain()
+               });
+
+               this.colPageId = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pageid',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_id',
+                       hidden: true,
+                       header: mw.message('bs-filerepo-headerpageid').plain()
+               });
+
+               this.colPageTitle = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pagetitle',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_title',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerpagetitle').plain()
+               });
+
+               this.colPageLatest = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pagelatest',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_latest',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerpagelatest').plain()
+               });
+
+               this.colPageNamespace = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pagenamespace',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_namespace',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerpagenamespace').plain()
+               });
+
+               this.colPageIsRedirect = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pageidredirect',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_is_redirect',
+                       hidden: true,
+                       renderer: this.renderBool,
+                       header: 
mw.message('bs-filerepo-headerpageisredirect').plain()
+               });
+
+               this.colPageIsNew = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pageisnew',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_is_new',
+                       hidden: true,
+                       renderer: this.renderBool,
+                       header: 
mw.message('bs-filerepo-headerpageisnew').plain()
+               });
+
+               this.colPageTouched = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pagetouched',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_touched',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerpagetouched').plain()
+               });
+
+               this.colPageLink = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pagelink',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_link',
+                       flex:1,
+                       header: mw.message('bs-filerepo-headerfilename').plain()
+               });
+
+               this.colFileLink = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filelink',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_link',
+                       hidden: true,
+                       header: mw.message('bs-filerepo-headerfilelink').plain()
+               });
+
+               this.colPageCategories = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'pagecategories',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'page_categories',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerpagecategories').plain()
+               });
+
+               this.colFileUrl = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'fileurl',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_url',
+                       hidden: true,
+                       header: mw.message('bs-filerepo-headerfileurl').plain()
+               });
+
+               this.colFileBits = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filebits',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_bits',
+                       hidden: true,
+                       header: mw.message('bs-filerepo-headerfilebits').plain()
+               });
+
+               this.colFileUser = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'fileuser',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_user',
+                       header: 
mw.message('bs-filerepo-headerfileuser').plain(),
+                       hidden: true
+               });
+
+               this.colFileWidth = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filewidth',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_width',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfilewidth').plain()
+               });
+
+               this.colFileHeight = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'fileheight',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_height',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfileheight').plain()
+               });
+
+               this.colFileMimetype = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filemimetype',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_mimetype',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfilemimetype').plain()
+               });
+
+               this.colFileUserText = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'fileusertext',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_user_text',
+                       header: 
mw.message('bs-filerepo-headerfileusertext').plain()
+               });
+
+               this.colFileExtension = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'fileextension',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_extension',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfileextension').plain()
+               });
+
+               this.colFileTimestamp = Ext.create( 'Ext.grid.column.Date', {
+                       id: 'filetimestamp',
+                       sortable: true,
+                       filterable: true,
+                       dateFormat: 'Y-m-d H:i:s',
+                       dataIndex: 'file_timestamp',
+                       width:100,
+                       header: 
mw.message('bs-filerepo-headerfiletimestamp').plain()
+               });
+
+               this.colFileMediaType = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filemediatype',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_mediatype',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfilemediatype').plain()
+               });
+
+               this.colFileDescription = Ext.create( 'Ext.grid.column.Column', 
{
+                       id: 'filedescription',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_description',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfiledescription').plain()
+               });
+
+               this.colFileDisplayText = Ext.create( 'Ext.grid.column.Column', 
{
+                       id: 'filedisplaytext',
+                       sortable: true,
+                       filterable: true,
+                       dataIndex: 'file_display_text',
+                       hidden: true,
+                       header: 
mw.message('bs-filerepo-headerfiledisplaytext').plain()
+               });
+
+               this.colFilename = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filename',
+                       header: 
mw.message('bs-filerepo-headerfilename').plain(),
+                       sortable: true,
+                       dataIndex: 'page_link',
+                       flex:1,
+                       hidden: true,
+                       filterable: true
+               });
+
+               this.colFilesize = Ext.create( 'Ext.grid.column.Column', {
+                       id: 'filesize',
+                       header: 
mw.message('bs-filerepo-headerfilesize').plain(),
+                       sortable: true,
+                       dataIndex: 'file_size',
+                       renderer: this.renderFilesize,
+                       width: 100,
+                       filterable: true
+               });
+
+               this.columns = [
+                       this.colFileThumb,
+                       this.colPageLink,
+                       this.colFilename,
+                       this.colFilesize,
+                       this.colPageId,
+                       this.colPageTitle,
+                       this.colPageLatest,
+                       this.colPageNamespace,
+                       this.colPageIsRedirect,
+                       this.colPageIsNew,
+                       this.colPageTouched,
+                       this.colPageCategories,
+                       this.colFileUrl,
+                       this.colFileBits,
+                       this.colFileUser,
+                       this.colFileWidth,
+                       this.colFileHeight,
+                       this.colFileMimetype,
+                       this.colFileUserText,
+                       this.colFileExtension,
+                       this.colFileTimestamp,
+                       this.colFileMediaType,
+                       this.colFileDescription,
+                       this.colFileDisplayText,
+                       this.colFileLink
+               ];
+
+               this.bbar = new Ext.PagingToolbar({
+                       store: this.store,
+                       displayInfo: true
+               });
+
+               this.sfFilter = Ext.create( 'Ext.ux.form.SearchField', {
+                       fieldLabel: 
mw.message('bs-filerepo-labelfilter').plain(),
+                       width:500,
+                       labelWidth:40,
+                       store: this.store,
+                                       listeners: {
+                                               change: function ( field, 
newValue, oldValue, eOpts ) {
+                                                       field.onTrigger2Click();
+                                                       return true;
+                                               }
+                                       }
+               });
+
+               this.btnUpload = Ext.create( 'Ext.Button', {
+                       text: mw.message('bs-filerepo-labelupload').plain()
+               });
+
+               this.btnUpload.on('click', this.btnUploadClick, this);
+
+               var allowedFileExtensions = 
mw.config.get('bsFileExtensions').concat(mw.config.get('bsImageExtensions'));
+
+               this.dlgUpload = Ext.create( 'BS.panel.Upload', {
+                       title: mw.message('bs-filerepo-uploaddialogtitle'),
+                       allowedFileExtensions: allowedFileExtensions
+               });
+
+               this.dlgUpload.on ('ok', this.onUploadComplete, this);
+
+               var toolBarItems = [
+                       this.sfFilter
+               ];
+
+               if (mw.config.get('bsEnableUploads')){
+                       toolBarItems.push( '->' );
+                       toolBarItems.push(this.btnUpload);
+               };
+
+               this.tbGridTools = Ext.create('Ext.toolbar.Toolbar', {
+                       dock: 'top',
+                       items: toolBarItems
+               });
+
+               this.dockedItems = [this.tbGridTools];
+
+               this.callParent(arguments);
+       },
+
+       renderFilesize: function( val ){
+               return Ext.util.Format.fileSize( val );
+       },
+
+       renderThumb: function( url, meta, record ) {
+               var attr = {
+                       style: 'background-image:url('+url+'); height: 48px; 
width:48px;background-position: center center; background-repeat: no-repeat;'
+               };
+               return mw.html.element( 'div', attr);
+       },
+       renderBool: function(value){
+               if(value===true){
+                       return mw.message('bs-filerepo-yes').plain();
+               } else {
+                       return mw.message('bs-filerepo-no').plain();
+               }
+       },
+
+       btnUploadClick: function(sender, event) {
+               this.dlgUpload.show();
+       },
+
+       onUploadComplete: function(sender, event){
+               this.store.reload();
+       }
+});
\ No newline at end of file
diff --git a/resources/bluespice.extjs/BS/model/File.js 
b/resources/bluespice.extjs/BS/model/File.js
index e1e159a..6625d3b 100644
--- a/resources/bluespice.extjs/BS/model/File.js
+++ b/resources/bluespice.extjs/BS/model/File.js
@@ -17,7 +17,8 @@
                { name: 'page_is_redirect', type: 'bool', defaultValue: false },
                { name: 'page_is_new', type: 'bool', defaultValue: true },
                { name: 'page_touched', type: 'date', defaultValue: 
'19700101000000', dateFormat: 'YmdHis' },
-
+               { name: 'page_link', type: 'string'},
+               { name: 'file_link', type: 'string'},
                //Here come custom fields that are calculated on the server side
                { name: 'page_categories', type: 'array', defaultValue: [] },
 
diff --git a/resources/bluespice.extjs/BS/panel/Upload.js 
b/resources/bluespice.extjs/BS/panel/Upload.js
new file mode 100755
index 0000000..63fe498
--- /dev/null
+++ b/resources/bluespice.extjs/BS/panel/Upload.js
@@ -0,0 +1,39 @@
+Ext.define( 'BS.panel.Upload', {
+       extend: 'BS.Window',
+       requires: [
+               'Ext.Button'
+       ],
+       minHeight: 50,
+       padding:null,
+       title: null,
+       layout: 'fit',
+       
+       afterInitComponent: function(){
+               this.upMain = Ext.create( 'BS.panel.UploadPanel', {
+                       id: this.getId()+'-upload-panel',
+                       allowedFileExtensions: this.allowedFileExtensions
+               });
+               this.upMain.on ('upload-complete', this.onUpMainUploadComplete, 
this);
+
+               this.items = [
+                       this.upMain
+               ];
+
+               this.callParent(arguments);
+       },
+
+       onBtnOKClick: function() {
+               this.upMain.uploadFile();
+       },
+
+       onUpMainUploadComplete: function( panel, upload ){
+               this.fireEvent( 'ok', this, upload );
+               
+               this.close();
+       },
+
+       resetData: function(){
+               this.upMain.getForm().reset();
+               this.callParent(arguments);
+       }
+});
\ No newline at end of file
diff --git a/resources/bluespice.extjs/BS/panel/UploadPanel.js 
b/resources/bluespice.extjs/BS/panel/UploadPanel.js
new file mode 100755
index 0000000..8e6c98d
--- /dev/null
+++ b/resources/bluespice.extjs/BS/panel/UploadPanel.js
@@ -0,0 +1,453 @@
+Ext.define ( 'BS.panel.UploadPanel', {
+       extend: 'Ext.form.Panel',
+       require: [
+               'BS.form.action.MediaWikiApiCall'
+       ],
+       fileUpload: true,
+       layout: {
+               type: 'vbox', 
+               align: 'stretch'
+       },
+
+       initComponent: function(){
+
+               this.fuFile = Ext.create('Ext.form.field.File', {
+                       fieldLabel: 
mw.message('bs-upload-uploadfilefieldlabel').plain(),
+                       buttonText: 
mw.message('bs-upload-uploadbuttontext').plain(),
+                       id: this.getId()+'-file',
+                       name: 'file',
+                       emptyText: 
mw.message('bs-upload-uploadfileemptytext').plain(),
+                       validator: this.validateFile,
+                       validateOnChange: true,
+                       clearOnSubmit: false,
+                       msgTarget: 'under'
+
+               });
+       
+               
+               this.fuFile.on( 'change', this.fuFileChange, this);
+
+               this.tfFileName = Ext.create('Ext.form.TextField', {
+                       fieldLabel: 
mw.message('bs-upload-uploaddestfilelabel').plain(),
+                       id: this.getId()+'-filename',
+                       maskRe: new RegExp(/[^\/\?\*\"\#\<\>\|\\]/),
+                       name: 'filename'
+               });
+
+               this.tfFileName.on( 'change', this.tfFileNameChange, this);
+
+               this.taDescription = Ext.create('Ext.form.field.TextArea', {
+                       fieldLabel: 
mw.message('bs-upload-descfilelabel').plain(),
+                       id: this.getId()+'-description',
+                       value: '',
+                       submitValue: false
+               });
+
+               //This hidden field will store the combined data of 
this.storeLicenses,
+               //this.taDescription and this.bsCategories on submit
+               this.hfText = Ext.create('Ext.form.field.Hidden', {
+                       id: this.getId()+'-text',
+                       value: '',
+                       name: 'text'
+               });
+
+               this.storeLicenses = Ext.create( 'BS.store.BSApi', {
+                       apiAction: 'bs-upload-license-store',
+                       fields: ['text', 'value', 'indent'],
+                       submitValue: false
+               });
+
+               this.cbLicenses = Ext.create('Ext.form.ComboBox',{
+                       fieldLabel: mw.message('bs-upload-license').plain(),
+                       //autoSelect: true,
+                       //forceSelection: true,
+                       //typeAhead: true,
+                       //triggerAction: 'all',
+                       //lazyRender: true,
+                       mode: 'local',
+                       store: this.storeLicenses,
+                       valueField: 'value',
+                       displayField: 'text',
+                       tpl: new Ext.XTemplate(
+                               '<ul class="x-list-plain">',
+                                 '<tpl for=".">',
+                                   '<tpl if="this.hasValue(value) == false">',
+                                     '<li role="option" 
class="x-boundlist-item no-value">{text}</li>',
+                                   '</tpl>',
+                                   '<tpl if="this.hasValue(value)">',
+                                     '<li role="option" 
class="x-boundlist-item indent-{indent}">{text}</li>',
+                                   '</tpl>',
+                                 '</tpl>',
+                               '</ul>',
+                               {
+                                       compiled: true,
+                                       disableFormats: true,
+                                       // member functions:
+                                       hasValue: function(value) {
+                                               return value !== '';
+                                       }
+                               }
+                       )
+               });
+
+               this.cbxWatch = Ext.create('Ext.form.field.Checkbox', {
+                       boxLabel: 
mw.message('bs-upload-uploadwatchthislabel').plain(),
+                       id: this.getId()+'watch_page',
+                       name: 'watchlist',
+                       inputValue: 'watch'
+               });
+
+               this.cbxWarnings = Ext.create('Ext.form.field.Checkbox', {
+                       boxLabel: 
mw.message('bs-upload-uploadignorewarningslabel').plain(),
+                       id: this.getId()+'ignorewarnings',
+                       name: 'ignorewarnings'
+               });
+
+               this.bsCategories = Ext.create( 'BS.form.CategoryBoxSelect', {
+                       id: this.getId()+'categories',
+                       fieldLabel: mw.message('bs-upload-categories').plain(),
+                       submitValue: false
+               });
+
+               this.fsDetails = Ext.create( 'Ext.form.FieldSet', {
+                       title: 'Details',
+                       collapsed: true,
+                       collapsible: true,
+                       anchor: '98%',
+                       defaults: {
+                               anchor: '100%',
+                               labelWidth: 90,
+                               labelAlign: 'right'
+                       }
+               });
+
+               this.panelItems = [
+                       this.fuFile,
+                       this.tfFileName,
+                       this.fsDetails
+               ];
+
+               var detailsItems = [
+                       this.hfText,
+                       this.bsCategories,
+                       this.taDescription,
+                       this.cbLicenses,
+                       this.cbxWarnings,
+                       this.cbxWatch
+               ];
+
+               $(document).trigger( 'BSUploadPanelInitComponent', [ this, 
this.panelItems, detailsItems ] );
+
+               this.fsDetails.add( detailsItems );
+
+               this.items = this.panelItems;
+
+               this.addEvents( 'uploadcomplete' );
+
+               this.callParent(arguments);
+
+       },
+
+
+       fuFileChange: function(field, value, eOpts){
+               //Remove path info
+               value = value.replace(/^.*?([^\\\/:]*?\.[a-z0-9]+)$/img, "$1");
+               value = value.replace(/\s/g, "_");
+               if( mw.config.get('bsIsWindows') ) {
+                       value = value.replace(/[^\u0000-\u007F]/gmi, ''); 
//Replace Non-ASCII
+               }
+               //apply value without 'C:\fakepath\' to file filed as well
+               field.setRawValue(value);
+
+               this.tfFileName.setValue(value);
+               this.tfFileName.fireEvent('change', this.tfFileName, value);
+
+       },
+
+       
+
+       tfFileNameChange: function(sender, newValue, oldValue, eOpts){
+               var Api = new mw.Api();
+               var me = this;
+               Api.get({
+                       action: 'query',
+                       format: 'json',
+                       titles: 'File:' + newValue,
+                       prop: 'imageinfo',
+                       iiprop: 'uploadwarning',
+                       indexpageids: ''
+               }).done( function ( response ) {
+                       //file does not exist
+                       if( response.query.pageids[0] === "-1" ) {
+                               return null;
+                       }
+                       //replacement warning, notify user and if user ignores, 
set ignore warnings automatically
+                       bs.util.alert(
+                               me.getId()+'-existswarning',
+                               {
+                                       titleMsg: 'bs-extjs-title-warning',
+                                       text: 
response.query.pages[response.query.pageids[0]]
+                                               .imageinfo[0]
+                                               .html
+                               },
+                               {
+                                       ok: function() {
+                                       
+                                               me.cbxWarnings.setValue(true);
+                                       },
+                                       scope: me
+                               }
+                       );
+               });
+       },
+
+       checkFileSize: function( ExtCmpId ) {
+               //No FileAPI? No love.
+               if(typeof window.FileReader === 'undefined') return true;
+
+               var allowedSize = mw.config.get('bsMaxUploadSize');
+               if( allowedSize === null ) return true;
+
+               var filesize = this.fuFile.fileInputEl.dom.files[0].size;
+               if( filesize > allowedSize.php || filesize > 
allowedSize.mediawiki) {
+                       return false;
+               }
+               return true;
+       },
+
+       //PW(12.03.2015) TODO: Make a second ajax request to edit file 
description
+       //(text), cause mediawiki api for action upload does not allow to 
change an
+       //existing text while uploading.
+       uploadFile: function( sessionKeyForReupload ) {
+               var desc = this.taDescription.getValue();
+               var license = this.cbLicenses.getValue();
+               if( license ) {
+                       desc += license + "\n";
+               }
+
+               var categories = this.bsCategories.getValue();
+               var formattedNamespaces = 
mw.config.get('wgFormattedNamespaces');
+               for( var i = 0; i < categories.length; i++ ) {
+                       var categoryLink = new bs.wikiText.Link({
+                               title: categories[i].ucFirst(),
+                               nsText: formattedNamespaces[bs.ns.NS_CATEGORY],
+                               link: false //TDOD: fix this in 
"bs.wikiText.Link"
+                       });
+                       desc += "\n" + categoryLink.toString();
+               }
+               this.hfText.setValue(desc);
+
+               this.cbLicenses.disable(); //To prevent the form from 
submitting a generated name
+
+               var params = {
+                       action: 'upload',
+                       token: mw.user.tokens.get('editToken'),
+                       //IE9 has an issue with this API call returnug a 
application/json
+                       //content-type. Therefore we let the server return a 
"text/xml"
+                       //content-type header
+                       //HINT: 
http://stackoverflow.com/questions/18571719/extjs-file-uploading-error-on-ie8-ie9
+                       format: 'xml'
+               };
+
+               if( sessionKeyForReupload ) {
+                       params.sessionkey = sessionKeyForReupload;
+               }
+
+               this.getForm().doAction( 
Ext.create('BS.form.action.MediaWikiApiCall', {
+                       form: this.getForm(), //Required
+                       url: mw.util.wikiScript('api'),
+                       params: params,
+                       success: this.onUploadSuccess,
+                       failure: this.onUploadFailure,
+                       scope: this
+               }));
+
+               //We mask only the FormPanel, because masking the whole 
document using
+               // "waitMsg" param on MediaWikiApiCall does no automatic 
unmasking.
+               //This is because MediaWikiApiCall overrides the 
onSuccess/onFailure
+               //methods of action "Submit"
+               this.getEl().mask(
+                       mw.message('bs-upload-upload-waitmessage').plain(),
+                       Ext.baseCSSPrefix + 'mask-loading'
+               );
+       },
+
+       onUploadSuccess: function( response, action ) {
+               this.getEl().unmask();
+               this.cbLicenses.enable();
+               
+               var errorTag = response.responseXML
+                       .documentElement.getElementsByTagName('error').item(0);
+
+               if( errorTag !== null ) {
+                       //Mw API only renders un-localized 
windows-nonascii-filename
+                       if( errorTag.getAttribute('invalidparameter') === 
"filename" ) {
+                               if( 
errorTag.getAttribute('info').indexOf('windows-nonascii-filename') >= 0 ) {
+                                       bs.util.alert(
+                                               this.getId()+'-error', {
+                                                       title: 
mw.message('bs-upload-error').plain(),
+                                                       text: 
mw.message('windows-nonascii-filename').plain()
+                                               }
+                                       );
+                                       return;
+                               }
+                       }
+                       bs.util.alert(
+                               this.getId()+'-error',
+                               {
+                                       title: 
mw.message('bs-upload-error').plain(),
+                                       text: errorTag.getAttribute('info')
+                               }
+                       );
+                       
+                       return;
+               }
+
+               //As we process XML instead of JSON (see reason above) we have 
to
+               //create a suitable JS object from the XML response to be 
compatible
+               var uploadTag = response.responseXML
+                       .documentElement.getElementsByTagName('upload').item(0);
+
+               var imageinfoTag = 
uploadTag.getElementsByTagName('imageinfo').item(0);
+
+               var warningsTag = 
uploadTag.getElementsByTagName('warnings').item(0);
+               if( warningsTag !== null && imageinfoTag === null ) {
+                       var duplicate = 
warningsTag.getElementsByTagName('duplicate');
+                       if( duplicate !== null && duplicate.length > 0 ) {
+                               var dupUrls = [];
+                               $.each(duplicate, function() {
+                                       dupUrls.push( "File:"+this.textContent 
);
+                               });
+                               this.duplicateWarning({
+                                       titles: dupUrls
+                               });
+                               return;
+                       }
+                       // Unknown warnings
+                       bs.util.alert(
+                               this.getId()+'-warning',
+                               {
+                                       title: 
mw.message('bs-upload-error').plain(),
+                                       text: $(warningsTag).html()
+                               }
+                       );
+                       
+                       return;
+               }
+
+               var imageinfo = {};
+               if( imageinfoTag.attributes ) {
+                       for( var i = 0; i < imageinfoTag.attributes.length; i++ 
) {
+                               var attribute = imageinfoTag.attributes.item(i);
+                               imageinfo[attribute.nodeName] = 
attribute.nodeValue;
+                       }
+               }
+               var upload = {
+                       result: uploadTag.getAttribute('result'),
+                       filename: uploadTag.getAttribute('filename'),
+                       imageinfo: imageinfo
+               };
+               
+               this.fireEvent( 'upload-complete', this, upload );
+               //walkaround for reseting fuFile Field
+               this.fuFile.setRawValue('');
+               this.getForm().reset();
+       },
+
+       onUploadFailure: function( response, action ) {
+               //This would only happen when a server error occurred but not 
when the
+               //MediaWiki API returns an JSON encoded error
+               
+               this.getEl().unmask();
+               this.getForm().reset();
+               this.cbLicenses.enable();
+               //walkaround for reseting fuFile Field
+               this.fuFile.setRawValue('');
+               bs.util.alert(
+                               this.getId()+'-error',
+                               {
+                                       title: 
mw.message('bs-upload-error').plain(),
+                                       text: 
mw.message('bs-upload-error-long').plain()
+                               }
+                       );
+               
+       },
+
+       //scope: "this" == fuFile
+       validateFile: function( value ) {
+               if( value === "" ) return true;
+               var me = this.up('form');
+               
+               
+               var nameParts = value.split('.');
+               
+               var fileExtension = nameParts[nameParts.length-1].toLowerCase();
+               var extensionFound = false;
+
+               for (i = 0; i<me.allowedFileExtensions.length; i++){
+
+                       if (me.allowedFileExtensions[i].toLowerCase() === 
fileExtension.toLowerCase()){
+
+                               extensionFound = true;
+
+                               break;
+                       }
+               }
+               if(!extensionFound){
+                       return 
mw.message('bs-upload-filetypenotsupported').plain();
+               }
+
+               if( me.checkFileSize() === false ) {
+                       return mw.message( 'largefileserver' ).plain();
+               }
+
+               return true;
+               
+       },
+
+       duplicateWarning: function( params ) {
+               var Api = new mw.Api();
+               var me = this;
+               params = $.extend({
+                       action: 'query',
+                       format: 'json',
+                       titles: [],
+                       prop: 'imageinfo',
+                       iiprop: 'url|uploadwarning',
+                       iiurlwidth: 64,
+                       indexpageids: ''
+               }, params);
+               params.titles = params.titles.join('|');
+
+               Api.get(params).done( function ( response ) {
+                       var text = '';
+                       var count = 0;
+                       for( var i in response.query.pages ) {
+                               if( i < 0 ) {
+                                       continue;
+                               }
+                               count ++;
+                               var href = 
response.query.pages[i].imageinfo[0].descriptionurl;
+                               var src = 
response.query.pages[i].imageinfo[0].thumburl;
+                               var title = response.query.pages[i].title;
+                               text += "<div class='thumbinner' 
style='width:62px; float:left'><a target='_blank' class='image' href="+href+" 
title="+title+"><img src='"+src+"' /></a></div>";
+                       }
+
+                       bs.util.alert(
+                               me.getId()+'-existswarning',
+                               {
+                                       titleMsg: 'bs-extjs-title-warning',
+                                       text: 
mw.message('file-exists-duplicate',count).text() + '<br /><div 
style="clear:both">' + text + '</div>'
+                               },
+                               {
+                                       ok: function() {
+                                               //User is noticed. Now let's 
set the
+                                               //ignore warnings flag 
automatically
+                                               me.cbxWarnings.setValue(true);
+                                       },
+                                       scope: me
+                               }
+                       );
+               });
+       }
+});
\ No newline at end of file

-- 
To view, visit https://gerrit.wikimedia.org/r/311676
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I36bfbc5897562a035f5c2dac4c0d4340760b5722
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation
Gerrit-Branch: master
Gerrit-Owner: ItSpiderman <d.savulje...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to