aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorKate <26026535+provokateurin@users.noreply.github.com>2024-09-20 17:36:11 +0200
committerGitHub <noreply@github.com>2024-09-20 17:36:11 +0200
commit74bfca4f09e620a6180f3abf6a24cd6c40855fe4 (patch)
tree9607b4b9ef7807a079ea900124b5a01c349ba8bc /apps
parentbc5222726b7f6f04308231ff55b8e89ade582d37 (diff)
parent368569c094808117bfd1dd7b775ebfa8507fe66b (diff)
downloadnextcloud-server-74bfca4f09e620a6180f3abf6a24cd6c40855fe4.tar.gz
nextcloud-server-74bfca4f09e620a6180f3abf6a24cd6c40855fe4.zip
Merge pull request #48241 from nextcloud/chore/remove-legacy-files-scripts
Diffstat (limited to 'apps')
-rw-r--r--apps/files/css/detailsView.css5
-rw-r--r--apps/files/css/detailsView.css.map1
-rw-r--r--apps/files/css/detailsView.css.map.license3
-rw-r--r--apps/files/css/detailsView.scss133
-rw-r--r--apps/files/css/files.css11
-rw-r--r--apps/files/css/files.css.map1
-rw-r--r--apps/files/css/files.css.map.license3
-rw-r--r--apps/files/css/files.scss1313
-rw-r--r--apps/files/css/merged.css28
-rw-r--r--apps/files/css/merged.css.map1
-rw-r--r--apps/files/css/merged.css.map.license2
-rw-r--r--apps/files/css/merged.scss9
-rw-r--r--apps/files/css/mobile.css8
-rw-r--r--apps/files/css/mobile.css.map1
-rw-r--r--apps/files/css/mobile.css.map.license3
-rw-r--r--apps/files/css/mobile.scss95
-rw-r--r--apps/files/css/upload.css5
-rw-r--r--apps/files/css/upload.css.map1
-rw-r--r--apps/files/css/upload.css.map.license3
-rw-r--r--apps/files/css/upload.scss215
-rw-r--r--apps/files/js/app.js398
-rw-r--r--apps/files/js/breadcrumb.js352
-rw-r--r--apps/files/js/detailfileinfoview.js51
-rw-r--r--apps/files/js/detailsview.js284
-rw-r--r--apps/files/js/detailtabview.js137
-rw-r--r--apps/files/js/file-upload.js1448
-rw-r--r--apps/files/js/fileactions.js915
-rw-r--r--apps/files/js/fileactionsmenu.js143
-rw-r--r--apps/files/js/fileinfomodel.js144
-rw-r--r--apps/files/js/filelist.js4038
-rw-r--r--apps/files/js/filemultiselectmenu.js92
-rw-r--r--apps/files/js/files.js543
-rw-r--r--apps/files/js/filesummary.js267
-rw-r--r--apps/files/js/gotoplugin.js58
-rw-r--r--apps/files/js/jquery-visibility.js91
-rw-r--r--apps/files/js/jquery.fileupload.js1504
-rw-r--r--apps/files/js/keyboardshortcuts.js170
-rw-r--r--apps/files/js/mainfileinfodetailview.js191
-rw-r--r--apps/files/js/merged-index.json28
-rw-r--r--apps/files/js/merged-index.json.license2
-rw-r--r--apps/files/js/newfilemenu.js261
-rwxr-xr-xapps/files/js/operationprogressbar.js77
-rw-r--r--apps/files/js/recentfilelist.js102
-rw-r--r--apps/files/js/semaphore.js36
-rw-r--r--apps/files/js/sidebarpreviewmanager.js130
-rw-r--r--apps/files/js/sidebarpreviewtext.js43
-rw-r--r--apps/files/js/tagsplugin.js267
-rw-r--r--apps/files/js/templates.js430
-rw-r--r--apps/files/js/templates/detailsview.handlebars13
-rw-r--r--apps/files/js/templates/favorite_mark.handlebars4
-rw-r--r--apps/files/js/templates/file_action_trigger.handlebars13
-rw-r--r--apps/files/js/templates/fileactionsmenu.handlebars17
-rw-r--r--apps/files/js/templates/filemultiselectmenu.handlebars14
-rw-r--r--apps/files/js/templates/filesummary.handlebars7
-rw-r--r--apps/files/js/templates/mainfileinfodetailsview.handlebars21
-rw-r--r--apps/files/js/templates/newfilemenu.handlebars10
-rw-r--r--apps/files/js/templates/newfilemenu_filename_form.handlebars4
-rw-r--r--apps/files/js/templates/operationprogressbar.handlebars6
-rw-r--r--apps/files/js/templates/operationprogressbarlabel.handlebars4
-rw-r--r--apps/files/js/templates/template_addbutton.handlebars4
-rw-r--r--apps/files/js/upload.js13
-rw-r--r--apps/files/lib/Controller/ViewController.php1
-rw-r--r--apps/files/lib/Listener/LoadSidebarListener.php3
-rw-r--r--apps/files/src/main-personal-settings.js8
-rw-r--r--apps/files/templates/fileexists.html35
-rw-r--r--apps/files/tests/js/breadcrumbSpec.js648
-rw-r--r--apps/files/tests/js/detailsviewSpec.js240
-rw-r--r--apps/files/tests/js/fileactionsSpec.js739
-rw-r--r--apps/files/tests/js/fileactionsmenuSpec.js344
-rw-r--r--apps/files/tests/js/filelistSpec.js3405
-rw-r--r--apps/files/tests/js/filesSpec.js129
-rw-r--r--apps/files/tests/js/filesummarySpec.js227
-rw-r--r--apps/files/tests/js/mainfileinfodetailviewSpec.js271
-rw-r--r--apps/files/tests/js/newfilemenuSpec.js134
-rw-r--r--apps/files/tests/js/tagspluginspec.js153
-rw-r--r--apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php1
-rw-r--r--apps/settings/templates/settings/frame.php1
77 files changed, 3 insertions, 20509 deletions
diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css
deleted file mode 100644
index 13a07ec303e..00000000000
--- a/apps/files/css/detailsView.css
+++ /dev/null
@@ -1,5 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */.app-sidebar .detailFileInfoContainer{min-height:50px;padding:15px}.app-sidebar .detailFileInfoContainer>div{clear:both}.app-sidebar .mainFileInfoView .icon{display:inline-block;background-size:16px 16px}.app-sidebar .mainFileInfoView .permalink{padding:6px 10px;vertical-align:top;opacity:.6}.app-sidebar .mainFileInfoView .permalink:hover,.app-sidebar .mainFileInfoView .permalink:focus{opacity:1}.app-sidebar .mainFileInfoView .permalink-field>input{clear:both;width:90%}.app-sidebar .thumbnailContainer.large{margin-inline:-15px -35px;margin-top:-15px}.app-sidebar .thumbnailContainer.large.portrait{margin:0}.app-sidebar .large .thumbnail{width:100%;display:block;background-repeat:no-repeat;background-position:center;background-size:100%;float:none;margin:0;height:auto}.app-sidebar .large .thumbnail .stretcher{content:"";display:block;padding-bottom:56.25%}.app-sidebar .large.portrait .thumbnail{background-position:50% top}.app-sidebar .large.portrait .thumbnail{background-size:contain}.app-sidebar .large.text{overflow-y:scroll;overflow-x:hidden;padding-top:14px;font-size:80%;margin-inline-start:0}.app-sidebar .thumbnail{width:100%;min-height:75px;display:inline-block;float:left;margin-inline-end:10px;background-size:contain;background-repeat:no-repeat}.app-sidebar .ellipsis{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.app-sidebar .fileName{font-size:16px;padding-top:13px;padding-bottom:3px}.app-sidebar .fileName h3{width:calc(100% - 42px);display:inline-block;padding:5px 0;margin:-5px 0}.app-sidebar .file-details{color:var(--color-text-maxcontrast)}.app-sidebar .action-favorite{vertical-align:sub;padding:10px;margin:-10px}.app-sidebar .action-favorite>span{opacity:.7 !important}.app-sidebar .detailList{float:left}.app-sidebar .close{position:absolute;top:0;inset-inline-end:0;opacity:.5;z-index:1;width:44px;height:44px}/*# sourceMappingURL=detailsView.css.map */
diff --git a/apps/files/css/detailsView.css.map b/apps/files/css/detailsView.css.map
deleted file mode 100644
index 6155e246553..00000000000
--- a/apps/files/css/detailsView.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sourceRoot":"","sources":["detailsView.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GAKA,sCACC,gBACA,aAGD,0CACC,WAID,qCACC,qBACA,0BAGD,0CACC,iBACA,mBACA,WAEA,gGAEC,UAGF,sDACC,WACA,UAGD,uCACC,0BACA,iBAGD,gDACC,SAGD,+BACC,WACA,cACA,4BACA,2BACA,qBACA,WACA,SACA,YAGD,0CACC,WACA,cACA,sBAGD,wCACC,4BAGD,wCACC,wBAGD,yBACC,kBACA,kBACA,iBACA,cACA,sBAGD,wBACC,WACA,gBACA,qBACA,WACA,uBACA,wBACA,4BAGD,uBACC,mBACA,uBACA,gBAGD,uBACC,eACA,iBACA,mBAGD,0BACC,wBACA,qBACA,cACA,cAGD,2BACC,oCAGD,8BACC,mBACA,aACA,aAGD,mCACC,sBAGD,yBACC,WAGD,oBACC,kBACA,MACA,mBACA,WACA,UACA,WACA","file":"detailsView.css"} \ No newline at end of file
diff --git a/apps/files/css/detailsView.css.map.license b/apps/files/css/detailsView.css.map.license
deleted file mode 100644
index 97c7e742c56..00000000000
--- a/apps/files/css/detailsView.css.map.license
+++ /dev/null
@@ -1,3 +0,0 @@
-SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/files/css/detailsView.scss b/apps/files/css/detailsView.scss
deleted file mode 100644
index 2fa09606ed7..00000000000
--- a/apps/files/css/detailsView.scss
+++ /dev/null
@@ -1,133 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-.app-sidebar .detailFileInfoContainer {
- min-height: 50px;
- padding: 15px;
-}
-
-.app-sidebar .detailFileInfoContainer > div {
- clear: both;
-}
-
-
-.app-sidebar .mainFileInfoView .icon {
- display: inline-block;
- background-size: 16px 16px;
-}
-
-.app-sidebar .mainFileInfoView .permalink {
- padding: 6px 10px;
- vertical-align: top;
- opacity: .6;
-
- &:hover,
- &:focus {
- opacity: 1;
- }
-}
-.app-sidebar .mainFileInfoView .permalink-field>input {
- clear: both;
- width: 90%;
-}
-
-.app-sidebar .thumbnailContainer.large {
- margin-inline: -15px -35px; /* 15 + 20 for the close button */
- margin-top: -15px;
-}
-
-.app-sidebar .thumbnailContainer.large.portrait {
- margin: 0; /* if we don't fit the image anyway we give it back the margin */
-}
-
-.app-sidebar .large .thumbnail {
- width:100%;
- display:block;
- background-repeat: no-repeat;
- background-position: center;
- background-size: 100%;
- float: none;
- margin: 0;
- height: auto;
-}
-
-.app-sidebar .large .thumbnail .stretcher {
- content: '';
- display: block;
- padding-bottom: 56.25%; /* sets height of .thumbnail to 9/16 of the width */
-}
-
-.app-sidebar .large.portrait .thumbnail {
- background-position: 50% top;
-}
-
-.app-sidebar .large.portrait .thumbnail {
- background-size: contain;
-}
-
-.app-sidebar .large.text {
- overflow-y: scroll;
- overflow-x: hidden;
- padding-top: 14px;
- font-size: 80%;
- margin-inline-start: 0;
-}
-
-.app-sidebar .thumbnail {
- width: 100%;
- min-height: 75px;
- display: inline-block;
- float: left;
- margin-inline-end: 10px;
- background-size: contain;
- background-repeat: no-repeat;
-}
-
-.app-sidebar .ellipsis {
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
-}
-
-.app-sidebar .fileName {
- font-size: 16px;
- padding-top: 13px;
- padding-bottom: 3px;
-}
-
-.app-sidebar .fileName h3 {
- width: calc(100% - 42px); /* 36px is the with of the copy link icon, but this breaks so we add some more to be sure */
- display: inline-block;
- padding: 5px 0;
- margin: -5px 0;
-}
-
-.app-sidebar .file-details {
- color: var(--color-text-maxcontrast);
-}
-
-.app-sidebar .action-favorite {
- vertical-align: sub;
- padding: 10px;
- margin: -10px;
-}
-
-.app-sidebar .action-favorite > span{
- opacity: .7 !important;
-}
-
-.app-sidebar .detailList {
- float: left;
-}
-
-.app-sidebar .close {
- position: absolute;
- top: 0;
- inset-inline-end: 0;
- opacity: .5;
- z-index: 1;
- width: 44px;
- height: 44px;
-}
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
deleted file mode 100644
index 4be196fdcd1..00000000000
--- a/apps/files/css/files.css
+++ /dev/null
@@ -1,11 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- *//*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *//*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */.actions{padding:3px;height:100%;display:inline-block;float:left}.actions input,.actions button,.actions .button{margin:0;float:left}.actions .button a{color:#555}.actions .button a:hover,.actions .button a:focus{background-color:var(--color-background-hover)}.actions .button a:active{background-color:var(--color-primary-element-light)}.actions.creatable{position:relative;display:flex;flex:1 1}.actions.creatable .button:not(:last-child){margin-inline-end:3px;width:unset;gap:14px;background-color:var(--color-primary-element-light);color:var(--color-primary-element-light-text);border:unset;padding:0px 20px}.actions.hidden{display:none}#trash{margin-inline-end:8px;float:right;z-index:1010;padding:10px;font-weight:normal}.newFileMenu .error,.newFileMenu .error+.icon-confirm,.files-fileList .error{color:var(--color-error);border-color:var(--color-error)}.files-filestable{position:relative;width:100%;min-width:250px;display:block;flex-direction:column}.emptycontent:not(.hidden)~.files-filestable{display:none}.files-filestable thead{position:-webkit-sticky;position:sticky;top:44px;z-index:60;display:block;background-color:var(--color-main-background-translucent)}.files-filestable tbody{display:table;width:100%}.files-filestable tbody tr[data-permissions="0"],.files-filestable tbody tr[data-permissions="16"]{background-color:var(--color-background-dark)}.files-filestable tbody tr[data-permissions="0"] td.filename .nametext .innernametext,.files-filestable tbody tr[data-permissions="16"] td.filename .nametext .innernametext{color:var(--color-text-maxcontrast)}.files-filestable tbody tr[data-e2eencrypted=true] .selection{pointer-events:none}.files-filestable.hidden{display:none}.app-files #app-content>.viewcontainer{min-height:0%;width:100%}.app-files #app-content{width:calc(100% - 300px);overflow-anchor:none}.file-drag,.file-drag .files-filestable tbody tr,.file-drag .files-filestable tbody tr:hover{background-color:var(--color-primary-element-light) !important}.app-files #app-content.dir-drop{background-color:var(--color-main-background) !important}.file-drag .files-filestable tbody tr,.file-drag .files-filestable tbody tr:hover{background-color:rgba(0,0,0,0) !important}.app-files #app-content.dir-drop .files-filestable tbody tr.dropping-to-dir{background-color:var(--color-primary-element-light) !important}#app-navigation .nav-files a.new{width:40px;height:32px;padding:0 10px;margin:0;cursor:pointer}#app-navigation .nav-files a.new.hidden{display:none}#app-navigation .nav-files a.new.disabled{opacity:.3}.files-filestable tbody tr{height:51px}.files-filestable tbody tr:hover,.files-filestable tbody tr:focus,.files-filestable tbody .name:focus,.files-filestable tbody tr:hover .filename form,table tr.mouseOver td{background-color:var(--color-background-hover)}.files-filestable tbody tr:active,.files-filestable tbody tr.highlighted,.files-filestable tbody tr.highlighted .name:focus,.files-filestable tbody tr.selected,.files-filestable tbody tr.searchresult{background-color:var(--color-primary-element-light)}tbody a{color:var(--color-main-text)}span.conflict-path,span.extension,span.uploading,td.date{color:var(--color-text-maxcontrast)}span.conflict-path,span.extension{-webkit-transition:opacity 300ms;-moz-transition:opacity 300ms;-o-transition:opacity 300ms;transition:opacity 300ms;vertical-align:top}tr:hover span.conflict-path,tr:focus span.conflict-path,tr:hover span.extension,tr:focus span.extension{opacity:1;color:var(--color-text-maxcontrast)}table th,table th a{color:var(--color-text-maxcontrast)}table.multiselect th a{color:var(--color-main-text)}table th .columntitle{display:block;padding:15px;height:50px;box-sizing:border-box;-moz-box-sizing:border-box;vertical-align:middle}table th .columntitle:focus-visible{border-radius:2px}table.multiselect th .columntitle{display:inline-block;margin-inline-end:-20px}table th .columntitle.name{padding-inline-start:0;margin-inline-start:44px}table.multiselect th .columntitle.name{margin-inline-start:0}table th .sort-indicator{width:10px;height:8px;margin-inline-start:5px;display:inline-block;vertical-align:text-bottom;opacity:.3}.sort-indicator.hidden,.multiselect .sort-indicator,table.multiselect th:hover .sort-indicator.hidden,table.multiselect th:focus .sort-indicator.hidden{visibility:hidden}.multiselect .sort,.multiselect .sort span{cursor:default}table th:hover .sort-indicator.hidden,table th:focus .sort-indicator.hidden{visibility:visible}table th,table td{border-bottom:1px solid var(--color-border);text-align:start;font-weight:normal}table td{padding:0 15px;font-style:normal;background-position:8px center;background-repeat:no-repeat}table th.column-name{position:relative;width:9999px;padding:0}.column-name-container{position:relative;height:50px}table th.column-selection{padding-top:2px}table th.column-size,table td.filesize{text-align:end}table th.column-mtime,table td.date,table th.column-last,table td.column-last{-moz-box-sizing:border-box;box-sizing:border-box;position:relative;min-width:130px}#app-content-recent,#app-content-favorites,#app-content-shareoverview,#app-content-sharingout,#app-content-sharingin,#app-content-sharinglinks,#app-content-deletedshares,#app-content-pendingshares{margin-top:22px}#app-content-recent thead,#app-content-favorites thead,#app-content-shareoverview thead,#app-content-sharingout thead,#app-content-sharingin thead,#app-content-sharinglinks thead,#app-content-deletedshares thead,#app-content-pendingshares thead{top:0}table.multiselect thead th{background-color:var(--color-main-background-translucent);font-weight:bold}#app-content.with-app-sidebar table.multiselect thead{margin-inline-end:27%}table.multiselect .column-name{position:relative;width:9999px}table.multiselect .column-mtime>a{display:none}table td.selection,table th.selection,table td.fileaction{width:32px;text-align:center}table td.filename a.name,table td.filename p.name{display:flex;position:relative;-moz-box-sizing:border-box;box-sizing:border-box;height:50px;line-height:50px;padding:0}table td.filename .thumbnail-wrapper{width:0;min-width:50px;max-width:50px;height:50px}table td.filename .thumbnail-wrapper.icon-loading-small:after{z-index:10}table td.filename .thumbnail-wrapper.icon-loading-small .thumbnail{opacity:.2}table td.filename .thumbnail{display:inline-block;width:32px;height:32px;background-size:contain;background-position:center;background-repeat:no-repeat;margin-inline-start:9px;margin-top:9px;border-radius:var(--border-radius);cursor:pointer;position:absolute;z-index:4}table td.filename p.name .thumbnail{cursor:default}table tr[data-has-preview=true] .thumbnail{border:1px solid var(--color-border)}table:not(.view-grid) td.filename input.filename{width:70% !important;margin-inline-start:48px !important;cursor:text}table td.filename form{margin-top:-40px;position:relative;top:-6px}table td.filename a,table td.login,table td.logout,table td.download,table td.upload,table td.create,table td.delete{padding:3px 8px 8px 3px}table td.filename .nametext,.modified,.column-last>span:first-child{float:left;padding:15px 0}.modified,.column-last>span:first-child{position:relative;overflow:hidden;text-overflow:ellipsis;width:110px}table td.filename{max-width:0}table td.filename .nametext{width:0;flex-grow:1;display:flex;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;height:100%;z-index:10;padding:0;padding-inline-end:20px}.hide-hidden-files .files-filestable .files-fileList tr.hidden-file,.hide-hidden-files .files-filestable .files-fileList tr.hidden-file.dragging{display:none !important}.files-fileList tr.animate-opacity{-webkit-transition:opacity 250ms;-moz-transition:opacity 250ms;-o-transition:opacity 250ms;transition:opacity 250ms}.files-fileList tr.dragging{opacity:.2}table td.filename .nametext .innernametext{text-overflow:ellipsis;overflow:hidden;position:relative;vertical-align:top}table td.filename .uploadtext{position:absolute;font-weight:normal;margin-inline-start:50px;inset-inline-start:0;bottom:0;height:20px;padding:0 4px;padding-inline-start:1px;font-size:11px;line-height:22px;color:var(--color-text-maxcontrast);text-overflow:ellipsis;white-space:nowrap}table td.selection{padding:0}.files-fileList tr td.selection>.selectCheckBox+label:before{opacity:.3;margin-inline-end:0}.files-fileList tr:hover td.selection>.selectCheckBox+label:before,.files-fileList tr:focus td.selection>.selectCheckBox+label:before,.files-fileList tr td.selection>.selectCheckBox:checked+label:before,.files-fileList tr.selected td.selection>.selectCheckBox+label:before{opacity:1}.files-fileList tr.halfselected td.selection>.selectCheckBox+label:before{opacity:.5}.files-fileList tr td.selection>.selectCheckBox+label,.select-all+label{padding:16px}.files-fileList tr td.selection>.selectCheckBox:focus-visible+label,.select-all:focus-visible+label{background-color:var(--color-background-hover);border-radius:var(--border-radius-pill);outline:none !important;border:2px solid var(--color-primary-element) !important;padding:14px}.files-fileList tr td.selection>.selectCheckBox:focus-visible+label,.select-all:focus-visible+label{outline-offset:0px}.files-fileList tr td.filename{position:relative;width:100%;padding-inline:0;-webkit-transition:background-image 500ms;-moz-transition:background-image 500ms;-o-transition:background-image 500ms;transition:background-image 500ms}.files-fileList tr td.filename a.name label,.files-fileList tr td.filename p.name label{position:absolute;width:80%;height:50px}.files-fileList tr td.filename .favorite{display:inline-block;float:left}.files-fileList tr td.filename .favorite-mark{position:absolute;display:block;top:-8px;inset-inline-end:-8px;line-height:100%;text-align:center}.files-fileList tr td.filename .favorite-mark.permanent{background-color:var(--color-main-background);mask:var(--icon-star-rounded-white) no-repeat;mask-size:22px 22px;width:22px;height:22px;display:flex;align-content:center;justify-content:center}.files-fileList tr:hover td.filename .favorite-mark.permanent{background-color:var(--color-background-hover)}#uploadsize-message,#delete-confirm{display:none}.fileactions{z-index:50}.busy .fileactions,.busy .action{visibility:hidden}.bubble,#app-navigation .app-navigation-entry-menu{min-width:100px}.files-fileList .icon-loading-small{opacity:1 !important;display:inline !important}.files-fileList .action.action-share-notification span,.files-fileList a.name{cursor:default !important}.files-fileList a.name.disabled *{cursor:default}.files-fileList a.name.disabled a,.files-fileList a.name.disabled a *{cursor:pointer}.files-fileList a.name.disabled:focus{background:none}a.action>img{height:16px;width:16px;vertical-align:text-bottom}a.action.action-editlocally img.icon,a.action.action-setreminder img.icon{filter:var(--background-invert-if-dark)}.selectedActions{position:relative;display:inline-block;vertical-align:middle}.selectedActions.hidden{display:none}.selectedActions a{display:inline;line-height:50px;padding:16px 5px}.selectedActions a.hidden{display:none}.selectedActions a img{position:relative;vertical-align:text-bottom;margin-bottom:-1px}.selectedActions .actions-selected .icon-more{margin-top:-3px}.files-fileList td a a.action{display:inline;padding:17px 8px;line-height:50px;opacity:.3}.files-fileList td a a.action.action-share{padding:17px 14px}.files-fileList td a a.action.action-share.permanent:not(.shared-style) .icon-shared+span{position:absolute;inset-inline-start:-10000px;top:auto;width:1px;height:1px;overflow:hidden}.files-fileList td a a.action.action-share .avatar{display:inline-block;vertical-align:middle}.files-fileList td a a.action.action-menu{padding-top:17px;padding-bottom:17px;padding-inline:14px}.files-fileList td a a.action.no-permission:hover,.files-fileList td a a.action.no-permission:focus{opacity:.3}.files-fileList td a a.action.disabled:hover,.files-fileList td a a.action.disabled:focus,.files-fileList td a a.action.disabled img{opacity:.3}.files-fileList td a a.action.disabled.action-download{opacity:.7}.files-fileList td a a.action.disabled.action-download:hover,.files-fileList td a a.action.disabled.action-download:focus{opacity:.7}.files-fileList td a a.action:hover,.files-fileList td a a.action:focus{opacity:1}.files-fileList td a a.action:focus{background-color:var(--color-background-hover);border-radius:var(--border-radius-pill)}.files-fileList td a .fileActionsMenu a.action,.files-fileList td a a.action.action-share.shared-style{opacity:.7}.files-fileList td a .fileActionsMenu .action.permanent{opacity:1}.files-fileList .action.action-share.permanent.shared-style span:not(.icon){display:inline-block;max-width:70px;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;margin-inline-end:6px}.files-fileList .remoteAddress .userDomain{margin-inline-start:0 !important}.files-fileList .favorite-mark.permanent{opacity:1}.files-fileList .fileActionsMenu a.action:hover,.files-fileList .fileActionsMenu a.action:focus,.files-fileList a.action.action-share.shared-style:hover,.files-fileList a.action.action-share.shared-style:focus{opacity:1}.files-fileList tr a.action.disabled{background:none}.selectedActions a.download.disabled,.files-fileList tr a.action.action-download.disabled{color:#000}.files-fileList tr:hover a.action.disabled:hover *{cursor:default}.summary{color:var(--color-text-maxcontrast);height:330px}.files-filestable .summary .filesummary{width:100%;padding-inline-start:101px}#body-public .summary{height:180px}.summary:hover,.summary:focus,.summary,table tr.summary td{background-color:rgba(0,0,0,0)}.summary td{border-bottom:none;vertical-align:top;padding-top:20px}.summary td:first-child{padding:0}.hiddeninfo{white-space:pre-line}table.dragshadow{width:auto;z-index:2000}table.dragshadow td.filename{padding-inline:60px 16px;height:36px;max-width:unset}table.dragshadow td.size{padding-inline-end:8px}.mask{z-index:50;position:absolute;inset:0;background-color:var(--color-main-background);background-repeat:no-repeat no-repeat;background-position:50%;opacity:.7;transition:opacity 100ms;-moz-transition:opacity 100ms;-o-transition:opacity 100ms;-ms-transition:opacity 100ms;-webkit-transition:opacity 100ms}.mask.transparent{opacity:0}.newFileMenu{font-weight:300;top:100%;inset-inline-start:-48px !important;margin-top:4px;min-width:100px;z-index:1001}.newFileMenu::after{inset-inline-start:84px !important}.files-controls{box-sizing:border-box;position:-webkit-sticky;position:sticky;height:50px;padding:0;margin:0;background-color:var(--color-main-background-translucent);z-index:62;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;top:0;padding-inline-start:50px}.files-controls .actions>div>.button,.files-controls .actions>div button,.files-controls .actions>.button,.files-controls .actions button{box-sizing:border-box;display:inline-block;display:flex;height:44px;width:44px;padding:9px;align-items:center;justify-content:center}.files-controls .actions>div .button.hidden,.files-controls .actions .button.hidden{display:none}.viewer-mode #app-navigation+#app-content .files-controls{inset-inline-start:0}.files-filestable .filename .action .icon,.files-filestable .selectedActions a .icon,.files-filestable .filename .favorite-mark .icon,.files-controls .actions .button .icon{display:inline-block;vertical-align:middle;background-size:16px 16px}.files-filestable .filename .favorite-mark .icon-star{background-image:none}.files-filestable .filename .favorite-mark .icon-starred{background-image:var(--icon-starred-yellow) !important}.files-filestable .filename .action .icon.hidden,.files-filestable .selectedActions a .icon.hidden,.files-controls .actions .button .icon.hidden{display:none}.files-filestable .filename .action .icon.loading,.files-filestable .selectedActions a .icon.loading,.files-controls .actions .button .icon.loading{width:15px;height:15px}.app-files .actions .button.new{position:relative;width:unset;gap:14px;background-color:var(--color-primary-element-light);color:var(--color-primary-element-light-text);border:unset;padding:0px 20px}.breadcrumb{align-items:center}.breadcrumb .icon-home{border-radius:var(--border-radius)}.breadcrumb .canDrop>a,.files-filestable tbody tr.canDrop{background-color:rgba(0,130,201,.3)}.dropzone-background{background-color:rgba(0,130,201,.3)}.dropzone-background :hover{box-shadow:none !important}.notCreatable{margin-inline:12px 44px;margin-top:12px;color:var(--color-main-text);overflow:auto;min-width:160px;height:54px}.notCreatable:not(.hidden){display:flex}.notCreatable .icon-alert-outline{top:-15px;position:relative;margin-inline-end:4px}.quota-navigation-item{margin:0 !important;border:none;border-radius:0;background-color:rgba(0,0,0,0);z-index:1;height:44px;display:flex !important;flex-direction:column}.quota-navigation-item__text{height:30px}.quota-navigation-item[href="#"],.quota-navigation-item[href="#"] *{cursor:default !important}.quota-navigation-item__container{height:5px;border-radius:var(--border-radius)}.files-filestable.view-grid:not(.hidden) thead tr{display:block;border-bottom:1px solid var(--color-border);background-color:var(--color-main-background-translucent)}.files-filestable.view-grid:not(.hidden) thead tr th{width:auto;border:none}.files-filestable.view-grid:not(.hidden) tbody{display:grid;grid-template-columns:repeat(auto-fill, 160px);justify-content:space-around;row-gap:15px;margin:15px 0}.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden){display:block;position:relative;height:190px;border-radius:var(--border-radius)}.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted{background-color:rgba(0,0,0,0)}.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted .fileactions{background-color:var(--color-background-hover)}.files-filestable.view-grid:not(.hidden) tbody td{display:inline;border-bottom:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .thumbnail-wrapper{min-width:0;max-width:none;position:absolute;width:160px;height:160px;padding:14px;top:0;inset-inline-start:0;z-index:-1}.files-filestable.view-grid:not(.hidden) tbody td.filename .thumbnail-wrapper .thumbnail{width:calc(100% - 2*14px);height:calc(100% - 2*14px);background-size:contain;margin:0;border-radius:var(--border-radius);background-repeat:no-repeat;background-position:center}.files-filestable.view-grid:not(.hidden) tbody td.filename .thumbnail-wrapper .thumbnail .favorite-mark{inset-inline-start:auto;top:-11px;inset-inline-end:-11px}.files-filestable.view-grid:not(.hidden) tbody td.filename .uploadtext{width:100%;margin:0;top:0;bottom:auto;height:28px;padding-top:4px;padding-inline-start:28px}.files-filestable.view-grid:not(.hidden) tbody td.filename .name{height:100%;border-radius:var(--border-radius);overflow:hidden;cursor:pointer !important}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext{display:flex;height:44px;margin-top:146px;text-align:center;line-height:44px;padding:0}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext .innernametext{display:inline-block;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext:before{content:"";flex:1;min-width:14px}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext:after{content:"";flex:1;min-width:44px}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext .extension{display:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .system-tags{display:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .fileactions{height:initial;margin-top:146px;display:flex;align-items:center;position:absolute;inset-inline-end:0}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .fileactions .action{padding:14px;width:44px;height:44px;display:flex;align-items:center;justify-content:center}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .fileactions .action:not(.action-menu){display:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-share-container.hidden{display:block !important}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-share-container.hidden .action-share img{padding:6px;border-radius:50%}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-restore-container.hidden{display:block !important}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-comment-container.hidden{display:block !important}.files-filestable.view-grid:not(.hidden) tbody td.filename form{padding:3px 14px;border-radius:var(--border-radius)}.files-filestable.view-grid:not(.hidden) tbody td.filename form input.filename{width:100%;margin-inline-start:0;cursor:text}.files-filestable.view-grid:not(.hidden) tbody td.filesize,.files-filestable.view-grid:not(.hidden) tbody td.date{display:none}.files-filestable.view-grid:not(.hidden) tbody td.selection,.files-filestable.view-grid:not(.hidden) tbody td.filename .favorite-mark{position:absolute;top:-8px;inset-inline-start:-8px;display:flex;z-index:10}.files-filestable.view-grid:not(.hidden) tbody td.selection label,.files-filestable.view-grid:not(.hidden) tbody td.filename .favorite-mark label{width:44px;height:44px;display:inline-flex;padding:14px}.files-filestable.view-grid:not(.hidden) tbody td.selection label::before,.files-filestable.view-grid:not(.hidden) tbody td.filename .favorite-mark label::before{margin:0;width:14px;height:14px}.files-filestable.view-grid:not(.hidden) tbody td .popovermenu{inset-inline-start:0;width:150px;margin:0 5px}.files-filestable.view-grid:not(.hidden) tbody td .popovermenu .menuitem span:not(.icon){overflow:hidden;text-overflow:ellipsis}.files-filestable.view-grid:not(.hidden) tr.hidden-file td.filename .name .nametext .extension{display:block}.files-filestable.view-grid:not(.hidden) tfoot{display:grid}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden){display:inline-block;margin:0 auto;height:418px}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td{padding-top:50px}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td:first-child,.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td.date{display:none}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td .info{margin-inline-start:0}#view-toggle{background-color:var(--color-main-background-translucent);border:none;margin:0;padding:22px;opacity:.5;float:right;inset-inline-end:var(--default-grid-baseline);top:var(--default-grid-baseline);z-index:100;position:sticky}#view-toggle:hover,#view-toggle:focus,#showgridview:focus+#view-toggle{opacity:1}#view-toggle:focus-visible,#showgridview:focus-visible+#view-toggle{box-shadow:inset 0 0 0 2px var(--color-primary-element) !important}#showgridview{position:fixed;top:0}#body-public .files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext .innernametext{max-width:124px}#body-public .files-filestable.view-grid:not(.hidden) tbody td .popovermenu{inset-inline-start:-80px}#body-public #view-toggle{position:absolute;inset-inline-end:0;top:0}#gallery-button{display:none}#tag_multiple_files_container{overflow:hidden;background-color:#fff;border-radius:3px;position:relative;display:flex;flex-wrap:wrap;margin-bottom:10px}#tag_multiple_files_container h3{width:100%;padding:0 18px}#tag_multiple_files_container .systemTagsInputFieldContainer{flex:1 1 80%;min-width:0;margin:0 12px}/*# sourceMappingURL=files.css.map */
diff --git a/apps/files/css/files.css.map b/apps/files/css/files.css.map
deleted file mode 100644
index f7bb52b3d60..00000000000
--- a/apps/files/css/files.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sourceRoot":"","sources":["files.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GASA,SAEC,YACA,YACA,qBACA,WAED,oEACA,8BACA,kDAEC,+CAED,0BACC,oDAGD,mBACC,kBACA,aACA,SACA,4CACC,sBACA,YACA,SACA,oDACA,8CACA,aACA,iBAIF,gBACC,aAGD,OACC,sBACA,YACA,aACA,aACA,mBAGD,6EAGC,yBACA,gCAID,kBACC,kBACA,WACA,gBACA,cACA,sBAEA,6CACC,aAGD,wBACC,wBACA,gBAEA,SAEA,WACA,cACA,0DAMD,wBACC,cACA,WAEA,mGAEC,8CAEA,6KACC,oCAKF,8DACC,oBAKH,yBACC,aAID,uCACC,cACA,WAGD,wBAGC,yBAEA,qBAGD,6FACC,+DAGD,iCACC,yDAGD,kFACC,0CAGD,4EACC,+DAID,iCACC,WACA,YACA,eACA,SACA,eAGD,wCACC,aAGD,0CACC,WAGD,2BACC,YAED,4KAKC,+CAED,wMAKC,oDAGD,qCAEA,yDACC,oCAED,kCACC,iCACA,8BACA,4BACA,yBACA,mBAED,wGAIC,UACA,oCAGD,oBACC,oCAED,uBACC,6BAED,sBACC,cACA,aACA,YACA,sBACA,2BACA,sBACA,oCACC,kBAGF,kCACC,qBACA,wBAED,2BACC,uBACA,yBAGD,uCACC,sBAGD,yBACC,WACA,WACA,wBACA,qBACA,2BACA,WAED,wJAIC,kBAED,2CACC,eAED,4EAEC,mBAGD,kBAEC,4CACA,iBACA,mBAED,SACC,eACA,kBACA,+BACA,4BAED,qBACC,kBACA,aACA,UAGD,uBACC,kBACA,YAGD,0BACC,gBAED,uCACC,eAED,8EAEC,2BACA,sBACA,kBAEA,gBAGD,qMAQC,gBACA,qPACC,MAIF,2BACC,0DACA,iBAGD,sDACC,sBAGD,+BACC,kBACA,aAED,kCACC,aAGD,0DAGC,WACA,kBAED,kDAEC,aACA,kBACA,2BACA,sBACA,YACA,iBACA,UAED,qCAEC,QACA,eACA,eACA,YAGA,8DACC,WAED,mEACC,WAGF,6BACC,qBACA,WACA,YACA,wBACA,2BACA,4BACA,wBACA,eACA,mCACA,eACA,kBACA,UAED,oCACC,eAID,2CACC,qCAGD,iDACC,qBACA,oCACA,YAED,uBACC,iBACA,kBACA,SAGD,6IACA,8FAEA,wCACC,kBACA,gBACA,uBACA,YAKA,kBACC,YACA,4BACC,QACA,YACA,aACA,gBACA,mBACA,uBACA,YACA,WACA,UACA,wBAKH,iJAEC,wBAGD,mCACC,iCACA,8BACA,4BACA,yBAED,4BACC,WAGD,2CACC,uBACA,gBACA,kBACA,mBAKD,8BACC,kBACA,mBAEA,yBACA,qBACA,SACA,YACA,cAEA,yBACA,eAEA,iBACA,oCACA,uBACA,mBAGD,mBACC,UAID,6DACC,WACA,oBAID,iRAIC,UAID,0EACC,WAMA,wEACC,aAGD,oGACC,+CACA,wCACA,wBACA,yDACA,aAIF,oGAEC,mBAGD,+BACC,kBACA,WACA,iBACA,wJAGD,wFAEC,kBACA,UACA,YAGD,yCACC,qBACA,WAED,8CACC,kBACA,cACA,SACA,sBACA,iBACA,kBACA,wDAEC,8CACA,8CACA,oBAEA,WACA,YACA,aACA,qBACA,uBAGF,8DACC,+CAGD,iDAGA,aACC,WAGD,iCACC,kBAID,mDAEC,gBAID,oCACC,qBACA,0BAGD,8EACC,0BAOA,kCACC,eAGD,sEACC,eAGD,sCACC,gBAIF,aACC,YACA,WACA,2BAKA,0EACC,wCAKF,iBACI,kBACA,qBACA,sBAEJ,wBACI,aAEJ,mBACC,eACA,iBACA,iBAGD,0BACC,aAED,uBACC,kBACA,2BACA,mBAGD,8CACC,gBAIA,8BACC,eACA,iBACA,iBACA,WACA,2CACC,kBACA,0FAGC,kBACA,4BACA,SACA,UACA,WACA,gBAED,mDACC,qBACA,sBAGF,0CACC,iBACA,oBACA,oBAGA,oGACC,WAID,qIAEC,WAED,uDACC,WACA,0HACC,WAIH,wEACC,UAED,oCACC,+CACA,wCAGF,uGACC,WAED,wDACC,UAKF,4EACC,qBACA,eACA,gBACA,uBACA,sBACA,sBAGD,2CACC,iCAGD,yCACC,UAGD,kNAKC,UAGD,qCACC,gBAGD,0FAEC,WAGD,mDACC,eAGD,SACC,oCAGA,aAED,wCACC,WAEA,2BAKD,sBACC,aAED,2DAIC,+BAED,YACC,mBACA,mBACA,iBAED,wBACC,UAED,YACC,qBAGD,iBACC,WACA,aAED,6BACC,yBACA,YAGA,gBAED,yBACC,uBAED,MACC,WACA,kBACA,QACA,8CACA,sCACA,wBACA,WACA,yBACA,8BACA,4BACA,6BACA,iCAED,kBACC,UAGD,aACC,gBACA,SACA,oCACA,eACA,gBACA,aAGA,oBACC,mCAKF,gBACC,sBACA,wBACA,gBACA,YACA,UACA,SACA,0DACA,WACA,yBACA,sBACA,qBACA,iBACA,aACA,MACA,0BAKE,0IACC,sBACA,qBACA,aACA,YACA,WACA,YACA,mBACA,uBAED,oFACC,aAQJ,0DACC,qBAGD,6KAIC,qBACA,sBACA,0BAMA,sDACC,sBAED,yDACC,uDAIF,iJAGC,aAGD,oJAGC,WACA,YAGD,gCACC,kBACA,YACA,SACA,oDACA,8CACA,aACA,iBAGD,YACC,mBAEA,uBACC,mCAIF,0DAEC,oCAED,qBACC,oCACA,4BACC,2BAIF,cACC,wBACA,gBACA,6BACA,cACA,gBACA,YAEA,2BACC,aAGD,kCACC,UACA,kBACA,sBAIF,uBACC,oBACA,YACA,gBACA,+BACA,UACA,YACA,wBACA,sBAEA,6BACC,YAKA,oEACC,0BAIF,kCACC,WACA,mCAWA,kDACC,cACA,4CACA,0DACA,qDACC,WACA,YAMH,+CACC,aACA,+CACA,6BACA,aACA,cAGA,+DACC,cACA,kBACA,aACA,mCAEA,0fAKC,+BAEA,oxDAGC,+CAKH,kDACC,eACA,mBAGC,8EACC,YACA,eACA,kBACA,MAvDQ,MAwDR,OAxDQ,MAyDR,QAxDO,KAyDP,MACA,qBACA,WAEA,yFACC,0BACA,2BACA,wBACA,SACA,mCACA,4BACA,2BAKA,wGACC,wBACA,UACA,uBAKH,uEACC,WACA,SACA,MACA,YAEA,YACA,gBAEA,0BAGD,iEACC,YACA,mCAIA,gBAKA,0BAEA,2EACC,aACA,YACA,iBACA,kBACA,iBACA,UAEA,0FACC,qBACA,kBACA,gBACA,uBACA,mBAED,kFACC,WACA,OACA,eAED,iFACC,WACA,OACA,eAID,sFACC,aAKF,8EACC,aAGD,8EACC,eACA,iBACA,aACA,mBACA,kBACA,mBAEA,sFACC,QAxJK,KAyJL,WACA,YACA,aACA,mBACA,uBAGA,wGACC,aAQH,2GACC,yBAEA,6HACC,YACA,kBAIF,6GACC,yBAGD,6GACC,yBAIF,gEACC,iBACA,mCAEA,+EACC,WACA,sBACA,YAMH,kHAEC,aAGD,sIAEC,kBACA,SACA,wBACA,aACA,WAEA,kJACC,WACA,YACA,oBACA,QAzNO,KA0NP,kKACC,SACA,MA5NM,KA6NN,OA7NM,KAmOT,+DACC,qBACA,YACA,aAGA,yFACC,gBACA,uBAMJ,+FACC,cAID,+CACC,aAEA,qEACC,qBACA,cAEA,aAEA,wEACC,iBAEA,iKAEC,aAGD,8EACI,sBAQR,aACC,0DACA,YACA,SACA,aACA,WACA,YACA,8CACA,iCACA,YACA,gBAEA,uEAGC,UAGD,oEAEC,mEASF,cACC,eACA,MAOC,uGACC,gBAID,4EACC,yBAKF,0BACC,kBACA,mBACA,MAKF,gBACC,aAGD,8BACC,gBACA,sBACA,kBACA,kBACA,aACA,eACA,mBAEA,iCACC,WACA,eAGD,6DACC,aACA,YACA","file":"files.css"} \ No newline at end of file
diff --git a/apps/files/css/files.css.map.license b/apps/files/css/files.css.map.license
deleted file mode 100644
index 1c70f3b7dd4..00000000000
--- a/apps/files/css/files.css.map.license
+++ /dev/null
@@ -1,3 +0,0 @@
-SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss
deleted file mode 100644
index 53d00a11092..00000000000
--- a/apps/files/css/files.scss
+++ /dev/null
@@ -1,1313 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-@use 'variables';
-@import 'functions';
-
-/* FILE MENU */
-.actions {
- // (50px - 44px) / 2
- padding: 3px;
- height: 100%;
- display: inline-block;
- float: left;
-}
-.actions input, .actions button, .actions .button { margin:0; float:left; }
-.actions .button a { color: #555; }
-.actions .button a:hover,
-.actions .button a:focus {
- background-color: var(--color-background-hover);
-}
-.actions .button a:active {
- background-color: var(--color-primary-element-light);
-}
-
-.actions.creatable {
- position: relative;
- display: flex;
- flex: 1 1;
- .button:not(:last-child) {
- margin-inline-end: 3px;
- width: unset;
- gap: 14px;
- background-color: var(--color-primary-element-light);
- color: var(--color-primary-element-light-text);
- border: unset;
- padding: 0px 20px;
- }
-}
-
-.actions.hidden {
- display: none;
-}
-
-#trash {
- margin-inline-end: 8px;
- float: right;
- z-index: 1010;
- padding: 10px;
- font-weight: normal;
-}
-
-.newFileMenu .error,
-.newFileMenu .error + .icon-confirm,
-.files-fileList .error {
- color: var(--color-error);
- border-color: var(--color-error);
-}
-
-/* FILE TABLE */
-.files-filestable {
- position: relative;
- width: 100%;
- min-width: 250px;
- display: block;
- flex-direction: column;
- // hide table if emptycontent is not hidden
- .emptycontent:not(.hidden) ~ & {
- display: none;
- }
- // floating header
- thead {
- position: -webkit-sticky;
- position: sticky;
- // breadcrumbs
- top: 44px;
- // under breadcrumbs, over file list
- z-index: 60;
- display: block;
- background-color: var(--color-main-background-translucent);
- }
-
- /**
- * This is a dirty hack as the sticky header requires us to use a different display type on the table element
- */
- tbody {
- display: table;
- width: 100%;
-
- tr[data-permissions="0"],
- tr[data-permissions="16"] {
- background-color: var(--color-background-dark);
-
- td.filename .nametext .innernametext {
- color: var(--color-text-maxcontrast);
- }
- }
-
- // Deactivates the possiblility to checkmark or click on the encrypted folder
- tr[data-e2eencrypted="true"] .selection {
- pointer-events: none;
- }
- }
-}
-
-.files-filestable.hidden {
- display: none;
-}
-
-/* fit app list view heights */
-.app-files #app-content > .viewcontainer {
- min-height: 0%;
- width: 100%;
-}
-
-.app-files #app-content {
- // force the width to be the full width to not go bigger than the screen
- // flex will grow for the mobile view if necessary
- width: calc(100% - 300px);
- // disable overflow-anchor which causes undesired behaviour on Firefox
- overflow-anchor: none;
-}
-
-.file-drag, .file-drag .files-filestable tbody tr, .file-drag .files-filestable tbody tr:hover {
- background-color: var(--color-primary-element-light) !important;
-}
-
-.app-files #app-content.dir-drop {
- background-color: var(--color-main-background) !important;
-}
-
-.file-drag .files-filestable tbody tr, .file-drag .files-filestable tbody tr:hover{
- background-color: transparent !important;
-}
-
-.app-files #app-content.dir-drop .files-filestable tbody tr.dropping-to-dir{
- background-color: var(--color-primary-element-light) !important;
-}
-
-/* button needs overrides due to navigation styles */
-#app-navigation .nav-files a.new {
- width: 40px;
- height: 32px;
- padding: 0 10px;
- margin: 0;
- cursor: pointer;
-}
-
-#app-navigation .nav-files a.new.hidden {
- display: none;
-}
-
-#app-navigation .nav-files a.new.disabled {
- opacity: 0.3;
-}
-
-.files-filestable tbody tr {
- height: 51px;
-}
-.files-filestable tbody tr:hover,
-.files-filestable tbody tr:focus,
-.files-filestable tbody .name:focus,
-.files-filestable tbody tr:hover .filename form,
-table tr.mouseOver td {
- background-color: var(--color-background-hover);
-}
-.files-filestable tbody tr:active,
-.files-filestable tbody tr.highlighted,
-.files-filestable tbody tr.highlighted .name:focus,
-.files-filestable tbody tr.selected,
-.files-filestable tbody tr.searchresult {
- background-color: var(--color-primary-element-light);
-}
-
-tbody a { color: var(--color-main-text); }
-
-span.conflict-path, span.extension, span.uploading, td.date {
- color: var(--color-text-maxcontrast);
-}
-span.conflict-path, span.extension {
- -webkit-transition: opacity 300ms;
- -moz-transition: opacity 300ms;
- -o-transition: opacity 300ms;
- transition: opacity 300ms;
- vertical-align: top;
-}
-tr:hover span.conflict-path,
-tr:focus span.conflict-path,
-tr:hover span.extension,
-tr:focus span.extension {
- opacity: 1;
- color: var(--color-text-maxcontrast);
-}
-
-table th, table th a {
- color: var(--color-text-maxcontrast);
-}
-table.multiselect th a {
- color: var(--color-main-text);
-}
-table th .columntitle {
- display: block;
- padding: 15px;
- height: 50px;
- box-sizing: border-box;
- -moz-box-sizing: border-box;
- vertical-align: middle;
- &:focus-visible {
- border-radius: 2px;
- }
-}
-table.multiselect th .columntitle {
- display: inline-block;
- margin-inline-end: -20px;
-}
-table th .columntitle.name {
- padding-inline-start: 0;
- margin-inline-start: 44px;
-}
-
-table.multiselect th .columntitle.name {
- margin-inline-start: 0;
-}
-
-table th .sort-indicator {
- width: 10px;
- height: 8px;
- margin-inline-start: 5px;
- display: inline-block;
- vertical-align: text-bottom;
- opacity: .3;
-}
-.sort-indicator.hidden,
-.multiselect .sort-indicator,
-table.multiselect th:hover .sort-indicator.hidden,
-table.multiselect th:focus .sort-indicator.hidden {
- visibility: hidden;
-}
-.multiselect .sort, .multiselect .sort span {
- cursor: default;
-}
-table th:hover .sort-indicator.hidden,
-table th:focus .sort-indicator.hidden {
- visibility: visible;
-}
-
-table th,
-table td {
- border-bottom: 1px solid var(--color-border);
- text-align: start;
- font-weight: normal;
-}
-table td {
- padding: 0 15px;
- font-style: normal;
- background-position: 8px center;
- background-repeat: no-repeat;
-}
-table th.column-name {
- position: relative;
- width: 9999px; /* not really sure why this works better than 100% … table styling */
- padding: 0;
-}
-
-.column-name-container {
- position: relative;
- height: 50px;
-}
-
-table th.column-selection {
- padding-top: 2px;
-}
-table th.column-size, table td.filesize {
- text-align: end;
-}
-table th.column-mtime, table td.date,
-table th.column-last, table td.column-last {
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- position: relative;
- /* this can not be just width, both need to be set … table styling */
- min-width: 130px;
-}
-
-#app-content-recent,
-#app-content-favorites,
-#app-content-shareoverview,
-#app-content-sharingout,
-#app-content-sharingin,
-#app-content-sharinglinks,
-#app-content-deletedshares,
-#app-content-pendingshares {
- margin-top: 22px;
- thead {
- top: 0;
- }
-}
-
-table.multiselect thead th {
- background-color: var(--color-main-background-translucent);
- font-weight: bold;
-}
-
-#app-content.with-app-sidebar table.multiselect thead{
- margin-inline-end: 27%;
-}
-
-table.multiselect .column-name {
- position: relative;
- width: 9999px; /* when we use 100%, the styling breaks on mobile … table styling */
-}
-table.multiselect .column-mtime>a {
- display: none;
-}
-
-table td.selection,
-table th.selection,
-table td.fileaction {
- width: 32px;
- text-align: center;
-}
-table td.filename a.name,
-table td.filename p.name {
- display: flex;
- position:relative; /* Firefox needs to explicitly have this default set … */
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- height: 50px;
- line-height: 50px;
- padding: 0;
-}
-table td.filename .thumbnail-wrapper {
- /* we need this to make sure flex is working inside a table cell */
- width: 0;
- min-width: 50px;
- max-width: 50px;
- height: 50px;
-}
-table td.filename .thumbnail-wrapper.icon-loading-small {
- &:after {
- z-index: 10;
- }
- .thumbnail {
- opacity: 0.2;
- }
-}
-table td.filename .thumbnail {
- display: inline-block;
- width: 32px;
- height: 32px;
- background-size: contain;
- background-position: center;
- background-repeat: no-repeat;
- margin-inline-start: 9px;
- margin-top: 9px;
- border-radius: var(--border-radius);
- cursor: pointer;
- position: absolute;
- z-index: 4;
-}
-table td.filename p.name .thumbnail {
- cursor: default;
-}
-
-// Show slight border around previews for images, txt, etc.
-table tr[data-has-preview='true'] .thumbnail {
- border: 1px solid var(--color-border);
-}
-
-table:not(.view-grid) td.filename input.filename {
- width: 70% !important;
- margin-inline-start: 48px !important;
- cursor: text;
-}
-table td.filename form {
- margin-top: -40px;
- position: relative;
- top: -6px;
-}
-
-table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:3px 8px 8px 3px; }
-table td.filename .nametext, .modified, .column-last>span:first-child { float:left; padding:15px 0; }
-
-.modified, .column-last>span:first-child {
- position: relative;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 110px;
-}
-
-/* TODO fix usability bug (accidental file/folder selection) */
-table {
- td.filename {
- max-width: 0;
- .nametext {
- width: 0;
- flex-grow: 1;
- display: flex;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- height: 100%;
- z-index: 10;
- padding: 0;
- padding-inline-end: 20px;
- }
- }
-}
-
-.hide-hidden-files .files-filestable .files-fileList tr.hidden-file,
-.hide-hidden-files .files-filestable .files-fileList tr.hidden-file.dragging {
- display: none !important;
-}
-
-.files-fileList tr.animate-opacity {
- -webkit-transition:opacity 250ms;
- -moz-transition:opacity 250ms;
- -o-transition:opacity 250ms;
- transition:opacity 250ms;
-}
-.files-fileList tr.dragging {
- opacity: 0.2;
-}
-
-table td.filename .nametext .innernametext {
- text-overflow: ellipsis;
- overflow: hidden;
- position: relative;
- vertical-align: top;
-}
-
-/* for smaller resolutions - see mobile.css */
-
-table td.filename .uploadtext {
- position: absolute;
- font-weight: normal;
- // checkbox width
- margin-inline-start: 50px;
- inset-inline-start: 0;
- bottom: 0;
- height: 20px;
- padding: 0 4px;
- // align with file name
- padding-inline-start: 1px;
- font-size: 11px;
- // double the font size
- line-height: 22px;
- color: var(--color-text-maxcontrast);
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-table td.selection {
- padding: 0;
-}
-
-/* File checkboxes */
-.files-fileList tr td.selection>.selectCheckBox + label:before {
- opacity: 0.3;
- margin-inline-end: 0;
-}
-
-/* Show checkbox with full opacity when hovering, checked, or selected */
-.files-fileList tr:hover td.selection>.selectCheckBox + label:before,
-.files-fileList tr:focus td.selection>.selectCheckBox + label:before,
-.files-fileList tr td.selection>.selectCheckBox:checked + label:before,
-.files-fileList tr.selected td.selection>.selectCheckBox + label:before {
- opacity: 1;
-}
-
-/* Show checkbox with half opacity when selecting range */
-.files-fileList tr.halfselected td.selection>.selectCheckBox + label:before {
- opacity: 0.5;
-}
-
-/* Use label to have bigger clickable size for checkbox */
-.files-fileList tr td.selection>.selectCheckBox,
-.select-all {
- & + label {
- padding: 16px;
- }
-
- &:focus-visible + label {
- background-color: var(--color-background-hover);
- border-radius: var(--border-radius-pill);
- outline: none !important;
- border: 2px solid var(--color-primary-element) !important;
- padding: 14px;
- }
-}
-
-.files-fileList tr td.selection>.selectCheckBox:focus-visible + label,
-.select-all:focus-visible + label {
- outline-offset: 0px;
-}
-
-.files-fileList tr td.filename {
- position: relative;
- width: 100%;
- padding-inline: 0;
- -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms;
-}
-
-.files-fileList tr td.filename a.name label,
-.files-fileList tr td.filename p.name label {
- position: absolute;
- width: 80%;
- height: 50px;
-}
-
-.files-fileList tr td.filename .favorite {
- display: inline-block;
- float: left;
-}
-.files-fileList tr td.filename .favorite-mark {
- position: absolute;
- display: block;
- top: -8px;
- inset-inline-end: -8px;
- line-height: 100%;
- text-align: center;
- &.permanent {
- // Create background around the favorite marker to ensure there is enough contrast
- background-color: var(--color-main-background);
- mask: var(--icon-star-rounded-white) no-repeat;
- mask-size: 22px 22px;
-
- width: 22px;
- height: 22px;
- display: flex;
- align-content: center;
- justify-content: center;
- }
-}
-.files-fileList tr:hover td.filename .favorite-mark.permanent {
- background-color: var(--color-background-hover);
-}
-
-#uploadsize-message,#delete-confirm { display:none; }
-
-/* File actions */
-.fileactions {
- z-index: 50;
-}
-
-.busy .fileactions, .busy .action {
- visibility: hidden;
-}
-
-/* fix position of bubble pointer for Files app */
-.bubble,
-#app-navigation .app-navigation-entry-menu {
- min-width: 100px;
-}
-
-/* force show the loading icon, not only on hover */
-.files-fileList .icon-loading-small {
- opacity: 1 !important;
- display: inline !important;
-}
-
-.files-fileList .action.action-share-notification span, .files-fileList a.name {
- cursor: default !important;
-}
-
-/*
- * Make the disabled link look not like a link in file list rows
- */
-.files-fileList a.name.disabled {
- * {
- cursor: default;
- }
-
- a, a * {
- cursor: pointer;
- }
-
- &:focus {
- background: none;
- }
-}
-
-a.action > img {
- height: 16px;
- width: 16px;
- vertical-align: text-bottom;
-}
-
-a.action.action-editlocally,
-a.action.action-setreminder {
- img.icon {
- filter: var(--background-invert-if-dark);
- }
-}
-
-/* Actions for selected files */
-.selectedActions {
- position: relative;
- display: inline-block;
- vertical-align: middle;
-}
-.selectedActions.hidden {
- display: none;
-}
-.selectedActions a {
- display: inline;
- line-height: 50px;
- padding: 16px 5px;
-}
-
-.selectedActions a.hidden {
- display: none;
-}
-.selectedActions a img {
- position:relative;
- vertical-align: text-bottom;
- margin-bottom: -1px;
-}
-
-.selectedActions .actions-selected .icon-more {
- margin-top: -3px;
-}
-
-.files-fileList td a {
- a.action {
- display: inline;
- padding: 17px 8px;
- line-height: 50px;
- opacity: .3;
- &.action-share {
- padding: 17px 14px;
- &.permanent:not(.shared-style) .icon-shared + span {
- /* hide text of the share action */
- /* .hidden-visually for accessbility */
- position: absolute;
- inset-inline-start:-10000px;
- top: auto;
- width: 1px;
- height: 1px;
- overflow: hidden;
- }
- .avatar {
- display: inline-block;
- vertical-align: middle;
- }
- }
- &.action-menu {
- padding-top: 17px;
- padding-bottom: 17px;
- padding-inline: 14px;
- }
- &.no-permission {
- &:hover, &:focus {
- opacity: .3;
- }
- }
- &.disabled {
- &:hover, &:focus,
- img {
- opacity: .3;
- }
- &.action-download {
- opacity: .7;
- &:hover, &:focus {
- opacity: .7;
- }
- }
- }
- &:hover, &:focus {
- opacity: 1;
- }
- &:focus {
- background-color: var(--color-background-hover);
- border-radius: var(--border-radius-pill);
- }
- }
- .fileActionsMenu a.action, a.action.action-share.shared-style {
- opacity: .7;
- }
- .fileActionsMenu .action.permanent {
- opacity: 1;
- }
-}
-
-// Ellipsize long sharer names
-.files-fileList .action.action-share.permanent.shared-style span:not(.icon) {
- display: inline-block;
- max-width: 70px;
- overflow: hidden;
- text-overflow: ellipsis;
- vertical-align: middle;
- margin-inline-end: 6px;
-}
-
-.files-fileList .remoteAddress .userDomain {
- margin-inline-start: 0 !important;
-}
-
-.files-fileList .favorite-mark.permanent {
- opacity: 1;
-}
-
-.files-fileList .fileActionsMenu a.action:hover,
-.files-fileList .fileActionsMenu a.action:focus,
-/* show share action of shared items darker to distinguish from non-shared */
-.files-fileList a.action.action-share.shared-style:hover,
-.files-fileList a.action.action-share.shared-style:focus {
- opacity: 1;
-}
-
-.files-fileList tr a.action.disabled {
- background: none;
-}
-
-.selectedActions a.download.disabled,
-.files-fileList tr a.action.action-download.disabled {
- color: #000000;
-}
-
-.files-fileList tr:hover a.action.disabled:hover * {
- cursor: default;
-}
-
-.summary {
- color: var(--color-text-maxcontrast);
- /* add whitespace to bottom of files list to correctly show dropdowns */
- $action-menu-items-count: 7; // list view has currently max 7 items in its action menu
- height: 44px * ($action-menu-items-count + 0.5); // 0.5 is added to show some whitespace below
-}
-.files-filestable .summary .filesummary {
- width: 100%;
- /* Width of checkbox and file preview */
- padding-inline-start: 101px;
-}
-/* Less whitespace needed on link share page
- * as there is a footer and action menus have fewer entries.
- */
-#body-public .summary {
- height: 180px;
-}
-.summary:hover,
-.summary:focus,
-.summary,
-table tr.summary td {
- background-color: transparent;
-}
-.summary td {
- border-bottom: none;
- vertical-align: top;
- padding-top: 20px;
-}
-.summary td:first-child {
- padding: 0;
-}
-.hiddeninfo {
- white-space: pre-line;
-}
-
-table.dragshadow {
- width:auto;
- z-index: 2000;
-}
-table.dragshadow td.filename {
- padding-inline: 60px 16px;
- height: 36px;
-
- /* Override "max-width: 0" to prevent file name and size from overlapping */
- max-width: unset;
-}
-table.dragshadow td.size {
- padding-inline-end: 8px;
-}
-.mask {
- z-index: 50;
- position: absolute;
- inset: 0;
- background-color: var(--color-main-background);
- background-repeat: no-repeat no-repeat;
- background-position: 50%;
- opacity: 0.7;
- transition: opacity 100ms;
- -moz-transition: opacity 100ms;
- -o-transition: opacity 100ms;
- -ms-transition: opacity 100ms;
- -webkit-transition: opacity 100ms;
-}
-.mask.transparent{
- opacity: 0;
-}
-
-.newFileMenu {
- font-weight: 300;
- top: 100%;
- inset-inline-start: -48px !important;
- margin-top: 4px;
- min-width: 100px;
- z-index: 1001;
-
- /* Center triangle */
- &::after {
- inset-inline-start: 84px !important;
- }
-}
-
-
-.files-controls {
- box-sizing: border-box;
- position: -webkit-sticky;
- position: sticky;
- height: 50px; /* height of the nav toggle button; */
- padding: 0;
- margin: 0;
- background-color: var(--color-main-background-translucent);
- z-index: 62; /* must be above the filelist sticky header and texteditor menubar */
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- display: flex;
- top: 0;
- padding-inline-start: 50px; /* width of the nav toggle button; */
-
- .actions {
- > div,
- & {
- > .button, button {
- box-sizing: border-box;
- display: inline-block;
- display: flex;
- height: 44px;
- width: 44px;
- padding: 9px; // width - border - icon width = 18px
- align-items: center;
- justify-content: center;
- }
- .button.hidden {
- display: none;
- }
- }
- }
-}
-
-/* position controls for apps with app-navigation */
-
-.viewer-mode #app-navigation + #app-content .files-controls {
- inset-inline-start: 0;
-}
-
-.files-filestable .filename .action .icon,
-.files-filestable .selectedActions a .icon,
-.files-filestable .filename .favorite-mark .icon,
-.files-controls .actions .button .icon {
- display: inline-block;
- vertical-align: middle;
- background-size: 16px 16px;
-}
-
-.files-filestable .filename .favorite-mark {
- // Override default icons to always hide the star icon and always show the
- // starred icon even when hovered or focused.
- & .icon-star {
- background-image: none;
- }
- & .icon-starred {
- background-image: var(--icon-starred-yellow) !important;
- }
-}
-
-.files-filestable .filename .action .icon.hidden,
-.files-filestable .selectedActions a .icon.hidden,
-.files-controls .actions .button .icon.hidden {
- display: none;
-}
-
-.files-filestable .filename .action .icon.loading,
-.files-filestable .selectedActions a .icon.loading,
-.files-controls .actions .button .icon.loading {
- width: 15px;
- height: 15px;
-}
-
-.app-files .actions .button.new {
- position: relative;
- width: unset;
- gap: 14px;
- background-color: var(--color-primary-element-light);
- color: var(--color-primary-element-light-text);
- border: unset;
- padding: 0px 20px;
-}
-
-.breadcrumb {
- align-items: center;
-
- .icon-home {
- border-radius: var(--border-radius);
- }
-}
-
-.breadcrumb .canDrop > a,
-.files-filestable tbody tr.canDrop {
- background-color: rgba( variables.$color-primary, .3 );
-}
-.dropzone-background {
- background-color: rgba( variables.$color-primary, .3 );
- :hover{
- box-shadow: none !important;
- }
-}
-
-.notCreatable {
- margin-inline: 12px 44px;
- margin-top: 12px;
- color: var(--color-main-text);
- overflow: auto;
- min-width: 160px;
- height: 54px;
-
- &:not(.hidden) {
- display: flex;
- }
-
- .icon-alert-outline {
- top: -15px;
- position: relative;
- margin-inline-end: 4px;
- }
-}
-
-.quota-navigation-item {
- margin: 0 !important;
- border: none;
- border-radius: 0;
- background-color: transparent;
- z-index:1;
- height: 44px;
- display: flex !important;
- flex-direction: column;
-
- &__text {
- height: 30px;
- }
-
- &[href='#'] {
- // if no link is set, no mouse feedback
- &, * {
- cursor: default !important;
- }
- }
-
- &__container {
- height: 5px;
- border-radius: var(--border-radius);
- }
-}
-
-/* GRID */
-.files-filestable.view-grid:not(.hidden) {
- $grid-size: 160px;
- $grid-pad: 14px;
-
- /* HEADER and MULTISELECT */
- thead {
- tr {
- display: block;
- border-bottom: 1px solid var(--color-border);
- background-color: var(--color-main-background-translucent);
- th {
- width: auto;
- border: none;
- }
- }
- }
-
- /* MAIN FILE LIST */
- tbody {
- display: grid;
- grid-template-columns: repeat(auto-fill, $grid-size);
- justify-content: space-around;
- row-gap: 15px;
- margin: 15px 0;
-
- // ensure search still filters tr with .hidden
- tr:not(.hidden) {
- display: block;
- position: relative;
- height: $grid-size + 44px - $grid-pad;
- border-radius: var(--border-radius);
-
- &:hover, &:focus, &:active,
- &.selected,
- &.searchresult,
- .name:focus,
- &.highlighted {
- background-color: transparent;
-
- .thumbnail-wrapper,
- .nametext,
- .fileactions {
- background-color: var(--color-background-hover);
- }
- }
- }
-
- td {
- display: inline;
- border-bottom: none;
-
- &.filename {
- .thumbnail-wrapper {
- min-width: 0;
- max-width: none;
- position: absolute;
- width: $grid-size;
- height: $grid-size;
- padding: $grid-pad; // same as action icon bottom and right padding
- top: 0;
- inset-inline-start: 0;
- z-index: -1; // make sure the default click is the link
-
- .thumbnail {
- width: calc(100% - 2 * #{$grid-pad});
- height: calc(100% - 2 * #{$grid-pad}); //action icon padding
- background-size: contain;
- margin: 0;
- border-radius: var(--border-radius);
- background-repeat: no-repeat;
- background-position: center;
-
- /* Position favorite star related to checkbox to left and 3-dot menu below
- * Position is inherited from the selection while in grid view
- */
- .favorite-mark {
- inset-inline-start: auto;
- top: -11px; // center in corner of thumbnail
- inset-inline-end: -11px; // center in corner of thumbnail
- }
- }
- }
-
- .uploadtext {
- width: 100%;
- margin: 0;
- top: 0;
- bottom: auto;
- // checkbox align
- height: 28px;
- padding-top: 4px;
- // checkbox margins
- padding-inline-start: calc(44px - 16px);
- }
-
- .name {
- height: 100%;
- border-radius: var(--border-radius);
- // since we're using thumbnail, name and actions bg
- // we need to hide the overflow for the radius to show
- // luckily the popovermenu is outside .name
- overflow: hidden;
- // we but the thumbnail in background to ensure
- // the name is the default click handler
- // force back the cursor which has been overridden
- // and disabled for some reason...
- cursor: pointer !important;
-
- .nametext {
- display: flex;
- height: 44px;
- margin-top: $grid-size - $grid-pad;
- text-align: center;
- line-height: 44px;
- padding: 0;
-
- .innernametext {
- display: inline-block;
- text-align: center;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- &:before {
- content: '';
- flex: 1;
- min-width: 14px;
- }
- &:after {
- content: '';
- flex: 1;
- min-width: 44px;
- }
-
- /* No space for extension in grid view */
- .extension {
- display: none;
- }
- }
-
- /* System tags */
- .system-tags {
- display: none;
- }
-
- .fileactions {
- height: initial;
- margin-top: $grid-size - $grid-pad;
- display: flex;
- align-items: center;
- position: absolute;
- inset-inline-end: 0;
-
- .action {
- padding: $grid-pad;
- width: 44px;
- height: 44px;
- display: flex;
- align-items: center;
- justify-content: center;
-
- // hide all actions in grid view that are not the menu
- &:not(.action-menu) {
- display: none;
- }
- }
- }
- }
-
- .fileActionsMenu {
- // force show the sharing entry in the dropdown menu
- .action-share-container.hidden {
- display: block !important;
- // avatar in shared by user dropdown menu
- .action-share img {
- padding: 6px;
- border-radius: 50%;
- }
- }
- // force show the sharing entry in the dropdown menu
- .action-restore-container.hidden {
- display: block !important;
- }
- // force show the sharing entry in the dropdown menu
- .action-comment-container.hidden {
- display: block !important;
- }
- }
-
- form {
- padding: 3px 14px;
- border-radius: var(--border-radius);
-
- input.filename {
- width: 100%;
- margin-inline-start: 0;
- cursor: text;
- }
- }
- }
-
- /* No space for filesize and date in grid view */
- &.filesize,
- &.date {
- display: none;
- }
-
- &.selection,
- &.filename .favorite-mark {
- position: absolute;
- top: -8px; // half the checkbox width, center on corner of thumbnail
- inset-inline-start: -8px; // half the checkbox width, center on corner of thumbnail
- display: flex;
- z-index: 10;
-
- label {
- width: 44px;
- height: 44px;
- display: inline-flex;
- padding: $grid-pad; // like any action icon
- &::before {
- margin: 0;
- width: $grid-pad; // 16px - border
- height: $grid-pad; // 16px - border
- }
- }
- }
-
- /* Position actions menu below file */
- .popovermenu {
- inset-inline-start: 0;
- width: $grid-size - 10px; // 2 * margin
- margin: 0 5px;
-
- /* Ellipsize long entries, normally menu width is adjusted but for grid we use fixed width. */
- .menuitem span:not(.icon) {
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
- }
- }
-
- tr.hidden-file td.filename .name .nametext .extension {
- display: block;
- }
-
- /* Center align the footer file number & size summary */
- tfoot {
- display: grid;
-
- .summary:not(.hidden) {
- display: inline-block;
- margin: 0 auto;
- $action-menu-items-count: 9; // grid view has currently max 9 items in its action menu
- height: 44px * ($action-menu-items-count + 0.5); // 0.5 is added to show some whitespace below
-
- td {
- padding-top: 50px;
-
- &:first-child,
- &.date {
- display: none;
- }
-
- .info {
- margin-inline-start: 0;
- }
- }
- }
- }
-}
-
-/* Grid view toggle */
-#view-toggle {
- background-color: var(--color-main-background-translucent);
- border: none;
- margin: 0;
- padding: 22px;
- opacity: .5;
- float: right;
- inset-inline-end: var(--default-grid-baseline);
- top: var(--default-grid-baseline);
- z-index: 100;
- position: sticky;
-
- &:hover,
- &:focus,
- #showgridview:focus + & {
- opacity: 1;
- }
-
- &:focus-visible,
- #showgridview:focus-visible + & {
- box-shadow: inset 0 0 0 2px var(--color-primary-element) !important;
- }
-}
-
-/**
- * Make sure the hidden input is always
- * on the visible scrolled area of the
- * page to avoid scrolling to top when focusing
- */
-#showgridview {
- position: fixed;
- top: 0;
-}
-
-/* Adjustments for link share page */
-#body-public {
- .files-filestable.view-grid:not(.hidden) tbody td {
- /* More space for filename since there is no share icon */
- &.filename .name .nametext .innernametext {
- max-width: 124px;
- }
-
- /* Position actions menu correctly below 3-dot-menu */
- .popovermenu {
- inset-inline-start: -80px;
- }
- }
-
- /* Right-align view toggle on link share page */
- #view-toggle {
- position: absolute;
- inset-inline-end: 0;
- top: 0;
- }
-}
-
-/* Hide legacy Gallery toggle */
-#gallery-button {
- display: none;
-}
-
-#tag_multiple_files_container {
- overflow: hidden;
- background-color: #fff;
- border-radius: 3px;
- position: relative;
- display: flex;
- flex-wrap: wrap;
- margin-bottom: 10px;
-
- h3 {
- width: 100%;
- padding: 0 18px;
- }
-
- .systemTagsInputFieldContainer {
- flex: 1 1 80%;
- min-width: 0;
- margin: 0 12px;
- }
-}
diff --git a/apps/files/css/merged.css b/apps/files/css/merged.css
deleted file mode 100644
index 1332033c1ed..00000000000
--- a/apps/files/css/merged.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *//*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *//*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */.actions{padding:3px;height:100%;display:inline-block;float:left}.actions input,.actions button,.actions .button{margin:0;float:left}.actions .button a{color:#555}.actions .button a:hover,.actions .button a:focus{background-color:var(--color-background-hover)}.actions .button a:active{background-color:var(--color-primary-element-light)}.actions.creatable{position:relative;display:flex;flex:1 1}.actions.creatable .button:not(:last-child){margin-inline-end:3px;width:unset;gap:14px;background-color:var(--color-primary-element-light);color:var(--color-primary-element-light-text);border:unset;padding:0px 20px}.actions.hidden{display:none}#trash{margin-inline-end:8px;float:right;z-index:1010;padding:10px;font-weight:normal}.newFileMenu .error,.newFileMenu .error+.icon-confirm,.files-fileList .error{color:var(--color-error);border-color:var(--color-error)}.files-filestable{position:relative;width:100%;min-width:250px;display:block;flex-direction:column}.emptycontent:not(.hidden)~.files-filestable{display:none}.files-filestable thead{position:-webkit-sticky;position:sticky;top:44px;z-index:60;display:block;background-color:var(--color-main-background-translucent)}.files-filestable tbody{display:table;width:100%}.files-filestable tbody tr[data-permissions="0"],.files-filestable tbody tr[data-permissions="16"]{background-color:var(--color-background-dark)}.files-filestable tbody tr[data-permissions="0"] td.filename .nametext .innernametext,.files-filestable tbody tr[data-permissions="16"] td.filename .nametext .innernametext{color:var(--color-text-maxcontrast)}.files-filestable tbody tr[data-e2eencrypted=true] .selection{pointer-events:none}.files-filestable.hidden{display:none}.app-files #app-content>.viewcontainer{min-height:0%;width:100%}.app-files #app-content{width:calc(100% - 300px);overflow-anchor:none}.file-drag,.file-drag .files-filestable tbody tr,.file-drag .files-filestable tbody tr:hover{background-color:var(--color-primary-element-light) !important}.app-files #app-content.dir-drop{background-color:var(--color-main-background) !important}.file-drag .files-filestable tbody tr,.file-drag .files-filestable tbody tr:hover{background-color:rgba(0,0,0,0) !important}.app-files #app-content.dir-drop .files-filestable tbody tr.dropping-to-dir{background-color:var(--color-primary-element-light) !important}#app-navigation .nav-files a.new{width:40px;height:32px;padding:0 10px;margin:0;cursor:pointer}#app-navigation .nav-files a.new.hidden{display:none}#app-navigation .nav-files a.new.disabled{opacity:.3}.files-filestable tbody tr{height:51px}.files-filestable tbody tr:hover,.files-filestable tbody tr:focus,.files-filestable tbody .name:focus,.files-filestable tbody tr:hover .filename form,table tr.mouseOver td{background-color:var(--color-background-hover)}.files-filestable tbody tr:active,.files-filestable tbody tr.highlighted,.files-filestable tbody tr.highlighted .name:focus,.files-filestable tbody tr.selected,.files-filestable tbody tr.searchresult{background-color:var(--color-primary-element-light)}tbody a{color:var(--color-main-text)}span.conflict-path,span.extension,span.uploading,td.date{color:var(--color-text-maxcontrast)}span.conflict-path,span.extension{-webkit-transition:opacity 300ms;-moz-transition:opacity 300ms;-o-transition:opacity 300ms;transition:opacity 300ms;vertical-align:top}tr:hover span.conflict-path,tr:focus span.conflict-path,tr:hover span.extension,tr:focus span.extension{opacity:1;color:var(--color-text-maxcontrast)}table th,table th a{color:var(--color-text-maxcontrast)}table.multiselect th a{color:var(--color-main-text)}table th .columntitle{display:block;padding:15px;height:50px;box-sizing:border-box;-moz-box-sizing:border-box;vertical-align:middle}table th .columntitle:focus-visible{border-radius:2px}table.multiselect th .columntitle{display:inline-block;margin-inline-end:-20px}table th .columntitle.name{padding-inline-start:0;margin-inline-start:44px}table.multiselect th .columntitle.name{margin-inline-start:0}table th .sort-indicator{width:10px;height:8px;margin-inline-start:5px;display:inline-block;vertical-align:text-bottom;opacity:.3}.sort-indicator.hidden,.multiselect .sort-indicator,table.multiselect th:hover .sort-indicator.hidden,table.multiselect th:focus .sort-indicator.hidden{visibility:hidden}.multiselect .sort,.multiselect .sort span{cursor:default}table th:hover .sort-indicator.hidden,table th:focus .sort-indicator.hidden{visibility:visible}table th,table td{border-bottom:1px solid var(--color-border);text-align:start;font-weight:normal}table td{padding:0 15px;font-style:normal;background-position:8px center;background-repeat:no-repeat}table th.column-name{position:relative;width:9999px;padding:0}.column-name-container{position:relative;height:50px}table th.column-selection{padding-top:2px}table th.column-size,table td.filesize{text-align:end}table th.column-mtime,table td.date,table th.column-last,table td.column-last{-moz-box-sizing:border-box;box-sizing:border-box;position:relative;min-width:130px}#app-content-recent,#app-content-favorites,#app-content-shareoverview,#app-content-sharingout,#app-content-sharingin,#app-content-sharinglinks,#app-content-deletedshares,#app-content-pendingshares{margin-top:22px}#app-content-recent thead,#app-content-favorites thead,#app-content-shareoverview thead,#app-content-sharingout thead,#app-content-sharingin thead,#app-content-sharinglinks thead,#app-content-deletedshares thead,#app-content-pendingshares thead{top:0}table.multiselect thead th{background-color:var(--color-main-background-translucent);font-weight:bold}#app-content.with-app-sidebar table.multiselect thead{margin-inline-end:27%}table.multiselect .column-name{position:relative;width:9999px}table.multiselect .column-mtime>a{display:none}table td.selection,table th.selection,table td.fileaction{width:32px;text-align:center}table td.filename a.name,table td.filename p.name{display:flex;position:relative;-moz-box-sizing:border-box;box-sizing:border-box;height:50px;line-height:50px;padding:0}table td.filename .thumbnail-wrapper{width:0;min-width:50px;max-width:50px;height:50px}table td.filename .thumbnail-wrapper.icon-loading-small:after{z-index:10}table td.filename .thumbnail-wrapper.icon-loading-small .thumbnail{opacity:.2}table td.filename .thumbnail{display:inline-block;width:32px;height:32px;background-size:contain;background-position:center;background-repeat:no-repeat;margin-inline-start:9px;margin-top:9px;border-radius:var(--border-radius);cursor:pointer;position:absolute;z-index:4}table td.filename p.name .thumbnail{cursor:default}table tr[data-has-preview=true] .thumbnail{border:1px solid var(--color-border)}table:not(.view-grid) td.filename input.filename{width:70% !important;margin-inline-start:48px !important;cursor:text}table td.filename form{margin-top:-40px;position:relative;top:-6px}table td.filename a,table td.login,table td.logout,table td.download,table td.upload,table td.create,table td.delete{padding:3px 8px 8px 3px}table td.filename .nametext,.modified,.column-last>span:first-child{float:left;padding:15px 0}.modified,.column-last>span:first-child{position:relative;overflow:hidden;text-overflow:ellipsis;width:110px}table td.filename{max-width:0}table td.filename .nametext{width:0;flex-grow:1;display:flex;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;height:100%;z-index:10;padding:0;padding-inline-end:20px}.hide-hidden-files .files-filestable .files-fileList tr.hidden-file,.hide-hidden-files .files-filestable .files-fileList tr.hidden-file.dragging{display:none !important}.files-fileList tr.animate-opacity{-webkit-transition:opacity 250ms;-moz-transition:opacity 250ms;-o-transition:opacity 250ms;transition:opacity 250ms}.files-fileList tr.dragging{opacity:.2}table td.filename .nametext .innernametext{text-overflow:ellipsis;overflow:hidden;position:relative;vertical-align:top}table td.filename .uploadtext{position:absolute;font-weight:normal;margin-inline-start:50px;inset-inline-start:0;bottom:0;height:20px;padding:0 4px;padding-inline-start:1px;font-size:11px;line-height:22px;color:var(--color-text-maxcontrast);text-overflow:ellipsis;white-space:nowrap}table td.selection{padding:0}.files-fileList tr td.selection>.selectCheckBox+label:before{opacity:.3;margin-inline-end:0}.files-fileList tr:hover td.selection>.selectCheckBox+label:before,.files-fileList tr:focus td.selection>.selectCheckBox+label:before,.files-fileList tr td.selection>.selectCheckBox:checked+label:before,.files-fileList tr.selected td.selection>.selectCheckBox+label:before{opacity:1}.files-fileList tr.halfselected td.selection>.selectCheckBox+label:before{opacity:.5}.files-fileList tr td.selection>.selectCheckBox+label,.select-all+label{padding:16px}.files-fileList tr td.selection>.selectCheckBox:focus-visible+label,.select-all:focus-visible+label{background-color:var(--color-background-hover);border-radius:var(--border-radius-pill);outline:none !important;border:2px solid var(--color-primary-element) !important;padding:14px}.files-fileList tr td.selection>.selectCheckBox:focus-visible+label,.select-all:focus-visible+label{outline-offset:0px}.files-fileList tr td.filename{position:relative;width:100%;padding-inline:0;-webkit-transition:background-image 500ms;-moz-transition:background-image 500ms;-o-transition:background-image 500ms;transition:background-image 500ms}.files-fileList tr td.filename a.name label,.files-fileList tr td.filename p.name label{position:absolute;width:80%;height:50px}.files-fileList tr td.filename .favorite{display:inline-block;float:left}.files-fileList tr td.filename .favorite-mark{position:absolute;display:block;top:-8px;inset-inline-end:-8px;line-height:100%;text-align:center}.files-fileList tr td.filename .favorite-mark.permanent{background-color:var(--color-main-background);mask:var(--icon-star-rounded-white) no-repeat;mask-size:22px 22px;width:22px;height:22px;display:flex;align-content:center;justify-content:center}.files-fileList tr:hover td.filename .favorite-mark.permanent{background-color:var(--color-background-hover)}#uploadsize-message,#delete-confirm{display:none}.fileactions{z-index:50}.busy .fileactions,.busy .action{visibility:hidden}.bubble,#app-navigation .app-navigation-entry-menu{min-width:100px}.files-fileList .icon-loading-small{opacity:1 !important;display:inline !important}.files-fileList .action.action-share-notification span,.files-fileList a.name{cursor:default !important}.files-fileList a.name.disabled *{cursor:default}.files-fileList a.name.disabled a,.files-fileList a.name.disabled a *{cursor:pointer}.files-fileList a.name.disabled:focus{background:none}a.action>img{height:16px;width:16px;vertical-align:text-bottom}a.action.action-editlocally img.icon,a.action.action-setreminder img.icon{filter:var(--background-invert-if-dark)}.selectedActions{position:relative;display:inline-block;vertical-align:middle}.selectedActions.hidden{display:none}.selectedActions a{display:inline;line-height:50px;padding:16px 5px}.selectedActions a.hidden{display:none}.selectedActions a img{position:relative;vertical-align:text-bottom;margin-bottom:-1px}.selectedActions .actions-selected .icon-more{margin-top:-3px}.files-fileList td a a.action{display:inline;padding:17px 8px;line-height:50px;opacity:.3}.files-fileList td a a.action.action-share{padding:17px 14px}.files-fileList td a a.action.action-share.permanent:not(.shared-style) .icon-shared+span{position:absolute;inset-inline-start:-10000px;top:auto;width:1px;height:1px;overflow:hidden}.files-fileList td a a.action.action-share .avatar{display:inline-block;vertical-align:middle}.files-fileList td a a.action.action-menu{padding-top:17px;padding-bottom:17px;padding-inline:14px}.files-fileList td a a.action.no-permission:hover,.files-fileList td a a.action.no-permission:focus{opacity:.3}.files-fileList td a a.action.disabled:hover,.files-fileList td a a.action.disabled:focus,.files-fileList td a a.action.disabled img{opacity:.3}.files-fileList td a a.action.disabled.action-download{opacity:.7}.files-fileList td a a.action.disabled.action-download:hover,.files-fileList td a a.action.disabled.action-download:focus{opacity:.7}.files-fileList td a a.action:hover,.files-fileList td a a.action:focus{opacity:1}.files-fileList td a a.action:focus{background-color:var(--color-background-hover);border-radius:var(--border-radius-pill)}.files-fileList td a .fileActionsMenu a.action,.files-fileList td a a.action.action-share.shared-style{opacity:.7}.files-fileList td a .fileActionsMenu .action.permanent{opacity:1}.files-fileList .action.action-share.permanent.shared-style span:not(.icon){display:inline-block;max-width:70px;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;margin-inline-end:6px}.files-fileList .remoteAddress .userDomain{margin-inline-start:0 !important}.files-fileList .favorite-mark.permanent{opacity:1}.files-fileList .fileActionsMenu a.action:hover,.files-fileList .fileActionsMenu a.action:focus,.files-fileList a.action.action-share.shared-style:hover,.files-fileList a.action.action-share.shared-style:focus{opacity:1}.files-fileList tr a.action.disabled{background:none}.selectedActions a.download.disabled,.files-fileList tr a.action.action-download.disabled{color:#000}.files-fileList tr:hover a.action.disabled:hover *{cursor:default}.summary{color:var(--color-text-maxcontrast);height:330px}.files-filestable .summary .filesummary{width:100%;padding-inline-start:101px}#body-public .summary{height:180px}.summary:hover,.summary:focus,.summary,table tr.summary td{background-color:rgba(0,0,0,0)}.summary td{border-bottom:none;vertical-align:top;padding-top:20px}.summary td:first-child{padding:0}.hiddeninfo{white-space:pre-line}table.dragshadow{width:auto;z-index:2000}table.dragshadow td.filename{padding-inline:60px 16px;height:36px;max-width:unset}table.dragshadow td.size{padding-inline-end:8px}.mask{z-index:50;position:absolute;inset:0;background-color:var(--color-main-background);background-repeat:no-repeat no-repeat;background-position:50%;opacity:.7;transition:opacity 100ms;-moz-transition:opacity 100ms;-o-transition:opacity 100ms;-ms-transition:opacity 100ms;-webkit-transition:opacity 100ms}.mask.transparent{opacity:0}.newFileMenu{font-weight:300;top:100%;inset-inline-start:-48px !important;margin-top:4px;min-width:100px;z-index:1001}.newFileMenu::after{inset-inline-start:84px !important}.files-controls{box-sizing:border-box;position:-webkit-sticky;position:sticky;height:50px;padding:0;margin:0;background-color:var(--color-main-background-translucent);z-index:62;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:flex;top:0;padding-inline-start:50px}.files-controls .actions>div>.button,.files-controls .actions>div button,.files-controls .actions>.button,.files-controls .actions button{box-sizing:border-box;display:inline-block;display:flex;height:44px;width:44px;padding:9px;align-items:center;justify-content:center}.files-controls .actions>div .button.hidden,.files-controls .actions .button.hidden{display:none}.viewer-mode #app-navigation+#app-content .files-controls{inset-inline-start:0}.files-filestable .filename .action .icon,.files-filestable .selectedActions a .icon,.files-filestable .filename .favorite-mark .icon,.files-controls .actions .button .icon{display:inline-block;vertical-align:middle;background-size:16px 16px}.files-filestable .filename .favorite-mark .icon-star{background-image:none}.files-filestable .filename .favorite-mark .icon-starred{background-image:var(--icon-starred-yellow) !important}.files-filestable .filename .action .icon.hidden,.files-filestable .selectedActions a .icon.hidden,.files-controls .actions .button .icon.hidden{display:none}.files-filestable .filename .action .icon.loading,.files-filestable .selectedActions a .icon.loading,.files-controls .actions .button .icon.loading{width:15px;height:15px}.app-files .actions .button.new{position:relative;width:unset;gap:14px;background-color:var(--color-primary-element-light);color:var(--color-primary-element-light-text);border:unset;padding:0px 20px}.breadcrumb{align-items:center}.breadcrumb .icon-home{border-radius:var(--border-radius)}.breadcrumb .canDrop>a,.files-filestable tbody tr.canDrop{background-color:rgba(0,130,201,.3)}.dropzone-background{background-color:rgba(0,130,201,.3)}.dropzone-background :hover{box-shadow:none !important}.notCreatable{margin-inline:12px 44px;margin-top:12px;color:var(--color-main-text);overflow:auto;min-width:160px;height:54px}.notCreatable:not(.hidden){display:flex}.notCreatable .icon-alert-outline{top:-15px;position:relative;margin-inline-end:4px}.quota-navigation-item{margin:0 !important;border:none;border-radius:0;background-color:rgba(0,0,0,0);z-index:1;height:44px;display:flex !important;flex-direction:column}.quota-navigation-item__text{height:30px}.quota-navigation-item[href="#"],.quota-navigation-item[href="#"] *{cursor:default !important}.quota-navigation-item__container{height:5px;border-radius:var(--border-radius)}.files-filestable.view-grid:not(.hidden) thead tr{display:block;border-bottom:1px solid var(--color-border);background-color:var(--color-main-background-translucent)}.files-filestable.view-grid:not(.hidden) thead tr th{width:auto;border:none}.files-filestable.view-grid:not(.hidden) tbody{display:grid;grid-template-columns:repeat(auto-fill, 160px);justify-content:space-around;row-gap:15px;margin:15px 0}.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden){display:block;position:relative;height:190px;border-radius:var(--border-radius)}.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted{background-color:rgba(0,0,0,0)}.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):hover .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):focus .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden):active .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).selected .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).searchresult .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden) .name:focus .fileactions,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted .thumbnail-wrapper,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted .nametext,.files-filestable.view-grid:not(.hidden) tbody tr:not(.hidden).highlighted .fileactions{background-color:var(--color-background-hover)}.files-filestable.view-grid:not(.hidden) tbody td{display:inline;border-bottom:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .thumbnail-wrapper{min-width:0;max-width:none;position:absolute;width:160px;height:160px;padding:14px;top:0;inset-inline-start:0;z-index:-1}.files-filestable.view-grid:not(.hidden) tbody td.filename .thumbnail-wrapper .thumbnail{width:calc(100% - 2*14px);height:calc(100% - 2*14px);background-size:contain;margin:0;border-radius:var(--border-radius);background-repeat:no-repeat;background-position:center}.files-filestable.view-grid:not(.hidden) tbody td.filename .thumbnail-wrapper .thumbnail .favorite-mark{inset-inline-start:auto;top:-11px;inset-inline-end:-11px}.files-filestable.view-grid:not(.hidden) tbody td.filename .uploadtext{width:100%;margin:0;top:0;bottom:auto;height:28px;padding-top:4px;padding-inline-start:28px}.files-filestable.view-grid:not(.hidden) tbody td.filename .name{height:100%;border-radius:var(--border-radius);overflow:hidden;cursor:pointer !important}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext{display:flex;height:44px;margin-top:146px;text-align:center;line-height:44px;padding:0}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext .innernametext{display:inline-block;text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext:before{content:"";flex:1;min-width:14px}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext:after{content:"";flex:1;min-width:44px}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext .extension{display:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .system-tags{display:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .fileactions{height:initial;margin-top:146px;display:flex;align-items:center;position:absolute;inset-inline-end:0}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .fileactions .action{padding:14px;width:44px;height:44px;display:flex;align-items:center;justify-content:center}.files-filestable.view-grid:not(.hidden) tbody td.filename .name .fileactions .action:not(.action-menu){display:none}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-share-container.hidden{display:block !important}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-share-container.hidden .action-share img{padding:6px;border-radius:50%}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-restore-container.hidden{display:block !important}.files-filestable.view-grid:not(.hidden) tbody td.filename .fileActionsMenu .action-comment-container.hidden{display:block !important}.files-filestable.view-grid:not(.hidden) tbody td.filename form{padding:3px 14px;border-radius:var(--border-radius)}.files-filestable.view-grid:not(.hidden) tbody td.filename form input.filename{width:100%;margin-inline-start:0;cursor:text}.files-filestable.view-grid:not(.hidden) tbody td.filesize,.files-filestable.view-grid:not(.hidden) tbody td.date{display:none}.files-filestable.view-grid:not(.hidden) tbody td.selection,.files-filestable.view-grid:not(.hidden) tbody td.filename .favorite-mark{position:absolute;top:-8px;inset-inline-start:-8px;display:flex;z-index:10}.files-filestable.view-grid:not(.hidden) tbody td.selection label,.files-filestable.view-grid:not(.hidden) tbody td.filename .favorite-mark label{width:44px;height:44px;display:inline-flex;padding:14px}.files-filestable.view-grid:not(.hidden) tbody td.selection label::before,.files-filestable.view-grid:not(.hidden) tbody td.filename .favorite-mark label::before{margin:0;width:14px;height:14px}.files-filestable.view-grid:not(.hidden) tbody td .popovermenu{inset-inline-start:0;width:150px;margin:0 5px}.files-filestable.view-grid:not(.hidden) tbody td .popovermenu .menuitem span:not(.icon){overflow:hidden;text-overflow:ellipsis}.files-filestable.view-grid:not(.hidden) tr.hidden-file td.filename .name .nametext .extension{display:block}.files-filestable.view-grid:not(.hidden) tfoot{display:grid}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden){display:inline-block;margin:0 auto;height:418px}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td{padding-top:50px}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td:first-child,.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td.date{display:none}.files-filestable.view-grid:not(.hidden) tfoot .summary:not(.hidden) td .info{margin-inline-start:0}#view-toggle{background-color:var(--color-main-background-translucent);border:none;margin:0;padding:22px;opacity:.5;float:right;inset-inline-end:var(--default-grid-baseline);top:var(--default-grid-baseline);z-index:100;position:sticky}#view-toggle:hover,#view-toggle:focus,#showgridview:focus+#view-toggle{opacity:1}#view-toggle:focus-visible,#showgridview:focus-visible+#view-toggle{box-shadow:inset 0 0 0 2px var(--color-primary-element) !important}#showgridview{position:fixed;top:0}#body-public .files-filestable.view-grid:not(.hidden) tbody td.filename .name .nametext .innernametext{max-width:124px}#body-public .files-filestable.view-grid:not(.hidden) tbody td .popovermenu{inset-inline-start:-80px}#body-public #view-toggle{position:absolute;inset-inline-end:0;top:0}#gallery-button{display:none}#tag_multiple_files_container{overflow:hidden;background-color:#fff;border-radius:3px;position:relative;display:flex;flex-wrap:wrap;margin-bottom:10px}#tag_multiple_files_container h3{width:100%;padding:0 18px}#tag_multiple_files_container .systemTagsInputFieldContainer{flex:1 1 80%;min-width:0;margin:0 12px}/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */#upload{box-sizing:border-box;height:36px;width:39px;padding:0 !important;margin-inline-start:3px;overflow:hidden;vertical-align:top;position:relative;z-index:-20}#upload .icon-upload{position:relative;display:block;width:100%;height:44px;width:44px;margin:-5px -3px;cursor:pointer;z-index:10;opacity:.65}.file_upload_target{display:none}.file_upload_form{display:inline;float:left;margin:0;padding:0;cursor:pointer;overflow:visible}.uploadprogresswrapper,.uploadprogresswrapper *{box-sizing:border-box}.uploadprogresswrapper{display:inline-block;vertical-align:top;height:36px;margin-inline-start:3px}.uploadprogresswrapper>input[type=button]{height:36px;margin-inline-start:3px}#uploadprogressbar{border-color:var(--color-border-dark);border-radius:var(--border-radius-pill) 0 0 var(--border-radius-pill);border-inline-end:0;position:relative;float:left;width:200px;height:44px;display:inline-block;text-align:center}#uploadprogressbar .ui-progressbar-value{margin-top:.1em}#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left{height:calc(100% + 2px);top:-2px;inset-inline-start:-1px;position:absolute;overflow:hidden;background-color:var(--color-primary-element)}#uploadprogressbar .label{top:8px;opacity:1;overflow:hidden;white-space:nowrap;font-weight:normal}#uploadprogressbar .label.inner{color:var(--color-primary-element-text);position:absolute;display:block;width:200px}#uploadprogressbar .label.outer{position:relative;color:var(--color-main-text)}#uploadprogressbar .desktop{display:block}#uploadprogressbar .mobile{display:none}#uploadprogressbar+.stop{border-start-start-radius:0;border-end-start-radius:0}.oc-dialog .fileexists{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-bottom:30px}.oc-dialog .fileexists .conflict .filename,.oc-dialog .fileexists .conflict .mtime,.oc-dialog .fileexists .conflict .size{-webkit-touch-callout:initial;-webkit-user-select:initial;-khtml-user-select:initial;-moz-user-select:initial;-ms-user-select:initial;user-select:initial}.oc-dialog .fileexists .conflict .message{color:#e9322d}.oc-dialog .fileexists table{width:100%}.oc-dialog .fileexists th{padding-inline:0}.oc-dialog .fileexists th input[type=checkbox]{margin-inline-end:3px}.oc-dialog .fileexists th:first-child{width:225px}.oc-dialog .fileexists th label{font-weight:normal;color:var(--color-main-text)}.oc-dialog .fileexists th .count{margin-inline-start:3px}.oc-dialog .fileexists .conflicts .template{display:none}.oc-dialog .fileexists .conflict{width:100%;height:85px}.oc-dialog .fileexists .conflict .filename{color:#777;word-break:break-all;clear:left}.oc-dialog .fileexists .icon{width:64px;height:64px;margin:0px 5px 5px 5px;background-repeat:no-repeat;background-size:64px 64px;float:left}.oc-dialog .fileexists .original,.oc-dialog .fileexists .replacement{float:left;width:50%}.oc-dialog .fileexists .conflicts{overflow-y:auto;max-height:225px}.oc-dialog .fileexists .conflict input[type=checkbox]{float:left}.oc-dialog .fileexists #allfileslabel{float:right}.oc-dialog .fileexists #allfiles{vertical-align:bottom;position:relative;top:-3px}.oc-dialog .fileexists #allfiles+span{vertical-align:bottom}.oc-dialog .oc-dialog-buttonrow{width:100%;text-align:right}.oc-dialog .oc-dialog-buttonrow .cancel{float:left}.highlightUploaded{-webkit-animation:highlightAnimation 2s 1;-moz-animation:highlightAnimation 2s 1;-o-animation:highlightAnimation 2s 1;animation:highlightAnimation 2s 1}@-webkit-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-moz-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-o-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}/*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *//*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */@media only screen and (max-width: 988px)and (min-width: 1025px),only screen and (max-width: 688px){.app-files #app-content.dir-drop{background-color:#fff !important}table th.column-size,table td.filesize,table th.column-mtime,table td.date{display:none}table td{padding:0}table.multiselect thead{padding-inline-start:0}.fileList a.action.action-menu img{padding-inline-start:0}.fileList .fileActionsMenu{margin-inline-end:6px}.fileList a.action-share span:not(.icon):not(.avatar){position:absolute;inset-inline-start:-10000px;top:auto;width:1px;height:1px;overflow:hidden}td.filename a.name .system-tags{display:none}#uploadprogressbar,#uploadprogressbar .label.inner{width:50px}#uploadprogressbar .desktop{display:none !important}#uploadprogressbar .mobile{display:block !important}table.dragshadow{z-index:1000}}@media only screen and (max-width: 480px){table th .selectedActions{float:right}table th .selectedActions>a span:not(.icon){display:none}table th .selectedActions a{padding:17px 14px}table.multiselect th .columntitle.name{margin-inline-start:0}}/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */.app-sidebar .detailFileInfoContainer{min-height:50px;padding:15px}.app-sidebar .detailFileInfoContainer>div{clear:both}.app-sidebar .mainFileInfoView .icon{display:inline-block;background-size:16px 16px}.app-sidebar .mainFileInfoView .permalink{padding:6px 10px;vertical-align:top;opacity:.6}.app-sidebar .mainFileInfoView .permalink:hover,.app-sidebar .mainFileInfoView .permalink:focus{opacity:1}.app-sidebar .mainFileInfoView .permalink-field>input{clear:both;width:90%}.app-sidebar .thumbnailContainer.large{margin-inline:-15px -35px;margin-top:-15px}.app-sidebar .thumbnailContainer.large.portrait{margin:0}.app-sidebar .large .thumbnail{width:100%;display:block;background-repeat:no-repeat;background-position:center;background-size:100%;float:none;margin:0;height:auto}.app-sidebar .large .thumbnail .stretcher{content:"";display:block;padding-bottom:56.25%}.app-sidebar .large.portrait .thumbnail{background-position:50% top}.app-sidebar .large.portrait .thumbnail{background-size:contain}.app-sidebar .large.text{overflow-y:scroll;overflow-x:hidden;padding-top:14px;font-size:80%;margin-inline-start:0}.app-sidebar .thumbnail{width:100%;min-height:75px;display:inline-block;float:left;margin-inline-end:10px;background-size:contain;background-repeat:no-repeat}.app-sidebar .ellipsis{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.app-sidebar .fileName{font-size:16px;padding-top:13px;padding-bottom:3px}.app-sidebar .fileName h3{width:calc(100% - 42px);display:inline-block;padding:5px 0;margin:-5px 0}.app-sidebar .file-details{color:var(--color-text-maxcontrast)}.app-sidebar .action-favorite{vertical-align:sub;padding:10px;margin:-10px}.app-sidebar .action-favorite>span{opacity:.7 !important}.app-sidebar .detailList{float:left}.app-sidebar .close{position:absolute;top:0;inset-inline-end:0;opacity:.5;z-index:1;width:44px;height:44px}/*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */.whatsNewPopover{bottom:35px !important;inset-inline-start:15px !important;width:270px;z-index:700}.whatsNewPopover p{width:auto !important}.whatsNewPopover .caption{font-weight:bold;cursor:auto !important}.whatsNewPopover .icon-close{position:absolute;inset-inline-end:0}.whatsNewPopover::after{content:none}/*# sourceMappingURL=merged.css.map */
diff --git a/apps/files/css/merged.css.map b/apps/files/css/merged.css.map
deleted file mode 100644
index e6987e4e3f9..00000000000
--- a/apps/files/css/merged.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sourceRoot":"","sources":["merged.scss","files.scss","upload.scss","../../../core/css/variables.scss","mobile.scss","detailsView.scss","../../../core/css/whatsnew.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCSA,SAEC,YACA,YACA,qBACA,WAED,oEACA,8BACA,kDAEC,+CAED,0BACC,oDAGD,mBACC,kBACA,aACA,SACA,4CACC,sBACA,YACA,SACA,oDACA,8CACA,aACA,iBAIF,gBACC,aAGD,OACC,sBACA,YACA,aACA,aACA,mBAGD,6EAGC,yBACA,gCAID,kBACC,kBACA,WACA,gBACA,cACA,sBAEA,6CACC,aAGD,wBACC,wBACA,gBAEA,SAEA,WACA,cACA,0DAMD,wBACC,cACA,WAEA,mGAEC,8CAEA,6KACC,oCAKF,8DACC,oBAKH,yBACC,aAID,uCACC,cACA,WAGD,wBAGC,yBAEA,qBAGD,6FACC,+DAGD,iCACC,yDAGD,kFACC,0CAGD,4EACC,+DAID,iCACC,WACA,YACA,eACA,SACA,eAGD,wCACC,aAGD,0CACC,WAGD,2BACC,YAED,4KAKC,+CAED,wMAKC,oDAGD,qCAEA,yDACC,oCAED,kCACC,iCACA,8BACA,4BACA,yBACA,mBAED,wGAIC,UACA,oCAGD,oBACC,oCAED,uBACC,6BAED,sBACC,cACA,aACA,YACA,sBACA,2BACA,sBACA,oCACC,kBAGF,kCACC,qBACA,wBAED,2BACC,uBACA,yBAGD,uCACC,sBAGD,yBACC,WACA,WACA,wBACA,qBACA,2BACA,WAED,wJAIC,kBAED,2CACC,eAED,4EAEC,mBAGD,kBAEC,4CACA,iBACA,mBAED,SACC,eACA,kBACA,+BACA,4BAED,qBACC,kBACA,aACA,UAGD,uBACC,kBACA,YAGD,0BACC,gBAED,uCACC,eAED,8EAEC,2BACA,sBACA,kBAEA,gBAGD,qMAQC,gBACA,qPACC,MAIF,2BACC,0DACA,iBAGD,sDACC,sBAGD,+BACC,kBACA,aAED,kCACC,aAGD,0DAGC,WACA,kBAED,kDAEC,aACA,kBACA,2BACA,sBACA,YACA,iBACA,UAED,qCAEC,QACA,eACA,eACA,YAGA,8DACC,WAED,mEACC,WAGF,6BACC,qBACA,WACA,YACA,wBACA,2BACA,4BACA,wBACA,eACA,mCACA,eACA,kBACA,UAED,oCACC,eAID,2CACC,qCAGD,iDACC,qBACA,oCACA,YAED,uBACC,iBACA,kBACA,SAGD,6IACA,8FAEA,wCACC,kBACA,gBACA,uBACA,YAKA,kBACC,YACA,4BACC,QACA,YACA,aACA,gBACA,mBACA,uBACA,YACA,WACA,UACA,wBAKH,iJAEC,wBAGD,mCACC,iCACA,8BACA,4BACA,yBAED,4BACC,WAGD,2CACC,uBACA,gBACA,kBACA,mBAKD,8BACC,kBACA,mBAEA,yBACA,qBACA,SACA,YACA,cAEA,yBACA,eAEA,iBACA,oCACA,uBACA,mBAGD,mBACC,UAID,6DACC,WACA,oBAID,iRAIC,UAID,0EACC,WAMA,wEACC,aAGD,oGACC,+CACA,wCACA,wBACA,yDACA,aAIF,oGAEC,mBAGD,+BACC,kBACA,WACA,iBACA,wJAGD,wFAEC,kBACA,UACA,YAGD,yCACC,qBACA,WAED,8CACC,kBACA,cACA,SACA,sBACA,iBACA,kBACA,wDAEC,8CACA,8CACA,oBAEA,WACA,YACA,aACA,qBACA,uBAGF,8DACC,+CAGD,iDAGA,aACC,WAGD,iCACC,kBAID,mDAEC,gBAID,oCACC,qBACA,0BAGD,8EACC,0BAOA,kCACC,eAGD,sEACC,eAGD,sCACC,gBAIF,aACC,YACA,WACA,2BAKA,0EACC,wCAKF,iBACI,kBACA,qBACA,sBAEJ,wBACI,aAEJ,mBACC,eACA,iBACA,iBAGD,0BACC,aAED,uBACC,kBACA,2BACA,mBAGD,8CACC,gBAIA,8BACC,eACA,iBACA,iBACA,WACA,2CACC,kBACA,0FAGC,kBACA,4BACA,SACA,UACA,WACA,gBAED,mDACC,qBACA,sBAGF,0CACC,iBACA,oBACA,oBAGA,oGACC,WAID,qIAEC,WAED,uDACC,WACA,0HACC,WAIH,wEACC,UAED,oCACC,+CACA,wCAGF,uGACC,WAED,wDACC,UAKF,4EACC,qBACA,eACA,gBACA,uBACA,sBACA,sBAGD,2CACC,iCAGD,yCACC,UAGD,kNAKC,UAGD,qCACC,gBAGD,0FAEC,WAGD,mDACC,eAGD,SACC,oCAGA,aAED,wCACC,WAEA,2BAKD,sBACC,aAED,2DAIC,+BAED,YACC,mBACA,mBACA,iBAED,wBACC,UAED,YACC,qBAGD,iBACC,WACA,aAED,6BACC,yBACA,YAGA,gBAED,yBACC,uBAED,MACC,WACA,kBACA,QACA,8CACA,sCACA,wBACA,WACA,yBACA,8BACA,4BACA,6BACA,iCAED,kBACC,UAGD,aACC,gBACA,SACA,oCACA,eACA,gBACA,aAGA,oBACC,mCAKF,gBACC,sBACA,wBACA,gBACA,YACA,UACA,SACA,0DACA,WACA,yBACA,sBACA,qBACA,iBACA,aACA,MACA,0BAKE,0IACC,sBACA,qBACA,aACA,YACA,WACA,YACA,mBACA,uBAED,oFACC,aAQJ,0DACC,qBAGD,6KAIC,qBACA,sBACA,0BAMA,sDACC,sBAED,yDACC,uDAIF,iJAGC,aAGD,oJAGC,WACA,YAGD,gCACC,kBACA,YACA,SACA,oDACA,8CACA,aACA,iBAGD,YACC,mBAEA,uBACC,mCAIF,0DAEC,oCAED,qBACC,oCACA,4BACC,2BAIF,cACC,wBACA,gBACA,6BACA,cACA,gBACA,YAEA,2BACC,aAGD,kCACC,UACA,kBACA,sBAIF,uBACC,oBACA,YACA,gBACA,+BACA,UACA,YACA,wBACA,sBAEA,6BACC,YAKA,oEACC,0BAIF,kCACC,WACA,mCAWA,kDACC,cACA,4CACA,0DACA,qDACC,WACA,YAMH,+CACC,aACA,+CACA,6BACA,aACA,cAGA,+DACC,cACA,kBACA,aACA,mCAEA,0fAKC,+BAEA,oxDAGC,+CAKH,kDACC,eACA,mBAGC,8EACC,YACA,eACA,kBACA,MAvDQ,MAwDR,OAxDQ,MAyDR,QAxDO,KAyDP,MACA,qBACA,WAEA,yFACC,0BACA,2BACA,wBACA,SACA,mCACA,4BACA,2BAKA,wGACC,wBACA,UACA,uBAKH,uEACC,WACA,SACA,MACA,YAEA,YACA,gBAEA,0BAGD,iEACC,YACA,mCAIA,gBAKA,0BAEA,2EACC,aACA,YACA,iBACA,kBACA,iBACA,UAEA,0FACC,qBACA,kBACA,gBACA,uBACA,mBAED,kFACC,WACA,OACA,eAED,iFACC,WACA,OACA,eAID,sFACC,aAKF,8EACC,aAGD,8EACC,eACA,iBACA,aACA,mBACA,kBACA,mBAEA,sFACC,QAxJK,KAyJL,WACA,YACA,aACA,mBACA,uBAGA,wGACC,aAQH,2GACC,yBAEA,6HACC,YACA,kBAIF,6GACC,yBAGD,6GACC,yBAIF,gEACC,iBACA,mCAEA,+EACC,WACA,sBACA,YAMH,kHAEC,aAGD,sIAEC,kBACA,SACA,wBACA,aACA,WAEA,kJACC,WACA,YACA,oBACA,QAzNO,KA0NP,kKACC,SACA,MA5NM,KA6NN,OA7NM,KAmOT,+DACC,qBACA,YACA,aAGA,yFACC,gBACA,uBAMJ,+FACC,cAID,+CACC,aAEA,qEACC,qBACA,cAEA,aAEA,wEACC,iBAEA,iKAEC,aAGD,8EACI,sBAQR,aACC,0DACA,YACA,SACA,aACA,WACA,YACA,8CACA,iCACA,YACA,gBAEA,uEAGC,UAGD,oEAEC,mEASF,cACC,eACA,MAOC,uGACC,gBAID,4EACC,yBAKF,0BACC,kBACA,mBACA,MAKF,gBACC,aAGD,8BACC,gBACA,sBACA,kBACA,kBACA,aACA,eACA,mBAEA,iCACC,WACA,eAGD,6DACC,aACA,YACA,cC9xCF;AAAA;AAAA;AAAA;AAAA,GAKA,QACC,sBACA,YACA,WACA,qBACA,wBACA,gBACA,mBACA,kBACA,YAED,qBACC,kBACA,cACA,WACA,YACA,WACA,iBACA,eACA,WACA,YAED,iCACA,+FAEA,gDACC,sBAGD,uBACC,qBACA,mBACA,YACA,wBAED,0CACC,YACA,wBAED,mBACC,sCACA,sEACA,oBACA,kBACA,WACA,YACA,YACA,qBACA,kBAEA,yCACC,gBAGF,yEACC,wBACA,SACA,wBACA,kBACA,gBACA,8CAED,0BACC,QACA,UACA,gBACA,mBACA,mBAED,gCACC,wCACA,kBACA,cACA,YAED,gCACC,kBACA,6BAED,4BACC,cAED,2BACC,aAGD,yBACC,4BACA,0BAGD,uBACC,2BACA,yBACA,wBACA,sBACA,qBACA,iBACA,mBAGD,0HAGC,8BACA,4BACA,2BACA,yBACA,wBACA,oBAED,0CACC,cAED,6BACC,WAED,0BACC,iBAED,+CACC,sBAED,sCACC,YAED,gCACC,mBACA,6BAED,iCACC,wBAED,4CACC,aAED,iCACC,WACA,YAED,2CACC,WACA,qBACA,WAED,6BACC,WACA,YACA,uBACA,4BACA,0BACA,WAGD,qEAEC,WACA,UAED,kCACC,gBACA,iBAED,sDACC,WAED,sCACC,YAED,iCACC,sBACA,kBACA,SAED,sCACC,sBAGD,gCACC,WACA,iBAEA,wCACC,WAIF,mBACC,0CACA,uCACA,qCACA,kCAGD,sCACE,4BACA,qCAEF,mCACE,4BACA,qCAEF,iCACE,4BACA,qCAEF,8BACE,4BACA,qCCrNF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GCWA,oGAEA,iCACC,iCAGD,2EAIC,aAID,SACC,UAID,wBACC,uBAGD,mCACC,uBAGD,2BACC,sBAID,sDACC,kBACA,4BACA,SACA,UACA,WACA,gBAID,gCACC,aAKD,mDACC,WAGD,4BACC,wBAED,2BACC,yBAID,iBACC,cAID,0CAEC,0BACC,YAED,4CACC,aAID,4BACC,kBAID,uCACC,uBC5FF;AAAA;AAAA;AAAA;AAAA,GAKA,sCACC,gBACA,aAGD,0CACC,WAID,qCACC,qBACA,0BAGD,0CACC,iBACA,mBACA,WAEA,gGAEC,UAGF,sDACC,WACA,UAGD,uCACC,0BACA,iBAGD,gDACC,SAGD,+BACC,WACA,cACA,4BACA,2BACA,qBACA,WACA,SACA,YAGD,0CACC,WACA,cACA,sBAGD,wCACC,4BAGD,wCACC,wBAGD,yBACC,kBACA,kBACA,iBACA,cACA,sBAGD,wBACC,WACA,gBACA,qBACA,WACA,uBACA,wBACA,4BAGD,uBACC,mBACA,uBACA,gBAGD,uBACC,eACA,iBACA,mBAGD,0BACC,wBACA,qBACA,cACA,cAGD,2BACC,oCAGD,8BACC,mBACA,aACA,aAGD,mCACC,sBAGD,yBACC,WAGD,oBACC,kBACA,MACA,mBACA,WACA,UACA,WACA,YCnID;AAAA;AAAA;AAAA,GAIA,iBACE,uBACA,mCACA,YACA,YAGF,mBACE,sBAGF,0BACE,iBACA,uBAGF,6BACE,kBACA,mBAGF,wBACE","file":"merged.css"} \ No newline at end of file
diff --git a/apps/files/css/merged.css.map.license b/apps/files/css/merged.css.map.license
deleted file mode 100644
index 7f637358d26..00000000000
--- a/apps/files/css/merged.css.map.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/apps/files/css/merged.scss b/apps/files/css/merged.scss
deleted file mode 100644
index b5a9a9324cb..00000000000
--- a/apps/files/css/merged.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-@import 'files.scss';
-@import 'upload.scss';
-@import 'mobile.scss';
-@import 'detailsView.scss';
-@import '../../../core/css/whatsnew.scss';
diff --git a/apps/files/css/mobile.css b/apps/files/css/mobile.css
deleted file mode 100644
index cdc9d59773b..00000000000
--- a/apps/files/css/mobile.css
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- *//*!
- * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */@media only screen and (max-width: 988px)and (min-width: 1025px),only screen and (max-width: 688px){.app-files #app-content.dir-drop{background-color:#fff !important}table th.column-size,table td.filesize,table th.column-mtime,table td.date{display:none}table td{padding:0}table.multiselect thead{padding-inline-start:0}.fileList a.action.action-menu img{padding-inline-start:0}.fileList .fileActionsMenu{margin-inline-end:6px}.fileList a.action-share span:not(.icon):not(.avatar){position:absolute;inset-inline-start:-10000px;top:auto;width:1px;height:1px;overflow:hidden}td.filename a.name .system-tags{display:none}#uploadprogressbar,#uploadprogressbar .label.inner{width:50px}#uploadprogressbar .desktop{display:none !important}#uploadprogressbar .mobile{display:block !important}table.dragshadow{z-index:1000}}@media only screen and (max-width: 480px){table th .selectedActions{float:right}table th .selectedActions>a span:not(.icon){display:none}table th .selectedActions a{padding:17px 14px}table.multiselect th .columntitle.name{margin-inline-start:0}}/*# sourceMappingURL=mobile.css.map */
diff --git a/apps/files/css/mobile.css.map b/apps/files/css/mobile.css.map
deleted file mode 100644
index 8ae25b008dc..00000000000
--- a/apps/files/css/mobile.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sourceRoot":"","sources":["mobile.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAWA,oGAEA,iCACC,iCAGD,2EAIC,aAID,SACC,UAID,wBACC,uBAGD,mCACC,uBAGD,2BACC,sBAID,sDACC,kBACA,4BACA,SACA,UACA,WACA,gBAID,gCACC,aAKD,mDACC,WAGD,4BACC,wBAED,2BACC,yBAID,iBACC,cAID,0CAEC,0BACC,YAED,4CACC,aAID,4BACC,kBAID,uCACC","file":"mobile.css"} \ No newline at end of file
diff --git a/apps/files/css/mobile.css.map.license b/apps/files/css/mobile.css.map.license
deleted file mode 100644
index f800692cd8f..00000000000
--- a/apps/files/css/mobile.css.map.license
+++ /dev/null
@@ -1,3 +0,0 @@
-SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/files/css/mobile.scss b/apps/files/css/mobile.scss
deleted file mode 100644
index 9efdf1d9d7a..00000000000
--- a/apps/files/css/mobile.scss
+++ /dev/null
@@ -1,95 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-@use 'variables';
-
-/* 938 = table min-width(688) + app-navigation width: 250\
- $breakpoint-mobile +1 = size where app-navigation is hidden +1
- 688 = table min-width */
-$min-table-width: 688px;
-@media only screen and (max-width: $min-table-width + variables.$navigation-width) and (min-width: variables.$breakpoint-mobile + 1), only screen and (max-width: $min-table-width) {
-
-.app-files #app-content.dir-drop{
- background-color: rgba(255, 255, 255, 1)!important;
-}
-
-table th.column-size,
-table td.filesize,
-table th.column-mtime,
-table td.date {
- display: none;
-}
-
-/* remove padding to let border bottom fill the whole width*/
-table td {
- padding: 0;
-}
-
-/* remove shift for multiselect bar to account for missing navigation */
-table.multiselect thead {
- padding-inline-start: 0;
-}
-
-.fileList a.action.action-menu img {
- padding-inline-start: 0;
-}
-
-.fileList .fileActionsMenu {
- margin-inline-end: 6px;
-}
-/* hide text of the share action on mobile */
-/* .hidden-visually for accessbility */
-.fileList a.action-share span:not(.icon):not(.avatar) {
- position: absolute;
- inset-inline-start: -10000px;
- top: auto;
- width: 1px;
- height: 1px;
- overflow: hidden;
-}
-
-// Hide system tags on mobile
-td.filename a.name .system-tags {
- display: none;
-}
-
-
-/* shorten elements for mobile */
-#uploadprogressbar, #uploadprogressbar .label.inner {
- width: 50px;
-}
-/* hide desktop-only parts */
-#uploadprogressbar .desktop {
- display: none !important;
-}
-#uploadprogressbar .mobile {
- display: block !important;
-}
-
-/* ensure that it is visible over #app-content */
-table.dragshadow {
- z-index: 1000;
-}
-
-}
-@media only screen and (max-width: 480px) {
- /* Only show icons */
- table th .selectedActions {
- float: right;
- }
- table th .selectedActions > a span:not(.icon) {
- display: none;
- }
-
- /* Increase touch area for the icons */
- table th .selectedActions a {
- padding: 17px 14px;
- }
-
- /* Remove the margin to reduce the overlap between the name and the icons */
- table.multiselect th .columntitle.name {
- margin-inline-start: 0;
- }
-}
diff --git a/apps/files/css/upload.css b/apps/files/css/upload.css
deleted file mode 100644
index 04891a9dff3..00000000000
--- a/apps/files/css/upload.css
+++ /dev/null
@@ -1,5 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */#upload{box-sizing:border-box;height:36px;width:39px;padding:0 !important;margin-inline-start:3px;overflow:hidden;vertical-align:top;position:relative;z-index:-20}#upload .icon-upload{position:relative;display:block;width:100%;height:44px;width:44px;margin:-5px -3px;cursor:pointer;z-index:10;opacity:.65}.file_upload_target{display:none}.file_upload_form{display:inline;float:left;margin:0;padding:0;cursor:pointer;overflow:visible}.uploadprogresswrapper,.uploadprogresswrapper *{box-sizing:border-box}.uploadprogresswrapper{display:inline-block;vertical-align:top;height:36px;margin-inline-start:3px}.uploadprogresswrapper>input[type=button]{height:36px;margin-inline-start:3px}#uploadprogressbar{border-color:var(--color-border-dark);border-radius:var(--border-radius-pill) 0 0 var(--border-radius-pill);border-inline-end:0;position:relative;float:left;width:200px;height:44px;display:inline-block;text-align:center}#uploadprogressbar .ui-progressbar-value{margin-top:.1em}#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left{height:calc(100% + 2px);top:-2px;inset-inline-start:-1px;position:absolute;overflow:hidden;background-color:var(--color-primary-element)}#uploadprogressbar .label{top:8px;opacity:1;overflow:hidden;white-space:nowrap;font-weight:normal}#uploadprogressbar .label.inner{color:var(--color-primary-element-text);position:absolute;display:block;width:200px}#uploadprogressbar .label.outer{position:relative;color:var(--color-main-text)}#uploadprogressbar .desktop{display:block}#uploadprogressbar .mobile{display:none}#uploadprogressbar+.stop{border-start-start-radius:0;border-end-start-radius:0}.oc-dialog .fileexists{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin-bottom:30px}.oc-dialog .fileexists .conflict .filename,.oc-dialog .fileexists .conflict .mtime,.oc-dialog .fileexists .conflict .size{-webkit-touch-callout:initial;-webkit-user-select:initial;-khtml-user-select:initial;-moz-user-select:initial;-ms-user-select:initial;user-select:initial}.oc-dialog .fileexists .conflict .message{color:#e9322d}.oc-dialog .fileexists table{width:100%}.oc-dialog .fileexists th{padding-inline:0}.oc-dialog .fileexists th input[type=checkbox]{margin-inline-end:3px}.oc-dialog .fileexists th:first-child{width:225px}.oc-dialog .fileexists th label{font-weight:normal;color:var(--color-main-text)}.oc-dialog .fileexists th .count{margin-inline-start:3px}.oc-dialog .fileexists .conflicts .template{display:none}.oc-dialog .fileexists .conflict{width:100%;height:85px}.oc-dialog .fileexists .conflict .filename{color:#777;word-break:break-all;clear:left}.oc-dialog .fileexists .icon{width:64px;height:64px;margin:0px 5px 5px 5px;background-repeat:no-repeat;background-size:64px 64px;float:left}.oc-dialog .fileexists .original,.oc-dialog .fileexists .replacement{float:left;width:50%}.oc-dialog .fileexists .conflicts{overflow-y:auto;max-height:225px}.oc-dialog .fileexists .conflict input[type=checkbox]{float:left}.oc-dialog .fileexists #allfileslabel{float:right}.oc-dialog .fileexists #allfiles{vertical-align:bottom;position:relative;top:-3px}.oc-dialog .fileexists #allfiles+span{vertical-align:bottom}.oc-dialog .oc-dialog-buttonrow{width:100%;text-align:right}.oc-dialog .oc-dialog-buttonrow .cancel{float:left}.highlightUploaded{-webkit-animation:highlightAnimation 2s 1;-moz-animation:highlightAnimation 2s 1;-o-animation:highlightAnimation 2s 1;animation:highlightAnimation 2s 1}@-webkit-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-moz-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@-o-keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}@keyframes highlightAnimation{0%{background-color:#ffff8c}100%{background-color:rgba(0,0,0,0)}}/*# sourceMappingURL=upload.css.map */
diff --git a/apps/files/css/upload.css.map b/apps/files/css/upload.css.map
deleted file mode 100644
index 7c1d81d514d..00000000000
--- a/apps/files/css/upload.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sourceRoot":"","sources":["upload.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GAKA,QACC,sBACA,YACA,WACA,qBACA,wBACA,gBACA,mBACA,kBACA,YAED,qBACC,kBACA,cACA,WACA,YACA,WACA,iBACA,eACA,WACA,YAED,iCACA,+FAEA,gDACC,sBAGD,uBACC,qBACA,mBACA,YACA,wBAED,0CACC,YACA,wBAED,mBACC,sCACA,sEACA,oBACA,kBACA,WACA,YACA,YACA,qBACA,kBAEA,yCACC,gBAGF,yEACC,wBACA,SACA,wBACA,kBACA,gBACA,8CAED,0BACC,QACA,UACA,gBACA,mBACA,mBAED,gCACC,wCACA,kBACA,cACA,YAED,gCACC,kBACA,6BAED,4BACC,cAED,2BACC,aAGD,yBACC,4BACA,0BAGD,uBACC,2BACA,yBACA,wBACA,sBACA,qBACA,iBACA,mBAGD,0HAGC,8BACA,4BACA,2BACA,yBACA,wBACA,oBAED,0CACC,cAED,6BACC,WAED,0BACC,iBAED,+CACC,sBAED,sCACC,YAED,gCACC,mBACA,6BAED,iCACC,wBAED,4CACC,aAED,iCACC,WACA,YAED,2CACC,WACA,qBACA,WAED,6BACC,WACA,YACA,uBACA,4BACA,0BACA,WAGD,qEAEC,WACA,UAED,kCACC,gBACA,iBAED,sDACC,WAED,sCACC,YAED,iCACC,sBACA,kBACA,SAED,sCACC,sBAGD,gCACC,WACA,iBAEA,wCACC,WAIF,mBACC,0CACA,uCACA,qCACA,kCAGD,sCACE,4BACA,qCAEF,mCACE,4BACA,qCAEF,iCACE,4BACA,qCAEF,8BACE,4BACA","file":"upload.css"} \ No newline at end of file
diff --git a/apps/files/css/upload.css.map.license b/apps/files/css/upload.css.map.license
deleted file mode 100644
index ada7fbdd4ca..00000000000
--- a/apps/files/css/upload.css.map.license
+++ /dev/null
@@ -1,3 +0,0 @@
-SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
diff --git a/apps/files/css/upload.scss b/apps/files/css/upload.scss
deleted file mode 100644
index 4ab46d90701..00000000000
--- a/apps/files/css/upload.scss
+++ /dev/null
@@ -1,215 +0,0 @@
-/*!
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-#upload {
- box-sizing: border-box;
- height: 36px;
- width: 39px;
- padding: 0 !important; /* override default control bar button padding */
- margin-inline-start: 3px;
- overflow: hidden;
- vertical-align: top;
- position: relative;
- z-index: -20;
-}
-#upload .icon-upload {
- position: relative;
- display: block;
- width: 100%;
- height: 44px;
- width: 44px;
- margin: -5px -3px;
- cursor: pointer;
- z-index: 10;
- opacity: .65;
-}
-.file_upload_target { display:none; }
-.file_upload_form { display:inline; float:left; margin:0; padding:0; cursor:pointer; overflow:visible; }
-
-.uploadprogresswrapper, .uploadprogresswrapper * {
- box-sizing: border-box;
-}
-
-.uploadprogresswrapper {
- display: inline-block;
- vertical-align: top;
- height: 36px;
- margin-inline-start: 3px;
-}
-.uploadprogresswrapper > input[type='button'] {
- height: 36px;
- margin-inline-start: 3px;
-}
-#uploadprogressbar {
- border-color: var(--color-border-dark);
- border-radius: var(--border-radius-pill) 0 0 var(--border-radius-pill);
- border-inline-end: 0;
- position:relative;
- float: left;
- width: 200px;
- height: 44px;
- display:inline-block;
- text-align: center;
-
- .ui-progressbar-value {
- margin-top:.1em;
- }
-}
-#uploadprogressbar .ui-progressbar-value.ui-widget-header.ui-corner-left {
- height: calc(100% + 2px);
- top: -2px;
- inset-inline-start: -1px;
- position: absolute;
- overflow: hidden;
- background-color: var(--color-primary-element);
-}
-#uploadprogressbar .label {
- top: 8px;
- opacity: 1;
- overflow: hidden;
- white-space: nowrap;
- font-weight: normal;
-}
-#uploadprogressbar .label.inner {
- color: var(--color-primary-element-text);
- position: absolute;
- display: block;
- width: 200px;
-}
-#uploadprogressbar .label.outer {
- position: relative;
- color: var(--color-main-text);
-}
-#uploadprogressbar .desktop {
- display: block;
-}
-#uploadprogressbar .mobile {
- display: none;
-}
-
-#uploadprogressbar + .stop {
- border-start-start-radius: 0;
- border-end-start-radius: 0;
-}
-
-.oc-dialog .fileexists {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- margin-bottom: 30px;
-}
-
-.oc-dialog .fileexists .conflict .filename,
-.oc-dialog .fileexists .conflict .mtime,
-.oc-dialog .fileexists .conflict .size {
- -webkit-touch-callout: initial;
- -webkit-user-select: initial;
- -khtml-user-select: initial;
- -moz-user-select: initial;
- -ms-user-select: initial;
- user-select: initial;
-}
-.oc-dialog .fileexists .conflict .message {
- color: #e9322d;
-}
-.oc-dialog .fileexists table {
- width: 100%;
-}
-.oc-dialog .fileexists th {
- padding-inline: 0;
-}
-.oc-dialog .fileexists th input[type='checkbox'] {
- margin-inline-end: 3px;
-}
-.oc-dialog .fileexists th:first-child {
- width: 225px;
-}
-.oc-dialog .fileexists th label {
- font-weight: normal;
- color: var(--color-main-text);
-}
-.oc-dialog .fileexists th .count {
- margin-inline-start: 3px;
-}
-.oc-dialog .fileexists .conflicts .template {
- display: none;
-}
-.oc-dialog .fileexists .conflict {
- width: 100%;
- height: 85px;
-}
-.oc-dialog .fileexists .conflict .filename {
- color:#777;
- word-break: break-all;
- clear: left;
-}
-.oc-dialog .fileexists .icon {
- width: 64px;
- height: 64px;
- margin: 0px 5px 5px 5px;
- background-repeat: no-repeat;
- background-size: 64px 64px;
- float: left;
-}
-
-.oc-dialog .fileexists .original,
-.oc-dialog .fileexists .replacement {
- float: left;
- width: 50%;
-}
-.oc-dialog .fileexists .conflicts {
- overflow-y: auto;
- max-height: 225px;
-}
-.oc-dialog .fileexists .conflict input[type='checkbox'] {
- float: left;
-}
-.oc-dialog .fileexists #allfileslabel {
- float:right;
-}
-.oc-dialog .fileexists #allfiles {
- vertical-align: bottom;
- position: relative;
- top: -3px;
-}
-.oc-dialog .fileexists #allfiles + span{
- vertical-align: bottom;
-}
-
-.oc-dialog .oc-dialog-buttonrow {
- width:100%;
- text-align:right;
-
- .cancel {
- float:left;
- }
-}
-
-.highlightUploaded {
- -webkit-animation: highlightAnimation 2s 1;
- -moz-animation: highlightAnimation 2s 1;
- -o-animation: highlightAnimation 2s 1;
- animation: highlightAnimation 2s 1;
-}
-
-@-webkit-keyframes highlightAnimation {
- 0% { background-color: rgba(255, 255, 140, 1); }
- 100% { background-color: rgba(0, 0, 0, 0); }
-}
-@-moz-keyframes highlightAnimation {
- 0% { background-color: rgba(255, 255, 140, 1); }
- 100% { background-color: rgba(0, 0, 0, 0); }
-}
-@-o-keyframes highlightAnimation {
- 0% { background-color: rgba(255, 255, 140, 1); }
- 100% { background-color: rgba(0, 0, 0, 0); }
-}
-@keyframes highlightAnimation {
- 0% { background-color: rgba(255, 255, 140, 1); }
- 100% { background-color: rgba(0, 0, 0, 0); }
-}
diff --git a/apps/files/js/app.js b/apps/files/js/app.js
deleted file mode 100644
index 820aa2c1ef8..00000000000
--- a/apps/files/js/app.js
+++ /dev/null
@@ -1,398 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-/* global dragOptions, folderDropOptions, OC */
-(function() {
-
- if (!OCA.Files) {
- /**
- * Namespace for the files app
- * @namespace OCA.Files
- */
- OCA.Files = {};
- }
-
- /**
- * @namespace OCA.Files.App
- */
- OCA.Files.App = {
- /**
- * Navigation instance
- *
- * @member {OCP.Files.Navigation}
- */
- navigation: null,
-
- /**
- * File list for the "All files" section.
- *
- * @member {OCA.Files.FileList}
- */
- fileList: null,
-
- currentFileList: null,
-
- /**
- * Backbone model for storing files preferences
- */
- _filesConfig: null,
-
- /**
- * Initializes the files app
- */
- initialize: function() {
- this.$showHiddenFiles = $('input#showhiddenfilesToggle');
- var showHidden = $('#showHiddenFiles').val() === "1";
- this.$showHiddenFiles.prop('checked', showHidden);
-
- // Toggle for grid view
- this.$showGridView = $('input#showgridview');
- this.$showGridView.on('change', _.bind(this._onGridviewChange, this));
-
- if ($('#fileNotFound').val() === "1") {
- OC.Notification.show(t('files', 'File could not be found'), {type: 'error'});
- }
-
- this._filesConfig = OCP.InitialState.loadState('files', 'config', {})
-
- var { fileid, scrollto, openfile } = OC.Util.History.parseUrlQuery();
- var fileActions = new OCA.Files.FileActions();
- // default actions
- fileActions.registerDefaultActions();
- // regular actions
- fileActions.merge(OCA.Files.fileActions);
-
- this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
- OCA.Files.fileActions.on('setDefault.app-files', this._onActionsUpdated);
- OCA.Files.fileActions.on('registerAction.app-files', this._onActionsUpdated);
-
- this.files = OCA.Files.Files;
-
- // TODO: ideally these should be in a separate class / app (the embedded "all files" app)
- this.fileList = new OCA.Files.FileList(
- $('#app-content-files'), {
- dragOptions: dragOptions,
- folderDropOptions: folderDropOptions,
- fileActions: fileActions,
- allowLegacyActions: true,
- scrollTo: scrollto,
- openFile: openfile,
- filesClient: OC.Files.getClient(),
- multiSelectMenu: [
- {
- name: 'copyMove',
- displayName: t('files', 'Move or copy'),
- iconClass: 'icon-external',
- order: 10,
- },
- {
- name: 'download',
- displayName: t('files', 'Download'),
- iconClass: 'icon-download',
- order: 10,
- },
- OCA.Files.FileList.MultiSelectMenuActions.ToggleSelectionModeAction,
- {
- name: 'delete',
- displayName: t('files', 'Delete'),
- iconClass: 'icon-delete',
- order: 99,
- },
- ...(
- OCA?.SystemTags === undefined ? [] : ([{
- name: 'tags',
- displayName: t('files', 'Tags'),
- iconClass: 'icon-tag',
- order: 100,
- }])
- ),
- ],
- sorting: {
- mode: $('#defaultFileSorting').val() === 'basename'
- ? 'name'
- : $('#defaultFileSorting').val(),
- direction: $('#defaultFileSortingDirection').val()
- },
- config: this._filesConfig,
- enableUpload: true,
- maxChunkSize: OC.appConfig.files && OC.appConfig.files.max_chunk_size
- }
- );
- this.updateCurrentFileList(this.fileList)
- this.files.initialize();
-
- // for backward compatibility, the global FileList will
- // refer to the one of the "files" view
- window.FileList = this.fileList;
-
- OC.Plugins.attach('OCA.Files.App', this);
-
- this._setupEvents();
-
- if (sessionStorage.getItem('WhatsNewServerCheck') < (Date.now() - 3600*1000)) {
- OCP.WhatsNew.query(); // for Nextcloud server
- sessionStorage.setItem('WhatsNewServerCheck', Date.now());
- }
-
- window._nc_event_bus.emit('files:legacy-view:initialized', this);
-
- this.navigation = OCP.Files.Navigation
- },
-
- /**
- * Destroy the app
- */
- destroy: function() {
- this.fileList.destroy();
- this.fileList = null;
- this.files = null;
- OCA.Files.fileActions.off('setDefault.app-files', this._onActionsUpdated);
- OCA.Files.fileActions.off('registerAction.app-files', this._onActionsUpdated);
- },
-
- _onActionsUpdated: function(ev) {
- // forward new action to the file list
- if (ev.action) {
- this.fileList.fileActions.registerAction(ev.action);
- } else if (ev.defaultAction) {
- this.fileList.fileActions.setDefault(
- ev.defaultAction.mime,
- ev.defaultAction.name
- );
- }
- },
-
- /**
- * Set the currently active file list
- *
- * Due to the file list implementations being registered after clicking the
- * navigation item for the first time, OCA.Files.App is not aware of those until
- * they have initialized themselves. Therefore the files list needs to call this
- * method manually
- *
- * @param {OCA.Files.FileList} newFileList -
- */
- updateCurrentFileList: function(newFileList) {
- if (this.currentFileList === newFileList) {
- return
- }
-
- this.currentFileList = newFileList;
- if (this.currentFileList !== null) {
- // update grid view to the current value
- const isGridView = this.$showGridView.is(':checked');
- this.currentFileList.setGridView(isGridView);
- }
- },
-
- /**
- * Return the currently active file list
- * @return {?OCA.Files.FileList}
- */
- getCurrentFileList: function () {
- return this.currentFileList;
- },
-
- /**
- * Returns the container of the currently visible app.
- *
- * @return app container
- */
- getCurrentAppContainer: function() {
- var viewId = this.getActiveView();
- return $('#app-content-' + viewId);
- },
-
- /**
- * Sets the currently active view
- * @param viewId view id
- */
- setActiveView: function(viewId) {
- // The Navigation API will handle the final event
- window._nc_event_bus.emit('files:legacy-navigation:changed', { id: viewId })
- },
-
- /**
- * Returns the view id of the currently active view
- * @return view id
- */
- getActiveView: function() {
- return this.navigation
- && this.navigation.active
- && this.navigation.active.id;
- },
-
- /**
- *
- * @returns {Backbone.Model}
- */
- getFilesConfig: function() {
- return this._filesConfig;
- },
-
- /**
- * Setup events based on URL changes
- */
- _setupEvents: function() {
- OC.Util.History.addOnPopStateHandler(_.bind(this._onPopState, this));
-
- // detect when app changed their current directory
- $('#app-content').delegate('>div', 'changeDirectory', _.bind(this._onDirectoryChanged, this));
- $('#app-content').delegate('>div', 'afterChangeDirectory', _.bind(this._onAfterDirectoryChanged, this));
- $('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this));
- },
-
- /**
- * Event handler for when the current navigation item has changed
- */
- _onNavigationChanged: function(view) {
- var params;
- if (view && (view.itemId || view.id)) {
- if (view.id) {
- params = {
- view: view.id,
- dir: '/',
- }
- } else {
- // Legacy handling
- params = {
- view: typeof view.view === 'string' && view.view !== '' ? view.view : view.itemId,
- dir: view.dir ? view.dir : '/'
- }
- }
- this._changeUrl(params.view, params.dir);
- OCA.Files.Sidebar.close();
- this.getCurrentAppContainer().trigger(new $.Event('urlChanged', params));
- window._nc_event_bus.emit('files:navigation:changed')
- }
- },
-
- /**
- * Event handler for when an app notified that its directory changed
- */
- _onDirectoryChanged: function(e) {
- if (e.dir && !e.changedThroughUrl) {
- this._changeUrl(this.getActiveView(), e.dir, e.fileId);
- }
- },
-
- /**
- * Event handler for when an app notified that its directory changed
- */
- _onAfterDirectoryChanged: function(e) {
- if (e.dir && e.fileId) {
- this._changeUrl(this.getActiveView(), e.dir, e.fileId);
- }
- },
-
- /**
- * Event handler for when an app notifies that it needs space
- * for viewer mode.
- */
- _onChangeViewerMode: function(e) {
- var state = !!e.viewerModeEnabled;
- if (e.viewerModeEnabled) {
- OCA.Files.Sidebar.close();
- }
- $('#app-navigation').toggleClass('hidden', state);
- $('.app-files').toggleClass('viewer-mode no-sidebar', state);
- },
-
- /**
- * Event handler for when the URL changed
- */
- _onPopState: function(params) {
- params = _.extend({
- dir: '/',
- view: 'files'
- }, params);
-
- var lastId = this.getActiveView();
- if (!this.navigation.views.find(view => view.id === params.view)) {
- params.view = 'files';
- }
-
- this.setActiveView(params.view, {silent: true});
- if (lastId !== this.getActiveView()) {
- this.getCurrentAppContainer().trigger(new $.Event('show', params));
- window._nc_event_bus.emit('files:navigation:changed')
- }
-
- this.getCurrentAppContainer().trigger(new $.Event('urlChanged', params));
-
- },
-
- /**
- * Encode URL params into a string, except for the "dir" attribute
- * that gets encoded as path where "/" is not encoded
- *
- * @param {Object.<string>} params
- * @return {string} encoded params
- */
- _makeUrlParams: function(params) {
- var dir = params.dir;
- delete params.dir;
- return 'dir=' + OC.encodePath(dir) + '&' + OC.buildQueryString(params);
- },
-
- /**
- * Change the URL to point to the given dir and view
- */
- _changeUrl: function(view, dir, fileId) {
- var params = { dir: dir };
- if (view !== 'files') {
- params.view = view;
- } else if (fileId) {
- params.fileid = fileId;
- }
- var currentParams = OC.Util.History.parseUrlQuery();
- if (currentParams.dir === params.dir && currentParams.view === params.view) {
- if (currentParams.fileid !== params.fileid) {
- // if only fileid changed or was added, replace instead of push
- OC.Util.History.replaceState(this._makeUrlParams(params));
- return
- }
- } else {
- OC.Util.History.pushState(this._makeUrlParams(params));
- return
- }
- },
-
- /**
- * Toggle showing gridview by default or not
- *
- * @returns {undefined}
- */
- _onGridviewChange: function() {
- const isGridView = this.$showGridView.is(':checked');
- // only save state if user is logged in
- if (OC.currentUser) {
- $.post(OC.generateUrl('/apps/files/api/v1/showgridview'), {
- show: isGridView,
- });
- }
- this.$showGridView.next('#view-toggle')
- .removeClass('icon-toggle-filelist icon-toggle-pictures')
- .addClass(isGridView ? 'icon-toggle-filelist' : 'icon-toggle-pictures')
- this.$showGridView.next('#view-toggle')
- .attr('title', isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'))
- this.$showGridView.attr('aria-label', isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'))
-
- if (this.currentFileList) {
- this.currentFileList.setGridView(isGridView);
- }
- },
-
- };
-})();
-
-window.addEventListener('DOMContentLoaded', function() {
- // wait for other apps/extensions to register their event handlers and file actions
- // in the "ready" clause
- _.defer(function() {
- OCA.Files.App.initialize();
- });
-});
diff --git a/apps/files/js/breadcrumb.js b/apps/files/js/breadcrumb.js
deleted file mode 100644
index b4e1d194446..00000000000
--- a/apps/files/js/breadcrumb.js
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2013-2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- /**
- * @class BreadCrumb
- * @memberof OCA.Files
- * @classdesc Breadcrumbs that represent the current path.
- *
- * @param {Object} [options] options
- * @param {Function} [options.onClick] click event handler
- * @param {Function} [options.onDrop] drop event handler
- * @param {Function} [options.getCrumbUrl] callback that returns
- * the URL of a given breadcrumb
- */
- var BreadCrumb = function(options){
- this.$el = $('<nav></nav>');
- this.$menu = $('<div class="popovermenu menu-center"><ul></ul></div>');
-
- this.crumbSelector = '.crumb:not(.hidden):not(.crumbhome):not(.crumbmenu)';
- this.hiddenCrumbSelector = '.crumb.hidden:not(.crumbhome):not(.crumbmenu)';
- options = options || {};
- if (options.onClick) {
- this.onClick = options.onClick;
- }
- if (options.onDrop) {
- this.onDrop = options.onDrop;
- this.onOver = options.onOver;
- this.onOut = options.onOut;
- }
- if (options.getCrumbUrl) {
- this.getCrumbUrl = options.getCrumbUrl;
- }
- this._detailViews = [];
- };
-
- /**
- * @memberof OCA.Files
- */
- BreadCrumb.prototype = {
- $el: null,
- dir: null,
- dirInfo: null,
-
- /**
- * Total width of all breadcrumbs
- * @type int
- * @private
- */
- totalWidth: 0,
- breadcrumbs: [],
- onClick: null,
- onDrop: null,
- onOver: null,
- onOut: null,
-
- /**
- * Sets the directory to be displayed as breadcrumb.
- * This will re-render the breadcrumb.
- * @param dir path to be displayed as breadcrumb
- */
- setDirectory: function(dir) {
- dir = dir.replace(/\\/g, '/');
- dir = dir || '/';
- if (dir !== this.dir) {
- this.dir = dir;
- this.render();
- }
- },
-
- setDirectoryInfo: function(dirInfo) {
- if (dirInfo !== this.dirInfo) {
- this.dirInfo = dirInfo;
- this.render();
- }
- },
-
- /**
- * @param {Backbone.View} detailView
- */
- addDetailView: function(detailView) {
- this._detailViews.push(detailView);
- },
-
- /**
- * Returns the full URL to the given directory
- *
- * @param {Object.<String, String>} part crumb data as map
- * @param {number} index crumb index
- * @return full URL
- */
- getCrumbUrl: function(part, index) {
- return '#';
- },
-
- /**
- * Renders the breadcrumb elements
- */
- render: function() {
- // Menu is destroyed on every change, we need to init it
- OC.unregisterMenu($('.crumbmenu > .icon-more'), $('.crumbmenu > .popovermenu'));
-
- var parts = this._makeCrumbs(this.dir || '/');
- var $crumb;
- var $menuItem;
- this.$el.empty();
- this.breadcrumbs = [];
- var $crumbList = $('<ul class="breadcrumb"></ul>');
-
- for (var i = 0; i < parts.length; i++) {
- var part = parts[i];
- var $image;
- var $link = $('<a></a>');
- $crumb = $('<li class="crumb svg"></li>');
- if(part.dir) {
- $link.attr('href', this.getCrumbUrl(part, i));
- }
- if(part.name) {
- $link.text(part.name);
- }
- $link.addClass(part.linkclass);
- $crumb.append($link);
- $crumb.data('dir', part.dir);
- // Ignore menu button
- $crumb.data('crumb-id', i - 1);
- $crumb.addClass(part.class);
-
- if (part.img) {
- $image = $('<img class="svg"></img>');
- $image.attr('src', part.img);
- $image.attr('alt', part.alt);
- $link.append($image);
- }
- this.breadcrumbs.push($crumb);
- $crumbList.append($crumb);
- // Only add feedback if not menu
- if (this.onClick && i !== 0) {
- $link.on('click', this.onClick);
- }
- }
- this.$el.append($crumbList);
-
- // Menu creation
- this._createMenu();
- for (var j = 0; j < parts.length; j++) {
- var menuPart = parts[j];
- if(menuPart.dir) {
- $menuItem = $('<li class="crumblist"><a><span class="icon-folder"></span><span></span></a></li>');
- $menuItem.data('dir', menuPart.dir);
- $menuItem.find('a').attr('href', this.getCrumbUrl(part, j));
- $menuItem.find('span:eq(1)').text(menuPart.name);
- this.$menu.children('ul').append($menuItem);
- if (this.onClick) {
- $menuItem.on('click', this.onClick);
- }
- }
- }
- _.each(this._detailViews, function(view) {
- view.render({
- dirInfo: this.dirInfo
- });
- $crumb.append(view.$el);
- $menuItem.append(view.$el.clone(true));
- }, this);
-
- // setup drag and drop
- if (this.onDrop) {
- this.$el.find('.crumb:not(:last-child):not(.crumbmenu), .crumblist:not(:last-child)').droppable({
- drop: this.onDrop,
- over: this.onOver,
- out: this.onOut,
- tolerance: 'pointer',
- hoverClass: 'canDrop',
- greedy: true
- });
- }
-
- // Menu is destroyed on every change, we need to init it
- OC.registerMenu($('.crumbmenu > .icon-more'), $('.crumbmenu > .popovermenu'));
-
- this._resize();
- },
-
- /**
- * Makes a breadcrumb structure based on the given path
- *
- * @param {String} dir path to split into a breadcrumb structure
- * @param {String} [rootIcon=icon-home] icon to use for root
- * @return {Object.<String, String>} map of {dir: path, name: displayName}
- */
- _makeCrumbs: function(dir, rootIcon) {
- var crumbs = [];
- var pathToHere = '';
- // trim leading and trailing slashes
- dir = dir.replace(/^\/+|\/+$/g, '');
- var parts = dir.split('/');
- if (dir === '') {
- parts = [];
- }
- // menu part
- crumbs.push({
- class: 'crumbmenu hidden',
- linkclass: 'icon-more menutoggle'
- });
- // root part
- crumbs.push({
- name: t('files', 'Home'),
- dir: '/',
- class: 'crumbhome',
- linkclass: rootIcon || 'icon-home'
- });
- for (var i = 0; i < parts.length; i++) {
- var part = parts[i];
- pathToHere = pathToHere + '/' + part;
- crumbs.push({
- dir: pathToHere,
- name: part
- });
- }
- return crumbs;
- },
-
- /**
- * Calculate real width based on individual crumbs
- *
- * @param {boolean} ignoreHidden ignore hidden crumbs
- */
- getTotalWidth: function(ignoreHidden) {
- // The width has to be calculated by adding up the width of all the
- // crumbs; getting the width of the breadcrumb element is not a
- // valid approach, as the returned value could be clamped to its
- // parent width.
- var totalWidth = 0;
- for (var i = 0; i < this.breadcrumbs.length; i++ ) {
- var $crumb = $(this.breadcrumbs[i]);
- if(!$crumb.hasClass('hidden') || ignoreHidden === true) {
- totalWidth += $crumb.outerWidth(true);
- }
- }
- return totalWidth;
- },
-
- /**
- * Hide the middle crumb
- */
- _hideCrumb: function() {
- var length = this.$el.find(this.crumbSelector).length;
- // Get the middle one floored down
- var elmt = Math.floor(length / 2 - 0.5);
- this.$el.find(this.crumbSelector+':eq('+elmt+')').addClass('hidden');
- },
-
- /**
- * Get the crumb to show
- */
- _getCrumbElement: function() {
- var hidden = this.$el.find(this.hiddenCrumbSelector).length;
- var shown = this.$el.find(this.crumbSelector).length;
- // Get the outer one with priority to the highest
- var elmt = (1 - shown % 2) * (hidden - 1);
- return this.$el.find(this.hiddenCrumbSelector + ':eq('+elmt+')');
- },
-
- /**
- * Show the middle crumb
- */
- _showCrumb: function() {
- if(this.$el.find(this.hiddenCrumbSelector).length === 1) {
- this.$el.find(this.hiddenCrumbSelector).removeClass('hidden');
- }
- this._getCrumbElement().removeClass('hidden');
- },
-
- /**
- * Create and append the popovermenu
- */
- _createMenu: function() {
- this.$el.find('.crumbmenu').append(this.$menu);
- this.$menu.children('ul').empty();
- },
-
- /**
- * Update the popovermenu
- */
- _updateMenu: function() {
- var menuItems = this.$el.find(this.hiddenCrumbSelector);
-
- this.$menu.find('li').addClass('in-breadcrumb');
- for (var i = 0; i < menuItems.length; i++) {
- var crumbId = $(menuItems[i]).data('crumb-id');
- this.$menu.find('li:eq('+crumbId+')').removeClass('in-breadcrumb');
- }
- },
-
- _resize: function() {
-
- if (this.breadcrumbs.length <= 2) {
- // home & menu
- return;
- }
-
- // Always hide the menu to ensure that it does not interfere with
- // the width calculations; otherwise, the result could be different
- // depending on whether the menu was previously being shown or not.
- this.$el.find('.crumbmenu').addClass('hidden');
-
- // Show the crumbs to compress the siblings before hiding again the
- // crumbs. This is needed when the siblings expand to fill all the
- // available width, as in that case their old width would limit the
- // available width for the crumbs.
- // Note that the crumbs shown always overflow the parent width
- // (except, of course, when they all fit in).
- while (this.$el.find(this.hiddenCrumbSelector).length > 0
- && Math.round(this.getTotalWidth()) <= Math.round(this.$el.parent().width())) {
- this._showCrumb();
- }
-
- var siblingsWidth = 0;
- this.$el.prevAll(':visible').each(function () {
- siblingsWidth += $(this).outerWidth(true);
- });
- this.$el.nextAll(':visible').each(function () {
- siblingsWidth += $(this).outerWidth(true);
- });
-
- var availableWidth = this.$el.parent().width() - siblingsWidth;
-
- // If container is smaller than content
- // AND if there are crumbs left to hide
- while (Math.round(this.getTotalWidth()) > Math.round(availableWidth)
- && this.$el.find(this.crumbSelector).length > 0) {
- // As soon as one of the crumbs is hidden the menu will be
- // shown. This is needed for proper results in further width
- // checks.
- // Note that the menu is not shown only when all the crumbs were
- // being shown and they all fit the available space; if any of
- // the crumbs was not being shown then those shown would
- // overflow the available width, so at least one will be hidden
- // and thus the menu will be shown.
- this.$el.find('.crumbmenu').removeClass('hidden');
- this._hideCrumb();
- }
-
- this._updateMenu();
- }
- };
-
- OCA.Files.BreadCrumb = BreadCrumb;
-})();
diff --git a/apps/files/js/detailfileinfoview.js b/apps/files/js/detailfileinfoview.js
deleted file mode 100644
index a93a4f40207..00000000000
--- a/apps/files/js/detailfileinfoview.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- /**
- * @class OCA.Files.DetailFileInfoView
- * @classdesc
- *
- * Displays a block of details about the file info.
- *
- */
- var DetailFileInfoView = OC.Backbone.View.extend({
- tagName: 'div',
- className: 'detailFileInfoView',
-
- _template: null,
-
- /**
- * returns the jQuery object for HTML output
- *
- * @returns {jQuery}
- */
- get$: function() {
- return this.$el;
- },
-
- /**
- * Sets the file info to be displayed in the view
- *
- * @param {OCA.Files.FileInfo} fileInfo file info to set
- */
- setFileInfo: function(fileInfo) {
- this.model = fileInfo;
- this.render();
- },
-
- /**
- * Returns the file info.
- *
- * @return {OCA.Files.FileInfo} file info
- */
- getFileInfo: function() {
- return this.model;
- }
- });
-
- OCA.Files.DetailFileInfoView = DetailFileInfoView;
-})();
-
diff --git a/apps/files/js/detailsview.js b/apps/files/js/detailsview.js
deleted file mode 100644
index d8262425635..00000000000
--- a/apps/files/js/detailsview.js
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- /**
- * @class OCA.Files.DetailsView
- * @classdesc
- *
- * The details view show details about a selected file.
- *
- */
- var DetailsView = OC.Backbone.View.extend({
- id: 'app-sidebar',
- tabName: 'div',
- className: 'detailsView scroll-container',
-
- /**
- * List of detail tab views
- *
- * @type Array<OCA.Files.DetailTabView>
- */
- _tabViews: [],
-
- /**
- * List of detail file info views
- *
- * @type Array<OCA.Files.DetailFileInfoView>
- */
- _detailFileInfoViews: [],
-
- /**
- * Id of the currently selected tab
- *
- * @type string
- */
- _currentTabId: null,
-
- /**
- * Dirty flag, whether the view needs to be rerendered
- */
- _dirty: false,
-
- events: {
- 'click a.close': '_onClose',
- 'click .tabHeaders .tabHeader': '_onClickTab',
- 'keyup .tabHeaders .tabHeader': '_onKeyboardActivateTab'
- },
-
- /**
- * Initialize the details view
- */
- initialize: function() {
- this._tabViews = [];
- this._detailFileInfoViews = [];
-
- this._dirty = true;
- },
-
- _onClose: function(event) {
- OC.Apps.hideAppSidebar(this.$el);
- event.preventDefault();
- },
-
- _onClickTab: function(e) {
- var $target = $(e.target);
- e.preventDefault();
- if (!$target.hasClass('tabHeader')) {
- $target = $target.closest('.tabHeader');
- }
- var tabId = $target.attr('data-tabid');
- if (_.isUndefined(tabId)) {
- return;
- }
-
- this.selectTab(tabId);
- },
-
- _onKeyboardActivateTab: function (event) {
- if (event.key === " " || event.key === "Enter") {
- this._onClickTab(event);
- }
- },
-
- template: function(vars) {
- return OCA.Files.Templates['detailsview'](vars);
- },
-
- /**
- * Renders this details view
- */
- render: function() {
- var templateVars = {
- closeLabel: t('files', 'Close')
- };
-
- this._tabViews = this._tabViews.sort(function(tabA, tabB) {
- var orderA = tabA.order || 0;
- var orderB = tabB.order || 0;
- if (orderA === orderB) {
- return OC.Util.naturalSortCompare(tabA.getLabel(), tabB.getLabel());
- }
- return orderA - orderB;
- });
-
- templateVars.tabHeaders = _.map(this._tabViews, function(tabView, i) {
- return {
- tabId: tabView.id,
- label: tabView.getLabel(),
- tabIcon: tabView.getIcon()
- };
- });
-
- this.$el.html(this.template(templateVars));
-
- var $detailsContainer = this.$el.find('.detailFileInfoContainer');
-
- // render details
- _.each(this._detailFileInfoViews, function(detailView) {
- $detailsContainer.append(detailView.get$());
- });
-
- if (!this._currentTabId && this._tabViews.length > 0) {
- this._currentTabId = this._tabViews[0].id;
- }
-
- this.selectTab(this._currentTabId);
-
- this._updateTabVisibilities();
-
- this._dirty = false;
- },
-
- /**
- * Selects the given tab by id
- *
- * @param {string} tabId tab id
- */
- selectTab: function(tabId) {
- if (!tabId) {
- return;
- }
-
- var tabView = _.find(this._tabViews, function(tab) {
- return tab.id === tabId;
- });
-
- if (!tabView) {
- console.warn('Details view tab with id "' + tabId + '" not found');
- return;
- }
-
- this._currentTabId = tabId;
-
- var $tabsContainer = this.$el.find('.tabsContainer');
- var $tabEl = $tabsContainer.find('#' + tabId);
-
- // hide other tabs
- $tabsContainer.find('.tab').addClass('hidden');
-
- $tabsContainer.attr('class', 'tabsContainer');
- $tabsContainer.addClass(tabView.getTabsContainerExtraClasses());
-
- // tab already rendered ?
- if (!$tabEl.length) {
- // render tab
- $tabsContainer.append(tabView.$el);
- $tabEl = tabView.$el;
- }
-
- // this should trigger tab rendering
- tabView.setFileInfo(this.model);
-
- $tabEl.removeClass('hidden');
-
- // update tab headers
- var $tabHeaders = this.$el.find('.tabHeaders li');
- $tabHeaders.removeClass('selected');
- $tabHeaders.filterAttr('data-tabid', tabView.id).addClass('selected');
- },
-
- /**
- * Sets the file info to be displayed in the view
- *
- * @param {OCA.Files.FileInfoModel} fileInfo file info to set
- */
- setFileInfo: function(fileInfo) {
- this.model = fileInfo;
-
- if (this._dirty) {
- this.render();
- } else {
- this._updateTabVisibilities();
- }
-
- if (this._currentTabId) {
- // only update current tab, others will be updated on-demand
- var tabId = this._currentTabId;
- var tabView = _.find(this._tabViews, function(tab) {
- return tab.id === tabId;
- });
- tabView.setFileInfo(fileInfo);
- }
-
- _.each(this._detailFileInfoViews, function(detailView) {
- detailView.setFileInfo(fileInfo);
- });
- },
-
- /**
- * Update tab headers based on the current model
- */
- _updateTabVisibilities: function() {
- // update tab header visibilities
- var self = this;
- var deselect = false;
- var countVisible = 0;
- var $tabHeaders = this.$el.find('.tabHeaders li');
- _.each(this._tabViews, function(tabView) {
- var isVisible = tabView.canDisplay(self.model);
- if (isVisible) {
- countVisible += 1;
- }
- if (!isVisible && self._currentTabId === tabView.id) {
- deselect = true;
- }
- $tabHeaders.filterAttr('data-tabid', tabView.id).toggleClass('hidden', !isVisible);
- });
-
- // hide the whole container if there is only one tab
- this.$el.find('.tabHeaders').toggleClass('hidden', countVisible <= 1);
-
- if (deselect) {
- // select the first visible tab instead
- var visibleTabId = this.$el.find('.tabHeader:not(.hidden):first').attr('data-tabid');
- this.selectTab(visibleTabId);
- }
-
- },
-
- /**
- * Returns the file info.
- *
- * @return {OCA.Files.FileInfoModel} file info
- */
- getFileInfo: function() {
- return this.model;
- },
-
- /**
- * Adds a tab in the tab view
- *
- * @param {OCA.Files.DetailTabView} tab view
- */
- addTabView: function(tabView) {
- this._tabViews.push(tabView);
- this._dirty = true;
- },
-
- /**
- * Adds a detail view for file info.
- *
- * @param {OCA.Files.DetailFileInfoView} detail view
- */
- addDetailView: function(detailView) {
- this._detailFileInfoViews.push(detailView);
- this._dirty = true;
- },
-
- /**
- * Returns an array with the added DetailFileInfoViews.
- *
- * @return Array<OCA.Files.DetailFileInfoView> an array with the added
- * DetailFileInfoViews.
- */
- getDetailViews: function() {
- return [].concat(this._detailFileInfoViews);
- }
- });
-
- OCA.Files.DetailsView = DetailsView;
-})();
diff --git a/apps/files/js/detailtabview.js b/apps/files/js/detailtabview.js
deleted file mode 100644
index 865b0e05f5e..00000000000
--- a/apps/files/js/detailtabview.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
-
- /**
- * @class OCA.Files.DetailTabView
- * @classdesc
- *
- * Base class for tab views to display file information.
- *
- */
- var DetailTabView = OC.Backbone.View.extend({
- tag: 'div',
-
- className: 'tab',
-
- /**
- * Tab label
- */
- _label: null,
-
- _template: null,
-
- initialize: function(options) {
- options = options || {};
- if (!this.id) {
- this.id = 'detailTabView' + DetailTabView._TAB_COUNT;
- DetailTabView._TAB_COUNT++;
- }
- if (options.order) {
- this.order = options.order || 0;
- }
- },
-
- /**
- * Returns the extra CSS classes used by the tabs container when this
- * tab is the selected one.
- *
- * In general you should not extend this method, as tabs should not
- * modify the classes of its container; this is reserved as a last
- * resort for very specific cases in which there is no other way to get
- * the proper style or behaviour.
- *
- * @return {String} space-separated CSS classes
- */
- getTabsContainerExtraClasses: function() {
- return '';
- },
-
- /**
- * Returns the tab label
- *
- * @return {String} label
- */
- getLabel: function() {
- return 'Tab ' + this.id;
- },
-
- /**
- * Returns the tab label
- *
- * @return {String}|{null} icon class
- */
- getIcon: function() {
- return null
- },
-
- /**
- * returns the jQuery object for HTML output
- *
- * @returns {jQuery}
- */
- get$: function() {
- return this.$el;
- },
-
- /**
- * Renders this details view
- *
- * @abstract
- */
- render: function() {
- // to be implemented in subclass
- // FIXME: code is only for testing
- this.$el.html('<div>Hello ' + this.id + '</div>');
- },
-
- /**
- * Sets the file info to be displayed in the view
- *
- * @param {OCA.Files.FileInfoModel} fileInfo file info to set
- */
- setFileInfo: function(fileInfo) {
- if (this.model !== fileInfo) {
- this.model = fileInfo;
- this.render();
- }
- },
-
- /**
- * Returns the file info.
- *
- * @return {OCA.Files.FileInfoModel} file info
- */
- getFileInfo: function() {
- return this.model;
- },
-
- /**
- * Load the next page of results
- */
- nextPage: function() {
- // load the next page, if applicable
- },
-
- /**
- * Returns whether the current tab is able to display
- * the given file info, for example based on mime type.
- *
- * @param {OCA.Files.FileInfoModel} fileInfo file info model
- * @return {boolean} whether to display this tab
- */
- canDisplay: function(fileInfo) {
- return true;
- }
- });
- DetailTabView._TAB_COUNT = 0;
-
- OCA.Files = OCA.Files || {};
-
- OCA.Files.DetailTabView = DetailTabView;
-})();
-
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
deleted file mode 100644
index 5014eccb349..00000000000
--- a/apps/files/js/file-upload.js
+++ /dev/null
@@ -1,1448 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-/**
- * The file upload code uses several hooks to interact with blueimps jQuery file upload library:
- * 1. the core upload handling hooks are added when initializing the plugin,
- * 2. if the browser supports progress events they are added in a separate set after the initialization
- * 3. every app can add its own triggers for fileupload
- * - files adds d'n'd handlers and also reacts to done events to add new rows to the filelist
- * - TODO pictures upload button
- * - TODO music upload button
- */
-
-/* global jQuery, md5 */
-
-/**
- * File upload object
- *
- * @class OC.FileUpload
- * @classdesc
- *
- * Represents a file upload
- *
- * @param {OC.Uploader} uploader uploader
- * @param {Object} data blueimp data
- */
-OC.FileUpload = function(uploader, data) {
- this.uploader = uploader;
- this.data = data;
- var basePath = '';
- if (this.uploader.fileList) {
- basePath = this.uploader.fileList.getCurrentDirectory();
- }
- var path = OC.joinPaths(basePath, this.getFile().relativePath || '', this.getFile().name);
- this.id = 'web-file-upload-' + md5(path) + '-' + (new Date()).getTime();
-};
-OC.FileUpload.CONFLICT_MODE_DETECT = 0;
-OC.FileUpload.CONFLICT_MODE_OVERWRITE = 1;
-OC.FileUpload.CONFLICT_MODE_AUTORENAME = 2;
-
-// IE11 polyfill
-// TODO: nuke out of orbit as well as this legacy code
-if (!FileReader.prototype.readAsBinaryString) {
- FileReader.prototype.readAsBinaryString = function(fileData) {
- var binary = ''
- var pt = this
- var reader = new FileReader()
- reader.onload = function (e) {
- var bytes = new Uint8Array(reader.result)
- var length = bytes.byteLength
- for (var i = 0; i < length; i++) {
- binary += String.fromCharCode(bytes[i])
- }
- // pt.result - readonly so assign binary
- pt.content = binary
- $(pt).trigger('onload')
- }
- reader.readAsArrayBuffer(fileData)
- }
-}
-
-OC.FileUpload.prototype = {
-
- /**
- * Unique upload id
- *
- * @type string
- */
- id: null,
-
- /**
- * Upload data structure
- */
- data: null,
-
- /**
- * Upload element
- *
- * @type Object
- */
- $uploadEl: null,
-
- /**
- * Target folder
- *
- * @type string
- */
- _targetFolder: '',
-
- /**
- * @type int
- */
- _conflictMode: OC.FileUpload.CONFLICT_MODE_DETECT,
-
- /**
- * New name from server after autorename
- *
- * @type String
- */
- _newName: null,
-
- /**
- * Returns the unique upload id
- *
- * @return string
- */
- getId: function() {
- return this.id;
- },
-
- /**
- * Returns the file to be uploaded
- *
- * @return {File} file
- */
- getFile: function() {
- return this.data.files[0];
- },
-
- /**
- * Return the final filename.
- *
- * @return {String} file name
- */
- getFileName: function() {
- // autorenamed name
- if (this._newName) {
- return this._newName;
- }
- return this.getFile().name;
- },
-
- setTargetFolder: function(targetFolder) {
- this._targetFolder = targetFolder;
- },
-
- getTargetFolder: function() {
- return this._targetFolder;
- },
-
- /**
- * Get full path for the target file, including relative path,
- * without the file name.
- *
- * @return {String} full path
- */
- getFullPath: function() {
- return OC.joinPaths(this._targetFolder, this.getFile().relativePath || '');
- },
-
- /**
- * Get full path for the target file,
- * including relative path and file name.
- *
- * @return {String} full path
- */
- getFullFilePath: function() {
- return OC.joinPaths(this.getFullPath(), this.getFile().name);
- },
-
- /**
- * Returns conflict resolution mode.
- *
- * @return {number} conflict mode
- */
- getConflictMode: function() {
- return this._conflictMode || OC.FileUpload.CONFLICT_MODE_DETECT;
- },
-
- /**
- * Set conflict resolution mode.
- * See CONFLICT_MODE_* constants.
- *
- * @param {number} mode conflict mode
- */
- setConflictMode: function(mode) {
- this._conflictMode = mode;
- },
-
- deleteUpload: function() {
- delete this.data.jqXHR;
- },
-
- /**
- * Trigger autorename and append "(2)".
- * Multiple calls will increment the appended number.
- */
- autoRename: function() {
- var name = this.getFile().name;
- if (!this._renameAttempt) {
- this._renameAttempt = 1;
- }
-
- var dotPos = name.lastIndexOf('.');
- var extPart = '';
- if (dotPos > 0) {
- this._newName = name.substr(0, dotPos);
- extPart = name.substr(dotPos);
- } else {
- this._newName = name;
- }
-
- // generate new name
- this._renameAttempt++;
- this._newName = this._newName + ' (' + this._renameAttempt + ')' + extPart;
- },
-
- /**
- * Submit the upload
- */
- submit: function() {
- var self = this;
- var data = this.data;
- var file = this.getFile();
-
- // if file is a directory, just create it
- // files are handled separately
- if (file.isDirectory) {
- return this.uploader.ensureFolderExists(OC.joinPaths(this._targetFolder, file.fullPath));
- }
-
- if (self.aborted === true) {
- return $.Deferred().resolve().promise();
- }
- // it was a folder upload, so make sure the parent directory exists already
- var folderPromise;
- if (file.relativePath) {
- folderPromise = this.uploader.ensureFolderExists(this.getFullPath());
- } else {
- folderPromise = $.Deferred().resolve().promise();
- }
-
- if (this.uploader.fileList) {
- this.data.url = this.uploader.fileList.getUploadUrl(this.getFileName(), this.getFullPath());
- }
-
- if (!this.data.headers) {
- this.data.headers = {};
- }
-
- // webdav without multipart
- this.data.multipart = false;
- this.data.type = 'PUT';
-
- delete this.data.headers['If-None-Match'];
- if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_DETECT
- || this._conflictMode === OC.FileUpload.CONFLICT_MODE_AUTORENAME) {
- this.data.headers['If-None-Match'] = '*';
- }
-
- var userName = this.uploader.davClient.getUserName();
- var password = this.uploader.davClient.getPassword();
- if (userName) {
- // copy username/password from DAV client
- this.data.headers['Authorization'] =
- 'Basic ' + btoa(userName + ':' + (password || ''));
- }
-
- var chunkFolderPromise;
- if ($.support.blobSlice
- && this.uploader.fileUploadParam.maxChunkSize
- && this.getFile().size > this.uploader.fileUploadParam.maxChunkSize
- ) {
- data.isChunked = true;
- var headers = {
- Destination: this.uploader.davClient._buildUrl(this.getTargetDestination())
- };
-
- chunkFolderPromise = this.uploader.davClient.createDirectory(
- 'uploads/' + OC.getCurrentUser().uid + '/' + this.getId(), headers
- );
- // TODO: if fails, it means same id already existed, need to retry
- } else {
- chunkFolderPromise = $.Deferred().resolve().promise();
- var mtime = this.getFile().lastModified;
- if (mtime) {
- data.headers['X-OC-Mtime'] = mtime / 1000;
- }
- }
-
- // wait for creation of the required directory before uploading
- return Promise.all([folderPromise, chunkFolderPromise]).then(function() {
- if (self.aborted !== true) {
- data.submit();
- }
- }, function() {
- self.abort();
- });
-
- },
-
- /**
- * Process end of transfer
- */
- done: function() {
- if (!this.data.isChunked) {
- return $.Deferred().resolve().promise();
- }
-
- var uid = OC.getCurrentUser().uid;
- var mtime = this.getFile().lastModified;
- var size = this.getFile().size;
- var headers = {};
- if (mtime) {
- headers['X-OC-Mtime'] = mtime / 1000;
- }
- if (size) {
- headers['OC-Total-Length'] = size;
- }
- headers['Destination'] = this.uploader.davClient._buildUrl(this.getTargetDestination());
-
- return this.uploader.davClient.move(
- 'uploads/' + uid + '/' + this.getId() + '/.file',
- this.getTargetDestination(),
- true,
- headers
- );
- },
-
- getTargetDestination: function() {
- var uid = OC.getCurrentUser().uid;
- return 'files/' + uid + '/' + OC.joinPaths(this.getFullPath(), this.getFileName());
- },
-
- _deleteChunkFolder: function() {
- // delete transfer directory for this upload
- this.uploader.davClient.remove(
- 'uploads/' + OC.getCurrentUser().uid + '/' + this.getId()
- );
- },
-
- _delete: function() {
- if (this.data.isChunked) {
- this._deleteChunkFolder()
- }
- this.deleteUpload();
- },
-
- /**
- * Abort the upload
- */
- abort: function() {
- if (this.aborted) {
- return
- }
- this.aborted = true;
- if (this.data) {
- // abort running XHR
- this.data.abort();
- }
- this._delete();
- },
-
- /**
- * Fail the upload
- */
- fail: function() {
- if (this.aborted) {
- return
- }
- this._delete();
- },
-
- /**
- * Returns the server response
- *
- * @return {Object} response
- */
- getResponse: function() {
- var response = this.data.response();
- if (response.errorThrown || response.textStatus === 'error') {
- // attempt parsing Sabre exception is available
- var xml = response.jqXHR.responseXML;
- if (xml && xml.documentElement.localName === 'error' && xml.documentElement.namespaceURI === 'DAV:') {
- var messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message');
- var exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception');
- if (messages.length) {
- response.message = messages[0].textContent;
- }
- if (exceptions.length) {
- response.exception = exceptions[0].textContent;
- }
- return response;
- }
- }
-
- if (typeof response.result !== 'string' && response.result) {
- //fetch response from iframe
- response = $.parseJSON(response.result[0].body.innerText);
- if (!response) {
- // likely due to internal server error
- response = {status: 500};
- }
- } else {
- response = response.result;
- }
- return response;
- },
-
- /**
- * Returns the status code from the response
- *
- * @return {number} status code
- */
- getResponseStatus: function() {
- if (this.uploader.isXHRUpload()) {
- var xhr = this.data.response().jqXHR;
- if (xhr) {
- return xhr.status;
- }
- return null;
- }
- return this.getResponse().status;
- },
-
- /**
- * Returns the response header by name
- *
- * @param {String} headerName header name
- * @return {Array|String} response header value(s)
- */
- getResponseHeader: function(headerName) {
- headerName = headerName.toLowerCase();
- if (this.uploader.isXHRUpload()) {
- return this.data.response().jqXHR.getResponseHeader(headerName);
- }
-
- var headers = this.getResponse().headers;
- if (!headers) {
- return null;
- }
-
- var value = _.find(headers, function(value, key) {
- return key.toLowerCase() === headerName;
- });
- if (_.isArray(value) && value.length === 1) {
- return value[0];
- }
- return value;
- }
-};
-
-/**
- * keeps track of uploads in progress and implements callbacks for the conflicts dialog
- * @namespace
- */
-
-OC.Uploader = function() {
- this.init.apply(this, arguments);
-};
-
-OC.Uploader.prototype = _.extend({
- /**
- * @type Array<OC.FileUpload>
- */
- _uploads: {},
-
- /**
- * Count of upload done promises that have not finished yet.
- *
- * @type int
- */
- _pendingUploadDoneCount: 0,
-
- /**
- * Is it currently uploading?
- *
- * @type boolean
- */
- _uploading: false,
-
- /**
- * List of directories known to exist.
- *
- * Key is the fullpath and value is boolean, true meaning that the directory
- * was already created so no need to create it again.
- */
- _knownDirs: {},
-
- /**
- * @type OCA.Files.FileList
- */
- fileList: null,
-
- /**
- * @type OCA.Files.OperationProgressBar
- */
- progressBar: null,
-
- /**
- * @type OC.Files.Client
- */
- filesClient: null,
-
- /**
- * Webdav client pointing at the root "dav" endpoint
- *
- * @type OC.Files.Client
- */
- davClient: null,
-
- /**
- * Function that will allow us to know if Ajax uploads are supported
- * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html
- * also see article @link http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata
- */
- _supportAjaxUploadWithProgress: function() {
- if (window.TESTING) {
- return true;
- }
- return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData();
-
- // Is the File API supported?
- function supportFileAPI() {
- var fi = document.createElement('INPUT');
- fi.type = 'file';
- return 'files' in fi;
- }
-
- // Are progress events supported?
- function supportAjaxUploadProgressEvents() {
- var xhr = new XMLHttpRequest();
- return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload));
- }
-
- // Is FormData supported?
- function supportFormData() {
- return !! window.FormData;
- }
- },
-
- /**
- * Returns whether an XHR upload will be used
- *
- * @return {boolean} true if XHR upload will be used,
- * false for iframe upload
- */
- isXHRUpload: function () {
- return !this.fileUploadParam.forceIframeTransport &&
- ((!this.fileUploadParam.multipart && $.support.xhrFileUpload) ||
- $.support.xhrFormDataFileUpload);
- },
-
- /**
- * Makes sure that the upload folder and its parents exists
- *
- * @param {String} fullPath full path
- * @return {Promise} promise that resolves when all parent folders
- * were created
- */
- ensureFolderExists: function(fullPath) {
- if (!fullPath || fullPath === '/') {
- return $.Deferred().resolve().promise();
- }
-
- // remove trailing slash
- if (fullPath.charAt(fullPath.length - 1) === '/') {
- fullPath = fullPath.substr(0, fullPath.length - 1);
- }
-
- var self = this;
- var promise = this._knownDirs[fullPath];
-
- if (this.fileList) {
- // assume the current folder exists
- this._knownDirs[this.fileList.getCurrentDirectory()] = $.Deferred().resolve().promise();
- }
-
- if (!promise) {
- var deferred = new $.Deferred();
- promise = deferred.promise();
- this._knownDirs[fullPath] = promise;
-
- // make sure all parents already exist
- var parentPath = OC.dirname(fullPath);
- var parentPromise = this._knownDirs[parentPath];
- if (!parentPromise) {
- parentPromise = this.ensureFolderExists(parentPath);
- }
-
- parentPromise.then(function() {
- self.filesClient.createDirectory(fullPath).always(function(status) {
- // 405 is expected if the folder already exists
- if ((status >= 200 && status < 300) || status === 405) {
- if (status !== 405) {
- self.trigger('createdfolder', fullPath);
- }
- deferred.resolve();
- return;
- }
- OC.Notification.show(t('files', 'Could not create folder "{dir}"', {dir: fullPath}), {type: 'error'});
- deferred.reject();
- });
- }, function() {
- deferred.reject();
- });
- }
-
- return promise;
- },
-
- /**
- * Submit the given uploads
- *
- * @param {Array} array of uploads to start
- */
- submitUploads: function(uploads) {
- var self = this;
- _.each(uploads, function(upload) {
- self._uploads[upload.data.uploadId] = upload;
- });
- if (!self._uploading) {
- self.totalToUpload = 0;
- self.totalUploadCount = 0;
- }
- self.totalToUpload += _.reduce(uploads, function(memo, upload) { return memo+upload.getFile().size; }, 0);
- self.totalUploadCount += uploads.length;
- var semaphore = new OCA.Files.Semaphore(5);
- var promises = _.map(uploads, function(upload) {
- return semaphore.acquire().then(function(){
- return upload.submit().then(function(){
- semaphore.release();
- });
- });
- });
- },
-
- confirmBeforeUnload: function() {
- if (this._uploading) {
- return t('files', 'This will stop your current uploads.')
- }
- },
-
- /**
- * Show conflict for the given file object
- *
- * @param {OC.FileUpload} file upload object
- */
- showConflict: function(fileUpload) {
- //show "file already exists" dialog
- var self = this;
- var file = fileUpload.getFile();
- // already attempted autorename but the server said the file exists ? (concurrently added)
- if (fileUpload.getConflictMode() === OC.FileUpload.CONFLICT_MODE_AUTORENAME) {
- // attempt another autorename, defer to let the current callback finish
- _.defer(function() {
- self.onAutorename(fileUpload);
- });
- return;
- }
- // retrieve more info about this file
- this.filesClient.getFileInfo(fileUpload.getFullFilePath()).then(function(status, fileInfo) {
- var original = fileInfo;
- var replacement = file;
- original.directory = original.path;
- OC.dialogs.fileexists(fileUpload, original, replacement, self);
- });
- },
- /**
- * cancels all uploads
- */
- cancelUploads:function() {
- this.log('canceling uploads');
- jQuery.each(this._uploads, function(i, upload) {
- upload.abort();
- });
- this.clear();
- },
- /**
- * Clear uploads
- */
- clear: function() {
- this._knownDirs = {};
- },
- /**
- * Returns an upload by id
- *
- * @param {number} data uploadId
- * @return {OC.FileUpload} file upload
- */
- getUpload: function(data) {
- if (_.isString(data)) {
- return this._uploads[data];
- } else if (data.uploadId && this._uploads[data.uploadId]) {
- this._uploads[data.uploadId].data = data;
- return this._uploads[data.uploadId];
- }
- return null;
- },
-
- /**
- * Removes an upload from the list of known uploads.
- *
- * @param {OC.FileUpload} upload the upload to remove.
- */
- removeUpload: function(upload) {
- if (!upload || !upload.data || !upload.data.uploadId) {
- return;
- }
-
- // defer as some calls/chunks might still be busy failing, so we need
- // the upload info there still
- var self = this;
- var uploadId = upload.data.uploadId;
- // mark as deleted for the progress bar
- this._uploads[uploadId].deleted = true;
- window.setTimeout(function() {
- delete self._uploads[uploadId];
- }, 5000)
- },
-
- _activeUploadCount: function() {
- var count = 0;
- for (var key in this._uploads) {
- if (!this._uploads[key].deleted) {
- count++;
- }
- }
-
- return count;
- },
-
- showUploadCancelMessage: _.debounce(function() {
- OC.Notification.show(t('files', 'Upload cancelled.'), { timeout : 7000, type: 'error' });
- }, 500),
-
- /**
- * callback for the conflicts dialog
- */
- onCancel:function() {
- this.cancelUploads();
- },
- /**
- * callback for the conflicts dialog
- * calls onSkip, onReplace or onAutorename for each conflict
- * @param {object} conflicts - list of conflict elements
- */
- onContinue:function(conflicts) {
- var self = this;
- //iterate over all conflicts
- jQuery.each(conflicts, function (i, conflict) {
- conflict = $(conflict);
- var keepOriginal = conflict.find('.original input[type="checkbox"]:checked').length === 1;
- var keepReplacement = conflict.find('.replacement input[type="checkbox"]:checked').length === 1;
- if (keepOriginal && keepReplacement) {
- // when both selected -> autorename
- self.onAutorename(conflict.data('data'));
- } else if (keepReplacement) {
- // when only replacement selected -> overwrite
- self.onReplace(conflict.data('data'));
- } else {
- // when only original selected -> skip
- // when none selected -> skip
- self.onSkip(conflict.data('data'));
- }
- });
- },
- /**
- * handle skipping an upload
- * @param {OC.FileUpload} upload
- */
- onSkip:function(upload) {
- this.log('skip', null, upload);
- upload.deleteUpload();
- },
- /**
- * handle replacing a file on the server with an uploaded file
- * @param {FileUpload} data
- */
- onReplace:function(upload) {
- this.log('replace', null, upload);
- upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_OVERWRITE);
- this.submitUploads([upload]);
- },
- /**
- * handle uploading a file and letting the server decide a new name
- * @param {object} upload
- */
- onAutorename:function(upload) {
- this.log('autorename', null, upload);
- upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_AUTORENAME);
-
- do {
- upload.autoRename();
- // if file known to exist on the client side, retry
- } while (this.fileList && this.fileList.inList(upload.getFileName()));
-
- // resubmit upload
- this.submitUploads([upload]);
- },
- _trace: false, //TODO implement log handler for JS per class?
- log: function(caption, e, data) {
- if (this._trace) {
- console.log(caption);
- console.log(data);
- }
- },
- /**
- * checks the list of existing files prior to uploading and shows a simple dialog to choose
- * skip all, replace all or choose which files to keep
- *
- * @param {array} selection of files to upload
- * @param {object} callbacks - object with several callback methods
- * @param {Function} callbacks.onNoConflicts
- * @param {Function} callbacks.onSkipConflicts
- * @param {Function} callbacks.onReplaceConflicts
- * @param {Function} callbacks.onChooseConflicts
- * @param {Function} callbacks.onCancel
- */
- checkExistingFiles: function (selection, callbacks) {
- var fileList = this.fileList;
- var conflicts = [];
- // only keep non-conflicting uploads
- selection.uploads = _.filter(selection.uploads, function(upload) {
- var file = upload.getFile();
- if (file.relativePath) {
- // can't check in subfolder contents
- return true;
- }
- if (!fileList) {
- // no list to check against
- return true;
- }
- if (upload.getTargetFolder() !== fileList.getCurrentDirectory()) {
- // not uploading to the current folder
- return true;
- }
- var fileInfo = fileList.findFile(file.name);
- if (fileInfo) {
- conflicts.push([
- // original
- _.extend(fileInfo, {
- directory: fileInfo.directory || fileInfo.path || fileList.getCurrentDirectory()
- }),
- // replacement (File object)
- upload
- ]);
- return false;
- }
- return true;
- });
- if (conflicts.length) {
- // wait for template loading
- OC.dialogs.fileexists(null, null, null, this).done(function() {
- _.each(conflicts, function(conflictData) {
- OC.dialogs.fileexists(conflictData[1], conflictData[0], conflictData[1].getFile(), this);
- });
- });
- }
-
- // upload non-conflicting files
- // note: when reaching the server they might still meet conflicts
- // if the folder was concurrently modified, these will get added
- // to the already visible dialog, if applicable
- callbacks.onNoConflicts(selection);
- },
-
- _updateProgressBarOnUploadStop: function() {
- if (this._pendingUploadDoneCount === 0) {
- // All the uploads ended and there is no pending operation, so hide
- // the progress bar.
- // Note that this happens here only with non-chunked uploads; if the
- // upload was chunked then this will have been executed after all
- // the uploads ended but before the upload done handler that reduces
- // the pending operation count was executed.
- this._hideProgressBar();
-
- return;
- }
-
- this._setProgressBarText(t('files', 'Processing files …'), t('files', '…'));
-
- // Nothing is being uploaded at this point, and the pending operations
- // can not be cancelled, so the cancel button should be hidden.
- this._hideCancelButton();
- },
-
- _hideProgressBar: function() {
- this.progressBar.hideProgressBar();
- },
-
- _hideCancelButton: function() {
- this.progressBar.hideCancelButton();
- },
-
- _showProgressBar: function() {
- this.progressBar.showProgressBar();
- },
-
- _setProgressBarValue: function(value) {
- this.progressBar.setProgressBarValue(value);
- },
-
- _setProgressBarText: function(textDesktop, textMobile, title) {
- this.progressBar.setProgressBarText(textDesktop, textMobile, title);
- },
-
- /**
- * Returns whether the given file is known to be a received shared file
- *
- * @param {Object} file file
- * @return {boolean} true if the file is a shared file
- */
- _isReceivedSharedFile: function(file) {
- if (!window.FileList) {
- return false;
- }
- var $tr = window.FileList.findFileEl(file.name);
- if (!$tr.length) {
- return false;
- }
-
- return ($tr.attr('data-mounttype') === 'shared-root' && $tr.attr('data-mime') !== 'httpd/unix-directory');
- },
-
- /**
- * Initialize the upload object
- *
- * @param {Object} $uploadEl upload element
- * @param {Object} options
- * @param {OCA.Files.FileList} [options.fileList] file list object
- * @param {OC.Files.Client} [options.filesClient] files client object
- * @param {Object} [options.dropZone] drop zone for drag and drop upload
- */
- init: function($uploadEl, options) {
- var self = this;
-
- options = options || {};
-
- this.fileList = options.fileList;
- this.progressBar = options.progressBar;
- this.filesClient = options.filesClient || OC.Files.getClient();
- this.davClient = new OC.Files.Client({
- host: this.filesClient.getHost(),
- root: OC.linkToRemoteBase('dav'),
- useHTTPS: OC.getProtocol() === 'https',
- userName: this.filesClient.getUserName(),
- password: this.filesClient.getPassword()
- });
-
- $uploadEl = $($uploadEl);
- this.$uploadEl = $uploadEl;
-
- if ($uploadEl.exists()) {
- this.progressBar.on('cancel', function() {
- self.cancelUploads();
- self.showUploadCancelMessage();
- });
-
- this.fileUploadParam = {
- type: 'PUT',
- dropZone: options.dropZone, // restrict dropZone to content div
- autoUpload: false,
- progressInterval: 300, // increased from the default of 100ms for more stable behvaviour when predicting remaining time
- sequentialUploads: false,
- limitConcurrentUploads: 4,
- /**
- * on first add of every selection
- * - check all files of originalFiles array with files in dir
- * - on conflict show dialog
- * - skip all -> remember as single skip action for all conflicting files
- * - replace all -> remember as single replace action for all conflicting files
- * - choose -> show choose dialog
- * - mark files to keep
- * - when only existing -> remember as single skip action
- * - when only new -> remember as single replace action
- * - when both -> remember as single autorename action
- * - start uploading selection
- * @param {object} e
- * @param {object} data
- * @returns {boolean}
- */
- add: function(e, data) {
- self.log('add', e, data);
- var that = $(this), freeSpace = 0;
-
- var upload = new OC.FileUpload(self, data);
- // can't link directly due to jQuery not liking cyclic deps on its ajax object
- data.uploadId = upload.getId();
-
- // create a container where we can store the data objects
- if ( ! data.originalFiles.selection ) {
- // initialize selection and remember number of files to upload
- data.originalFiles.selection = {
- uploads: [],
- filesToUpload: data.originalFiles.length,
- totalBytes: 0
- };
- }
- // TODO: move originalFiles to a separate container, maybe inside OC.Upload
- var selection = data.originalFiles.selection;
-
- // add uploads
- if ( selection.uploads.length < selection.filesToUpload ) {
- // remember upload
- selection.uploads.push(upload);
- }
-
- //examine file
- var file = upload.getFile();
- try {
- // FIXME: not so elegant... need to refactor that method to return a value
- Files.isFileNameValid(file.name);
- }
- catch (errorMessage) {
- data.textStatus = 'invalidcharacters';
- data.errorThrown = errorMessage;
- }
-
- if (data.targetDir) {
- upload.setTargetFolder(data.targetDir);
- delete data.targetDir;
- }
-
- // in case folder drag and drop is not supported file will point to a directory
- // http://stackoverflow.com/a/20448357
- if ( !file.type && file.size % 4096 === 0 && file.size <= 102400) {
- var dirUploadFailure = false;
- try {
- var reader = new FileReader();
- reader.readAsBinaryString(file);
- } catch (error) {
- console.log(reader, error)
- //file is a directory
- dirUploadFailure = true;
- }
-
- if (dirUploadFailure) {
- data.textStatus = 'dirorzero';
- data.errorThrown = t('files',
- 'Unable to upload {filename} as it is a directory or has 0 bytes',
- {filename: file.name}
- );
- }
- }
-
- // only count if we're not overwriting an existing shared file
- if (self._isReceivedSharedFile(file)) {
- file.isReceivedShare = true;
- } else {
- // add size
- selection.totalBytes += file.size;
- }
-
- // check free space
- if (!self.fileList || upload.getTargetFolder() === self.fileList.getCurrentDirectory()) {
- // Use global free space if there is no file list to check or the current directory is the target
- freeSpace = $('input[name=free_space]').val()
- } else if (upload.getTargetFolder().indexOf(self.fileList.getCurrentDirectory()) === 0) {
- // Check subdirectory free space if file is uploaded there
- // Retrieve the folder destination name
- var targetSubdir = upload._targetFolder.split('/').pop()
- freeSpace = parseInt(upload.uploader.fileList.getModelForFile(targetSubdir).get('quotaAvailableBytes'))
- }
- if (freeSpace >= 0 && selection.totalBytes > freeSpace) {
- data.textStatus = 'notenoughspace';
- data.errorThrown = t('files',
- 'Not enough free space, you are uploading {size1} but only {size2} is left', {
- 'size1': OC.Util.humanFileSize(selection.totalBytes, false, false),
- 'size2': OC.Util.humanFileSize(freeSpace, false, false)
- });
- }
-
- // end upload for whole selection on error
- if (data.errorThrown) {
- // trigger fileupload fail handler
- var fu = that.data('blueimp-fileupload') || that.data('fileupload');
- fu._trigger('fail', e, data);
- return false; //don't upload anything
- }
-
- // check existing files when all is collected
- if ( selection.uploads.length >= selection.filesToUpload ) {
-
- //remove our selection hack:
- delete data.originalFiles.selection;
-
- var callbacks = {
-
- onNoConflicts: function (selection) {
- self.submitUploads(selection.uploads);
- },
- onSkipConflicts: function (selection) {
- //TODO mark conflicting files as toskip
- },
- onReplaceConflicts: function (selection) {
- //TODO mark conflicting files as toreplace
- },
- onChooseConflicts: function (selection) {
- //TODO mark conflicting files as chosen
- },
- onCancel: function (selection) {
- $.each(selection.uploads, function(i, upload) {
- upload.abort();
- });
- }
- };
-
- self.checkExistingFiles(selection, callbacks);
-
- }
-
- return true; // continue adding files
- },
- /**
- * called after the first add, does NOT have the data param
- * @param {object} e
- */
- start: function(e) {
- self.log('start', e, null);
- self._uploading = true;
- },
- fail: function(e, data) {
- var upload = self.getUpload(data);
- var status = null;
- if (upload) {
- if (upload.aborted) {
- // uploads might fail with errors from the server when aborted
- return
- }
- status = upload.getResponseStatus();
- }
- self.log('fail', e, upload);
-
- self.removeUpload(upload);
-
- if (data.textStatus === 'abort' || data.errorThrown === 'abort') {
- return
- } else if (status === 412) {
- // file already exists
- self.showConflict(upload);
- } else if (status === 404) {
- // target folder does not exist any more
- OC.Notification.show(t('files', 'Target folder "{dir}" does not exist any more', {dir: upload.getFullPath()} ), {type: 'error'});
- self.cancelUploads();
- } else if (data.textStatus === 'notenoughspace') {
- // not enough space
- OC.Notification.show(t('files', 'Not enough free space'), {type: 'error'});
- self.cancelUploads();
- } else {
- // HTTP connection problem or other error
- var message = t('files', 'An unknown error has occurred');
- if (upload) {
- var response = upload.getResponse();
- if (response) {
- message = response.message;
- }
- }
- console.error(e, data, response)
- OC.Notification.show(message || data.errorThrown || t('files', 'File could not be uploaded'), {type: 'error'});
- }
-
- if (upload) {
- upload.fail();
- }
- },
- /**
- * called for every successful upload
- * @param {object} e
- * @param {object} data
- */
- done:function(e, data) {
- var upload = self.getUpload(data);
- var that = $(this);
- self.log('done', e, upload);
-
- self.removeUpload(upload);
-
- var status = upload.getResponseStatus();
- if (status < 200 || status >= 300) {
- // trigger fail handler
- var fu = that.data('blueimp-fileupload') || that.data('fileupload');
- fu._trigger('fail', e, data);
- return;
- }
- },
- /**
- * called after last upload
- * @param {object} e
- * @param {object} data
- */
- stop: function(e, data) {
- self.log('stop', e, data);
- self._uploading = false;
- }
- };
-
- if (options.maxChunkSize) {
- this.fileUploadParam.maxChunkSize = options.maxChunkSize;
- }
-
- // initialize jquery fileupload (blueimp)
- var fileupload = this.$uploadEl.fileupload(this.fileUploadParam);
-
- if (this._supportAjaxUploadWithProgress()) {
- //remaining time
- var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferTotal, smoothRemainingSeconds, smoothBitrate;
-
- var dragging = false;
-
- // add progress handlers
- fileupload.on('fileuploadadd', function(e, data) {
- self.log('progress handle fileuploadadd', e, data);
- self.trigger('add', e, data);
- });
- // add progress handlers
- fileupload.on('fileuploadstart', function(e, data) {
- self.log('progress handle fileuploadstart', e, data);
- self._setProgressBarText(t('files', 'Uploading …'), t('files', '…'));
- self._setProgressBarValue(0);
- self._showProgressBar();
- // initial remaining time variables
- lastUpdate = new Date().getTime();
- lastSize = 0;
- bufferSize = 20; // length of the ring buffer
- buffer = [];
- bufferIndex = 0; // index of the ring buffer, runs from 0 to bufferSize continuously
- bufferTotal = 0;
- newTotal = 0;
- smoothing = 0.02; // smoothing factor for EMA
- h = '';
- bufferFilled = false;
-
- for(var i = 0; i < bufferSize; i++){
- buffer[i] = 0;
- }
- self.trigger('start', e, data);
- });
- fileupload.on('fileuploadprogress', function(e, data) {
- self.log('progress handle fileuploadprogress', e, data);
- //TODO progressbar in row
- self.trigger('progress', e, data);
- });
- fileupload.on('fileuploadprogressall', function(e, data) {
- self.log('progress handle fileuploadprogressall', e, data);
- var total = self.totalToUpload;
- var progress = (data.loaded / total) * 100;
- var thisUpdate = new Date().getTime();
- var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s
- lastUpdate = thisUpdate;
- var diffSize = data.loaded - lastSize;
- if (diffSize <= 0) {
- diffSize = lastSize;
- }
- lastSize = data.loaded;
- diffSize = diffSize / diffUpdate; // apply timing factor, eg. 1MiB/2s = 0.5MiB/s, unit is byte per second
- var remainingSeconds = ((total - data.loaded) / diffSize);
-
- if(remainingSeconds >= 0) {
- // bufferTotal holds the sum of all entries in the buffer, initially 0 like the entries itself
- // substract current entry from total and add the current value to total
- bufferTotal = bufferTotal - (buffer[bufferIndex]) + remainingSeconds;
- // put current value to the entry
- buffer[bufferIndex] = remainingSeconds; //buffer to make it smoother
-
- bufferIndex = (bufferIndex + 1) % bufferSize;
- }
- if (bufferIndex === bufferSize - 1) {
- bufferFilled = true;
- }
- //console.log('#', ' idx: ',bufferIndex, ' Total: ', bufferTotal, ' remainSeconds: ', remainingSeconds, ' during: ', diffUpdate);
-
- if (smoothRemainingSeconds) {
- smoothRemainingSeconds = smoothing * (bufferTotal / bufferSize) + ((1-smoothing) * smoothRemainingSeconds);
- } else {
- smoothRemainingSeconds = bufferTotal / bufferSize;
- }
-
- // the number of currently running uploads
- const runningUploads = Object.keys(self._uploads).length;
-
- // Only show remaining time if enough buffer information is available and debounce to 1/4
- if (bufferFilled && bufferIndex % 4 === 0) {
- h = moment.duration(smoothRemainingSeconds, "seconds").humanize({m: 50, h: 50});
- if (self.totalUploadCount > 1) {
- h = t('files', '{remainingTime} ({currentNumber}/{total})', { remainingTime: h, currentNumber: self.totalUploadCount - runningUploads + 1, total: self.totalUploadCount });
- }
- }
-
- // wait for the buffer to be filled and also show "Uploading ..." for durations longer than 4 hours
- if (!bufferFilled || !(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400)) {
- // Do not show file index when there is just one
- if (self.totalUploadCount > 1) {
- h = t('files', 'Uploading … ({currentNumber}/{total})', { currentNumber: self.totalUploadCount - runningUploads + 1, total: self.totalUploadCount });
- } else {
- h = t('files', 'Uploading …');
- }
- }
-
- // smooth bitrate
- if (smoothBitrate) {
- smoothBitrate = smoothing * data.bitrate + ((1-smoothing) * smoothBitrate);
- } else {
- smoothBitrate = data.bitrate;
- }
-
- self._setProgressBarText(h, h, t('files', '{loadedSize} of {totalSize} ({bitrate})' , {
- loadedSize: OC.Util.humanFileSize(data.loaded, false, false),
- totalSize: OC.Util.humanFileSize(total, false, false),
- bitrate: OC.Util.humanFileSize(smoothBitrate / 8, false, false) + '/s'
- }));
- self._setProgressBarValue(progress);
- self.trigger('progressall', e, data);
- });
- fileupload.on('fileuploadstop', function(e, data) {
- self.log('progress handle fileuploadstop', e, data);
-
- self.clear();
- self._updateProgressBarOnUploadStop();
- self.trigger('stop', e, data);
- });
- fileupload.on('fileuploadfail', function(e, data) {
- self.log('progress handle fileuploadfail', e, data);
- self.trigger('fail', e, data);
- });
- fileupload.on('fileuploaddragover', function(e){
- $('#app-content').addClass('file-drag');
- $('.emptyfilelist.emptycontent .icon-folder').addClass('icon-filetype-folder-drag-accept');
-
- var filerow = $(e.delegatedEvent.target).closest('tr');
-
- if(!filerow.hasClass('dropping-to-dir')){
- $('.dropping-to-dir .icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept');
- $('.dropping-to-dir').removeClass('dropping-to-dir');
- $('.dir-drop').removeClass('dir-drop');
- }
-
- if(filerow.attr('data-type') === 'dir'){
- $('#app-content').addClass('dir-drop');
- filerow.addClass('dropping-to-dir');
- filerow.find('.thumbnail').addClass('icon-filetype-folder-drag-accept');
- }
-
- dragging = true;
- });
-
- var disableDropState = function() {
- $('#app-content').removeClass('file-drag');
- $('.dropping-to-dir').removeClass('dropping-to-dir');
- $('.dir-drop').removeClass('dir-drop');
- $('.icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept');
-
- dragging = false;
- };
-
- fileupload.on('fileuploaddragleave fileuploaddrop', disableDropState);
-
- // In some browsers the "drop" event can be triggered with no
- // files even if the "dragover" event seemed to suggest that a
- // file was being dragged (and thus caused "fileuploaddragover"
- // to be triggered).
- fileupload.on('fileuploaddropnofiles', function() {
- if (!dragging) {
- return;
- }
-
- disableDropState();
-
- OC.Notification.show(t('files', 'Uploading that item is not supported'), {type: 'error'});
- });
-
- fileupload.on('fileuploadchunksend', function(e, data) {
- // modify the request to adjust it to our own chunking
- var upload = self.getUpload(data);
- if (!upload) {
- // likely cancelled
- return
- }
- var range = data.contentRange.split(' ')[1];
- var chunkId = range.split('/')[0].split('-')[0];
- // Use a numeric chunk id and set the Destination header on all request for ChunkingV2
- chunkId = Math.ceil((data.chunkSize+Number(chunkId)) / upload.uploader.fileUploadParam.maxChunkSize);
- data.headers['Destination'] = self.davClient._buildUrl(upload.getTargetDestination());
-
- data.url = OC.getRootPath() +
- '/remote.php/dav/uploads' +
- '/' + OC.getCurrentUser().uid +
- '/' + upload.getId() +
- '/' + chunkId;
- delete data.contentRange;
- delete data.headers['Content-Range'];
- });
- fileupload.on('fileuploaddone', function(e, data) {
- var upload = self.getUpload(data);
-
- self._pendingUploadDoneCount++;
-
- upload.done().always(function() {
- self._pendingUploadDoneCount--;
- if (self._activeUploadCount() === 0 && self._pendingUploadDoneCount === 0) {
- // All the uploads ended and there is no pending
- // operation, so hide the progress bar.
- // Note that this happens here only with chunked
- // uploads; if the upload was non-chunked then this
- // handler is immediately executed, before the
- // jQuery upload done handler that removes the
- // upload from the list, and thus at this point
- // there is still at least one upload that has not
- // ended (although the upload stop handler is always
- // executed after all the uploads have ended, which
- // hides the progress bar in that case).
- self._hideProgressBar();
- }
- }).done(function() {
- self.trigger('done', e, upload);
- }).fail(function(status, response) {
- if (upload.aborted) {
- return
- }
-
- var message = response.message;
- if (status === 507) {
- // not enough space
- OC.Notification.show(message || t('files', 'Not enough free space'), {type: 'error'});
- self.cancelUploads();
- } else if (status === 409) {
- OC.Notification.show(message || t('files', 'Target folder does not exist any more'), {type: 'error'});
- } else if (status === 403) {
- OC.Notification.show(message || t('files', 'Operation is blocked by access control'), {type: 'error'});
- } else {
- OC.Notification.show(message || t('files', 'Error when assembling chunks, status code {status}', {status: status}), {type: 'error'});
- }
- self.trigger('fail', e, data);
- });
- });
- fileupload.on('fileuploaddrop', function(e, data) {
- self.trigger('drop', e, data);
- if (e.isPropagationStopped()) {
- return false;
- }
- });
- }
- window.onbeforeunload = function() {
- return self.confirmBeforeUnload();
- }
- }
-
- //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
- if (navigator.userAgent.search(/konqueror/i) === -1) {
- this.$uploadEl.attr('multiple', 'multiple');
- }
-
- return this.fileUploadParam;
- }
-}, OC.Backbone.Events);
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
deleted file mode 100644
index 579040d8d60..00000000000
--- a/apps/files/js/fileactions.js
+++ /dev/null
@@ -1,915 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
-
- /**
- * Construct a new FileActions instance
- * @constructs FileActions
- * @memberof OCA.Files
- */
- var FileActions = function() {
- this.initialize();
- };
- FileActions.TYPE_DROPDOWN = 0;
- FileActions.TYPE_INLINE = 1;
- FileActions.prototype = {
- /** @lends FileActions.prototype */
- actions: {},
- defaults: {},
- icons: {},
-
- /**
- * @deprecated
- */
- currentFile: null,
-
- /**
- * Dummy jquery element, for events
- */
- $el: null,
-
- _fileActionTriggerTemplate: null,
-
- /**
- * @private
- */
- initialize: function() {
- this.clear();
- // abusing jquery for events until we get a real event lib
- this.$el = $('<div class="dummy-fileactions hidden"></div>');
- $('body').append(this.$el);
-
- this._showMenuClosure = _.bind(this._showMenu, this);
- },
-
- /**
- * Adds an event handler
- *
- * @param {String} eventName event name
- * @param {Function} callback
- */
- on: function(eventName, callback) {
- this.$el.on(eventName, callback);
- },
-
- /**
- * Removes an event handler
- *
- * @param {String} eventName event name
- * @param {Function} callback
- */
- off: function(eventName, callback) {
- this.$el.off(eventName, callback);
- },
-
- /**
- * Notifies the event handlers
- *
- * @param {String} eventName event name
- * @param {Object} data data
- */
- _notifyUpdateListeners: function(eventName, data) {
- this.$el.trigger(new $.Event(eventName, data));
- },
-
- /**
- * Merges the actions from the given fileActions into
- * this instance.
- *
- * @param {OCA.Files.FileActions} fileActions instance of OCA.Files.FileActions
- */
- merge: function(fileActions) {
- var self = this;
- // merge first level to avoid unintended overwriting
- _.each(fileActions.actions, function(sourceMimeData, mime) {
- var targetMimeData = self.actions[mime];
- if (!targetMimeData) {
- targetMimeData = {};
- }
- self.actions[mime] = _.extend(targetMimeData, sourceMimeData);
- });
-
- this.defaults = _.extend(this.defaults, fileActions.defaults);
- this.icons = _.extend(this.icons, fileActions.icons);
- },
- /**
- * @deprecated use #registerAction() instead
- */
- register: function(mime, name, permissions, icon, action, displayName) {
- return this.registerAction({
- name: name,
- mime: mime,
- permissions: permissions,
- icon: icon,
- actionHandler: action,
- displayName: displayName || name
- });
- },
-
- /**
- * Register action
- *
- * @param {OCA.Files.FileAction} action object
- */
- registerAction: function (action) {
- var mime = action.mime;
- var name = action.name;
- var actionSpec = {
- action: function(fileName, context) {
- // Actions registered in one FileAction may be executed on a
- // different one (for example, due to the "merge" function),
- // so the listeners have to be updated on the FileActions
- // from the context instead of on the one in which it was
- // originally registered.
- if (context && context.fileActions) {
- context.fileActions._notifyUpdateListeners('beforeTriggerAction', {action: actionSpec, fileName: fileName, context: context});
- }
-
- action.actionHandler(fileName, context);
-
- if (context && context.fileActions) {
- context.fileActions._notifyUpdateListeners('afterTriggerAction', {action: actionSpec, fileName: fileName, context: context});
- }
- },
- name: name,
- displayName: action.displayName,
- mime: mime,
- filename: action.filename,
- order: action.order || 0,
- icon: action.icon,
- iconClass: action.iconClass,
- permissions: action.permissions,
- type: action.type || FileActions.TYPE_DROPDOWN,
- altText: action.altText || ''
- };
- if (_.isUndefined(action.displayName)) {
- actionSpec.displayName = t('files', name);
- }
- if (_.isFunction(action.render)) {
- actionSpec.render = action.render;
- }
- if (_.isFunction(action.shouldRender)) {
- actionSpec.shouldRender = action.shouldRender;
- }
- if (!this.actions[mime]) {
- this.actions[mime] = {};
- }
- this.actions[mime][name] = actionSpec;
- this.icons[name] = action.icon;
- this._notifyUpdateListeners('registerAction', {action: action});
- },
- /**
- * Clears all registered file actions.
- */
- clear: function() {
- this.actions = {};
- this.defaults = {};
- this.icons = {};
- this.currentFile = null;
- },
- /**
- * Sets the default action for a given mime type.
- *
- * @param {String} mime mime type
- * @param {String} name action name
- */
- setDefault: function (mime, name) {
- this.defaults[mime] = name;
- this._notifyUpdateListeners('setDefault', {defaultAction: {mime: mime, name: name}});
- },
-
- /**
- * Returns a map of file actions handlers matching the given conditions
- *
- * @param {string} mime mime type
- * @param {string} type "dir" or "file"
- * @param {number} permissions permissions
- * @param {string} filename filename
- *
- * @return {Object.<string,OCA.Files.FileActions~actionHandler>} map of action name to action spec
- */
- get: function(mime, type, permissions, filename) {
- var actions = this.getActions(mime, type, permissions, filename);
- var filteredActions = {};
- $.each(actions, function (name, action) {
- filteredActions[name] = action.action;
- });
- return filteredActions;
- },
-
- /**
- * Returns an array of file actions matching the given conditions
- *
- * @param {string} mime mime type
- * @param {string} type "dir" or "file"
- * @param {number} permissions permissions
- * @param {string} filename filename
- *
- * @return {Array.<OCA.Files.FileAction>} array of action specs
- */
- getActions: function(mime, type, permissions, filename) {
- var actions = {};
- if (this.actions.all) {
- actions = $.extend(actions, this.actions.all);
- }
- if (type) {//type is 'dir' or 'file'
- if (this.actions[type]) {
- actions = $.extend(actions, this.actions[type]);
- }
- }
- if (mime) {
- var mimePart = mime.substr(0, mime.indexOf('/'));
- if (this.actions[mimePart]) {
- actions = $.extend(actions, this.actions[mimePart]);
- }
- if (this.actions[mime]) {
- actions = $.extend(actions, this.actions[mime]);
- }
- }
-
- var filteredActions = {};
- var self = this;
- $.each(actions, function(name, action) {
- if (self.allowedPermissions(action.permissions, permissions) &&
- self.allowedFilename(action.filename, filename)) {
- filteredActions[name] = action;
- }
- });
-
- return filteredActions;
- },
-
-
- allowedPermissions: function(actionPermissions, permissions) {
- return (actionPermissions === OC.PERMISSION_NONE || (actionPermissions & permissions));
- },
-
- allowedFilename: function(actionFilename, filename) {
- return (!filename || filename === '' || !actionFilename
- || actionFilename === '' || actionFilename === filename);
- },
-
- /**
- * Returns the default file action handler for the given conditions
- *
- * @param {string} mime mime type
- * @param {string} type "dir" or "file"
- * @param {number} permissions permissions
- *
- * @return {OCA.Files.FileActions~actionHandler} action handler
- *
- * @deprecated use getDefaultFileAction instead
- */
- getDefault: function (mime, type, permissions) {
- var defaultActionSpec = this.getDefaultFileAction(mime, type, permissions);
- if (defaultActionSpec) {
- return defaultActionSpec.action;
- }
- return undefined;
- },
-
- /**
- * Returns the default file action handler for the current file
- *
- * @return {OCA.Files.FileActions~actionSpec} action spec
- * @since 8.2
- */
- getCurrentDefaultFileAction: function() {
- var mime = this.getCurrentMimeType();
- var type = this.getCurrentType();
- var permissions = this.getCurrentPermissions();
- return this.getDefaultFileAction(mime, type, permissions);
- },
-
- /**
- * Returns the default file action handler for the given conditions
- *
- * @param {string} mime mime type
- * @param {string} type "dir" or "file"
- * @param {number} permissions permissions
- *
- * @return {OCA.Files.FileActions~actionSpec} action spec
- * @since 8.2
- */
- getDefaultFileAction: function(mime, type, permissions) {
- var mimePart;
- if (mime) {
- mimePart = mime.substr(0, mime.indexOf('/'));
- }
- var name = false;
- if (mime && this.defaults[mime]) {
- name = this.defaults[mime];
- } else if (mime && this.defaults[mimePart]) {
- name = this.defaults[mimePart];
- } else if (type && this.defaults[type]) {
- name = this.defaults[type];
- } else {
- name = this.defaults.all;
- }
- var actions = this.getActions(mime, type, permissions);
- return actions[name];
- },
-
- /**
- * Default function to render actions
- *
- * @param {OCA.Files.FileAction} actionSpec file action spec
- * @param {boolean} isDefault true if the action is a default one,
- * false otherwise
- * @param {OCA.Files.FileActionContext} context action context
- */
- _defaultRenderAction: function(actionSpec, isDefault, context) {
- if (!isDefault) {
- var params = {
- name: actionSpec.name,
- nameLowerCase: actionSpec.name.toLowerCase(),
- displayName: actionSpec.displayName,
- icon: actionSpec.icon,
- iconClass: actionSpec.iconClass,
- altText: actionSpec.altText,
- hasDisplayName: !!actionSpec.displayName
- };
- if (_.isFunction(actionSpec.icon)) {
- params.icon = actionSpec.icon(context.$file.attr('data-file'), context);
- }
- if (_.isFunction(actionSpec.iconClass)) {
- params.iconClass = actionSpec.iconClass(context.$file.attr('data-file'), context);
- }
-
- var $actionLink = this._makeActionLink(params, context);
- context.$file.find('a.name>span.fileactions').append($actionLink);
- $actionLink.addClass('permanent');
- return $actionLink;
- }
- },
-
- /**
- * Renders the action link element
- *
- * @param {Object} params action params
- */
- _makeActionLink: function(params) {
- return $(OCA.Files.Templates['file_action_trigger'](params));
- },
-
- /**
- * Displays the file actions dropdown menu
- *
- * @param {string} fileName file name
- * @param {OCA.Files.FileActionContext} context rendering context
- */
- _showMenu: function(fileName, context) {
- var menu;
- var $trigger = context.$file.closest('tr').find('.fileactions .action-menu');
- $trigger.addClass('open');
- $trigger.attr('aria-expanded', 'true');
-
- menu = new OCA.Files.FileActionsMenu();
-
- context.$file.find('td.filename').append(menu.$el);
-
- menu.$el.on('afterHide', function() {
- context.$file.removeClass('mouseOver');
- $trigger.removeClass('open');
- $trigger.attr('aria-expanded', 'false');
- menu.remove();
- });
-
- context.$file.addClass('mouseOver');
- menu.show(context);
- },
-
- /**
- * Renders the menu trigger on the given file list row
- *
- * @param {Object} $tr file list row element
- * @param {OCA.Files.FileActionContext} context rendering context
- */
- _renderMenuTrigger: function($tr, context) {
- // remove previous
- $tr.find('.action-menu').remove();
-
- var $el = this._renderInlineAction({
- name: 'menu',
- displayName: '',
- iconClass: 'icon-more',
- altText: t('files', 'Actions'),
- action: this._showMenuClosure
- }, false, context);
-
- $el.addClass('permanent');
- $el.attr('aria-expanded', 'false');
-
- },
-
- /**
- * Renders the action element by calling actionSpec.render() and
- * registers the click event to process the action.
- *
- * @param {OCA.Files.FileAction} actionSpec file action to render
- * @param {boolean} isDefault true if the action is a default action,
- * false otherwise
- * @param {OCA.Files.FileActionContext} context rendering context
- */
- _renderInlineAction: function(actionSpec, isDefault, context) {
- if (actionSpec.shouldRender) {
- if (!actionSpec.shouldRender(context)) {
- return;
- }
- }
- var renderFunc = actionSpec.render || _.bind(this._defaultRenderAction, this);
- var $actionEl = renderFunc(actionSpec, isDefault, context);
- if (!$actionEl || !$actionEl.length) {
- return;
- }
- $actionEl.on(
- 'click', {
- a: null
- },
- function(event) {
- event.stopPropagation();
- event.preventDefault();
-
- if ($actionEl.hasClass('open')) {
- return;
- }
-
- var $file = $(event.target).closest('tr');
- if ($file.hasClass('busy')) {
- return;
- }
- var currentFile = $file.find('td.filename');
- var fileName = $file.attr('data-file');
-
- context.fileActions.currentFile = currentFile;
-
- var callContext = _.extend({}, context);
-
- if (!context.dir && context.fileList) {
- callContext.dir = $file.attr('data-path') || context.fileList.getCurrentDirectory();
- }
-
- if (!context.fileInfoModel && context.fileList) {
- callContext.fileInfoModel = context.fileList.getModelForFile(fileName);
- if (!callContext.fileInfoModel) {
- console.warn('No file info model found for file "' + fileName + '"');
- }
- }
-
- actionSpec.action(
- fileName,
- callContext
- );
- }
- );
-
- return $actionEl;
- },
-
- /**
- * Trigger the given action on the given file.
- *
- * @param {string} actionName action name
- * @param {OCA.Files.FileInfoModel} fileInfoModel file info model
- * @param {OCA.Files.FileList} [fileList] file list, for compatibility with older action handlers [DEPRECATED]
- *
- * @return {boolean} true if the action handler was called, false otherwise
- *
- * @since 8.2
- */
- triggerAction: function(actionName, fileInfoModel, fileList) {
- var actionFunc;
- var actions = this.get(
- fileInfoModel.get('mimetype'),
- fileInfoModel.isDirectory() ? 'dir' : 'file',
- fileInfoModel.get('permissions'),
- fileInfoModel.get('name')
- );
-
- if (actionName) {
- actionFunc = actions[actionName];
- } else {
- actionFunc = this.getDefault(
- fileInfoModel.get('mimetype'),
- fileInfoModel.isDirectory() ? 'dir' : 'file',
- fileInfoModel.get('permissions')
- );
- }
-
- if (!actionFunc) {
- actionFunc = actions['Download'];
- }
-
- if (!actionFunc) {
- return false;
- }
-
- var context = {
- fileActions: this,
- fileInfoModel: fileInfoModel,
- dir: fileInfoModel.get('path')
- };
-
- var fileName = fileInfoModel.get('name');
- this.currentFile = fileName;
-
- if (fileList) {
- // compatibility with action handlers that expect these
- context.fileList = fileList;
- context.$file = fileList.findFileEl(fileName);
- }
-
- actionFunc(fileName, context);
- },
-
- /**
- * Display file actions for the given element
- * @param parent "td" element of the file for which to display actions
- * @param triggerEvent if true, triggers the fileActionsReady on the file
- * list afterwards (false by default)
- * @param fileList OCA.Files.FileList instance on which the action is
- * done, defaults to OCA.Files.App.fileList
- */
- display: function (parent, triggerEvent, fileList) {
- if (!fileList) {
- console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance');
- return;
- }
- this.currentFile = parent;
- var self = this;
- var $tr = parent.closest('tr');
- var actions = this.getActions(
- this.getCurrentMimeType(),
- this.getCurrentType(),
- this.getCurrentPermissions(),
- this.getCurrentFile()
- );
- var nameLinks;
- if ($tr.data('renaming')) {
- return;
- }
-
- // recreate fileactions container
- nameLinks = parent.children('a.name');
- nameLinks.find('.fileactions, .nametext .action').remove();
- nameLinks.append('<span class="fileactions"></span>');
- var defaultAction = this.getDefaultFileAction(
- this.getCurrentMimeType(),
- this.getCurrentType(),
- this.getCurrentPermissions()
- );
-
- var context = {
- $file: $tr,
- fileActions: this,
- fileList: fileList
- };
-
- $.each(actions, function (name, actionSpec) {
- if (actionSpec.type === FileActions.TYPE_INLINE) {
- self._renderInlineAction(
- actionSpec,
- defaultAction && actionSpec.name === defaultAction.name,
- context
- );
- }
- });
-
- function objectValues(obj) {
- var res = [];
- for (var i in obj) {
- if (obj.hasOwnProperty(i)) {
- res.push(obj[i]);
- }
- }
- return res;
- }
- // polyfill
- if (!Object.values) {
- Object.values = objectValues;
- }
-
- var menuActions = Object.values(actions).filter(function (action) {
- return action.type !== OCA.Files.FileActions.TYPE_INLINE && (!defaultAction || action.name !== defaultAction.name)
- });
- // do not render the menu if nothing is in it
- if (menuActions.length > 0) {
- this._renderMenuTrigger($tr, context);
- }
-
- if (triggerEvent){
- fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList, $files: $tr}));
- }
- },
- getCurrentFile: function () {
- return this.currentFile.parent().attr('data-file');
- },
- getCurrentMimeType: function () {
- return this.currentFile.parent().attr('data-mime');
- },
- getCurrentType: function () {
- return this.currentFile.parent().attr('data-type');
- },
- getCurrentPermissions: function () {
- return this.currentFile.parent().data('permissions');
- },
-
- /**
- * Register the actions that are used by default for the files app.
- */
- registerDefaultActions: function() {
- this.registerAction({
- name: 'Download',
- displayName: t('files', 'Download'),
- order: -20,
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- iconClass: 'icon-download',
- actionHandler: function (filename, context) {
- var dir = context.dir || context.fileList.getCurrentDirectory();
- var isDir = context.$file.attr('data-type') === 'dir';
- var url = context.fileList.getDownloadUrl(filename, dir, isDir);
-
- var downloadFileaction = $(context.$file).find('.fileactions .action-download');
-
- // don't allow a second click on the download action
- if(downloadFileaction.hasClass('disabled')) {
- return;
- }
-
- if (url) {
- var disableLoadingState = function() {
- context.fileList.showFileBusyState(filename, false);
- };
-
- context.fileList.showFileBusyState(filename, true);
- OCA.Files.Files.handleDownload(url, disableLoadingState);
- }
- }
- });
-
- this.registerAction({
- name: 'Rename',
- displayName: t('files', 'Rename'),
- mime: 'all',
- order: -30,
- permissions: OC.PERMISSION_UPDATE,
- iconClass: 'icon-rename',
- actionHandler: function (filename, context) {
- context.fileList.rename(filename);
- }
- });
-
- this.registerAction({
- name: 'MoveCopy',
- displayName: function(context) {
- var permissions = context.fileInfoModel.attributes.permissions;
- if (permissions & OC.PERMISSION_UPDATE) {
- if (!context.fileInfoModel.canDownload()) {
- return t('files', 'Move');
- }
- return t('files', 'Move or copy');
- }
- return t('files', 'Copy');
- },
- mime: 'all',
- order: -25,
- permissions: $('#isPublic').val() ? OC.PERMISSION_UPDATE : OC.PERMISSION_READ,
- iconClass: 'icon-external',
- actionHandler: function (filename, context) {
- var permissions = context.fileInfoModel.attributes.permissions;
- var actions = OC.dialogs.FILEPICKER_TYPE_COPY;
- if (permissions & OC.PERMISSION_UPDATE) {
- if (!context.fileInfoModel.canDownload()) {
- actions = OC.dialogs.FILEPICKER_TYPE_MOVE;
- } else {
- actions = OC.dialogs.FILEPICKER_TYPE_COPY_MOVE;
- }
- }
- var dialogDir = context.dir;
- if (typeof context.fileList.dirInfo.dirLastCopiedTo !== 'undefined') {
- dialogDir = context.fileList.dirInfo.dirLastCopiedTo;
- }
- OC.dialogs.filepicker(t('files', 'Choose target folder'), function(targetPath, type) {
- if (type === OC.dialogs.FILEPICKER_TYPE_COPY) {
- context.fileList.copy(filename, targetPath, false, context.dir);
- }
- if (type === OC.dialogs.FILEPICKER_TYPE_MOVE) {
- context.fileList.move(filename, targetPath, false, context.dir);
- }
- context.fileList.dirInfo.dirLastCopiedTo = targetPath;
- }, false, "httpd/unix-directory", true, actions, dialogDir);
- }
- });
-
- if (Boolean(OC.appswebroots.files_reminders) && Boolean(OC.appswebroots.notifications)) {
- this.registerAction({
- name: 'SetReminder',
- displayName: function(_context) {
- return t('files', 'Set reminder');
- },
- mime: 'all',
- order: -24,
- icon: function(_filename, _context) {
- return OC.imagePath('files_reminders', 'alarm.svg')
- },
- permissions: $('#isPublic').val() ? null : OC.PERMISSION_READ,
- actionHandler: function(_filename, _context) {},
- });
- }
-
- if (!/Android|iPhone|iPad|iPod/i.test(navigator.userAgent) && !!window.oc_current_user) {
- this.registerAction({
- name: 'EditLocally',
- displayName: function(context) {
- var locked = context.$file.data('locked');
- if (!locked) {
- return t('files', 'Edit locally');
- }
- },
- mime: 'all',
- order: -23,
- icon: function(filename, context) {
- var locked = context.$file.data('locked');
- if (!locked) {
- return OC.imagePath('files', 'computer.svg')
- }
- },
- permissions: OC.PERMISSION_UPDATE,
- actionHandler: function (filename, context) {
- var dir = context.dir || context.fileList.getCurrentDirectory();
- var path = dir === '/' ? dir + filename : dir + '/' + filename;
- context.fileList.openLocalClient(path);
- },
- });
- }
-
- this.registerAction({
- name: 'Open',
- mime: 'dir',
- permissions: OC.PERMISSION_READ,
- icon: '',
- actionHandler: function (filename, context) {
- let dir, id
- if (context.$file) {
- dir = context.$file.attr('data-path')
- id = context.$file.attr('data-id')
- } else {
- dir = context.fileList.getCurrentDirectory()
- id = context.fileId
- }
- if (OCA.Files.App && OCA.Files.App.getActiveView() !== 'files') {
- OCA.Files.App.setActiveView('files', {silent: true});
- OCA.Files.App.fileList.changeDirectory(OC.joinPaths(dir, filename), true, true);
- } else {
- context.fileList.changeDirectory(OC.joinPaths(dir, filename), true, false, parseInt(id, 10));
- }
- },
- displayName: t('files', 'Open')
- });
-
- this.registerAction({
- name: 'Delete',
- displayName: function(context) {
- var mountType = context.$file.attr('data-mounttype');
- var type = context.$file.attr('data-type');
- var deleteTitle = (type && type === 'file')
- ? t('files', 'Delete file')
- : t('files', 'Delete folder')
- if (mountType === 'external-root') {
- deleteTitle = t('files', 'Disconnect storage');
- } else if (mountType === 'shared-root') {
- deleteTitle = t('files', 'Leave this share');
- }
- return deleteTitle;
- },
- mime: 'all',
- order: 1000,
- // permission is READ because we show a hint instead if there is no permission
- permissions: OC.PERMISSION_DELETE,
- iconClass: 'icon-delete',
- actionHandler: function(fileName, context) {
- // if there is no permission to delete do nothing
- if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) {
- return;
- }
- context.fileList.do_delete(fileName, context.dir);
- $('.tipsy').remove();
-
- // close sidebar on delete
- const path = context.dir + '/' + fileName
- if (OCA.Files.Sidebar && OCA.Files.Sidebar.file === path) {
- OCA.Files.Sidebar.close()
- }
- }
- });
-
- this.setDefault('dir', 'Open');
- }
- };
-
- OCA.Files.FileActions = FileActions;
-
- /**
- * Replaces the button icon with a loading spinner and vice versa
- * - also adds the class disabled to the passed in element
- *
- * @param {jQuery} $buttonElement The button element
- * @param {boolean} showIt whether to show the spinner(true) or to hide it(false)
- */
- OCA.Files.FileActions.updateFileActionSpinner = function($buttonElement, showIt) {
- var $icon = $buttonElement.find('.icon');
- if (showIt) {
- var $loadingIcon = $('<span class="icon icon-loading-small"></span>');
- $icon.after($loadingIcon);
- $icon.addClass('hidden');
- } else {
- $buttonElement.find('.icon-loading-small').remove();
- $buttonElement.find('.icon').removeClass('hidden');
- }
- };
-
- /**
- * File action attributes.
- *
- * @todo make this a real class in the future
- * @typedef {Object} OCA.Files.FileAction
- *
- * @property {String} name identifier of the action
- * @property {(String|OCA.Files.FileActions~displayNameFunction)} displayName
- * display name string for the action, or function that returns the display name.
- * Defaults to the name given in name property
- * @property {String} mime mime type
- * @property {String} filename filename
- * @property {number} permissions permissions
- * @property {(Function|String)} icon icon path to the icon or function that returns it (deprecated, use iconClass instead)
- * @property {(String|OCA.Files.FileActions~iconClassFunction)} iconClass class name of the icon (recommended for theming)
- * @property {OCA.Files.FileActions~renderActionFunction} [render] optional rendering function
- * @property {OCA.Files.FileActions~actionHandler} actionHandler action handler function
- */
-
- /**
- * File action context attributes.
- *
- * @typedef {Object} OCA.Files.FileActionContext
- *
- * @property {Object} $file jQuery file row element
- * @property {OCA.Files.FileActions} fileActions file actions object
- * @property {OCA.Files.FileList} fileList file list object
- */
-
- /**
- * Render function for actions.
- * The function must render a link element somewhere in the DOM
- * and return it. The function should NOT register the event handler
- * as this will be done after the link was returned.
- *
- * @callback OCA.Files.FileActions~renderActionFunction
- * @param {OCA.Files.FileAction} actionSpec action definition
- * @param {Object} $row row container
- * @param {boolean} isDefault true if the action is the default one,
- * false otherwise
- * @return {Object} jQuery link object
- */
-
- /**
- * Display name function for actions.
- * The function returns the display name of the action using
- * the given context information..
- *
- * @callback OCA.Files.FileActions~displayNameFunction
- * @param {OCA.Files.FileActionContext} context action context
- * @return {String} display name
- */
-
- /**
- * Icon class function for actions.
- * The function returns the icon class of the action using
- * the given context information.
- *
- * @callback OCA.Files.FileActions~iconClassFunction
- * @param {String} fileName name of the file on which the action must be performed
- * @param {OCA.Files.FileActionContext} context action context
- * @return {String} icon class
- */
-
- /**
- * Action handler function for file actions
- *
- * @callback OCA.Files.FileActions~actionHandler
- * @param {String} fileName name of the file on which the action must be performed
- * @param context context
- * @param {String} context.dir directory of the file
- * @param {OCA.Files.FileInfoModel} fileInfoModel file info model
- * @param {Object} [context.$file] jQuery element of the file [DEPRECATED]
- * @param {OCA.Files.FileList} [context.fileList] the FileList instance on which the action occurred [DEPRECATED]
- * @param {OCA.Files.FileActions} context.fileActions the FileActions instance on which the action occurred
- */
-
- // global file actions to be used by all lists
- OCA.Files.fileActions = new OCA.Files.FileActions();
-})();
diff --git a/apps/files/js/fileactionsmenu.js b/apps/files/js/fileactionsmenu.js
deleted file mode 100644
index 3d827615a40..00000000000
--- a/apps/files/js/fileactionsmenu.js
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
-
- /**
- * Construct a new FileActionsMenu instance
- * @constructs FileActionsMenu
- * @memberof OCA.Files
- */
- var FileActionsMenu = OC.Backbone.View.extend({
- tagName: 'div',
- className: 'fileActionsMenu popovermenu bubble hidden open menu',
-
- /**
- * Current context
- *
- * @type OCA.Files.FileActionContext
- */
- _context: null,
-
- events: {
- 'click a.action': '_onClickAction'
- },
-
- template: function(data) {
- return OCA.Files.Templates['fileactionsmenu'](data);
- },
-
- /**
- * Event handler whenever an action has been clicked within the menu
- *
- * @param {Object} event event object
- */
- _onClickAction: function(event) {
- var $target = $(event.target);
- if (!$target.is('a')) {
- $target = $target.closest('a');
- }
- var fileActions = this._context.fileActions;
- var actionName = $target.attr('data-action');
- var actions = fileActions.getActions(
- fileActions.getCurrentMimeType(),
- fileActions.getCurrentType(),
- fileActions.getCurrentPermissions(),
- fileActions.getCurrentFile()
- );
- var actionSpec = actions[actionName];
- var fileName = this._context.$file.attr('data-file');
-
- event.stopPropagation();
- event.preventDefault();
-
- OC.hideMenus();
-
- actionSpec.action(
- fileName,
- this._context
- );
- },
-
- /**
- * Renders the menu with the currently set items
- */
- render: function() {
- var self = this;
- var fileActions = this._context.fileActions;
- var actions = fileActions.getActions(
- fileActions.getCurrentMimeType(),
- fileActions.getCurrentType(),
- fileActions.getCurrentPermissions(),
- fileActions.getCurrentFile()
- );
-
- var defaultAction = fileActions.getCurrentDefaultFileAction();
-
- var items = _.filter(actions, function(actionSpec) {
- return !defaultAction || actionSpec.name !== defaultAction.name;
- });
- items = _.map(items, function(item) {
- if (_.isFunction(item.displayName)) {
- item = _.extend({}, item);
- item.displayName = item.displayName(self._context);
- }
- if (_.isFunction(item.iconClass)) {
- var fileName = self._context.$file.attr('data-file');
- item = _.extend({}, item);
- item.iconClass = item.iconClass(fileName, self._context);
- }
- if (_.isFunction(item.icon)) {
- var fileName = self._context.$file.attr('data-file');
- item = _.extend({}, item);
- item.icon = item.icon(fileName, self._context);
- }
- item.inline = item.type === OCA.Files.FileActions.TYPE_INLINE
- return item;
- });
- items = items.sort(function(actionA, actionB) {
- var orderA = actionA.order || 0;
- var orderB = actionB.order || 0;
- if (orderB === orderA) {
- return OC.Util.naturalSortCompare(actionA.displayName, actionB.displayName);
- }
- return orderA - orderB;
- });
-
- items = _.map(items, function(item) {
- item.nameLowerCase = item.name.toLowerCase();
- return item;
- });
-
- this.$el.html(this.template({
- items: items
- }));
- },
-
- /**
- * Displays the menu under the given element
- *
- * @param {OCA.Files.FileActionContext} context context
- * @param {Object} $trigger trigger element
- */
- show: function(context) {
- this._context = context;
-
- this.render();
- this.$el.removeClass('hidden');
-
- window._nc_event_bus.emit('files:action-menu:opened', {
- el: this.$el[0],
- context,
- })
-
- OC.showMenu(null, this.$el);
- }
- });
-
- OCA.Files.FileActionsMenu = FileActionsMenu;
-
-})();
diff --git a/apps/files/js/fileinfomodel.js b/apps/files/js/fileinfomodel.js
deleted file mode 100644
index 0c0061d4d13..00000000000
--- a/apps/files/js/fileinfomodel.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function(OC, OCA) {
-
- /**
- * @class OC.Files.FileInfo
- * @classdesc File information
- *
- * @param {Object} attributes file data
- * @param {number} attributes.id file id
- * @param {string} attributes.name file name
- * @param {string} attributes.path path leading to the file,
- * without the file name and with a leading slash
- * @param {number} attributes.size size
- * @param {string} attributes.mimetype mime type
- * @param {string} attributes.icon icon URL
- * @param {number} attributes.permissions permissions
- * @param {Date} attributes.mtime modification time
- * @param {string} attributes.etag etag
- * @param {string} mountType mount type
- *
- * @since 8.2
- */
- var FileInfoModel = OC.Backbone.Model.extend({
-
- defaults: {
- mimetype: 'application/octet-stream',
- path: ''
- },
-
- _filesClient: null,
-
- initialize: function(data, options) {
- if (!_.isUndefined(data.id)) {
- data.id = parseInt(data.id, 10);
- }
-
- if( options ){
- if (options.filesClient) {
- this._filesClient = options.filesClient;
- }
- }
- },
-
- /**
- * Returns whether this file is a directory
- *
- * @return {boolean} true if this is a directory, false otherwise
- */
- isDirectory: function() {
- return this.get('mimetype') === 'httpd/unix-directory';
- },
-
- /**
- * Returns whether this file is an image
- *
- * @return {boolean} true if this is an image, false otherwise
- */
- isImage: function() {
- if (!this.has('mimetype')) {
- return false;
- }
- return this.get('mimetype').substr(0, 6) === 'image/'
- || this.get('mimetype') === 'application/postscript'
- || this.get('mimetype') === 'application/illustrator'
- || this.get('mimetype') === 'application/x-photoshop';
- },
-
- /**
- * Returns the full path to this file
- *
- * @return {string} full path
- */
- getFullPath: function() {
- return OC.joinPaths(this.get('path'), this.get('name'));
- },
-
- /**
- * Returns the mimetype of the file
- *
- * @return {string} mimetype
- */
- getMimeType: function() {
- return this.get('mimetype');
- },
-
- /**
- * Reloads missing properties from server and set them in the model.
- * @param properties array of properties to be reloaded
- * @return ajax call object
- */
- reloadProperties: function(properties) {
- if( !this._filesClient ){
- return;
- }
-
- var self = this;
- var deferred = $.Deferred();
-
- var targetPath = OC.joinPaths(this.get('path') + '/', this.get('name'));
-
- this._filesClient.getFileInfo(targetPath, {
- properties: properties
- })
- .then(function(status, data) {
- // the following lines should be extracted to a mapper
-
- if( properties.indexOf(OC.Files.Client.PROPERTY_GETCONTENTLENGTH) !== -1
- || properties.indexOf(OC.Files.Client.PROPERTY_SIZE) !== -1 ) {
- self.set('size', data.size);
- }
-
- deferred.resolve(status, data);
- })
- .fail(function(status) {
- OC.Notification.show(t('files', 'Could not load info for file "{file}"', {file: self.get('name')}), {type: 'error'});
- deferred.reject(status);
- });
-
- return deferred.promise();
- },
-
- canDownload: function() {
- for (const i in this.attributes.shareAttributes) {
- const attr = this.attributes.shareAttributes[i]
- if (attr.scope === 'permissions' && attr.key === 'download') {
- return attr.value === true
- }
- }
-
- return true
- },
- });
-
- if (!OCA.Files) {
- OCA.Files = {};
- }
- OCA.Files.FileInfoModel = FileInfoModel;
-
-})(OC, OCA);
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
deleted file mode 100644
index ab9c57bfd46..00000000000
--- a/apps/files/js/filelist.js
+++ /dev/null
@@ -1,4038 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
-
- /**
- * @class OCA.Files.FileList
- * @classdesc
- *
- * The FileList class manages a file list view.
- * A file list view consists of a controls bar and
- * a file list table.
- *
- * @param $el container element with existing markup for the .files-controls
- * and a table
- * @param {Object} [options] map of options, see other parameters
- * @param {Object} [options.scrollContainer] scrollable container, defaults to $(window)
- * @param {Object} [options.dragOptions] drag options, disabled by default
- * @param {Object} [options.folderDropOptions] folder drop options, disabled by default
- * @param {boolean} [options.detailsViewEnabled=true] whether to enable details view
- * @param {boolean} [options.enableUpload=false] whether to enable uploader
- * @param {OC.Files.Client} [options.filesClient] files client to use
- */
- var FileList = function($el, options) {
- this.initialize($el, options);
- };
- /**
- * @memberof OCA.Files
- */
- FileList.prototype = {
- SORT_INDICATOR_ASC_CLASS: 'icon-triangle-n',
- SORT_INDICATOR_DESC_CLASS: 'icon-triangle-s',
-
- id: 'files',
- appName: t('files', 'Files'),
- isEmpty: true,
- useUndo:true,
-
- /**
- * Top-level container with controls and file list
- */
- $el: null,
-
- /**
- * Files table
- */
- $table: null,
-
- /**
- * List of rows (table tbody)
- */
- $fileList: null,
-
- $header: null,
- headers: [],
-
- $footer: null,
- footers: [],
-
- /**
- * @type OCA.Files.BreadCrumb
- */
- breadcrumb: null,
-
- /**
- * @type OCA.Files.FileSummary
- */
- fileSummary: null,
-
- /**
- * @type OCA.Files.DetailsView
- */
- _detailsView: null,
-
- /**
- * Files client instance
- *
- * @type OC.Files.Client
- */
- filesClient: null,
-
- /**
- * Whether the file list was initialized already.
- * @type boolean
- */
- initialized: false,
-
- /**
- * Wheater the file list was already shown once
- * @type boolean
- */
- shown: false,
-
- /**
- * Number of files per page
- * Always show a minimum of 1
- *
- * @return {number} page size
- */
- pageSize: function() {
- var isGridView = this.$table.hasClass('view-grid');
- var columns = 1;
- var rows = Math.ceil(this.$container.height() / 50);
- if (isGridView) {
- columns = Math.ceil(this.$container.width() / 160);
- rows = Math.ceil(this.$container.height() / 160);
- }
- return Math.max(columns*rows, columns);
- },
-
- /**
- * Array of files in the current folder.
- * The entries are of file data.
- *
- * @type Array.<OC.Files.FileInfo>
- */
- files: [],
-
- /**
- * Current directory entry
- *
- * @type OC.Files.FileInfo
- */
- dirInfo: null,
-
- /**
- * Whether to prevent or to execute the default file actions when the
- * file name is clicked.
- *
- * @type boolean
- */
- _defaultFileActionsDisabled: false,
-
- /**
- * File actions handler, defaults to OCA.Files.FileActions
- * @type OCA.Files.FileActions
- */
- fileActions: null,
- /**
- * File selection menu, defaults to OCA.Files.FileSelectionMenu
- * @type OCA.Files.FileSelectionMenu
- */
- fileMultiSelectMenu: null,
- /**
- * Whether selection is allowed, checkboxes and selection overlay will
- * be rendered
- */
- _allowSelection: true,
-
- /**
- * Map of file id to file data
- * @type Object.<int, Object>
- */
- _selectedFiles: {},
-
- /**
- * Summary of selected files.
- * @type OCA.Files.FileSummary
- */
- _selectionSummary: null,
-
- /**
- * If not empty, only files containing this string will be shown
- * @type String
- */
- _filter: '',
-
- /**
- * @type UserConfig
- * @see /apps/files/lib/Service/UserConfig.php
- */
- _filesConfig: undefined,
-
- /**
- * Sort attribute
- * @type String
- */
- _sort: 'name',
-
- /**
- * Sort direction: 'asc' or 'desc'
- * @type String
- */
- _sortDirection: 'asc',
-
- /**
- * Sort comparator function for the current sort
- * @type Function
- */
- _sortComparator: null,
-
- /**
- * Whether to do a client side sort.
- * When false, clicking on a table header will call reload().
- * When true, clicking on a table header will simply resort the list.
- */
- _clientSideSort: true,
-
- /**
- * Whether or not users can change the sort attribute or direction
- */
- _allowSorting: true,
-
- /**
- * Current directory
- * @type String
- */
- _currentDirectory: null,
-
- _dragOptions: null,
- _folderDropOptions: null,
-
- /**
- * @type OC.Uploader
- */
- _uploader: null,
-
- /**
- * Initialize the file list and its components
- *
- * @param $el container element with existing markup for the .files-controls
- * and a table
- * @param options map of options, see other parameters
- * @param options.scrollContainer scrollable container, defaults to $(window)
- * @param options.dragOptions drag options, disabled by default
- * @param options.folderDropOptions folder drop options, disabled by default
- * @param options.scrollTo name of file to scroll to after the first load
- * @param [options.dir='/'] current directory
- * @param {OC.Files.Client} [options.filesClient] files API client
- * @param {OC.Backbone.Model} [options.filesConfig] files app configuration
- * @private
- */
- initialize: function($el, options) {
- var self = this;
- options = options || {};
- if (this.initialized) {
- return;
- }
-
- if (options.shown) {
- this.shown = options.shown;
- }
-
- if (options.config) {
- this._filesConfig = options.config;
- } else if (!_.isUndefined(OCA.Files) && !_.isUndefined(OCA.Files.App)) {
- this._filesConfig = OCA.Files.App.getFilesConfig();
- } else {
- this._filesConfig = OCP.InitialState.loadState('files', 'config', {})
- }
-
- if (options.dragOptions) {
- this._dragOptions = options.dragOptions;
- }
- if (options.folderDropOptions) {
- this._folderDropOptions = options.folderDropOptions;
- }
- if (options.filesClient) {
- this.filesClient = options.filesClient;
- } else {
- // default client if not specified
- this.filesClient = OC.Files.getClient();
- }
-
- this.$el = $el;
- if (options.id) {
- this.id = options.id;
- }
- this.$container = options.scrollContainer || $('#app-content');
- this.$table = $el.find('table:first');
- this.$fileList = $el.find('.files-fileList');
- this.$header = $el.find('.filelist-header');
- this.$footer = $el.find('.filelist-footer');
-
- // Legacy mapper for new vue components
- window._nc_event_bus.subscribe('files:config:updated', ({ key, value }) => {
- // Replace existing config with new one
- Object.assign(this._filesConfig, { [key]: value })
-
- if (key === 'show_hidden') {
- self.$el.toggleClass('hide-hidden-files', !value);
- self.updateSelectionSummary();
-
- // hiding files could make the page too small, need to try rendering next page
- if (!value) {
- self._onScroll();
- }
- }
- if (key === 'crop_image_previews') {
- self.reload();
- }
- })
-
- var config = OCP.InitialState.loadState('files', 'config', {})
- if (config.show_hidden === false) {
- this.$el.addClass('hide-hidden-files');
- }
-
- if (_.isUndefined(options.detailsViewEnabled) || options.detailsViewEnabled) {
- this._detailsView = new OCA.Files.DetailsView();
- this._detailsView.$el.addClass('disappear');
- }
-
- if (options && options.defaultFileActionsDisabled) {
- this._defaultFileActionsDisabled = options.defaultFileActionsDisabled
- }
-
- this._initFileActions(options.fileActions);
-
- if (this._detailsView) {
- this._detailsView.addDetailView(new OCA.Files.MainFileInfoDetailView({fileList: this, fileActions: this.fileActions}));
- }
-
- this.files = [];
- this._selectedFiles = {};
- this._selectionSummary = new OCA.Files.FileSummary(undefined, {config: this._filesConfig});
- // dummy root dir info
- this.dirInfo = new OC.Files.FileInfo({});
-
- this.fileSummary = this._createSummary();
-
- if (options.multiSelectMenu) {
- this.multiSelectMenuItems = options.multiSelectMenu;
- for (var i=0; i<this.multiSelectMenuItems.length; i++) {
- if (_.isFunction(this.multiSelectMenuItems[i])) {
- this.multiSelectMenuItems[i] = this.multiSelectMenuItems[i](this);
- }
- }
- this._updateMultiSelectFileActions()
- }
-
- if (options.sorting) {
- this.setSort(options.sorting.mode, options.sorting.direction, false, false);
- } else {
- this.setSort('name', 'asc', false, false);
- }
-
- var breadcrumbOptions = {
- onClick: _.bind(this._onClickBreadCrumb, this),
- getCrumbUrl: function(part) {
- return self.linkTo(part.dir);
- }
- };
- // if dropping on folders is allowed, then also allow on breadcrumbs
- if (this._folderDropOptions) {
- breadcrumbOptions.onDrop = _.bind(this._onDropOnBreadCrumb, this);
- breadcrumbOptions.onOver = function() {
- self.$el.find('td.filename.ui-droppable').droppable('disable');
- };
- breadcrumbOptions.onOut = function() {
- self.$el.find('td.filename.ui-droppable').droppable('enable');
- };
- }
- this.breadcrumb = new OCA.Files.BreadCrumb(breadcrumbOptions);
-
- var $controls = this.$el.find('.files-controls');
- if ($controls.length > 0) {
- $controls.prepend(this.breadcrumb.$el);
- this.$table.addClass('has-controls');
- }
-
- this._renderNewButton();
-
- this.$el.find('thead th .columntitle').click(_.bind(this._onClickHeader, this));
-
- this._onResize = _.debounce(_.bind(this._onResize, this), 250);
- $('#app-content').on('appresized', this._onResize);
- $(window).resize(this._onResize);
-
- this.$el.on('show', this._onResize);
-
- // reload files list on share accept
- $('body').on('OCA.Notification.Action', function(eventObject) {
- if (eventObject.notification.app === 'files_sharing' && eventObject.action.type === 'POST') {
- self.reload()
- }
- });
-
- window._nc_event_bus.subscribe('files_sharing:share:created', () => { self.reload(true) });
- window._nc_event_bus.subscribe('files_sharing:share:deleted', () => { self.reload(true) });
-
- this.$fileList.on('click','td.filename>a.name, td.filesize, td.date', _.bind(this._onClickFile, this));
-
- this.$fileList.on("droppedOnFavorites", function (event, file) {
- self.fileActions.triggerAction('Favorite', self.getModelForFile(file), self);
- });
-
- this.$fileList.on('droppedOnTrash', function (event, filename, directory) {
- self.do_delete(filename, directory);
- });
-
- this.$fileList.on('change', 'td.selection>.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
- this.$fileList.on('mouseover', 'td.selection', _.bind(this._onMouseOverCheckbox, this));
- this.$el.on('show', _.bind(this._onShow, this));
- this.$el.on('urlChanged', _.bind(this._onUrlChanged, this));
- this.$el.find('.select-all').click(_.bind(this._onClickSelectAll, this));
- this.$el.find('.actions-selected').click(function () {
- self.fileMultiSelectMenu.show(self);
- return false;
- });
-
- this.$container.on('scroll', _.bind(this._onScroll, this));
-
- if (options.scrollTo) {
- this.$fileList.one('updated', function() {
- self.scrollTo(options.scrollTo);
- });
- }
-
- if (!_.isUndefined(options.dir)) {
- this._setCurrentDir(options.dir || '/', false);
- }
-
- if (options.openFile) {
- // Wait for some initialisation process to be over before triggering the default action.
- _.defer(() => {
- try {
- var fileInfo = JSON.parse(atob($('#initial-state-files-openFileInfo').val()))
- var spec = this.fileActions.getDefaultFileAction(fileInfo.mime, fileInfo.type, fileInfo.permissions)
- if (spec && spec.action) {
- spec.action(fileInfo.name, {
- fileId: fileInfo.id,
- fileList: this,
- fileActions: this.fileActions,
- dir: fileInfo.directory
- });
- } else {
- var url = this.getDownloadUrl(fileInfo.name, fileInfo.dir, true);
- OCA.Files.Files.handleDownload(url);
- }
-
- if (document.documentElement.clientWidth > 1024) {
- OCA.Files.Sidebar.open(fileInfo.path);
- }
- } catch (error) {
- console.error(`Failed to trigger default action on the file for URL: ${location.href}`, error)
- }
- })
- }
-
- this._operationProgressBar = new OCA.Files.OperationProgressBar();
- this._operationProgressBar.render();
- this.$el.find('#uploadprogresswrapper').replaceWith(this._operationProgressBar.$el);
-
- if (options.enableUpload) {
- // TODO: auto-create this element
- var $uploadEl = this.$el.find('#file_upload_start');
- if ($uploadEl.exists()) {
- this._uploader = new OC.Uploader($uploadEl, {
- progressBar: this._operationProgressBar,
- fileList: this,
- filesClient: this.filesClient,
- dropZone: $('#content'),
- maxChunkSize: options.maxChunkSize
- });
-
- this.setupUploadEvents(this._uploader);
- }
- }
- this.triedActionOnce = false;
-
- OC.Plugins.attach('OCA.Files.FileList', this);
-
- OCA.Files.App && OCA.Files.App.updateCurrentFileList(this);
-
- this.initHeadersAndFooters()
- },
-
- initHeadersAndFooters: function() {
- this.headers.sort(function(a, b) {
- return a.order - b.order;
- })
- this.footers.sort(function(a, b) {
- return a.order - b.order;
- })
- var uniqueIds = [];
- var self = this;
- this.headers.forEach(function(header) {
- if (header.id) {
- if (uniqueIds.indexOf(header.id) !== -1) {
- return
- }
- uniqueIds.push(header.id)
- }
- self.$header.append(header.el)
-
- setTimeout(function() {
- header.render(self)
- }, 0)
- })
-
- uniqueIds = [];
- this.footers.forEach(function(footer) {
- if (footer.id) {
- if (uniqueIds.indexOf(footer.id) !== -1) {
- return
- }
- uniqueIds.push(footer.id)
- }
- self.$footer.append(footer.el)
- setTimeout(function() {
- footer.render(self)
- }, 0)
- })
- },
-
- /**
- * Destroy / uninitialize this instance.
- */
- destroy: function() {
- if (this._newFileMenu) {
- this._newFileMenu.remove();
- }
- if (this._newButton) {
- this._newButton.remove();
- }
- if (this._detailsView) {
- this._detailsView.remove();
- }
- // TODO: also unregister other event handlers
- this.fileActions.off('registerAction', this._onFileActionsUpdated);
- this.fileActions.off('setDefault', this._onFileActionsUpdated);
- OC.Plugins.detach('OCA.Files.FileList', this);
- $('#app-content').off('appresized', this._onResize);
- },
-
- _selectionMode: 'single',
- _getCurrentSelectionMode: function () {
- return this._selectionMode;
- },
- _onClickToggleSelectionMode: function () {
- this._selectionMode = (this._selectionMode === 'range') ? 'single' : 'range';
- if (this._selectionMode === 'single') {
- this._removeHalfSelection();
- }
- },
-
- multiSelectMenuClick: function (ev, action) {
- var actionFunction = _.find(this.multiSelectMenuItems, function (item) {return item.name === action;}).action;
- if (actionFunction) {
- actionFunction(this.getSelectedFiles());
- return;
- }
- switch (action) {
- case 'delete':
- this._onClickDeleteSelected(ev)
- break;
- case 'download':
- this._onClickDownloadSelected(ev);
- break;
- case 'copyMove':
- this._onClickCopyMoveSelected(ev);
- break;
- case 'restore':
- this._onClickRestoreSelected(ev);
- break;
- case 'tags':
- this._onClickTagSelected(ev);
- break;
- }
- },
- /**
- * Initializes the file actions, set up listeners.
- *
- * @param {OCA.Files.FileActions} fileActions file actions
- */
- _initFileActions: function(fileActions) {
- var self = this;
- this.fileActions = fileActions;
- if (!this.fileActions) {
- this.fileActions = new OCA.Files.FileActions();
- this.fileActions.registerDefaultActions();
- }
-
- if (this._detailsView) {
- this.fileActions.registerAction({
- name: 'Details',
- displayName: t('files', 'Details'),
- mime: 'all',
- order: -50,
- iconClass: 'icon-details',
- permissions: OC.PERMISSION_NONE,
- actionHandler: function(fileName, context) {
- self._updateDetailsView(fileName);
- }
- });
- }
-
- this._onFileActionsUpdated = _.debounce(_.bind(this._onFileActionsUpdated, this), 100);
- this.fileActions.on('registerAction', this._onFileActionsUpdated);
- this.fileActions.on('setDefault', this._onFileActionsUpdated);
- },
-
- /**
- * Returns a unique model for the given file name.
- *
- * @param {string|object} fileName file name or jquery row
- * @return {OCA.Files.FileInfoModel} file info model
- */
- getModelForFile: function(fileName) {
- var self = this;
- var $tr;
- // jQuery object ?
- if (fileName.is) {
- $tr = fileName;
- fileName = $tr.attr('data-file');
- } else {
- $tr = this.findFileEl(fileName);
- }
-
- if (!$tr || !$tr.length) {
- return null;
- }
-
- // if requesting the selected model, return it
- if (this._currentFileModel && this._currentFileModel.get('name') === fileName) {
- return this._currentFileModel;
- }
-
- // TODO: note, this is a temporary model required for synchronising
- // state between different views.
- // In the future the FileList should work with Backbone.Collection
- // and contain existing models that can be used.
- // This method would in the future simply retrieve the matching model from the collection.
- var model = new OCA.Files.FileInfoModel(this.elementToFile($tr), {
- filesClient: this.filesClient
- });
- if (!model.get('path')) {
- model.set('path', this.getCurrentDirectory(), {silent: true});
- }
-
- model.on('change', function(model) {
- // re-render row
- var highlightState = $tr.hasClass('highlighted');
- $tr = self.updateRow(
- $tr,
- model.toJSON(),
- {updateSummary: true, silent: false, animate: true}
- );
-
- // restore selection state
- var selected = !!self._selectedFiles[$tr.data('id')];
- self._selectFileEl($tr, selected);
-
- $tr.toggleClass('highlighted', highlightState);
- });
- model.on('busy', function(model, state) {
- self.showFileBusyState($tr, state);
- });
-
- return model;
- },
-
- /**
- * Displays the details view for the given file and
- * selects the given tab
- *
- * @param {string|OCA.Files.FileInfoModel} fileName file name or FileInfoModel for which to show details
- * @param {string} [tabId] optional tab id to select
- */
- showDetailsView: function(fileName, tabId) {
- OC.debug && console.warn('showDetailsView is deprecated! Use OCA.Files.Sidebar.activeTab. It will be removed in nextcloud 20.');
- this._updateDetailsView(fileName);
- if (tabId) {
- OCA.Files.Sidebar.setActiveTab(tabId);
- }
- },
-
- /**
- * Update the details view to display the given file
- *
- * @param {string|OCA.Files.FileInfoModel} fileName file name from the current list or a FileInfoModel object
- * @param {boolean} [show=true] whether to open the sidebar if it was closed
- */
- _updateDetailsView: function(fileName, show) {
- if (!(OCA.Files && OCA.Files.Sidebar)) {
- console.error('No sidebar available');
- return;
- }
-
- if (!fileName && OCA.Files.Sidebar.close) {
- OCA.Files.Sidebar.close()
- return
- } else if (typeof fileName !== 'string') {
- fileName = ''
- }
-
- // this is the old (terrible) way of getting the context.
- // don't use it anywhere else. Just provide the full path
- // of the file to the sidebar service
- var tr = this.findFileEl(fileName)
- var model = this.getModelForFile(tr)
- var path = model.attributes.path + '/' + model.attributes.name
-
- // make sure the file list has the correct context available
- if (this._currentFileModel) {
- this._currentFileModel.off();
- }
- this.$fileList.children().removeClass('highlighted');
- tr.addClass('highlighted');
- this._currentFileModel = model;
-
- const secondaryActionsOpen = Boolean(tr.find('.actions-secondary-vue').length)
-
- // open sidebar and set file
- if (!secondaryActionsOpen && (typeof show === 'undefined' || !!show || (OCA.Files.Sidebar.file !== ''))) {
- OCA.Files.Sidebar.open(path.replace('//', '/'))
- }
- },
-
- /**
- * Replaces the current details view element with the details view
- * element of this file list.
- *
- * Each file list has its own DetailsView object, and each one has its
- * own root element, but there can be just one details view/sidebar
- * element in the document. This helper method replaces the current
- * details view/sidebar element in the document with the element from
- * the DetailsView object of this file list.
- */
- _replaceDetailsViewElementIfNeeded: function() {
- var $appSidebar = $('#app-sidebar');
- if ($appSidebar.length === 0) {
- this._detailsView.$el.insertAfter($('#app-content'));
- } else if ($appSidebar[0] !== this._detailsView.el) {
- // "replaceWith()" can not be used here, as it removes the old
- // element instead of just detaching it.
- this._detailsView.$el.insertBefore($appSidebar);
- $appSidebar.detach();
- }
- },
-
- /**
- * Event handler for when the window size changed
- */
- _onResize: function() {
- var containerWidth = this.$el.width();
- var actionsWidth = 0;
- $.each(this.$el.find('.files-controls .actions'), function(index, action) {
- actionsWidth += $(action).outerWidth();
- });
-
- this.breadcrumb._resize();
- },
-
- setGridView: function(isGridView) {
- this.$table.toggleClass('view-grid', isGridView);
- if (isGridView) {
- // If switching into grid view from list view, too few files might be displayed
- // Try rendering the next page
- this._onScroll();
- }
- },
-
- /**
- * Event handler when leaving previously hidden state
- */
- _onShow: function(e) {
- OCA.Files.App && OCA.Files.App.updateCurrentFileList(this);
- if (e.itemId === this.id) {
- this._setCurrentDir('/', false);
- }
- // Only reload if we don't navigate to a different directory
- if (typeof e.dir === 'undefined' || e.dir === this.getCurrentDirectory()) {
- this.reload();
- }
- },
-
- /**
- * Event handler for when the URL changed
- */
- _onUrlChanged: function(e) {
- if (e && _.isString(e.dir)) {
- var currentDir = this.getCurrentDirectory();
- // this._currentDirectory is NULL when fileList is first initialised
- if(this._currentDirectory && currentDir === e.dir) {
- return;
- }
- this.changeDirectory(e.dir, true, true, undefined, true);
- }
- },
-
- /**
- * Selected/deselects the given file element and updated
- * the internal selection cache.
- *
- * @param {Object} $tr single file row element
- * @param {boolean} state true to select, false to deselect
- */
- _selectFileEl: function($tr, state) {
- var $checkbox = $tr.find('td.selection>.selectCheckBox');
- var oldData = !!this._selectedFiles[$tr.data('id')];
- var data;
- $checkbox.prop('checked', state);
- $tr.toggleClass('selected', state);
- // already selected ?
- if (state === oldData) {
- return;
- }
- data = this.elementToFile($tr);
- if (state) {
- this._selectedFiles[$tr.data('id')] = data;
- this._selectionSummary.add(data);
- }
- else {
- delete this._selectedFiles[$tr.data('id')];
- this._selectionSummary.remove(data);
- }
- if (this._detailsView && !this._detailsView.$el.hasClass('disappear')) {
- // hide sidebar
- this._updateDetailsView(null);
- }
- this.$el.find('.select-all').prop('checked', this._selectionSummary.getTotal() === this.files.length);
- },
-
- _selectRange: function($tr) {
- var checked = $tr.hasClass('selected');
- var $lastTr = $(this._lastChecked);
- var lastIndex = $lastTr.index();
- var currentIndex = $tr.index();
- var $rows = this.$fileList.children('tr');
-
- // last clicked checkbox below current one ?
- if (lastIndex > currentIndex) {
- var aux = lastIndex;
- lastIndex = currentIndex;
- currentIndex = aux;
- }
-
- // auto-select everything in-between
- for (var i = lastIndex; i <= currentIndex; i++) {
- this._selectFileEl($rows.eq(i), !checked);
- }
- this._removeHalfSelection();
- this._selectionMode = 'single';
- },
-
- _selectSingle: function($tr) {
- var state = !$tr.hasClass('selected');
- this._selectFileEl($tr, state);
- },
-
- _onMouseOverCheckbox: function(e) {
- if (this._getCurrentSelectionMode() !== 'range') {
- return;
- }
- var $currentTr = $(e.target).closest('tr');
-
- var $lastTr = $(this._lastChecked);
- var lastIndex = $lastTr.index();
- var currentIndex = $currentTr.index();
- var $rows = this.$fileList.children('tr');
-
- // last clicked checkbox below current one ?
- if (lastIndex > currentIndex) {
- var aux = lastIndex;
- lastIndex = currentIndex;
- currentIndex = aux;
- }
-
- // auto-select everything in-between
- this._removeHalfSelection();
- for (var i = 0; i <= $rows.length; i++) {
- var $tr = $rows.eq(i);
- var $checkbox = $tr.find('td.selection>.selectCheckBox');
- if(lastIndex <= i && i <= currentIndex) {
- $tr.addClass('halfselected');
- $checkbox.prop('checked', true);
- }
- }
- },
-
- _removeHalfSelection: function() {
- var $rows = this.$fileList.children('tr');
- for (var i = 0; i <= $rows.length; i++) {
- var $tr = $rows.eq(i);
- $tr.removeClass('halfselected');
- var $checkbox = $tr.find('td.selection>.selectCheckBox');
- $checkbox.prop('checked', !!this._selectedFiles[$tr.data('id')]);
- }
- },
-
- /**
- * Event handler for when clicking on files to select them
- */
- _onClickFile: function(event) {
- var $tr = $(event.target).closest('tr');
- if ($tr.hasClass('dragging')) {
- return;
- }
- if (this._allowSelection && event.shiftKey) {
- event.preventDefault();
- this._selectRange($tr);
- this._lastChecked = $tr;
- this.updateSelectionSummary();
- } else if (!event.ctrlKey) {
- // clicked directly on the name
- if (!this._detailsView || $(event.target).is('.nametext, .name, .thumbnail') || $(event.target).closest('.nametext').length) {
- var filename = $tr.attr('data-file');
- var renaming = $tr.data('renaming');
- if (this._defaultFileActionsDisabled) {
- event.preventDefault();
- } else if (!renaming) {
- this.fileActions.currentFile = $tr.find('td');
- var spec = this.fileActions.getCurrentDefaultFileAction();
- if (spec && spec.action) {
- event.preventDefault();
- spec.action(filename, {
- $file: $tr,
- fileList: this,
- fileActions: this.fileActions,
- dir: $tr.attr('data-path') || this.getCurrentDirectory()
- });
- }
- // deselect row
- $(event.target).closest('a').blur();
- }
- } else {
- // Even if there is no Details action the default event
- // handler is prevented for consistency (although there
- // should always be a Details action); otherwise the link
- // would be downloaded by the browser when the user expected
- // the details to be shown.
- event.preventDefault();
- var filename = $tr.attr('data-file');
- this.fileActions.currentFile = $tr.find('td');
- var mime = this.fileActions.getCurrentMimeType();
- var type = this.fileActions.getCurrentType();
- var permissions = this.fileActions.getCurrentPermissions();
- var action = this.fileActions.get(mime, type, permissions, filename)['Details'];
- if (action) {
- action(filename, {
- $file: $tr,
- fileList: this,
- fileActions: this.fileActions,
- dir: $tr.attr('data-path') || this.getCurrentDirectory()
- });
- }
- }
- }
- },
-
- /**
- * Event handler for when clicking on a file's checkbox
- */
- _onClickFileCheckbox: function(e) {
- var $tr = $(e.target).closest('tr');
- if(this._getCurrentSelectionMode() === 'range') {
- this._selectRange($tr);
- } else {
- this._selectSingle($tr);
- }
- this._lastChecked = $tr;
- this.updateSelectionSummary();
- if (this._detailsView && !this._detailsView.$el.hasClass('disappear')) {
- // hide sidebar
- this._updateDetailsView(null);
- }
- },
-
- /**
- * Event handler for when selecting/deselecting all files
- */
- _onClickSelectAll: function(e) {
- var hiddenFiles = this.$fileList.find('tr.hidden');
- var checked = e.target.checked;
-
- if (hiddenFiles.length > 0) {
- // set indeterminate alongside checked
- e.target.indeterminate = checked;
- } else {
- e.target.indeterminate = false
- }
-
- // Select only visible checkboxes to filter out unmatched file in search
- this.$fileList.find('td.selection > .selectCheckBox:visible').prop('checked', checked)
- .closest('tr').toggleClass('selected', checked);
- // For prevents the selection of encrypted folders when clicking on the "Select all" checkbox
- this.$fileList.find('tr[data-e2eencrypted="true"]').find('td.selection > .selectCheckBox:visible').prop('checked', false).closest('tr').toggleClass('selected', false);
-
- if (checked) {
- for (var i = 0; i < this.files.length; i++) {
- // a search will automatically hide the unwanted rows
- // let's only select the matches
- var fileData = this.files[i];
- var fileRow = this.$fileList.find('tr[data-id=' + fileData.id + ']');
- // do not select already selected ones
- if (!fileRow.hasClass('hidden') && _.isUndefined(this._selectedFiles[fileData.id]) && (!fileData.isEncrypted)) {
- this._selectedFiles[fileData.id] = fileData;
- this._selectionSummary.add(fileData);
- }
- }
- } else {
- // if we have some hidden row, then we're in a search
- // Let's only deselect the visible ones
- if (hiddenFiles.length > 0) {
- var visibleFiles = this.$fileList.find('tr:not(.hidden)');
- var self = this;
- visibleFiles.each(function() {
- var id = parseInt($(this).data('id'));
- // do not deselect already deselected ones
- if (!_.isUndefined(self._selectedFiles[id])) {
- // a search will automatically hide the unwanted rows
- // let's only select the matches
- var fileData = self._selectedFiles[id];
- delete self._selectedFiles[fileData.id];
- self._selectionSummary.remove(fileData);
- }
- });
- } else {
- this._selectedFiles = {};
- this._selectionSummary.clear();
- }
- }
- this.updateSelectionSummary();
- if (this._detailsView && !this._detailsView.$el.hasClass('disappear')) {
- // hide sidebar
- this._updateDetailsView(null);
- }
- },
-
- /**
- * Event handler for when clicking on "Download" for the selected files
- */
- _onClickDownloadSelected: function(event) {
- var files;
- var self = this;
- var dir = this.getCurrentDirectory();
-
- if (this.isAllSelected() && this.getSelectedFiles().length > 1) {
- files = OC.basename(dir);
- dir = OC.dirname(dir) || '/';
- }
- else {
- files = _.pluck(this.getSelectedFiles(), 'name');
- }
-
- // don't allow a second click on the download action
- if(this.fileMultiSelectMenu.isDisabled('download')) {
- return false;
- }
-
- this.fileMultiSelectMenu.toggleLoading('download', true);
- var disableLoadingState = function(){
- self.fileMultiSelectMenu.toggleLoading('download', false);
- };
-
- if(this.getSelectedFiles().length > 1) {
- OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir, true), disableLoadingState);
- }
- else {
- var first = this.getSelectedFiles()[0];
- OCA.Files.Files.handleDownload(this.getDownloadUrl(first.name, dir, true), disableLoadingState);
- }
- event.preventDefault();
- },
-
- /**
- * Event handler for when clicking on "Move" for the selected files
- */
- _onClickCopyMoveSelected: function(event) {
- var files;
- var self = this;
-
- files = _.pluck(this.getSelectedFiles(), 'name');
-
- // don't allow a second click on the download action
- if(this.fileMultiSelectMenu.isDisabled('copyMove')) {
- return false;
- }
-
- var disableLoadingState = function(){
- self.fileMultiSelectMenu.toggleLoading('copyMove', false);
- };
-
- var actions = this.isSelectedMovable() ? OC.dialogs.FILEPICKER_TYPE_COPY_MOVE : OC.dialogs.FILEPICKER_TYPE_COPY;
- var dialogDir = self.getCurrentDirectory();
- if (typeof self.dirInfo.dirLastCopiedTo !== 'undefined') {
- dialogDir = self.dirInfo.dirLastCopiedTo;
- }
- OC.dialogs.filepicker(t('files', 'Choose target folder'), function(targetPath, type) {
- self.fileMultiSelectMenu.toggleLoading('copyMove', true);
- if (type === OC.dialogs.FILEPICKER_TYPE_COPY) {
- self.copy(files, targetPath, disableLoadingState);
- }
- if (type === OC.dialogs.FILEPICKER_TYPE_MOVE) {
- self.move(files, targetPath, disableLoadingState);
- }
- self.dirInfo.dirLastCopiedTo = targetPath;
- }, false, "httpd/unix-directory", true, actions, dialogDir);
- event.preventDefault();
- },
-
- /**
- * Event handler for when clicking on "Delete" for the selected files
- */
- _onClickDeleteSelected: function(event) {
- var files = null;
- if (!this.isAllSelected()) {
- files = _.pluck(this.getSelectedFiles(), 'name');
- }
- this.do_delete(files);
- event.preventDefault();
- },
-
- /**
- * CUSTOM CODE
- * Event handler for when clicking on "Tags" for the selected files
- */
- _onClickTagSelected: function(event) {
- var self = this;
- event.preventDefault();
- var commonTags = [];
-
- var selectedFiles = _.pluck(this.getSelectedFiles(), 'id')
- var tagCollections = [];
- var fetchTagPromises = [];
-
-
- selectedFiles.forEach(function(fileId) {
- var deferred = new $.Deferred();
- var tagCollection = new OC.SystemTags.SystemTagsMappingCollection([], {
- objectType: 'files',
- objectId: fileId});
- tagCollections.push(tagCollection);
- tagCollection.fetch({
- success: function(){
- deferred.resolve('success');
- },
- error: function() {
- deferred.resolve('failed');
- }
- })
- fetchTagPromises.push(deferred);
- });
-
- if (!self._inputView) {
- self._inputView = new OC.SystemTags.SystemTagsInputField({
- multiple: true,
- allowActions: true,
- allowCreate: true,
- isAdmin: OC.isUserAdmin(),
- });
- self._inputView.on('select', self._onSelectTag, self);
- self._inputView.on('deselect', self._onDeselectTag, self);
- self._inputView.render();
-
- // Build dom
- self.tagsTitle = $('<h3>'+ t('files', 'Please select tag(s) to add to the selection') +'</h3>');
- self.tagsSubmit = $('<button>' + t('files', 'Apply tag(s) to selection') + '</button>');
- self.tagsContainer = $('<tr id="tag_multiple_files_container"></tr>');
- self.tagsTitle.appendTo(self.tagsContainer)
- self.tagsContainer.append(self._inputView.el);
- self.tagsSubmit.appendTo(self.tagsContainer)
-
- // Inject everything
- self.$table.find('thead').append(self.tagsContainer);
-
- self.tagsSubmit.on('click', function(ev){
- self._onClickDocument(ev);
- });
- }
-
- self._inputView.$el.addClass('icon-loading');
- self.tagsContainer.show();
-
- Promise.all(fetchTagPromises).then(function() {
- //find tags which are common to all selected files
- commonTags =_.intersection.apply(null, tagCollections.map(function (tagCollection) {return tagCollection.getTagIds();}));
- self._inputView.setValues(commonTags);
- self._inputView.$el.removeClass('icon-loading');
- $(document).on('click',function(ev){
- self._onClickDocument(ev);
- });
- });
- },
-
- _onClickDocument: function(ev) {
- if(!$(ev.target).closest('#editor_container').length) {
- this._inputView.setValues([]);
- this.tagsContainer.hide();
- $(document).off('click', this._onClickDocument);
- }
-
- },
-
- /**
- * Custom code
- * Set tag for all selected files
- * @param {any} tagModel -
- * @private
- */
- _onSelectTag: function(tagModel) {
- var selectedFiles = _.pluck(this.getSelectedFiles(),'id')
- if (!_.isArray(selectedFiles)) {
- return;
- }
- selectedFiles.forEach(function(fileId) {
- $.ajax({
- url: OC.linkToRemote('dav') + '/systemtags-relations/files/' + fileId + '/'+ tagModel.attributes.id,
- type: 'PUT',
- });
- });
-
- },
- /**
- * remove tag from all selected files
- * @param {any} tagId -
- * @private
- */
- _onDeselectTag: function(tagId) {
- var selectedFiles = _.pluck(this.getSelectedFiles(),'id');
- if (!_.isArray(selectedFiles)) {
- return;
- }
- selectedFiles.forEach(function(fileId) {
- $.ajax({
- url: OC.linkToRemote('dav') + '/systemtags-relations/files/' +fileId + '/'+ tagId,
- type: 'DELETE'
- });
- });
- },
-
- /**
- * Event handler when clicking on a table header
- */
- _onClickHeader: function(e) {
- if (this.$table.hasClass('multiselect')) {
- return;
- }
- var $target = $(e.target);
- var sort;
- if (!$target.is('a')) {
- $target = $target.closest('a');
- }
- sort = $target.attr('data-sort');
- if (sort && this._allowSorting) {
- if (this._sort === sort) {
- this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc', true, true);
- }
- else {
- if ( sort === 'name' ) { //default sorting of name is opposite to size and mtime
- this.setSort(sort, 'asc', true, true);
- }
- else {
- this.setSort(sort, 'desc', true, true);
- }
- }
- }
- },
-
- /**
- * Event handler when clicking on a bread crumb
- */
- _onClickBreadCrumb: function(e) {
- // Select a crumb or a crumb in the menu
- var $el = $(e.target).closest('.crumb, .crumblist'),
- $targetDir = $el.data('dir');
-
- if ($targetDir !== undefined && e.which === 1) {
- e.preventDefault();
- this.changeDirectory($targetDir, true, true);
- }
- },
-
- /**
- * Event handler for when scrolling the list container.
- * This appends/renders the next page of entries when reaching the bottom.
- */
- _onScroll: function(e) {
- if (this.$container.scrollTop() + this.$container.height() > this.$el.height() - 300) {
- this._nextPage(true);
- }
- },
-
- /**
- * Event handler when dropping on a breadcrumb
- */
- _onDropOnBreadCrumb: function( event, ui ) {
- var self = this;
- var $target = $(event.target);
- if (!$target.is('.crumb, .crumblist')) {
- $target = $target.closest('.crumb, .crumblist');
- }
- var targetPath = $(event.target).data('dir');
- var dir = this.getCurrentDirectory();
- while (dir.substr(0,1) === '/') {//remove extra leading /'s
- dir = dir.substr(1);
- }
- dir = '/' + dir;
- if (dir.substr(-1,1) !== '/') {
- dir = dir + '/';
- }
- // do nothing if dragged on current dir
- if (targetPath === dir || targetPath + '/' === dir) {
- return;
- }
-
- var files = this.getSelectedFiles();
- if (files.length === 0) {
- // single one selected without checkbox?
- files = _.map(ui.helper.find('tr'), function(el) {
- return self.elementToFile($(el));
- });
- }
-
- var movePromise = this.move(_.pluck(files, 'name'), targetPath);
-
- // re-enable td elements to be droppable
- // sometimes the filename drop handler is still called after re-enable,
- // it seems that waiting for a short time before re-enabling solves the problem
- setTimeout(function() {
- self.$el.find('td.filename.ui-droppable').droppable('enable');
- }, 10);
-
- return movePromise;
- },
-
- /**
- * Sets a new page title
- */
- setPageTitle: function(title){
- if (title) {
- title += ' - ';
- } else {
- title = '';
- }
- title += this.appName;
- // Sets the page title with the " - Nextcloud" suffix as in templates
- window.document.title = title + ' - ' + OC.theme.title;
-
- return true;
- },
- /**
- * Returns the file info for the given file name from the internal collection.
- *
- * @param {string} fileName file name
- * @return {OCA.Files.FileInfo} file info or null if it was not found
- *
- * @since 8.2
- */
- findFile: function(fileName) {
- return _.find(this.files, function(aFile) {
- return (aFile.name === fileName);
- }) || null;
- },
- /**
- * Returns the tr element for a given file name, but only if it was already rendered.
- *
- * @param {string} fileName file name
- * @return {Object} jQuery object of the matching row
- */
- findFileEl: function(fileName){
- // use filterAttr to avoid escaping issues
- return this.$fileList.find('tr').filterAttr('data-file', fileName);
- },
-
- /**
- * Returns the file data from a given file element.
- * @param $el file tr element
- * @return file data
- */
- elementToFile: function($el){
- $el = $($el);
- var data = {
- id: parseInt($el.attr('data-id'), 10),
- name: $el.attr('data-file'),
- mimetype: $el.attr('data-mime'),
- mtime: parseInt($el.attr('data-mtime'), 10),
- type: $el.attr('data-type'),
- etag: $el.attr('data-etag'),
- quotaAvailableBytes: $el.attr('data-quota'),
- permissions: parseInt($el.attr('data-permissions'), 10),
- hasPreview: $el.attr('data-has-preview') === 'true',
- isEncrypted: $el.attr('data-e2eencrypted') === 'true'
- };
- var size = $el.attr('data-size');
- if (size) {
- data.size = parseInt(size, 10);
- }
- var icon = $el.attr('data-icon');
- if (icon) {
- data.icon = icon;
- }
- var mountType = $el.attr('data-mounttype');
- if (mountType) {
- data.mountType = mountType;
- }
- var path = $el.attr('data-path');
- if (path) {
- data.path = path;
- }
- return data;
- },
-
- /**
- * Appends the next page of files into the table
- * @param animate true to animate the new elements
- * @return array of DOM elements of the newly added files
- */
- _nextPage: function(animate) {
- var index = this.$fileList.children().length,
- count = this.pageSize(),
- hidden,
- tr,
- fileData,
- newTrs = [],
- isAllSelected = this.isAllSelected(),
- showHidden = this._filesConfig.show_hidden;
-
- if (index >= this.files.length) {
- return false;
- }
-
- while (count > 0 && index < this.files.length) {
- fileData = this.files[index];
- if (this._filter) {
- hidden = fileData.name.toLowerCase().indexOf(this._filter.toLowerCase()) === -1;
- } else {
- hidden = false;
- }
- tr = this._renderRow(fileData, {updateSummary: false, silent: true, hidden: hidden});
- this.$fileList.append(tr);
- if (isAllSelected || this._selectedFiles[fileData.id]) {
- tr.addClass('selected');
- tr.find('.selectCheckBox').prop('checked', true);
- }
- if (tr.attr('data-e2eencrypted') === 'true') {
- tr.toggleClass('selected', false);
- tr.find('td.selection > .selectCheckBox:visible').prop('checked', false);
- }
- if (animate) {
- tr.addClass('appear transparent');
- }
- newTrs.push(tr);
- index++;
- // only count visible rows
- if (showHidden || !tr.hasClass('hidden-file')) {
- count--;
- }
- }
-
- // trigger event for newly added rows
- if (newTrs.length > 0) {
- this.$fileList.trigger($.Event('fileActionsReady', {fileList: this, $files: newTrs}));
- }
-
- if (animate) {
- // defer, for animation
- window.setTimeout(function() {
- for (var i = 0; i < newTrs.length; i++ ) {
- newTrs[i].removeClass('transparent');
- }
- }, 0);
- }
-
- return newTrs;
- },
-
- /**
- * Event handler for when file actions were updated.
- * This will refresh the file actions on the list.
- */
- _onFileActionsUpdated: function() {
- var self = this;
- var $files = this.$fileList.find('tr');
- if (!$files.length) {
- return;
- }
-
- $files.each(function() {
- self.fileActions.display($(this).find('td.filename'), false, self);
- });
- this.$fileList.trigger($.Event('fileActionsReady', {fileList: this, $files: $files}));
-
- },
-
- /**
- * Register action for multiple selected files
- *
- * @param {OCA.Files.FileAction} fileAction object
- * The callback of FileAction will be called with an array of files that are currently selected
- */
- registerMultiSelectFileAction: function(fileAction) {
- if (typeof this.multiSelectMenuItems === 'undefined') {
- return;
- }
- this.multiSelectMenuItems.push(fileAction)
- this._updateMultiSelectFileActions()
- },
-
- _updateMultiSelectFileActions: function() {
- if (typeof this.multiSelectMenuItems === 'undefined') {
- return;
- }
- this.fileMultiSelectMenu = new OCA.Files.FileMultiSelectMenu(this.multiSelectMenuItems.sort(function(a, b) {
- return a.order - b.order
- }));
- this.fileMultiSelectMenu.render();
- this.$el.find('.selectedActions .filesSelectMenu').remove();
- this.$el.find('.selectedActions').append(this.fileMultiSelectMenu.$el);
- },
-
- /**
- * Sets the files to be displayed in the list.
- * This operation will re-render the list and update the summary.
- * @param filesArray array of file data (map)
- */
- setFiles: function(filesArray) {
- var self = this;
-
- // detach to make adding multiple rows faster
- this.files = filesArray;
-
- this.$fileList.empty();
-
- if (this._allowSelection) {
- // The results table, which has no selection column, checks
- // whether the main table has a selection column or not in order
- // to align its contents with those of the main table.
- this.$el.addClass('has-selection');
- }
-
- // clear "Select all" checkbox
- this.$el.find('.select-all').prop('checked', false);
-
- // Save full files list while rendering
-
- this.isEmpty = this.files.length === 0;
- this._nextPage();
-
- this.updateEmptyContent();
-
- this.fileSummary.calculate(this.files);
-
- this._selectedFiles = {};
- this._selectionSummary.clear();
- this.updateSelectionSummary();
- $(window).scrollTop(0);
-
- this.$fileList.trigger(jQuery.Event('updated'));
- _.defer(function() {
- self.$el.closest('#app-content').trigger(jQuery.Event('apprendered'));
- });
- },
-
- /**
- * Returns whether the given file info must be hidden
- *
- * @param {OC.Files.FileInfo} fileInfo file info
- *
- * @return {boolean} true if the file is a hidden file, false otherwise
- */
- _isHiddenFile: function(file) {
- return file.name && file.name.charAt(0) === '.';
- },
-
- /**
- * Returns the icon URL matching the given file info
- *
- * @param {OC.Files.FileInfo} fileInfo file info
- *
- * @return {string} icon URL
- */
- _getIconUrl: function(fileInfo) {
- var mimeType = fileInfo.mimetype || 'application/octet-stream';
- if (mimeType === 'httpd/unix-directory') {
- // use default folder icon
- if (fileInfo.mountType === 'shared' || fileInfo.mountType === 'shared-root') {
- return OC.MimeType.getIconUrl('dir-shared');
- } else if (fileInfo.mountType === 'external-root') {
- return OC.MimeType.getIconUrl('dir-external');
- } else if (fileInfo.mountType !== undefined && fileInfo.mountType !== '') {
- return OC.MimeType.getIconUrl('dir-' + fileInfo.mountType);
- } else if (fileInfo.shareTypes && (
- fileInfo.shareTypes.indexOf(OC.Share.SHARE_TYPE_LINK) > -1
- || fileInfo.shareTypes.indexOf(OC.Share.SHARE_TYPE_EMAIL) > -1)
- ) {
- return OC.MimeType.getIconUrl('dir-public')
- } else if (fileInfo.shareTypes && fileInfo.shareTypes.length > 0) {
- return OC.MimeType.getIconUrl('dir-shared')
- }
- return OC.MimeType.getIconUrl('dir');
- }
- return OC.MimeType.getIconUrl(mimeType);
- },
-
- /**
- * Creates a new table row element using the given file data.
- * @param {OC.Files.FileInfo} fileData file info attributes
- * @param options map of attributes
- * @return new tr element (not appended to the table)
- */
- _createRow: function(fileData, options) {
- var td, simpleSize, basename, extension, sizeColor,
- icon = fileData.icon || this._getIconUrl(fileData),
- name = fileData.name,
- // TODO: get rid of type, only use mime type
- type = fileData.type || 'file',
- mtime = parseInt(fileData.mtime, 10),
- mime = fileData.mimetype,
- path = fileData.path,
- dataIcon = null,
- linkUrl;
- options = options || {};
-
- if (isNaN(mtime)) {
- mtime = new Date().getTime();
- }
-
- if (type === 'dir') {
- mime = mime || 'httpd/unix-directory';
-
- if (fileData.isEncrypted) {
- icon = OC.MimeType.getIconUrl('dir-encrypted');
- dataIcon = icon;
- } else if (fileData.mountType && fileData.mountType.indexOf('external') === 0) {
- icon = OC.MimeType.getIconUrl('dir-external');
- dataIcon = icon;
- }
- }
-
- var permissions = fileData.permissions;
- if (permissions === undefined || permissions === null) {
- permissions = this.getDirectoryPermissions();
- }
-
- //containing tr
- var tr = $('<tr></tr>').attr({
- "data-id" : fileData.id,
- "data-type": type,
- "data-size": fileData.size,
- "data-file": name,
- "data-mime": mime,
- "data-mtime": mtime,
- "data-etag": fileData.etag,
- "data-quota": fileData.quotaAvailableBytes,
- "data-permissions": permissions,
- "data-has-preview": fileData.hasPreview !== false,
- "data-e2eencrypted": fileData.isEncrypted === true
- });
-
- if (dataIcon) {
- // icon override
- tr.attr('data-icon', dataIcon);
- }
-
- if (fileData.mountType) {
- // dirInfo (parent) only exist for the "real" file list
- if (this.dirInfo.id) {
- // FIXME: HACK: detect shared-root
- if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared' && this.dirInfo.mountType !== 'shared-root') {
- // if parent folder isn't share, assume the displayed folder is a share root
- fileData.mountType = 'shared-root';
- } else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external' && this.dirInfo.mountType !== 'external-root') {
- // if parent folder isn't external, assume the displayed folder is the external storage root
- fileData.mountType = 'external-root';
- }
- }
- tr.attr('data-mounttype', fileData.mountType);
- }
-
- if (!_.isUndefined(path)) {
- tr.attr('data-path', path);
- }
- else {
- path = this.getCurrentDirectory();
- }
-
- // selection td
- if (this._allowSelection) {
- td = $('<td class="selection"></td>');
-
- td.append(
- '<input id="select-' + this.id + '-' + fileData.id +
- '" type="checkbox" class="selectCheckBox checkbox" aria-describedby="innernametext_' + fileData.id + '" /><label for="select-' + this.id + '-' + fileData.id + '">' +
- '<span class="hidden-visually">' + (fileData.type === 'dir' ?
- t('files', 'Select directory "{dirName}"', {dirName: name}) :
- t('files', 'Select file "{fileName}"', {fileName: name})) + '</span>' +
- '</label>'
- );
-
- tr.append(td);
- }
-
- // filename td
- td = $('<td class="filename"></td>');
-
-
- var spec = this.fileActions.getDefaultFileAction(mime, type, permissions);
- // linkUrl
- if (mime === 'httpd/unix-directory') {
- linkUrl = this.linkTo(path + '/' + name);
- }
- else if (spec && spec.action) {
- linkUrl = this.getDefaultActionUrl(path, fileData.id);
- }
- else {
- linkUrl = this.getDownloadUrl(name, path, type === 'dir');
- }
- var linkElem = $('<a></a>').attr({
- "class": "name",
- "href": linkUrl
- });
- if (this._defaultFileActionsDisabled) {
- linkElem.addClass('disabled');
- }
-
- linkElem.append('<div class="thumbnail-wrapper"><div class="thumbnail" style="background-image:url(' + icon + ');"></div></div>');
-
- // from here work on the display name
- name = fileData.displayName || name;
-
- // show hidden files (starting with a dot) completely in gray
- if(name.indexOf('.') === 0) {
- basename = '';
- extension = name;
- // split extension from filename for non dirs
- } else if (mime !== 'httpd/unix-directory' && name.indexOf('.') !== -1) {
- basename = name.substr(0, name.lastIndexOf('.'));
- extension = name.substr(name.lastIndexOf('.'));
- } else {
- basename = name;
- extension = false;
- }
- var nameSpan=$('<span></span>').addClass('nametext')
-
- var innernameSpan = $('<span></span>').addClass('innernametext').text(basename).prop('title', basename).prop('id', `innernametext_${fileData.id}`);
-
-
- var conflictingItems = this.$fileList.find('tr[data-file="' + this._jqSelEscape(name) + '"]');
- if (conflictingItems.length !== 0) {
- if (conflictingItems.length === 1) {
- // Update the path on the first conflicting item
- var $firstConflict = $(conflictingItems[0]),
- firstConflictPath = $firstConflict.attr('data-path') + '/';
- if (firstConflictPath.charAt(0) === '/') {
- firstConflictPath = firstConflictPath.substr(1);
- }
- if (firstConflictPath && firstConflictPath !== '/') {
- $firstConflict.find('td.filename span.innernametext').prepend($('<span></span>').addClass('conflict-path').text(firstConflictPath));
- }
- }
-
- var conflictPath = path + '/';
- if (conflictPath.charAt(0) === '/') {
- conflictPath = conflictPath.substr(1);
- }
- if (path && path !== '/') {
- nameSpan.append($('<span></span>').addClass('conflict-path').text(conflictPath));
- }
- }
-
- nameSpan.append(innernameSpan);
- linkElem.append(nameSpan);
- if (extension) {
- nameSpan.append($('<span></span>').addClass('extension').text(extension));
- }
- if (fileData.extraData) {
- if (fileData.extraData.charAt(0) === '/') {
- fileData.extraData = fileData.extraData.substr(1);
- }
- nameSpan.addClass('extra-data').attr('title', fileData.extraData);
- }
- // dirs can show the number of uploaded files
- if (mime === 'httpd/unix-directory') {
- linkElem.append($('<span></span>').attr({
- 'class': 'uploadtext',
- 'currentUploads': 0
- }));
- }
- td.append(linkElem);
- tr.append(td);
-
- const enabledThemes = window.OCA?.Theming?.enabledThemes || []
- // Check enabled themes, if system default is selected check the browser
- const isDarkTheme = (enabledThemes.length === 0 || enabledThemes[0] === 'default')
- ? window.matchMedia('(prefers-color-scheme: dark)').matches
- : enabledThemes.join('').indexOf('dark') !== -1
-
- try {
- var maxContrastHex = window.getComputedStyle(document.documentElement)
- .getPropertyValue('--color-text-maxcontrast').trim()
- if (maxContrastHex.length < 4) {
- throw Error();
- }
- var maxContrast = parseInt(maxContrastHex.substring(1, 3), 16)
- } catch(error) {
- var maxContrast = isDarkTheme ? 130 : 118
- }
-
- // size column
- if (typeof(fileData.size) !== 'undefined' && fileData.size >= 0) {
- simpleSize = OC.Util.humanFileSize(parseInt(fileData.size, 10), true, false);
- // rgb(118, 118, 118) / #767676
- // min. color contrast for normal text on white background according to WCAG AA
- sizeColor = Math.round(118-Math.pow((fileData.size/(1024*1024)), 2));
-
- // ensure that the brightest color is still readable
- // min. color contrast for normal text on white background according to WCAG AA
- if (sizeColor >= maxContrast) {
- sizeColor = maxContrast;
- }
-
- if (isDarkTheme) {
- sizeColor = Math.abs(sizeColor);
- // ensure that the dimmest color is still readable
- // min. color contrast for normal text on black background according to WCAG AA
- if (sizeColor < maxContrast) {
- sizeColor = maxContrast;
- }
- }
- } else {
- simpleSize = t('files', 'Pending');
- }
-
- td = $('<td></td>').attr({
- "class": "filesize",
- "style": 'color:rgb(' + sizeColor + ',' + sizeColor + ',' + sizeColor + ')'
- }).text(simpleSize);
- tr.append(td);
-
- // date column (1000 milliseconds to seconds, 60 seconds, 60 minutes, 24 hours)
- // difference in days multiplied by 5 - brightest shade for files older than 32 days (160/5)
- var modifiedColor = Math.round(((new Date()).getTime() - mtime )/1000/60/60/24*5 );
-
- // ensure that the brightest color is still readable
- // min. color contrast for normal text on white background according to WCAG AA
- if (modifiedColor >= maxContrast) {
- modifiedColor = maxContrast;
- }
-
- if (isDarkTheme) {
- modifiedColor = Math.abs(modifiedColor);
-
- // ensure that the dimmest color is still readable
- // min. color contrast for normal text on black background according to WCAG AA
- if (modifiedColor < maxContrast) {
- modifiedColor = maxContrast;
- }
- }
-
- var formatted;
- var text;
- if (mtime > 0) {
- formatted = OC.Util.formatDate(mtime);
- text = OC.Util.relativeModifiedDate(mtime);
- } else {
- formatted = t('files', 'Unable to determine date');
- text = '?';
- }
- td = $('<td></td>').attr({ "class": "date" });
- td.append($('<span></span>').attr({
- "class": "modified live-relative-timestamp",
- "title": formatted,
- "data-timestamp": mtime,
- "style": 'color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')'
- }).text(text));
- tr.find('.filesize').text(simpleSize);
- tr.append(td);
- return tr;
- },
-
- /* escape a selector expression for jQuery */
- _jqSelEscape: function (expression) {
- if (expression) {
- return expression.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&');
- }
- return null;
- },
-
- /**
- * Adds an entry to the files array and also into the DOM
- * in a sorted manner.
- *
- * @param {OC.Files.FileInfo} fileData map of file attributes
- * @param {Object} [options] map of attributes
- * @param {boolean} [options.updateSummary] true to update the summary
- * after adding (default), false otherwise. Defaults to true.
- * @param {boolean} [options.silent] true to prevent firing events like "fileActionsReady",
- * defaults to false.
- * @param {boolean} [options.animate] true to animate the thumbnail image after load
- * defaults to true.
- * @return new tr element (not appended to the table)
- */
- add: function(fileData, options) {
- var self = this;
- var index;
- var $tr;
- var $rows;
- var $insertionPoint;
- options = _.extend({animate: true}, options || {});
-
- // there are three situations to cover:
- // 1) insertion point is visible on the current page
- // 2) insertion point is on a not visible page (visible after scrolling)
- // 3) insertion point is at the end of the list
-
- $rows = this.$fileList.children();
- index = this._findInsertionIndex(fileData);
- if (index > this.files.length) {
- index = this.files.length;
- }
- else {
- $insertionPoint = $rows.eq(index);
- }
-
- // is the insertion point visible ?
- if ($insertionPoint.length) {
- // only render if it will really be inserted
- $tr = this._renderRow(fileData, options);
- $insertionPoint.before($tr);
- }
- else {
- // if insertion point is after the last visible
- // entry, append
- if (index === $rows.length) {
- $tr = this._renderRow(fileData, options);
- this.$fileList.append($tr);
- }
- }
-
- this.isEmpty = false;
- this.files.splice(index, 0, fileData);
-
- if ($tr && options.animate) {
- $tr.addClass('appear transparent');
- window.setTimeout(function() {
- $tr.removeClass('transparent');
- self.$fileList.find('tr').removeClass('mouseOver');
- $tr.addClass('mouseOver');
- });
- }
-
- if (options.scrollTo) {
- this.scrollTo(fileData.name);
- }
-
- // defaults to true if not defined
- if (typeof(options.updateSummary) === 'undefined' || !!options.updateSummary) {
- this.fileSummary.add(fileData, true);
- this.updateEmptyContent();
- }
-
- return $tr;
- },
-
- /**
- * Creates a new row element based on the given attributes
- * and returns it.
- *
- * @param {OC.Files.FileInfo} fileData map of file attributes
- * @param {Object} [options] map of attributes
- * @param {number} [options.index] index at which to insert the element
- * @param {boolean} [options.updateSummary] true to update the summary
- * after adding (default), false otherwise. Defaults to true.
- * @param {boolean} [options.animate] true to animate the thumbnail image after load
- * defaults to true.
- * @return new tr element (not appended to the table)
- */
- _renderRow: function(fileData, options) {
- options = options || {};
- var type = fileData.type || 'file',
- mime = fileData.mimetype,
- path = fileData.path || this.getCurrentDirectory(),
- permissions = parseInt(fileData.permissions, 10) || 0;
-
- var isEndToEndEncrypted = (type === 'dir' && fileData.isEncrypted);
-
- if (!isEndToEndEncrypted && fileData.isShareMountPoint) {
- permissions = permissions | OC.PERMISSION_UPDATE;
- }
-
- if (type === 'dir') {
- mime = mime || 'httpd/unix-directory';
- }
- var tr = this._createRow(
- fileData,
- options
- );
- var filenameTd = tr.find('td.filename');
-
- // TODO: move dragging to FileActions ?
- // enable drag only for deletable files
- if (this._dragOptions && permissions & OC.PERMISSION_DELETE) {
- filenameTd.draggable(this._dragOptions);
- }
- // allow dropping on folders
- if (this._folderDropOptions && mime === 'httpd/unix-directory') {
- tr.droppable(this._folderDropOptions);
- }
-
- if (options.hidden) {
- tr.addClass('hidden');
- }
-
- if (this._isHiddenFile(fileData)) {
- tr.addClass('hidden-file');
- }
-
- // display actions
- this.fileActions.display(filenameTd, !options.silent, this);
-
- if (mime !== 'httpd/unix-directory' && fileData.hasPreview !== false) {
- var iconDiv = filenameTd.find('.thumbnail');
- // lazy load / newly inserted td ?
- // the typeof check ensures that the default value of animate is true
- if (typeof(options.animate) === 'undefined' || !!options.animate) {
- this.lazyLoadPreview({
- fileId: fileData.id,
- path: path + '/' + fileData.name,
- mime: mime,
- etag: fileData.etag,
- callback: function(url) {
- iconDiv.css('background-image', 'url("' + url + '")');
- }
- });
- }
- else {
- // set the preview URL directly
- var urlSpec = {
- file: path + '/' + fileData.name,
- c: fileData.etag
- };
- var previewUrl = this.generatePreviewUrl(urlSpec);
- previewUrl = previewUrl.replace(/\(/g, '%28').replace(/\)/g, '%29');
- iconDiv.css('background-image', 'url("' + previewUrl + '")');
- }
- }
- return tr;
- },
- /**
- * Returns the current directory
- * @method getCurrentDirectory
- * @return current directory
- */
- getCurrentDirectory: function(){
- return this._currentDirectory || '/';
- },
- /**
- * Returns the directory permissions
- * @return permission value as integer
- */
- getDirectoryPermissions: function() {
- return this && this.dirInfo && this.dirInfo.permissions ? this.dirInfo.permissions : parseInt(this.$el.find('#permissions').val(), 10);
- },
- /**
- * Changes the current directory and reload the file list.
- * @param {string} targetDir target directory (non URL encoded)
- * @param {boolean} [changeUrl=true] if the URL must not be changed (defaults to true)
- * @param {boolean} [force=false] set to true to force changing directory
- * @param {string} [fileId] optional file id, if known, to be appended in the URL
- * @param {bool} [changedThroughUrl=false] true if the dir was set through a URL change
- */
- changeDirectory: function(targetDir, changeUrl, force, fileId, changedThroughUrl) {
- var self = this;
- var currentDir = this.getCurrentDirectory();
- targetDir = targetDir || '/';
- if (!force && currentDir === targetDir) {
- return;
- }
- this._setCurrentDir(targetDir, changeUrl, fileId, changedThroughUrl);
-
- // discard finished uploads list, we'll get it through a regular reload
- this._uploads = {};
- return this.reload().then(function(success){
- if (!success) {
- self.changeDirectory(currentDir, true);
- }
- });
- },
- linkTo: function(dir) {
- return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/');
- },
-
- /**
- * @param {string} path
- * @returns {boolean}
- */
- _isValidPath: function(path) {
- var sections = path.split('/');
- for (var i = 0; i < sections.length; i++) {
- if (sections[i] === '..') {
- return false;
- }
- }
-
- return path.toLowerCase().indexOf(decodeURI('%0a')) === -1 &&
- path.toLowerCase().indexOf(decodeURI('%00')) === -1;
- },
-
- /**
- * Sets the current directory name and updates the breadcrumb.
- * @param targetDir directory to display
- * @param changeUrl true to also update the URL, false otherwise (default)
- * @param {string} [fileId] file id
- * @param {bool} changedThroughUrl true if the dir was set through a URL change
- */
- _setCurrentDir: function(targetDir, changeUrl, fileId, changedThroughUrl) {
- targetDir = targetDir.replace(/\\/g, '/');
- if (!this._isValidPath(targetDir)) {
- targetDir = '/';
- changeUrl = true;
- }
- var previousDir = this.getCurrentDirectory(),
- baseDir = OC.basename(targetDir);
-
- if (baseDir !== '') {
- this.setPageTitle(baseDir);
- }
- else {
- this.setPageTitle();
- }
-
- if (targetDir.length > 0 && targetDir[0] !== '/') {
- targetDir = '/' + targetDir;
- }
- this._currentDirectory = targetDir;
-
- if (changeUrl !== false) {
- var params = {
- dir: targetDir,
- previousDir: previousDir
- };
- if (fileId) {
- params.fileId = fileId;
- }
- params.changedThroughUrl = changedThroughUrl
- this.$el.trigger(jQuery.Event('changeDirectory', params));
- }
- this.breadcrumb.setDirectory(this.getCurrentDirectory());
- },
- /**
- * Sets the current sorting and refreshes the list
- *
- * @param sort sort attribute name
- * @param direction sort direction, one of "asc" or "desc"
- * @param update true to update the list, false otherwise (default)
- * @param persist true to save changes in the database (default)
- */
- setSort: function(sort, direction, update, persist) {
- var comparator = FileList.Comparators[sort] || FileList.Comparators.name;
- this._sort = sort;
- this._sortDirection = (direction === 'desc')?'desc':'asc';
- this._sortComparator = function(fileInfo1, fileInfo2) {
- var isFavorite = function(fileInfo) {
- return fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0;
- };
-
- if (isFavorite(fileInfo1) && !isFavorite(fileInfo2)) {
- return -1;
- } else if (!isFavorite(fileInfo1) && isFavorite(fileInfo2)) {
- return 1;
- }
-
- return direction === 'asc' ? comparator(fileInfo1, fileInfo2) : -comparator(fileInfo1, fileInfo2);
- };
-
- this.$el.find('thead th .sort-indicator')
- .removeClass(this.SORT_INDICATOR_ASC_CLASS)
- .removeClass(this.SORT_INDICATOR_DESC_CLASS)
- .toggleClass('hidden', true)
- .addClass(this.SORT_INDICATOR_DESC_CLASS);
-
- this.$el.find('thead th.column-' + sort + ' .sort-indicator')
- .removeClass(this.SORT_INDICATOR_ASC_CLASS)
- .removeClass(this.SORT_INDICATOR_DESC_CLASS)
- .toggleClass('hidden', false)
- .addClass(direction === 'desc' ? this.SORT_INDICATOR_DESC_CLASS : this.SORT_INDICATOR_ASC_CLASS);
- if (update) {
- if (this._clientSideSort) {
- this.files.sort(this._sortComparator);
- this.setFiles(this.files);
- }
- else {
- this.reload();
- }
- }
-
- if (persist && OC.getCurrentUser().uid) {
- $.post(OC.generateUrl('/apps/files/api/v1/sorting'), {
- // Compatibility with new files-to-vue API
- mode: sort === 'name' ? 'basename' : sort,
- direction: direction,
- view: 'files'
- });
- }
- },
-
- /**
- * Returns list of webdav properties to request
- */
- _getWebdavProperties: function() {
- return [].concat(this.filesClient.getPropfindProperties());
- },
-
- /**
- * Reloads the file list using ajax call
- *
- * @return ajax call object
- */
- reload: function(keepOpen) {
- this._selectedFiles = {};
- this._selectionSummary.clear();
- if (this._currentFileModel) {
- this._currentFileModel.off();
- }
- this._currentFileModel = null;
- this.$el.find('.select-all').prop('checked', false);
- this.showMask();
- this._reloadCall = this.filesClient.getFolderContents(
- this.getCurrentDirectory(), {
- includeParent: true,
- properties: this._getWebdavProperties()
- }
- );
- if (this._detailsView && !keepOpen) {
- // close sidebar
- this._updateDetailsView(null);
- }
- this._setCurrentDir(this.getCurrentDirectory(), false);
- var callBack = this.reloadCallback.bind(this);
- return this._reloadCall.then(callBack, callBack);
- },
- reloadCallback: function(status, result) {
- delete this._reloadCall;
- this.hideMask();
-
- if (status === 401) {
- // We are not authentificated, so reload the page so that we get
- // redirected to the login page while saving the current url.
- location.reload();
- }
-
- // Firewall Blocked request?
- if (status === 403) {
- // Go home
- this.changeDirectory('/');
- OC.Notification.show(t('files', 'This operation is forbidden'), {type: 'error'});
- return false;
- }
-
- // Did share service die or something else fail?
- if (status === 500) {
- // Go home
- this.changeDirectory('/');
- OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'),
- {type: 'error'}
- );
- return false;
- }
-
- if (status === 503) {
- // Go home
- if (this.getCurrentDirectory() !== '/') {
- this.changeDirectory('/');
- // TODO: read error message from exception
- OC.Notification.show(t('files', 'Storage is temporarily not available'),
- {type: 'error'}
- );
- }
- return false;
- }
-
- if (status === 400 || status === 404 || status === 405) {
- // go back home
- this.changeDirectory('/');
- return false;
- }
- // aborted ?
- if (status === 0){
- return true;
- }
-
- this.updateStorageStatistics(true);
-
- // first entry is the root
- this.dirInfo = result.shift();
- this.breadcrumb.setDirectoryInfo(this.dirInfo);
-
- if (this.dirInfo.permissions) {
- this._updateDirectoryPermissions();
- }
-
- result.sort(this._sortComparator);
- this.setFiles(result);
-
- if (this.dirInfo) {
- // Make sure the currentFileList is the current one
- // When navigating to the favorite or share with you virtual
- // folder, this is not correctly set during the initialisation
- // otherwise.
- OCA.Files.App && OCA.Files.App.updateCurrentFileList(this);
-
- var newFileId = this.dirInfo.id;
- // update fileid in URL
- var params = {
- dir: this.getCurrentDirectory()
- };
- if (newFileId) {
- params.fileId = newFileId;
- }
- this.$el.trigger(jQuery.Event('afterChangeDirectory', params));
- }
- return true;
- },
-
- updateStorageStatistics: function(force) {
- OCA.Files.Files.updateStorageStatistics(this.getCurrentDirectory(), force);
- },
-
- updateStorageQuotas: function() {
- OCA.Files.Files.updateStorageQuotas();
- },
-
- /**
- * @deprecated do not use nor override
- */
- getAjaxUrl: function(action, params) {
- return OCA.Files.Files.getAjaxUrl(action, params);
- },
-
- getDownloadUrl: function(files, dir, isDir) {
- return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory(), isDir);
- },
-
- getDefaultActionUrl: function(path, id) {
- return this.linkTo(path) + "&openfile="+id;
- },
-
- getUploadUrl: function(fileName, dir) {
- if (_.isUndefined(dir)) {
- dir = this.getCurrentDirectory();
- }
-
- var pathSections = dir.split('/');
- if (!_.isUndefined(fileName)) {
- pathSections.push(fileName);
- }
- var encodedPath = '';
- _.each(pathSections, function(section) {
- if (section !== '') {
- encodedPath += '/' + encodeURIComponent(section);
- }
- });
- return OC.linkToRemoteBase('webdav') + encodedPath;
- },
-
- /**
- * Generates a preview URL based on the URL space.
- * @param urlSpec attributes for the URL
- * @param {number} urlSpec.x width
- * @param {number} urlSpec.y height
- * @param {String} urlSpec.file path to the file
- * @return preview URL
- */
- generatePreviewUrl: function(urlSpec) {
- urlSpec = urlSpec || {};
- if (!urlSpec.x) {
- urlSpec.x = this.$table.data('preview-x') || 250;
- }
- if (!urlSpec.y) {
- urlSpec.y = this.$table.data('preview-y') || 250;
- }
- urlSpec.x *= window.devicePixelRatio;
- urlSpec.y *= window.devicePixelRatio;
- urlSpec.x = Math.ceil(urlSpec.x);
- urlSpec.y = Math.ceil(urlSpec.y);
- urlSpec.forceIcon = 0;
-
- /**
- * Images are cropped to a square by default. Append a=1 to the URL
- * if the user wants to see images with original aspect ratio.
- */
- urlSpec.a = this._filesConfig.crop_image_previews ? 0 : 1;
-
- if (typeof urlSpec.fileId !== 'undefined') {
- delete urlSpec.file;
- return OC.generateUrl('/core/preview?') + $.param(urlSpec);
- } else {
- delete urlSpec.fileId;
- return OC.generateUrl('/core/preview.png?') + $.param(urlSpec);
- }
-
- },
-
- /**
- * Lazy load a file's preview.
- *
- * @param path path of the file
- * @param mime mime type
- * @param callback callback function to call when the image was loaded
- * @param etag file etag (for caching)
- */
- lazyLoadPreview : function(options) {
- var self = this;
- var fileId = options.fileId;
- var path = options.path;
- var mime = options.mime;
- var ready = options.callback;
- var etag = options.etag;
-
- // get mime icon url
- var iconURL = OC.MimeType.getIconUrl(mime);
- var previewURL,
- urlSpec = {};
- ready(iconURL); // set mimeicon URL
-
- urlSpec.fileId = fileId;
- urlSpec.file = OCA.Files.Files.fixPath(path);
- if (options.x) {
- urlSpec.x = options.x;
- }
- if (options.y) {
- urlSpec.y = options.y;
- }
- if (options.a) {
- urlSpec.a = options.a;
- }
- if (options.mode) {
- urlSpec.mode = options.mode;
- }
-
- if (etag){
- // use etag as cache buster
- urlSpec.c = etag;
- }
-
- previewURL = self.generatePreviewUrl(urlSpec);
- previewURL = previewURL.replace(/\(/g, '%28').replace(/\)/g, '%29');
-
- // preload image to prevent delay
- // this will make the browser cache the image
- var img = new Image();
- img.onload = function(){
- // if loading the preview image failed (no preview for the mimetype) then img.width will < 5
- if (img.width > 5) {
- ready(previewURL, img);
- } else if (options.error) {
- options.error();
- }
- };
- if (options.error) {
- img.onerror = options.error;
- }
- img.src = previewURL;
- },
-
- _updateDirectoryPermissions: function() {
- var isCreatable = (this.dirInfo.permissions & OC.PERMISSION_CREATE) !== 0 && this.$el.find('#free_space').val() !== '0';
- this.$el.find('#permissions').val(this.dirInfo.permissions);
- this.$el.find('.creatable').toggleClass('hidden', !isCreatable);
- this.$el.find('.notCreatable').toggleClass('hidden', isCreatable);
- },
- /**
- * Shows/hides action buttons
- *
- * @param show true for enabling, false for disabling
- */
- showActions: function(show){
- this.$el.find('.actions').toggleClass('hidden', !show);
- if (show){
- // make sure to display according to permissions
- var permissions = this.getDirectoryPermissions();
- var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0;
- this.$el.find('.creatable').toggleClass('hidden', !isCreatable);
- this.$el.find('.notCreatable').toggleClass('hidden', isCreatable);
- // remove old style breadcrumbs (some apps might create them)
- this.$el.find('.files-controls .crumb').remove();
- // refresh breadcrumbs in case it was replaced by an app
- this.breadcrumb.render();
- }
- else{
- this.$el.find('.creatable, .notCreatable').addClass('hidden');
- }
- },
- /**
- * Enables/disables viewer mode.
- * In viewer mode, apps can embed themselves under the controls bar.
- * In viewer mode, the actions of the file list will be hidden.
- * @param show true for enabling, false for disabling
- */
- setViewerMode: function(show){
- this.showActions(!show);
- this.$el.find('.files-filestable').toggleClass('hidden', show);
- this.$el.trigger(new $.Event('changeViewerMode', {viewerModeEnabled: show}));
- },
- /**
- * Removes a file entry from the list
- * @param name name of the file to remove
- * @param {Object} [options] map of attributes
- * @param {boolean} [options.updateSummary] true to update the summary
- * after removing, false otherwise. Defaults to true.
- * @return deleted element
- */
- remove: function(name, options){
- options = options || {};
- var fileEl = this.findFileEl(name);
- var fileData = _.findWhere(this.files, {name: name});
- if (!fileData) {
- return;
- }
- var fileId = fileData.id;
- if (this._selectedFiles[fileId]) {
- // remove from selection first
- this._selectFileEl(fileEl, false);
- this.updateSelectionSummary();
- }
- if (this._selectedFiles[fileId]) {
- delete this._selectedFiles[fileId];
- this._selectionSummary.remove(fileData);
- this.updateSelectionSummary();
- }
- var index = this.files.findIndex(function(el){return el.name==name;});
- this.files.splice(index, 1);
-
- // TODO: improve performance on batch update
- this.isEmpty = !this.files.length;
- if (typeof(options.updateSummary) === 'undefined' || !!options.updateSummary) {
- this.updateEmptyContent();
- this.fileSummary.remove({type: fileData.type, size: fileData.size}, true);
- }
-
- if (!fileEl.length) {
- return null;
- }
-
- if (this._dragOptions && (fileEl.data('permissions') & OC.PERMISSION_DELETE)) {
- // file is only draggable when delete permissions are set
- fileEl.find('td.filename').draggable('destroy');
- }
- if (this._currentFileModel && this._currentFileModel.get('id') === fileId) {
- // Note: in the future we should call destroy() directly on the model
- // and the model will take care of the deletion.
- // Here we only trigger the event to notify listeners that
- // the file was removed.
- this._currentFileModel.trigger('destroy');
- this._updateDetailsView(null);
- }
- fileEl.remove();
-
- var lastIndex = this.$fileList.children().length;
- // if there are less elements visible than one page
- // but there are still pending elements in the array,
- // then directly append the next page
- if (lastIndex < this.files.length && lastIndex < this.pageSize()) {
- this._nextPage(true);
- }
-
- return fileEl;
- },
- /**
- * Finds the index of the row before which the given
- * fileData should be inserted, considering the current
- * sorting
- *
- * @param {OC.Files.FileInfo} fileData file info
- */
- _findInsertionIndex: function(fileData) {
- var index = 0;
- while (index < this.files.length && this._sortComparator(fileData, this.files[index]) > 0) {
- index++;
- }
- return index;
- },
-
- /**
- * Moves a file to a given target folder.
- *
- * @param fileNames array of file names to move
- * @param targetPath absolute target path
- * @param callback function to call when movement is finished
- * @param dir the dir path where fileNames are located (optional, will take current folder if undefined)
- */
- move: function(fileNames, targetPath, callback, dir) {
- var self = this;
-
- dir = typeof dir === 'string' ? dir : this.getCurrentDirectory();
- if (dir.charAt(dir.length - 1) !== '/') {
- dir += '/';
- }
- var target = OC.basename(targetPath);
- if (!_.isArray(fileNames)) {
- fileNames = [fileNames];
- }
-
- var moveFileFunction = function(fileName) {
- var $tr = self.findFileEl(fileName);
- self.showFileBusyState($tr, true);
- if (targetPath.charAt(targetPath.length - 1) !== '/') {
- // make sure we move the files into the target dir,
- // not overwrite it
- targetPath = targetPath + '/';
- }
- return self.filesClient.move(dir + fileName, targetPath + fileName)
- .done(function() {
- // if still viewing the same directory
- if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) {
- // recalculate folder size
- var oldFile = self.findFileEl(target);
- var newFile = self.findFileEl(fileName);
- var oldSize = oldFile.data('size');
- var newSize = oldSize + newFile.data('size');
- oldFile.data('size', newSize);
- oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize, false, false));
-
- self.remove(fileName);
- }
- })
- .fail(function(status) {
- if (status === 412) {
- // TODO: some day here we should invoke the conflict dialog
- OC.Notification.show(t('files', 'Could not move "{file}", target exists',
- {file: fileName}), {type: 'error'}
- );
- } else {
- OC.Notification.show(t('files', 'Could not move "{file}"',
- {file: fileName}), {type: 'error'}
- );
- }
- })
- .always(function() {
- self.showFileBusyState($tr, false);
- });
- };
- return this.reportOperationProgress(fileNames, moveFileFunction, callback).then(function() {
- self.updateStorageStatistics();
- self.updateStorageQuotas();
- });
- },
-
- _reflect: function (promise){
- return promise.then(function(v){ return {};}, function(e){ return {};});
- },
-
- reportOperationProgress: function (fileNames, operationFunction, callback){
- var self = this;
- self._operationProgressBar.showProgressBar(false);
- var mcSemaphore = new OCA.Files.Semaphore(5);
- var counter = 0;
- var promises = _.map(fileNames, function(arg) {
- return mcSemaphore.acquire().then(function(){
- return operationFunction(arg).always(function(){
- mcSemaphore.release();
- counter++;
- self._operationProgressBar.setProgressBarValue(100.0*counter/fileNames.length);
- });
- });
- });
-
- return Promise.all(_.map(promises, self._reflect)).then(function(){
- if (callback) {
- callback();
- }
- self._operationProgressBar.hideProgressBar();
- });
- },
-
- /**
- * Copies a file to a given target folder.
- *
- * @param fileNames array of file names to copy
- * @param targetPath absolute target path
- * @param callback to call when copy is finished with success
- * @param dir the dir path where fileNames are located (optional, will take current folder if undefined)
- */
- copy: function(fileNames, targetPath, callback, dir) {
- var self = this;
- var filesToNotify = [];
- var count = 0;
-
- dir = typeof dir === 'string' ? dir : this.getCurrentDirectory();
- if (dir.charAt(dir.length - 1) !== '/') {
- dir += '/';
- }
- var target = OC.basename(targetPath);
- if (!_.isArray(fileNames)) {
- fileNames = [fileNames];
- }
- var copyFileFunction = function(fileName) {
- var $tr = self.findFileEl(fileName);
- self.showFileBusyState($tr, true);
- if (targetPath.charAt(targetPath.length - 1) !== '/') {
- // make sure we move the files into the target dir,
- // not overwrite it
- targetPath = targetPath + '/';
- }
- var targetPathAndName = targetPath + fileName;
- if ((dir + fileName) === targetPathAndName) {
- var dotIndex = targetPathAndName.indexOf(".");
- if ( dotIndex > 1) {
- var leftPartOfName = targetPathAndName.substr(0, dotIndex);
- var fileNumber = leftPartOfName.match(/\d+/);
- // TRANSLATORS name that is appended to copied files with the same name, will be put in parenthesis and appended with a number if it is the second+ copy
- var copyNameLocalized = t('files', 'copy');
- if (isNaN(fileNumber) ) {
- fileNumber++;
- targetPathAndName = targetPathAndName.replace(/(?=\.[^.]+$)/g, " (" + copyNameLocalized + " " + fileNumber + ")");
- }
- else {
- // Check if we have other files with 'copy X' and the same name
- var maxNum = 1;
- if (self.files !== null) {
- leftPartOfName = leftPartOfName.replace("/", "");
- leftPartOfName = leftPartOfName.replace(new RegExp("\\(" + copyNameLocalized + "( \\d+)?\\)"),"");
- // find the last file with the number extension and add one to the new name
- for (var j = 0; j < self.files.length; j++) {
- var cName = self.files[j].name;
- if (cName.indexOf(leftPartOfName) > -1) {
- if (cName.indexOf("(" + copyNameLocalized + ")") > 0) {
- targetPathAndName = targetPathAndName.replace(new RegExp(" \\(" + copyNameLocalized + "\\)"),"");
- if (maxNum == 1) {
- maxNum = 2;
- }
- }
- else {
- var cFileNumber = cName.match(new RegExp("\\(" + copyNameLocalized + " (\\d+)\\)"));
- if (cFileNumber && parseInt(cFileNumber[1]) >= maxNum) {
- maxNum = parseInt(cFileNumber[1]) + 1;
- }
- }
- }
- }
- targetPathAndName = targetPathAndName.replace(new RegExp(" \\(" + copyNameLocalized + " \\d+\\)"),"");
- }
- // Create the new file name with _x at the end
- // Start from 2 per a special request of the 'standard'
- var extensionName = " (" + copyNameLocalized + " " + maxNum +")";
- if (maxNum == 1) {
- extensionName = " (" + copyNameLocalized + ")";
- }
- targetPathAndName = targetPathAndName.replace(/(?=\.[^.]+$)/g, extensionName);
- }
- }
- }
- return self.filesClient.copy(dir + fileName, targetPathAndName)
- .done(function () {
- filesToNotify.push(fileName);
-
- // if still viewing the same directory
- if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) {
- // recalculate folder size
- var oldFile = self.findFileEl(target);
- var newFile = self.findFileEl(fileName);
- var oldSize = oldFile.data('size');
- var newSize = oldSize + newFile.data('size');
- oldFile.data('size', newSize);
- oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize, false, false));
- }
- self.reload();
- })
- .fail(function(status) {
- if (status === 412) {
- // TODO: some day here we should invoke the conflict dialog
- OC.Notification.show(t('files', 'Could not copy "{file}", target exists',
- {file: fileName}), {type: 'error'}
- );
- } else {
- OC.Notification.show(t('files', 'Could not copy "{file}"',
- {file: fileName}), {type: 'error'}
- );
- }
- })
- .always(function() {
- self.showFileBusyState($tr, false);
- count++;
-
- /**
- * We only show the notifications once the last file has been copied
- */
- if (count === fileNames.length) {
- // Remove leading and ending /
- if (targetPath.slice(0, 1) === '/') {
- targetPath = targetPath.slice(1, targetPath.length);
- }
- if (targetPath.slice(-1) === '/') {
- targetPath = targetPath.slice(0, -1);
- }
-
- if (filesToNotify.length > 0) {
- // Since there's no visual indication that the files were copied, let's send some notifications !
- if (filesToNotify.length === 1) {
- OC.Notification.show(t('files', 'Copied {origin} inside {destination}',
- {
- origin: filesToNotify[0],
- destination: targetPath
- }
- ), {timeout: 10});
- } else if (filesToNotify.length > 0 && filesToNotify.length < 3) {
- OC.Notification.show(t('files', 'Copied {origin} inside {destination}',
- {
- origin: filesToNotify.join(', '),
- destination: targetPath
- }
- ), {timeout: 10});
- } else {
- OC.Notification.show(t('files', 'Copied {origin} and {nbfiles} other files inside {destination}',
- {
- origin: filesToNotify[0],
- nbfiles: filesToNotify.length - 1,
- destination: targetPath
- }
- ), {timeout: 10});
- }
- }
- }
- });
- };
- return this.reportOperationProgress(fileNames, copyFileFunction, callback).then(function() {
- self.updateStorageStatistics();
- self.updateStorageQuotas();
- });
- },
-
- openLocalClient: function(path) {
- var link = OC.linkToOCS('apps/files/api/v1', 2) + 'openlocaleditor?format=json';
-
- $.post(link, {
- path
- })
- .success(function(result) {
- var scheme = 'nc://';
- var command = 'open';
- var uid = OC.getCurrentUser().uid;
- var url = scheme + command + '/' + uid + '@' + window.location.host + OC.encodePath(path);
- url += '?token=' + result.ocs.data.token;
-
- window.location.href = url;
- })
- .fail(function() {
- OC.Notification.show(t('files', 'Failed to redirect to client'))
- })
- },
-
- /**
- * Updates the given row with the given file info
- *
- * @param {Object} $tr row element
- * @param {OCA.Files.FileInfo} fileInfo file info
- * @param {Object} options options
- *
- * @return {Object} new row element
- */
- updateRow: function($tr, fileInfo, options) {
- this.files.splice($tr.index(), 1);
- $tr.remove();
- options = _.extend({silent: true}, options);
- options = _.extend(options, {updateSummary: false});
- $tr = this.add(fileInfo, options);
- this.$fileList.trigger($.Event('fileActionsReady', {fileList: this, $files: $tr}));
- return $tr;
- },
-
- /**
- * Triggers file rename input field for the given file name.
- * If the user enters a new name, the file will be renamed.
- *
- * @param oldName file name of the file to rename
- */
- rename: function(oldName) {
- var self = this;
- var tr, td, input, form;
- tr = this.findFileEl(oldName);
- var oldFileInfo = this.files[tr.index()];
- tr.data('renaming',true);
- td = tr.children('td.filename');
- input = $('<input type="text" class="filename"/>').val(oldName);
- form = $('<form></form>');
- form.append(input);
- td.children('a.name').children(':not(.thumbnail-wrapper)').hide();
- td.append(form);
- input.focus();
- //preselect input
- var len = input.val().lastIndexOf('.');
- if ( len === -1 ||
- tr.data('type') === 'dir' ) {
- len = input.val().length;
- }
- input.selectRange(0, len);
- var checkInput = function () {
- var filename = input.val();
- if (filename !== oldName) {
- // Files.isFileNameValid(filename) throws an exception itself
- OCA.Files.Files.isFileNameValid(filename);
- if (self.inList(filename)) {
- throw t('files', '{newName} already exists', {newName: filename}, undefined, {
- escape: false
- });
- }
- }
- return true;
- };
-
- function restore() {
- tr.data('renaming',false);
- form.remove();
- td.children('a.name').children(':not(.thumbnail-wrapper)').show();
- }
-
- function updateInList(fileInfo) {
- self.updateRow(tr, fileInfo);
- self._updateDetailsView(fileInfo.name, false);
- }
-
- // TODO: too many nested blocks, move parts into functions
- form.submit(function(event) {
- event.stopPropagation();
- event.preventDefault();
- if (input.hasClass('error')) {
- return;
- }
-
- try {
- var newName = input.val().trim();
- form.remove();
-
- if (newName !== oldName) {
- checkInput();
- // mark as loading (temp element)
- self.showFileBusyState(tr, true);
- tr.attr('data-file', newName);
- var basename = newName;
- if (newName.indexOf('.') > 0 && tr.data('type') !== 'dir') {
- basename = newName.substr(0, newName.lastIndexOf('.'));
- }
- td.find('a.name span.nametext').text(basename);
- td.children('a.name').children(':not(.thumbnail-wrapper)').show();
-
- var path = tr.attr('data-path') || self.getCurrentDirectory();
- self.filesClient.move(OC.joinPaths(path, oldName), OC.joinPaths(path, newName))
- .done(function() {
- oldFileInfo.name = newName;
- updateInList(oldFileInfo);
- })
- .fail(function(status) {
- // TODO: 409 means current folder does not exist, redirect ?
- if (status === 404) {
- // source not found, so remove it from the list
- OC.Notification.show(t('files', 'Could not rename "{fileName}", it does not exist any more',
- {fileName: oldName}), {timeout: 7, type: 'error'}
- );
-
- self.remove(newName, {updateSummary: true});
- return;
- } else if (status === 412) {
- // target exists
- OC.Notification.show(
- t('files', 'The name "{targetName}" is already used in the folder "{dir}". Please choose a different name.',
- {
- targetName: newName,
- dir: self.getCurrentDirectory(),
- }),
- {
- type: 'error'
- }
- );
- } else {
- // restore the item to its previous state
- OC.Notification.show(t('files', 'Could not rename "{fileName}"',
- {fileName: oldName}), {type: 'error'}
- );
- }
- updateInList(oldFileInfo);
- });
- } else {
- // add back the old file info when cancelled
- self.files.splice(tr.index(), 1);
- tr.remove();
- tr = self.add(oldFileInfo, {updateSummary: false, silent: true});
- self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
- }
- } catch (error) {
- input.attr('title', error);
- input.addClass('error');
- }
- return false;
- });
- input.keyup(function(event) {
- // verify filename on typing
- try {
- checkInput();
- input.removeClass('error');
- } catch (error) {
- input.attr('title', error);
- input.addClass('error');
- }
- if (event.keyCode === 27) {
- restore();
- }
- });
- input.click(function(event) {
- event.stopPropagation();
- event.preventDefault();
- });
- input.blur(function() {
- if(input.hasClass('error')) {
- restore();
- } else {
- form.trigger('submit');
- }
- });
- },
-
- /**
- * Create an empty file inside the current directory.
- *
- * @param {string} name name of the file
- *
- * @return {Promise} promise that will be resolved after the
- * file was created
- *
- * @since 8.2
- */
- createFile: function(name, options) {
- var self = this;
- var deferred = $.Deferred();
- var promise = deferred.promise();
-
- OCA.Files.Files.isFileNameValid(name);
-
- if (this.lastAction) {
- this.lastAction();
- }
-
- name = this.getUniqueName(name);
- var targetPath = this.getCurrentDirectory() + '/' + name;
-
- self.filesClient.putFileContents(
- targetPath,
- ' ', // dont create empty files which fails on some storage backends
- {
- contentType: 'text/plain',
- overwrite: true
- }
- )
- .done(function() {
- // TODO: error handling / conflicts
- options = _.extend({scrollTo: true}, options || {});
- self.addAndFetchFileInfo(targetPath, '', options).then(function(status, data) {
- deferred.resolve(status, data);
- }, function() {
- OC.Notification.show(t('files', 'Could not create file "{file}"',
- {file: name}), {type: 'error'}
- );
- });
- })
- .fail(function(status) {
- if (status === 412) {
- OC.Notification.show(t('files', 'Could not create file "{file}" because it already exists',
- {file: name}), {type: 'error'}
- );
- } else {
- OC.Notification.show(t('files', 'Could not create file "{file}"',
- {file: name}), {type: 'error'}
- );
- }
- deferred.reject(status);
- });
-
- return promise;
- },
-
- /**
- * Create a directory inside the current directory.
- *
- * @param {string} name name of the directory
- *
- * @return {Promise} promise that will be resolved after the
- * directory was created
- *
- * @since 8.2
- */
- createDirectory: function(name) {
- var self = this;
- var deferred = $.Deferred();
- var promise = deferred.promise();
-
- OCA.Files.Files.isFileNameValid(name);
-
- if (this.lastAction) {
- this.lastAction();
- }
-
- name = this.getUniqueName(name);
- var targetPath = this.getCurrentDirectory() + '/' + name;
-
- this.filesClient.createDirectory(targetPath)
- .done(function() {
- self.addAndFetchFileInfo(targetPath, '', {scrollTo:true}).then(function(status, data) {
- deferred.resolve(status, data);
- }, function() {
- OC.Notification.show(t('files', 'Could not create folder "{dir}"',
- {dir: name}), {type: 'error'}
- );
- });
- })
- .fail(function(createStatus) {
- // method not allowed, folder might exist already
- if (createStatus === 405) {
- // add it to the list, for completeness
- self.addAndFetchFileInfo(targetPath, '', {scrollTo:true})
- .done(function(status, data) {
- OC.Notification.show(t('files', 'Could not create folder "{dir}" because it already exists',
- {dir: name}), {type: 'error'}
- );
- // still consider a failure
- deferred.reject(createStatus, data);
- })
- .fail(function() {
- OC.Notification.show(t('files', 'Could not create folder "{dir}"',
- {dir: name}), {type: 'error'}
- );
- deferred.reject(status);
- });
- } else {
- OC.Notification.show(t('files', 'Could not create folder "{dir}"',
- {dir: name}), {type: 'error'}
- );
- deferred.reject(createStatus);
- }
- });
-
- return promise;
- },
-
- /**
- * Add file into the list by fetching its information from the server first.
- *
- * If the given directory does not match the current directory, nothing will
- * be fetched.
- *
- * @param {String} fileName file name
- * @param {String} [dir] optional directory, defaults to the current one
- * @param {Object} options same options as #add
- * @return {Promise} promise that resolves with the file info, or an
- * already resolved Promise if no info was fetched. The promise rejects
- * if the file was not found or an error occurred.
- *
- * @since 9.0
- */
- addAndFetchFileInfo: function(fileName, dir, options) {
- var self = this;
- var deferred = $.Deferred();
- if (_.isUndefined(dir)) {
- dir = this.getCurrentDirectory();
- } else {
- dir = dir || '/';
- }
-
- var targetPath = OC.joinPaths(dir, fileName);
-
- if ((OC.dirname(targetPath) || '/') !== this.getCurrentDirectory()) {
- // no need to fetch information
- deferred.resolve();
- return deferred.promise();
- }
-
- var addOptions = _.extend({
- animate: true,
- scrollTo: false
- }, options || {});
-
- this.filesClient.getFileInfo(targetPath, {
- properties: this._getWebdavProperties()
- })
- .then(function(status, data) {
- // remove first to avoid duplicates
- self.remove(data.name);
- self.add(data, addOptions);
- deferred.resolve(status, data);
- })
- .fail(function(status) {
- OCP.Toast.error(
- t('files', 'Could not fetch file details "{file}"', { file: fileName })
- );
- deferred.reject(status);
- });
-
- return deferred.promise();
- },
-
- /**
- * Returns whether the given file name exists in the list
- *
- * @param {string} file file name
- *
- * @return {boolean} true if the file exists in the list, false otherwise
- */
- inList:function(file) {
- return this.findFile(file);
- },
-
- /**
- * Shows busy state on a given file row or multiple
- *
- * @param {string|Array.<string>} files file name or array of file names
- * @param {boolean} [busy=true] busy state, true for busy, false to remove busy state
- *
- * @since 8.2
- */
- showFileBusyState: function(files, state) {
- var self = this;
- if (!_.isArray(files) && !files.is) {
- files = [files];
- }
-
- if (_.isUndefined(state)) {
- state = true;
- }
-
- _.each(files, function(fileName) {
- // jquery element already ?
- var $tr;
- if (_.isString(fileName)) {
- $tr = self.findFileEl(fileName);
- } else {
- $tr = $(fileName);
- }
-
- var $thumbEl = $tr.find('.thumbnail');
- $tr.toggleClass('busy', state);
-
- if (state) {
- $thumbEl.parent().addClass('icon-loading-small');
- } else {
- $thumbEl.parent().removeClass('icon-loading-small');
- }
- });
- },
-
- /**
- * Delete the given files from the given dir
- * @param files file names list (without path)
- * @param dir directory in which to delete the files, defaults to the current
- * directory
- */
- do_delete:function(files, dir) {
- var self = this;
- if (files && files.substr) {
- files=[files];
- }
- if (!files) {
- // delete all files in directory
- files = _.pluck(this.files, 'name');
- }
- // Finish any existing actions
- if (this.lastAction) {
- this.lastAction();
- }
-
- dir = dir || this.getCurrentDirectory();
-
- var removeFunction = function(fileName) {
- var $tr = self.findFileEl(fileName);
- self.showFileBusyState($tr, true);
- return self.filesClient.remove(dir + '/' + fileName)
- .done(function() {
- if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) {
- self.remove(fileName);
- }
- })
- .fail(function(status) {
- if (status === 404) {
- // the file already did not exist, remove it from the list
- if (OC.joinPaths(self.getCurrentDirectory(), '/') === OC.joinPaths(dir, '/')) {
- self.remove(fileName);
- }
- } else {
- // only reset the spinner for that one file
- OC.Notification.show(t('files', 'Error deleting file "{fileName}".',
- {fileName: fileName}), {type: 'error'}
- );
- }
- })
- .always(function() {
- self.showFileBusyState($tr, false);
- });
- };
- return this.reportOperationProgress(files, removeFunction).then(function(){
- self.updateStorageStatistics();
- self.updateStorageQuotas();
- });
- },
-
- /**
- * Creates the file summary section
- */
- _createSummary: function() {
- var $tr = $('<tr class="summary"></tr>');
-
- if (this._allowSelection) {
- // Dummy column for selection, as all rows must have the same
- // number of columns.
- $tr.append('<td></td>');
- }
-
- this.$el.find('tfoot').append($tr);
-
- return new OCA.Files.FileSummary($tr, { config: this._filesConfig });
- },
- updateEmptyContent: function() {
- var permissions = this.getDirectoryPermissions();
- var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0;
- this.$el.find('.emptyfilelist.emptycontent').toggleClass('hidden', !this.isEmpty);
- this.$el.find('.emptyfilelist.emptycontent').toggleClass('hidden', !this.isEmpty);
- this.$el.find('.emptyfilelist.emptycontent .uploadmessage').toggleClass('hidden', !isCreatable || !this.isEmpty);
- this.$el.find('.files-filestable').toggleClass('hidden', this.isEmpty);
- this.$el.find('.files-filestable thead th').toggleClass('hidden', this.isEmpty);
- },
- /**
- * Shows the loading mask.
- *
- * @see OCA.Files.FileList#hideMask
- */
- showMask: function() {
- // in case one was shown before
- var $mask = this.$el.find('.mask');
- if ($mask.exists()) {
- return;
- }
-
- this.$table.addClass('hidden');
- this.$el.find('.emptyfilelist.emptycontent').addClass('hidden');
-
- $mask = $('<div class="mask transparent icon-loading"></div>');
-
- this.$el.append($mask);
-
- $mask.removeClass('transparent');
- },
- /**
- * Hide the loading mask.
- * @see OCA.Files.FileList#showMask
- */
- hideMask: function() {
- this.$el.find('.mask').remove();
- this.$table.removeClass('hidden');
- },
- scrollTo:function(file) {
- if (!_.isArray(file)) {
- file = [file];
- }
- if (file.length === 1) {
- _.defer(function() {
- if (document.documentElement.clientWidth > 1024) {
- this.showDetailsView(file[0]);
- }
- }.bind(this));
- }
- this.highlightFiles(file, function($tr) {
- $tr.addClass('searchresult');
- $tr.one('hover', function() {
- $tr.removeClass('searchresult');
- });
- });
- },
- /**
- * @deprecated use setFilter(filter)
- */
- filter:function(query) {
- this.setFilter('');
- },
- /**
- * @deprecated use setFilter('')
- */
- unfilter:function() {
- this.setFilter('');
- },
- /**
- * hide files matching the given filter
- * @param {any} filter -
- */
- setFilter:function(filter) {
- var total = 0;
- if (this._filter === filter) {
- return;
- }
- this._filter = filter;
- this.fileSummary.setFilter(filter, this.files);
- total = this.fileSummary.getTotal();
- if (!this.$el.find('.mask').exists()) {
- this.hideIrrelevantUIWhenNoFilesMatch();
- }
-
- var visibleCount = 0;
- filter = filter.toLowerCase();
-
- function filterRows(tr) {
- var $e = $(tr);
- if ($e.data('file').toString().toLowerCase().indexOf(filter) === -1) {
- $e.addClass('hidden');
- } else {
- visibleCount++;
- $e.removeClass('hidden');
- }
- }
-
- var $trs = this.$fileList.find('tr');
- do {
- _.each($trs, filterRows);
- if (visibleCount < total) {
- $trs = this._nextPage(false);
- }
- } while (visibleCount < total && $trs.length > 0);
-
- this.$container.trigger('scroll');
- },
- hideIrrelevantUIWhenNoFilesMatch:function() {
- if (this._filter && this.fileSummary.summary.totalDirs + this.fileSummary.summary.totalFiles === 0) {
- this.$el.find('.files-filestable thead th').addClass('hidden');
- this.$el.find('.emptyfilelist.emptycontent').addClass('hidden');
- $('#searchresults').addClass('filter-empty');
- $('#searchresults .emptycontent').addClass('emptycontent-search');
- if ( $('#searchresults').length === 0 || $('#searchresults').hasClass('hidden') ) {
- var error;
- if (this._filter.length > 2) {
- error = t('files', 'No search results in other folders for {tag}{filter}{endtag}', {filter:this._filter});
- } else {
- error = t('files', 'Enter more than two characters to search in other folders');
- }
- this.$el.find('.nofilterresults').removeClass('hidden').
- find('p').html(error.replace('{tag}', '<strong>').replace('{endtag}', '</strong>'));
- }
- } else {
- $('#searchresults').removeClass('filter-empty');
- $('#searchresults .emptycontent').removeClass('emptycontent-search');
- this.$el.find('.files-filestable thead th').toggleClass('hidden', this.isEmpty);
- if (!this.$el.find('.mask').exists()) {
- this.$el.find('.emptyfilelist.emptycontent').toggleClass('hidden', !this.isEmpty);
- }
- this.$el.find('.nofilterresults').addClass('hidden');
- }
- },
- /**
- * get the current filter
- * @param {any} filter -
- */
- getFilter:function(filter) {
- return this._filter;
- },
-
- /**
- * Update UI based on the current selection
- */
- updateSelectionSummary: function() {
- var summary = this._selectionSummary.summary;
- var selection;
-
- var showHidden = !!this._filesConfig.show_hidden;
- if (summary.totalFiles === 0 && summary.totalDirs === 0) {
- this.$el.find('.column-name a.name>span:first').text(t('files','Name'));
- this.$el.find('.column-size a>span:first').text(t('files','Size'));
- this.$el.find('.column-mtime a>span:first').text(t('files','Modified'));
- this.$el.find('table').removeClass('multiselect');
- this.$el.find('.selectedActions').addClass('hidden');
- }
- else {
- this.$el.find('.selectedActions').removeClass('hidden');
- this.$el.find('.column-size a>span:first').text(OC.Util.humanFileSize(summary.totalSize, false, false));
-
- var directoryInfo = n('files', '%n folder', '%n folders', summary.totalDirs);
- var fileInfo = n('files', '%n file', '%n files', summary.totalFiles);
-
- if (summary.totalDirs > 0 && summary.totalFiles > 0) {
- var selectionVars = {
- dirs: directoryInfo,
- files: fileInfo
- };
- selection = t('files', '{dirs} and {files}', selectionVars);
- } else if (summary.totalDirs > 0) {
- selection = directoryInfo;
- } else {
- selection = fileInfo;
- }
-
- if (!showHidden && summary.totalHidden > 0) {
- var hiddenInfo = n('files', 'including %n hidden', 'including %n hidden', summary.totalHidden);
- selection += ' (' + hiddenInfo + ')';
- }
-
- this.$el.find('.column-name a.name>span:first').text(selection);
- this.$el.find('.column-mtime a>span:first').text('');
- this.$el.find('table').addClass('multiselect');
-
- if (this.fileMultiSelectMenu) {
- this.fileMultiSelectMenu.toggleItemVisibility('download', this.isSelectedDownloadable());
- this.fileMultiSelectMenu.toggleItemVisibility('delete', this.isSelectedDeletable());
- this.fileMultiSelectMenu.toggleItemVisibility('copyMove', this.isSelectedCopiable());
- if (this.isSelectedCopiable()) {
- if (this.isSelectedMovable()) {
- this.fileMultiSelectMenu.updateItemText('copyMove', t('files', 'Move or copy'));
- } else {
- this.fileMultiSelectMenu.updateItemText('copyMove', t('files', 'Copy'));
- }
- } else {
- this.fileMultiSelectMenu.toggleItemVisibility('copyMove', false);
- }
- }
- }
- },
-
- /**
- * Check whether all selected files are copiable
- */
- isSelectedCopiable: function() {
- return _.reduce(this.getSelectedFiles(), function(copiable, file) {
- var requiredPermission = $('#isPublic').val() ? OC.PERMISSION_UPDATE : OC.PERMISSION_READ;
- return copiable && (file.permissions & requiredPermission);
- }, true);
- },
-
- /**
- * Check whether all selected files are movable
- */
- isSelectedMovable: function() {
- return _.reduce(this.getSelectedFiles(), function(movable, file) {
- return movable && (file.permissions & OC.PERMISSION_UPDATE);
- }, true);
- },
-
- /**
- * Check whether all selected files are downloadable
- */
- isSelectedDownloadable: function() {
- return _.reduce(this.getSelectedFiles(), function(downloadable, file) {
- return downloadable && (file.permissions & OC.PERMISSION_READ);
- }, true);
- },
-
- /**
- * Check whether all selected files are deletable
- */
- isSelectedDeletable: function() {
- return _.reduce(this.getSelectedFiles(), function(deletable, file) {
- return deletable && (file.permissions & OC.PERMISSION_DELETE);
- }, true);
- },
-
- /**
- * Are all files selected?
- *
- * @returns {Boolean} all files are selected
- */
- isAllSelected: function() {
- var checkbox = this.$el.find('.select-all')
- var checked = checkbox.prop('checked')
- var indeterminate = checkbox.prop('indeterminate')
- return checked && !indeterminate;
- },
-
- /**
- * Returns the file info of the selected files
- *
- * @return array of file names
- */
- getSelectedFiles: function() {
- return _.values(this._selectedFiles);
- },
-
- getUniqueName: function(name) {
- if (this.findFileEl(name).exists()) {
- var numMatch;
- var parts=name.split('.');
- var extension = "";
- if (parts.length > 1) {
- extension=parts.pop();
- }
- var base=parts.join('.');
- numMatch=base.match(/\((\d+)\)/);
- var num=2;
- if (numMatch && numMatch.length>0) {
- num=parseInt(numMatch[numMatch.length-1], 10)+1;
- base=base.split('(');
- base.pop();
- base=$.trim(base.join('('));
- }
- name=base+' ('+num+')';
- if (extension) {
- name = name+'.'+extension;
- }
- // FIXME: ugly recursion
- return this.getUniqueName(name);
- }
- return name;
- },
-
- /**
- * Shows a "permission denied" notification
- */
- _showPermissionDeniedNotification: function() {
- var message = t('files', 'You do not have permission to upload or create files here');
- OC.Notification.show(message, {type: 'error'});
- },
-
- /**
- * Setup file upload events related to the file-upload plugin
- *
- * @param {OC.Uploader} uploader
- */
- setupUploadEvents: function(uploader) {
- var self = this;
-
- self._uploads = {};
-
- // detect the progress bar resize
- uploader.on('resized', this._onResize);
-
- uploader.on('drop', function(e, data) {
- self._uploader.log('filelist handle fileuploaddrop', e, data);
-
- if (self.$el.hasClass('hidden')) {
- // do not upload to invisible lists
- e.preventDefault();
- return false;
- }
-
- var dropTarget = $(e.delegatedEvent.target);
-
- // check if dropped inside this container and not another one
- if (dropTarget.length
- && !self.$el.is(dropTarget) // dropped on list directly
- && !self.$el.has(dropTarget).length // dropped inside list
- && !dropTarget.is(self.$container) // dropped on main container
- && !self.$el.parent().is(dropTarget) // drop on the parent container (#app-content) since the main container might not have the full height
- ) {
- e.preventDefault();
- return false;
- }
-
- // find the closest tr or crumb to use as target
- dropTarget = dropTarget.closest('tr, .crumb');
-
- // if dropping on tr or crumb, drag&drop upload to folder
- if (dropTarget && (dropTarget.data('type') === 'dir' ||
- dropTarget.hasClass('crumb'))) {
-
- // remember as context
- data.context = dropTarget;
-
- // if permissions are specified, only allow if create permission is there
- var permissions = dropTarget.data('permissions');
- if (!_.isUndefined(permissions) && (permissions & OC.PERMISSION_CREATE) === 0) {
- self._showPermissionDeniedNotification();
- return false;
- }
- var dir = dropTarget.data('file');
- // if from file list, need to prepend parent dir
- if (dir) {
- var parentDir = self.getCurrentDirectory();
- if (parentDir[parentDir.length - 1] !== '/') {
- parentDir += '/';
- }
- dir = parentDir + dir;
- }
- else{
- // read full path from crumb
- dir = dropTarget.data('dir') || '/';
- }
-
- // add target dir
- data.targetDir = dir;
- } else {
- // cancel uploads to current dir if no permission
- var isCreatable = (self.getDirectoryPermissions() & OC.PERMISSION_CREATE) !== 0;
- if (!isCreatable) {
- self._showPermissionDeniedNotification();
- e.stopPropagation();
- return false;
- }
-
- // we are dropping somewhere inside the file list, which will
- // upload the file to the current directory
- data.targetDir = self.getCurrentDirectory();
- }
- });
- uploader.on('add', function(e, data) {
- self._uploader.log('filelist handle fileuploadadd', e, data);
-
- // add ui visualization to existing folder
- if (data.context && data.context.data('type') === 'dir') {
- // add to existing folder
-
- // update upload counter ui
- var uploadText = data.context.find('.uploadtext');
- var currentUploads = parseInt(uploadText.attr('currentUploads'), 10);
- currentUploads += 1;
- uploadText.attr('currentUploads', currentUploads);
-
- var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads);
- if (currentUploads === 1) {
- self.showFileBusyState(uploadText.closest('tr'), true);
- uploadText.text(translatedText);
- uploadText.show();
- } else {
- uploadText.text(translatedText);
- }
- }
-
- if (!data.targetDir) {
- data.targetDir = self.getCurrentDirectory();
- }
-
- });
- /*
- * when file upload done successfully add row to filelist
- * update counter when uploading to sub folder
- */
- uploader.on('done', function(e, upload) {
- var data = upload.data;
- self._uploader.log('filelist handle fileuploaddone', e, data);
-
- var status = data.jqXHR.status;
- if (status < 200 || status >= 300) {
- // error was handled in OC.Uploads already
- return;
- }
-
- var fileName = upload.getFileName();
- var fetchInfoPromise = self.addAndFetchFileInfo(fileName, upload.getFullPath());
- if (!self._uploads) {
- self._uploads = {};
- }
- if (OC.isSamePath(OC.dirname(upload.getFullPath() + '/'), self.getCurrentDirectory())) {
- self._uploads[fileName] = fetchInfoPromise;
- }
-
- var uploadText = self.$fileList.find('tr .uploadtext');
- self.showFileBusyState(uploadText.closest('tr'), false);
- uploadText.fadeOut();
- uploadText.attr('currentUploads', 0);
-
- self.updateStorageQuotas();
- });
- uploader.on('createdfolder', function(fullPath) {
- self.addAndFetchFileInfo(OC.basename(fullPath), OC.dirname(fullPath));
- });
- uploader.on('stop', function() {
- self._uploader.log('filelist handle fileuploadstop');
-
- // prepare list of uploaded file names in the current directory
- // and discard the other ones
- var promises = _.values(self._uploads);
- var fileNames = _.keys(self._uploads);
- self._uploads = [];
-
- // as soon as all info is fetched
- $.when.apply($, promises).then(function() {
- // highlight uploaded files
- self.highlightFiles(fileNames);
- self.updateStorageStatistics();
- });
-
- var uploadText = self.$fileList.find('tr .uploadtext');
- self.showFileBusyState(uploadText.closest('tr'), false);
- uploadText.fadeOut();
- uploadText.attr('currentUploads', 0);
- });
- uploader.on('fail', function(e, data) {
- self._uploader.log('filelist handle fileuploadfail', e, data);
- self._uploads = [];
-
- //if user pressed cancel hide upload chrome
- //cleanup uploading to a dir
- var uploadText = self.$fileList.find('tr .uploadtext');
- self.showFileBusyState(uploadText.closest('tr'), false);
- uploadText.fadeOut();
- uploadText.attr('currentUploads', 0);
- self.updateStorageStatistics();
- });
-
- },
-
- /**
- * Scroll to the last file of the given list
- * Highlight the list of files
- * @param files array of filenames,
- * @param {Function} [highlightFunction] optional function
- * to be called after the scrolling is finished
- */
- highlightFiles: function(files, highlightFunction) {
- // Detection of the uploaded element
- var filename = files[files.length - 1];
- var $fileRow = this.findFileEl(filename);
-
- while(!$fileRow.exists() && this._nextPage(false) !== false) { // Checking element existence
- $fileRow = this.findFileEl(filename);
- }
-
- if (!$fileRow.exists()) { // Element not present in the file list
- return;
- }
-
- var currentOffset = this.$container.scrollTop();
- var additionalOffset = this.$el.find(".files-controls").height()+this.$el.find(".files-controls").offset().top;
-
- // Animation
- var _this = this;
- var $scrollContainer = this.$container;
- if ($scrollContainer[0] === window) {
- // need to use "html" to animate scrolling
- // when the scroll container is the window
- $scrollContainer = $('html');
- }
- $scrollContainer.animate({
- // Scrolling to the top of the new element
- scrollTop: currentOffset + $fileRow.offset().top - $fileRow.height() * 2 - additionalOffset
- }, {
- duration: 500,
- complete: function() {
- // Highlighting function
- var highlightRow = highlightFunction;
-
- if (!highlightRow) {
- highlightRow = function($fileRow) {
- $fileRow.addClass("highlightUploaded");
- setTimeout(function() {
- $fileRow.removeClass("highlightUploaded");
- }, 2500);
- };
- }
-
- // Loop over uploaded files
- for(var i=0; i<files.length; i++) {
- var $fileRow = _this.findFileEl(files[i]);
-
- if($fileRow.length !== 0) { // Checking element existence
- highlightRow($fileRow);
- }
- }
-
- }
- });
- },
-
- _renderNewButton: function() {
- // if an upload button (legacy) already exists or no actions container exist, skip
- var $actionsContainer = this.$el.find('.files-controls .actions');
- if (!$actionsContainer.length || this.$el.find('.button.upload').length) {
- return;
- }
- var $newButton = $(OCA.Files.Templates['template_addbutton']({
- addText: t('files', 'New'),
- addLongText: t('files', 'New file/folder menu'),
- iconClass: 'icon-add',
- }));
-
- $actionsContainer.prepend($newButton);
- $newButton.attr('aria-expanded', 'false');
- $newButton.click(_.bind(this._onClickNewButton, this));
- this._newButton = $newButton;
- },
-
- _onClickNewButton: function(event) {
- var $target = $(event.target);
- if (!$target.hasClass('.button')) {
- $target = $target.closest('.button');
- }
- $target.attr('aria-expanded', 'true');
- event.preventDefault();
- if ($target.hasClass('disabled')) {
- return false;
- }
- if (!this._newFileMenu) {
- this._newFileMenu = new OCA.Files.NewFileMenu({
- fileList: this
- });
- this.$el.find('.files-controls .actions').append(this._newFileMenu.$el);
- }
- this._newFileMenu.showAt($target);
-
- return false;
- },
-
- /**
- * Register a tab view to be added to all views
- */
- registerTabView: function(tabView) {
- OC.debug && console.warn('registerTabView is deprecated! It will be removed in nextcloud 20.');
- const enabled = tabView.canDisplay || undefined
- if (tabView.id) {
- OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
- id: tabView.id,
- name: tabView.getLabel(),
- icon: tabView.getIcon(),
- mount: function(el, fileInfo) {
- tabView.setFileInfo(new OCA.Files.FileInfoModel(fileInfo))
- el.appendChild(tabView.el)
- },
- update: function(fileInfo) {
- tabView.setFileInfo(new OCA.Files.FileInfoModel(fileInfo))
- },
- destroy: function() {
- tabView.el.remove()
- },
- enabled: enabled
- }))
- }
- },
-
- /**
- * Register a detail view to be added to all views
- */
- registerDetailView: function(detailView) {
- OC.debug && console.warn('registerDetailView is deprecated! It will be removed in nextcloud 20.');
- if (detailView.el) {
- OCA.Files.Sidebar.registerSecondaryView(detailView)
- }
- },
-
- /**
- * Register a view to be added to the breadcrumb view
- */
- registerBreadCrumbDetailView: function(detailView) {
- if (this.breadcrumb) {
- this.breadcrumb.addDetailView(detailView);
- }
- },
-
- /**
- * Returns the registered detail views.
- *
- * @return null|Array<OCA.Files.DetailFileInfoView> an array with the
- * registered DetailFileInfoViews, or null if the details view
- * is not enabled.
- */
- getRegisteredDetailViews: function() {
- if (this._detailsView) {
- return this._detailsView.getDetailViews();
- }
-
- return null;
- },
-
- registerHeader: function(header) {
- this.headers.push(
- _.defaults(header, { order: 0 })
- );
- },
-
- registerFooter: function(footer) {
- this.footers.push(
- _.defaults(footer, { order: 0 })
- );
- }
- };
-
- FileList.MultiSelectMenuActions = {
- ToggleSelectionModeAction: function(fileList) {
- return {
- name: 'toggleSelectionMode',
- displayName: function(context) {
- return t('files', 'Select file range');
- },
- iconClass: 'icon-fullscreen',
- order: 15,
- action: function() {
- fileList._onClickToggleSelectionMode();
- },
- };
- },
- },
-
- /**
- * Sort comparators.
- * @namespace OCA.Files.FileList.Comparators
- * @private
- */
- FileList.Comparators = {
- /**
- * Compares two file infos by name, making directories appear
- * first.
- *
- * @param {OC.Files.FileInfo} fileInfo1 file info
- * @param {OC.Files.FileInfo} fileInfo2 file info
- * @return {number} -1 if the first file must appear before the second one,
- * 0 if they are identify, 1 otherwise.
- */
- name: function(fileInfo1, fileInfo2) {
- if (fileInfo1.type === 'dir' && fileInfo2.type !== 'dir') {
- return -1;
- }
- if (fileInfo1.type !== 'dir' && fileInfo2.type === 'dir') {
- return 1;
- }
- return OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name);
- },
- /**
- * Compares two file infos by size.
- *
- * @param {OC.Files.FileInfo} fileInfo1 file info
- * @param {OC.Files.FileInfo} fileInfo2 file info
- * @return {number} -1 if the first file must appear before the second one,
- * 0 if they are identify, 1 otherwise.
- */
- size: function(fileInfo1, fileInfo2) {
- return fileInfo1.size - fileInfo2.size;
- },
- /**
- * Compares two file infos by timestamp.
- *
- * @param {OC.Files.FileInfo} fileInfo1 file info
- * @param {OC.Files.FileInfo} fileInfo2 file info
- * @return {number} -1 if the first file must appear before the second one,
- * 0 if they are identify, 1 otherwise.
- */
- mtime: function(fileInfo1, fileInfo2) {
- return fileInfo1.mtime - fileInfo2.mtime;
- }
- };
-
- /**
- * File info attributes.
- *
- * @typedef {Object} OC.Files.FileInfo
- *
- * @lends OC.Files.FileInfo
- *
- * @deprecated use OC.Files.FileInfo instead
- *
- */
- OCA.Files.FileInfo = OC.Files.FileInfo;
-
- OCA.Files.FileList = FileList;
-})();
-
-window.addEventListener('DOMContentLoaded', function() {
- // FIXME: unused ?
- OCA.Files.FileList.useUndo = (window.onbeforeunload)?true:false;
- $(window).on('beforeunload', function () {
- if (OCA.Files.FileList.lastAction) {
- OCA.Files.FileList.lastAction();
- }
- });
-
-});
diff --git a/apps/files/js/filemultiselectmenu.js b/apps/files/js/filemultiselectmenu.js
deleted file mode 100644
index 46e90bb9c02..00000000000
--- a/apps/files/js/filemultiselectmenu.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- var FileMultiSelectMenu = OC.Backbone.View.extend({
- tagName: 'div',
- className: 'filesSelectMenu popovermenu bubble menu-center',
- _scopes: null,
- initialize: function(menuItems) {
- this._scopes = menuItems;
- },
- events: {
- 'click a.action': '_onClickAction'
- },
-
- /**
- * Renders the menu with the currently set items
- */
- render: function() {
- this.$el.html(OCA.Files.Templates['filemultiselectmenu']({
- items: this._scopes
- }));
- },
- /**
- * Displays the menu under the given element
- *
- * @param {OCA.Files.FileActionContext} context context
- * @param {Object} $trigger trigger element
- */
- show: function(context) {
- this._context = context;
- this.$el.removeClass('hidden');
- if (window.innerWidth < 480) {
- this.$el.removeClass('menu-center').addClass('menu-right');
- } else {
- this.$el.removeClass('menu-right').addClass('menu-center');
- }
- OC.showMenu(null, this.$el);
- return false;
- },
- toggleItemVisibility: function (itemName, show) {
- if (show) {
- this.$el.find('.item-' + itemName).removeClass('hidden');
- } else {
- this.$el.find('.item-' + itemName).addClass('hidden');
- }
- },
- updateItemText: function (itemName, translation) {
- this.$el.find('.item-' + itemName).find('.label').text(translation);
- },
- toggleLoading: function (itemName, showLoading) {
- var $actionElement = this.$el.find('.item-' + itemName);
- if ($actionElement.length === 0) {
- return;
- }
- var $icon = $actionElement.find('.icon');
- if (showLoading) {
- var $loadingIcon = $('<span class="icon icon-loading-small"></span>');
- $icon.after($loadingIcon);
- $icon.addClass('hidden');
- $actionElement.addClass('disabled');
- } else {
- $actionElement.find('.icon-loading-small').remove();
- $actionElement.find('.icon').removeClass('hidden');
- $actionElement.removeClass('disabled');
- }
- },
- isDisabled: function (itemName) {
- var $actionElement = this.$el.find('.item-' + itemName);
- return $actionElement.hasClass('disabled');
- },
- /**
- * Event handler whenever an action has been clicked within the menu
- *
- * @param {Object} event event object
- */
- _onClickAction: function (event) {
- var $target = $(event.currentTarget);
- if (!$target.hasClass('menuitem')) {
- $target = $target.closest('.menuitem');
- }
-
- OC.hideMenus();
- this._context.multiSelectMenuClick(event, $target.data('action'));
- return false;
- }
- });
-
- OCA.Files.FileMultiSelectMenu = FileMultiSelectMenu;
-})(OC, OCA);
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
deleted file mode 100644
index f1e20d9d011..00000000000
--- a/apps/files/js/files.js
+++ /dev/null
@@ -1,543 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-/* global getURLParameter */
-/**
- * Utility class for file related operations
- */
-(function() {
- var Files = {
- // file space size sync
- _updateStorageStatistics: function(currentDir) {
- var state = Files.updateStorageStatistics;
- if (state.dir){
- if (state.dir === currentDir) {
- return;
- }
- // cancel previous call, as it was for another dir
- state.call.abort();
- }
- state.dir = currentDir;
- state.call = $.getJSON(OC.generateUrl('apps/files/api/v1/stats?dir={dir}', {
- dir: currentDir,
- }), function(response) {
- state.dir = null;
- state.call = null;
- Files.updateMaxUploadFilesize(response);
- });
- },
- // update quota
- updateStorageQuotas: function() {
- Files._updateStorageQuotasThrottled();
- },
- _updateStorageQuotas: function() {
- var state = Files.updateStorageQuotas;
- state.call = $.getJSON(OC.generateUrl('apps/files/api/v1/stats'), function(response) {
- Files.updateQuota(response);
- });
- },
- /**
- * Update storage statistics such as free space, max upload,
- * etc based on the given directory.
- *
- * Note this function is debounced to avoid making too
- * many ajax calls in a row.
- *
- * @param dir directory
- * @param force whether to force retrieving
- */
- updateStorageStatistics: function(dir, force) {
- if (!OC.currentUser) {
- return;
- }
-
- if (force) {
- Files._updateStorageStatistics(dir);
- }
- else {
- Files._updateStorageStatisticsDebounced(dir);
- }
- },
-
- updateMaxUploadFilesize:function(response) {
- if (response === undefined) {
- return;
- }
- if (response.data !== undefined && response.data.uploadMaxFilesize !== undefined) {
- $('#free_space').val(response.data.freeSpace);
- $('#upload.button').attr('title', response.data.maxHumanFilesize);
- $('#usedSpacePercent').val(response.data.usedSpacePercent);
- $('#usedSpacePercent').data('mount-type', response.data.mountType);
- $('#usedSpacePercent').data('mount-point', response.data.mountPoint);
- $('#owner').val(response.data.owner);
- $('#ownerDisplayName').val(response.data.ownerDisplayName);
- Files.displayStorageWarnings();
- OCA.Files.App.fileList._updateDirectoryPermissions();
- }
- if (response[0] === undefined) {
- return;
- }
- if (response[0].uploadMaxFilesize !== undefined) {
- $('#upload.button').attr('title', response[0].maxHumanFilesize);
- $('#usedSpacePercent').val(response[0].usedSpacePercent);
- Files.displayStorageWarnings();
- }
-
- },
-
- updateQuota:function(response) {
- if (response === undefined) {
- return;
- }
- if (response.data !== undefined
- && response.data.quota !== undefined
- && response.data.total !== undefined
- && response.data.used !== undefined
- && response.data.usedSpacePercent !== undefined) {
- var humanUsed = OC.Util.humanFileSize(response.data.used, true, false);
- var humanTotal = OC.Util.humanFileSize(response.data.total, true, false);
- if (response.data.quota > 0) {
- $('#quota').attr('title', t('files', '{used}%', {used: Math.round(response.data.usedSpacePercent)}));
- $('#quota progress').val(response.data.usedSpacePercent);
- $('#quotatext').html(t('files', '{used} of {quota} used', {used: humanUsed, quota: humanTotal}));
- } else {
- $('#quotatext').html(t('files', '{used} used', {used: humanUsed}));
- }
- if (response.data.usedSpacePercent > 80) {
- $('#quota progress').addClass('warn');
- } else {
- $('#quota progress').removeClass('warn');
- }
- }
-
- },
-
- /**
- * Fix path name by removing double slash at the beginning, if any
- */
- fixPath: function(fileName) {
- if (fileName.substr(0, 2) == '//') {
- return fileName.substr(1);
- }
- return fileName;
- },
-
- /**
- * Checks whether the given file name is valid.
- * @param name file name to check
- * @return true if the file name is valid.
- * Throws a string exception with an error message if
- * the file name is not valid
- *
- * NOTE: This function is duplicated in the filepicker inside core/src/OC/dialogs.js
- */
- isFileNameValid: function (name) {
- var trimmedName = name.trim();
- if (trimmedName === '.' || trimmedName === '..')
- {
- throw t('files', '"{name}" is an invalid file name.', {name: name});
- } else if (trimmedName.length === 0) {
- throw t('files', 'File name cannot be empty.');
- } else if (trimmedName.indexOf('/') !== -1) {
- throw t('files', '"/" is not allowed inside a file name.');
- } else if (!!(trimmedName.match(OC.config.blacklist_files_regex))) {
- throw t('files', '"{name}" is not an allowed filetype', {name: name});
- }
-
- return true;
- },
- displayStorageWarnings: function() {
- if (!OC.Notification.isHidden()) {
- return;
- }
-
- var usedSpacePercent = $('#usedSpacePercent').val(),
- owner = $('#owner').val(),
- ownerDisplayName = $('#ownerDisplayName').val(),
- mountType = $('#usedSpacePercent').data('mount-type'),
- mountPoint = $('#usedSpacePercent').data('mount-point');
- if (usedSpacePercent > 98) {
- if (owner !== OC.getCurrentUser().uid) {
- OC.Notification.show(t('files', 'Storage of {owner} is full, files cannot be updated or synced anymore!',
- {owner: ownerDisplayName}), {type: 'error'}
- );
- } else if (mountType === 'group') {
- OC.Notification.show(t('files',
- 'Group folder "{mountPoint}" is full, files cannot be updated or synced anymore!',
- {mountPoint: mountPoint}),
- {type: 'error'}
- );
- } else if (mountType === 'external') {
- OC.Notification.show(t('files',
- 'External storage "{mountPoint}" is full, files cannot be updated or synced anymore!',
- {mountPoint: mountPoint}),
- {type : 'error'}
- );
- } else {
- OC.Notification.show(t('files',
- 'Your storage is full, files cannot be updated or synced anymore!'),
- {type: 'error'}
- );
- }
- } else if (usedSpacePercent > 90) {
- if (owner !== OC.getCurrentUser().uid) {
- OC.Notification.show(t('files', 'Storage of {owner} is almost full ({usedSpacePercent}%).',
- {
- usedSpacePercent: usedSpacePercent,
- owner: ownerDisplayName
- }),
- {
- type: 'error'
- }
- );
- } else if (mountType === 'group') {
- OC.Notification.show(t('files',
- 'Group folder "{mountPoint}" is almost full ({usedSpacePercent}%).',
- {mountPoint: mountPoint, usedSpacePercent: usedSpacePercent}),
- {type : 'error'}
- );
- } else if (mountType === 'external') {
- OC.Notification.show(t('files',
- 'External storage "{mountPoint}" is almost full ({usedSpacePercent}%).',
- {mountPoint: mountPoint, usedSpacePercent: usedSpacePercent}),
- {type : 'error'}
- );
- } else {
- OC.Notification.show(t('files', 'Your storage is almost full ({usedSpacePercent}%).',
- {usedSpacePercent: usedSpacePercent}),
- {type : 'error'}
- );
- }
- }
- },
-
- /**
- * Returns the download URL of the given file(s)
- * @param {string} filename string or array of file names to download
- * @param {string} [dir] optional directory in which the file name is, defaults to the current directory
- * @param {boolean} [isDir=false] whether the given filename is a directory and might need a special URL
- */
- getDownloadUrl: function(filename, dir, isDir) {
- if (!_.isArray(filename) && !isDir) {
- var pathSections = dir.split('/');
- pathSections.push(filename);
- var encodedPath = '';
- _.each(pathSections, function(section) {
- if (section !== '') {
- encodedPath += '/' + encodeURIComponent(section);
- }
- });
- return OC.linkToRemoteBase('webdav') + encodedPath;
- }
-
- if (_.isArray(filename)) {
- filename = JSON.stringify(filename);
- }
-
- var params = {
- dir: dir,
- files: filename
- };
- return this.getAjaxUrl('download', params);
- },
-
- /**
- * Returns the ajax URL for a given action
- * @param action action string
- * @param params optional params map
- */
- getAjaxUrl: function(action, params) {
- var q = '';
- if (params) {
- q = '?' + OC.buildQueryString(params);
- }
- return OC.filePath('files', 'ajax', action + '.php') + q;
- },
-
- /**
- * Fetch the icon url for the mimetype
- * @param {string} mime The mimetype
- * @param {Files~mimeicon} ready Function to call when mimetype is retrieved
- * @deprecated use OC.MimeType.getIconUrl(mime)
- */
- getMimeIcon: function(mime, ready) {
- ready(OC.MimeType.getIconUrl(mime));
- },
-
- /**
- * Generates a preview URL based on the URL space.
- * @param urlSpec attributes for the URL
- * @param {number} urlSpec.x width
- * @param {number} urlSpec.y height
- * @param {String} urlSpec.file path to the file
- * @return preview URL
- * @deprecated used OCA.Files.FileList.generatePreviewUrl instead
- */
- generatePreviewUrl: function(urlSpec) {
- OC.debug && console.warn('DEPRECATED: please use generatePreviewUrl() from an OCA.Files.FileList instance');
- return OCA.Files.App.fileList.generatePreviewUrl(urlSpec);
- },
-
- /**
- * Lazy load preview
- * @deprecated used OCA.Files.FileList.lazyLoadPreview instead
- */
- lazyLoadPreview : function(path, mime, ready, width, height, etag) {
- OC.debug && console.warn('DEPRECATED: please use lazyLoadPreview() from an OCA.Files.FileList instance');
- return FileList.lazyLoadPreview({
- path: path,
- mime: mime,
- callback: ready,
- width: width,
- height: height,
- etag: etag
- });
- },
-
- /**
- * Initialize the files view
- */
- initialize: function() {
- Files.bindKeyboardShortcuts(document, $);
-
- // drag&drop support using jquery.fileupload
- // TODO use OC.dialogs
- $(document).bind('drop dragover', function (e) {
- e.preventDefault(); // prevent browser from doing anything, if file isn't dropped in dropZone
- });
-
- // display storage warnings
- setTimeout(Files.displayStorageWarnings, 100);
-
- // only possible at the moment if user is logged in or the files app is loaded
- if (OC.currentUser && OCA.Files.App && OC.config.session_keepalive) {
- // start on load - we ask the server every 5 minutes
- var func = _.bind(OCA.Files.App.fileList.updateStorageStatistics, OCA.Files.App.fileList);
- var updateStorageStatisticsInterval = 5*60*1000;
- var updateStorageStatisticsIntervalId = setInterval(func, updateStorageStatisticsInterval);
-
- // TODO: this should also stop when switching to another view
- // Use jquery-visibility to de-/re-activate file stats sync
- if ($.support.pageVisibility) {
- $(document).on({
- 'show': function() {
- if (!updateStorageStatisticsIntervalId) {
- updateStorageStatisticsIntervalId = setInterval(func, updateStorageStatisticsInterval);
- }
- },
- 'hide': function() {
- clearInterval(updateStorageStatisticsIntervalId);
- updateStorageStatisticsIntervalId = 0;
- }
- });
- }
- }
-
-
- $('#webdavurl').on('click touchstart', function () {
- this.focus();
- this.setSelectionRange(0, this.value.length);
- });
-
- //FIXME scroll to and highlight preselected file
- /*
- if (getURLParameter('scrollto')) {
- FileList.scrollTo(getURLParameter('scrollto'));
- }
- */
- },
-
- /**
- * Handles the download and calls the callback function once the download has started
- * - browser sends download request and adds parameter with a token
- * - server notices this token and adds a set cookie to the download response
- * - browser now adds this cookie for the domain
- * - JS periodically checks for this cookie and then knows when the download has started to call the callback
- *
- * @param {string} url download URL
- * @param {Function} callback function to call once the download has started
- */
- handleDownload: function(url, callback) {
- var randomToken = Math.random().toString(36).substring(2),
- checkForDownloadCookie = function() {
- if (!OC.Util.isCookieSetToValue('ocDownloadStarted', randomToken)){
- return false;
- } else {
- callback();
- return true;
- }
- };
-
- if (url.indexOf('?') >= 0) {
- url += '&';
- } else {
- url += '?';
- }
- OC.redirect(url + 'downloadStartSecret=' + randomToken);
- OC.Util.waitFor(checkForDownloadCookie, 500);
- }
- };
-
- Files._updateStorageStatisticsDebounced = _.debounce(Files._updateStorageStatistics, 250);
- Files._updateStorageQuotasThrottled = _.throttle(Files._updateStorageQuotas, 30000);
- OCA.Files.Files = Files;
-})();
-
-// TODO: move to FileList
-var createDragShadow = function(event) {
- // FIXME: inject file list instance somehow
- /* global FileList, Files */
-
- //select dragged file
- var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
- if (!isDragSelected) {
- //select dragged file
- FileList._selectFileEl($(event.target).parents('tr:first'), true, false);
- }
-
- // do not show drag shadow for too many files
- var selectedFiles = _.first(FileList.getSelectedFiles(), FileList.pageSize());
- selectedFiles = _.sortBy(selectedFiles, FileList._fileInfoCompare);
-
- if (!isDragSelected && selectedFiles.length === 1) {
- //revert the selection
- FileList._selectFileEl($(event.target).parents('tr:first'), false, false);
- }
-
- // build dragshadow
- var dragshadow = $('<table class="dragshadow"></table>');
- var tbody = $('<tbody></tbody>');
- dragshadow.append(tbody);
-
- var dir = FileList.getCurrentDirectory();
-
- $(selectedFiles).each(function(i,elem) {
- // TODO: refactor this with the table row creation code
- var newtr = $('<tr></tr>')
- .attr('data-dir', dir)
- .attr('data-file', elem.name)
- .attr('data-origin', elem.origin);
- newtr.append($('<td class="filename"></td>').text(elem.name).css('background-size', 32));
- newtr.append($('<td class="size"></td>').text(OC.Util.humanFileSize(elem.size, false, false)));
- tbody.append(newtr);
- if (elem.type === 'dir') {
- newtr.find('td.filename')
- .css('background-image', 'url(' + OC.MimeType.getIconUrl('folder') + ')');
- } else {
- var path = dir + '/' + elem.name;
- Files.lazyLoadPreview(path, elem.mimetype, function(previewpath) {
- newtr.find('td.filename')
- .css('background-image', 'url(' + previewpath + ')');
- }, null, null, elem.etag);
- }
- });
-
- return dragshadow;
-};
-
-//options for file drag/drop
-//start&stop handlers needs some cleaning up
-// TODO: move to FileList class
-var dragOptions={
- revert: 'invalid',
- revertDuration: 300,
- opacity: 0.7,
- cursorAt: { left: 24, top: 18 },
- helper: createDragShadow,
- cursor: 'move',
-
- start: function(event, ui){
- var $selectedFiles = $('td.filename input:checkbox:checked');
- if (!$selectedFiles.length) {
- $selectedFiles = $(this);
- }
- $selectedFiles.closest('tr').addClass('animate-opacity dragging');
- $selectedFiles.closest('tr').filter('.ui-droppable').droppable( 'disable' );
- // Show breadcrumbs menu
- $('.crumbmenu').addClass('canDropChildren');
-
- },
- stop: function(event, ui) {
- var $selectedFiles = $('td.filename input:checkbox:checked');
- if (!$selectedFiles.length) {
- $selectedFiles = $(this);
- }
-
- var $tr = $selectedFiles.closest('tr');
- $tr.removeClass('dragging');
- $tr.filter('.ui-droppable').droppable( 'enable' );
-
- setTimeout(function() {
- $tr.removeClass('animate-opacity');
- }, 300);
- // Hide breadcrumbs menu
- $('.crumbmenu').removeClass('canDropChildren');
- },
- drag: function(event, ui) {
- // Prevent scrolling when hovering .files-controls
- if ($(event.originalEvent.target).parents('.files-controls').length > 0) {
- return
- }
-
- /** @type {JQuery<HTMLDivElement>} */
- const scrollingArea = FileList.$container;
-
- // Get the top and bottom scroll trigger y positions
- const containerHeight = scrollingArea.innerHeight() ?? 0
- const scrollTriggerArea = Math.min(Math.floor(containerHeight / 2), 100);
- const bottomTriggerY = containerHeight - scrollTriggerArea;
- const topTriggerY = scrollTriggerArea;
-
- // Get the cursor position relative to the container
- const containerOffset = scrollingArea.offset() ?? {left: 0, top: 0}
- const cursorPositionY = event.pageY - containerOffset.top
-
- const currentScrollTop = scrollingArea.scrollTop() ?? 0
-
- if (cursorPositionY < topTriggerY) {
- scrollingArea.scrollTop(currentScrollTop - 10)
- } else if (cursorPositionY > bottomTriggerY) {
- scrollingArea.scrollTop(currentScrollTop + 10)
- }
- }
-};
-// sane browsers support using the distance option
-if ( $('html.ie').length === 0) {
- dragOptions['distance'] = 20;
-}
-
-// TODO: move to FileList class
-var folderDropOptions = {
- hoverClass: "canDrop",
- drop: function( event, ui ) {
- // don't allow moving a file into a selected folder
- /* global FileList */
- if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) {
- return false;
- }
-
- var $tr = $(this).closest('tr');
- if (($tr.data('permissions') & OC.PERMISSION_CREATE) === 0) {
- FileList._showPermissionDeniedNotification();
- return false;
- }
- var targetPath = FileList.getCurrentDirectory() + '/' + $tr.data('file');
-
- var files = FileList.getSelectedFiles();
- if (files.length === 0) {
- // single one selected without checkbox?
- files = _.map(ui.helper.find('tr'), function(el) {
- return FileList.elementToFile($(el));
- });
- }
-
- FileList.move(_.pluck(files, 'name'), targetPath);
- },
- tolerance: 'pointer'
-};
-
-// for backward compatibility
-window.Files = OCA.Files.Files;
diff --git a/apps/files/js/filesummary.js b/apps/files/js/filesummary.js
deleted file mode 100644
index a4eef886969..00000000000
--- a/apps/files/js/filesummary.js
+++ /dev/null
@@ -1,267 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- /**
- * The FileSummary class encapsulates the file summary values and
- * the logic to render it in the given container
- *
- * @constructs FileSummary
- * @memberof OCA.Files
- *
- * @param $tr table row element
- * @param {OC.Backbone.Model} [options.filesConfig] files app configuration
- */
- var FileSummary = function($tr, options) {
- options = options || {};
- var self = this;
- this.$el = $tr;
- var filesConfig = options.config;
- if (filesConfig) {
- this._showHidden = !!filesConfig.show_hidden;
- window._nc_event_bus.subscribe('files:config:updated', ({ key, value }) => {
- if (key === 'show_hidden') {
- self._showHidden = !!value;
- self.update();
- }
- });
- }
- this.clear();
- this.render();
- };
-
- FileSummary.prototype = {
- _showHidden: null,
-
- summary: {
- totalFiles: 0,
- totalDirs: 0,
- totalHidden: 0,
- totalSize: 0,
- filter:'',
- sumIsPending:false
- },
-
- /**
- * Returns whether the given file info must be hidden
- *
- * @param {OC.Files.FileInfo} fileInfo file info
- *
- * @return {boolean} true if the file is a hidden file, false otherwise
- */
- _isHiddenFile: function(file) {
- return file.name && file.name.charAt(0) === '.';
- },
-
- /**
- * Adds file
- * @param {OC.Files.FileInfo} file file to add
- * @param {boolean} update whether to update the display
- */
- add: function(file, update) {
- if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) {
- return;
- }
- if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
- this.summary.totalDirs++;
- }
- else {
- this.summary.totalFiles++;
- }
- if (this._isHiddenFile(file)) {
- this.summary.totalHidden++;
- }
-
- var size = parseInt(file.size, 10) || 0;
- if (size >=0) {
- this.summary.totalSize += size;
- } else {
- this.summary.sumIsPending = true;
- }
- if (!!update) {
- this.update();
- }
- },
- /**
- * Removes file
- * @param {OC.Files.FileInfo} file file to remove
- * @param {boolean} update whether to update the display
- */
- remove: function(file, update) {
- if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) {
- return;
- }
- if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
- this.summary.totalDirs--;
- }
- else {
- this.summary.totalFiles--;
- }
- if (this._isHiddenFile(file)) {
- this.summary.totalHidden--;
- }
- var size = parseInt(file.size, 10) || 0;
- if (size >=0) {
- this.summary.totalSize -= size;
- }
- if (!!update) {
- this.update();
- }
- },
- setFilter: function(filter, files){
- this.summary.filter = filter.toLowerCase();
- this.calculate(files);
- },
- /**
- * Returns the total of files and directories
- */
- getTotal: function() {
- return this.summary.totalDirs + this.summary.totalFiles;
- },
- /**
- * Recalculates the summary based on the given files array
- * @param files array of files
- */
- calculate: function(files) {
- var file;
- var summary = {
- totalDirs: 0,
- totalFiles: 0,
- totalHidden: 0,
- totalSize: 0,
- filter: this.summary.filter,
- sumIsPending: false
- };
-
- for (var i = 0; i < files.length; i++) {
- file = files[i];
- if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) {
- continue;
- }
- if (file.type === 'dir' || file.mime === 'httpd/unix-directory') {
- summary.totalDirs++;
- }
- else {
- summary.totalFiles++;
- }
- if (this._isHiddenFile(file)) {
- summary.totalHidden++;
- }
- var size = parseInt(file.size, 10) || 0;
- if (size >=0) {
- summary.totalSize += size;
- } else {
- summary.sumIsPending = true;
- }
- }
- this.setSummary(summary);
- },
- /**
- * Clears the summary
- */
- clear: function() {
- this.calculate([]);
- },
- /**
- * Sets the current summary values
- * @param summary map
- */
- setSummary: function(summary) {
- this.summary = summary;
- if (typeof this.summary.filter === 'undefined') {
- this.summary.filter = '';
- }
- this.update();
- },
-
- _infoTemplate: function(data) {
- /* NOTE: To update the template make changes in filesummary.handlebars
- * and run:
- *
- * handlebars -n OCA.Files.FileSummary.Templates filesummary.handlebars -f filesummary_template.js
- */
- return OCA.Files.Templates['filesummary'](_.extend({
- connectorLabel: t('files', '{dirs} and {files}', {dirs: '', files: ''})
- }, data));
- },
-
- /**
- * Renders the file summary element
- */
- update: function() {
- if (!this.$el) {
- return;
- }
- if (!this.summary.totalFiles && !this.summary.totalDirs) {
- this.$el.addClass('hidden');
- return;
- }
- // There's a summary and data -> Update the summary
- this.$el.removeClass('hidden');
- var $dirInfo = this.$el.find('.dirinfo');
- var $fileInfo = this.$el.find('.fileinfo');
- var $connector = this.$el.find('.connector');
- var $filterInfo = this.$el.find('.filter');
- var $hiddenInfo = this.$el.find('.hiddeninfo');
-
- // Substitute old content with new translations
- $dirInfo.html(n('files', '%n folder', '%n folders', this.summary.totalDirs));
- $fileInfo.html(n('files', '%n file', '%n files', this.summary.totalFiles));
- $hiddenInfo.html(' (' + n('files', 'including %n hidden', 'including %n hidden', this.summary.totalHidden) + ')');
- var fileSize = this.summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(this.summary.totalSize, false, false);
- this.$el.find('.filesize').html(fileSize);
-
- // Show only what's necessary (may be hidden)
- if (this.summary.totalDirs === 0) {
- $dirInfo.addClass('hidden');
- $connector.addClass('hidden');
- } else {
- $dirInfo.removeClass('hidden');
- }
- if (this.summary.totalFiles === 0) {
- $fileInfo.addClass('hidden');
- $connector.addClass('hidden');
- } else {
- $fileInfo.removeClass('hidden');
- }
- if (this.summary.totalDirs > 0 && this.summary.totalFiles > 0) {
- $connector.removeClass('hidden');
- }
- $hiddenInfo.toggleClass('hidden', this.summary.totalHidden === 0 || this._showHidden)
- if (this.summary.filter === '') {
- $filterInfo.html('');
- $filterInfo.addClass('hidden');
- } else {
- $filterInfo.html(' ' + n('files', 'matches "{filter}"', 'match "{filter}"', this.summary.totalDirs + this.summary.totalFiles, {filter: this.summary.filter}));
- $filterInfo.removeClass('hidden');
- }
- },
- render: function() {
- if (!this.$el) {
- return;
- }
- var summary = this.summary;
-
- // don't show the filesize column, if filesize is NaN (e.g. in trashbin)
- var fileSize = '';
- if (!isNaN(summary.totalSize)) {
- fileSize = summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(summary.totalSize, false, false);
- fileSize = '<td class="filesize">' + fileSize + '</td>';
- }
-
- var $summary = $(
- '<td class="filesummary">'+ this._infoTemplate() + '</td>' +
- fileSize +
- '<td class="date"></td>'
- );
- this.$el.addClass('hidden');
- this.$el.append($summary);
- this.update();
- }
- };
- OCA.Files.FileSummary = FileSummary;
-})();
-
diff --git a/apps/files/js/gotoplugin.js b/apps/files/js/gotoplugin.js
deleted file mode 100644
index 78935ac20bc..00000000000
--- a/apps/files/js/gotoplugin.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function (OCA) {
-
- OCA.Files = OCA.Files || {};
-
- /**
- * @namespace OCA.Files.GotoPlugin
- *
- */
- OCA.Files.GotoPlugin = {
- name: 'Goto',
-
- disallowedLists: [
- 'files',
- 'trashbin'
- ],
-
- attach: function (fileList) {
- if (this.disallowedLists.indexOf(fileList.id) !== -1) {
- return;
- }
- // lists where the "Open" default action is disabled should
- // also have the goto action disabled
- if (fileList._defaultFileActionsDisabled) {
- return
- }
- var fileActions = fileList.fileActions;
-
- fileActions.registerAction({
- name: 'Goto',
- displayName: t('files', 'View in folder'),
- mime: 'all',
- permissions: OC.PERMISSION_ALL,
- iconClass: 'icon-goto nav-icon-extstoragemounts',
- type: OCA.Files.FileActions.TYPE_DROPDOWN,
- actionHandler: function (fileName, context) {
- var fileModel = context.fileInfoModel;
- OCA.Files.Sidebar.close();
- OCA.Files.App.setActiveView('files', { silent: true });
- OCA.Files.App.fileList.changeDirectory(fileModel.get('path'), true, true).then(function() {
- OCA.Files.App.fileList.scrollTo(fileModel.get('name'));
- });
- },
- render: function (actionSpec, isDefault, context) {
- return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context)
- .removeClass('permanent');
- }
- });
- }
- };
-})(OCA);
-
-OC.Plugins.register('OCA.Files.FileList', OCA.Files.GotoPlugin);
-
diff --git a/apps/files/js/jquery-visibility.js b/apps/files/js/jquery-visibility.js
deleted file mode 100644
index 67d96e5e0e7..00000000000
--- a/apps/files/js/jquery-visibility.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*!
- * jquery-visibility v1.0.11
- * Page visibility shim for jQuery.
- *
- * Project Website: http://mths.be/visibility
- *
- * @version 1.0.11
- *
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-FileCopyrightText: Mathias Bynens
- * SPDX-FileCopyrightText: Jan Paepke
- * SPDX-License-Identifier: MIT
- */
-;(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], function ($) {
- return factory(root, $);
- });
- } else if (typeof exports === 'object') {
- // Node/CommonJS
- module.exports = factory(root, require('jquery'));
- } else {
- // Browser globals
- factory(root, jQuery);
- }
-}(this, function(window, $, undefined) {
- "use strict";
-
- var
- document = window.document,
- property, // property name of document, that stores page visibility
- vendorPrefixes = ['webkit', 'o', 'ms', 'moz', ''],
- $support = $.support || {},
- // In Opera, `'onfocusin' in document == true`, hence the extra `hasFocus` check to detect IE-like behavior
- eventName = 'onfocusin' in document && 'hasFocus' in document ?
- 'focusin focusout' :
- 'focus blur';
-
- var prefix;
- while ((prefix = vendorPrefixes.pop()) !== undefined) {
- property = (prefix ? prefix + 'H': 'h') + 'idden';
- $support.pageVisibility = document[property] !== undefined;
- if ($support.pageVisibility) {
- eventName = prefix + 'visibilitychange';
- break;
- }
- }
-
- // normalize to and update document hidden property
- function updateState() {
- if (property !== 'hidden') {
- document.hidden = $support.pageVisibility ? document[property] : undefined;
- }
- }
- updateState();
-
- $(/blur$/.test(eventName) ? window : document).on(eventName, function(event) {
- var type = event.type;
- var originalEvent = event.originalEvent;
-
- // Avoid errors from triggered native events for which `originalEvent` is
- // not available.
- if (!originalEvent) {
- return;
- }
-
- var toElement = originalEvent.toElement;
-
- // If it’s a `{focusin,focusout}` event (IE), `fromElement` and `toElement`
- // should both be `null` or `undefined`; else, the page visibility hasn’t
- // changed, but the user just clicked somewhere in the doc. In IE9, we need
- // to check the `relatedTarget` property instead.
- if (
- !/^focus./.test(type) || (
- toElement === undefined &&
- originalEvent.fromElement === undefined &&
- originalEvent.relatedTarget === undefined
- )
- ) {
- $(document).triggerHandler(
- property && document[property] || /^(?:blur|focusout)$/.test(type) ?
- 'hide' :
- 'show'
- );
- }
- // and update the current state
- updateState();
- });
-}));
diff --git a/apps/files/js/jquery.fileupload.js b/apps/files/js/jquery.fileupload.js
deleted file mode 100644
index eb3a491aa4c..00000000000
--- a/apps/files/js/jquery.fileupload.js
+++ /dev/null
@@ -1,1504 +0,0 @@
-/*
- * jQuery File Upload Plugin 9.12.5
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
- * SPDX-FileCopyrightText: 2010 Sebastian Tschan <https://blueimp.net>
- * SPDX-License-Identifier: MIT
- */
-
-/* jshint nomen:false */
-/* global define, require, window, document, location, Blob, FormData */
-
-;(function (factory) {
- 'use strict';
- if (typeof define === 'function' && define.amd) {
- // Register as an anonymous AMD module:
- define([
- 'jquery',
- 'jquery.ui.widget'
- ], factory);
- } else if (typeof exports === 'object') {
- // Node/CommonJS:
- factory(
- require('jquery'),
- require('./vendor/jquery.ui.widget')
- );
- } else {
- // Browser globals:
- factory(window.jQuery);
- }
-}(function ($) {
- 'use strict';
-
- // Detect file input support, based on
- // http://viljamis.com/blog/2012/file-upload-support-on-mobile/
- $.support.fileInput = !(new RegExp(
- // Handle devices which give false positives for the feature detection:
- '(Android (1\\.[0156]|2\\.[01]))' +
- '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
- '|(w(eb)?OSBrowser)|(webOS)' +
- '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
- ).test(window.navigator.userAgent) ||
- // Feature detection for all other devices:
- $('<input type="file">').prop('disabled'));
-
- // The FileReader API is not actually used, but works as feature detection,
- // as some Safari versions (5?) support XHR file uploads via the FormData API,
- // but not non-multipart XHR file uploads.
- // window.XMLHttpRequestUpload is not available on IE10, so we check for
- // window.ProgressEvent instead to detect XHR2 file upload capability:
- $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
- $.support.xhrFormDataFileUpload = !!window.FormData;
-
- // Detect support for Blob slicing (required for chunked uploads):
- $.support.blobSlice = window.Blob && (Blob.prototype.slice ||
- Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
-
- // Helper function to create drag handlers for dragover/dragenter/dragleave:
- function getDragHandler(type) {
- var isDragOver = type === 'dragover';
- return function (e) {
- e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
- var dataTransfer = e.dataTransfer;
- if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
- this._trigger(
- type,
- $.Event(type, {delegatedEvent: e})
- ) !== false) {
- e.preventDefault();
- if (isDragOver) {
- dataTransfer.dropEffect = 'copy';
- }
- }
- };
- }
-
- // The fileupload widget listens for change events on file input fields defined
- // via fileInput setting and paste or drop events of the given dropZone.
- // In addition to the default jQuery Widget methods, the fileupload widget
- // exposes the "add" and "send" methods, to add or directly send files using
- // the fileupload API.
- // By default, files added via file input selection, paste, drag & drop or
- // "add" method are uploaded immediately, but it is possible to override
- // the "add" callback option to queue file uploads.
- $.widget('blueimp.fileupload', {
-
- options: {
- // The drop target element(s), by the default the complete document.
- // Set to null to disable drag & drop support:
- dropZone: $(document),
- // The paste target element(s), by the default undefined.
- // Set to a DOM node or jQuery object to enable file pasting:
- pasteZone: undefined,
- // The file input field(s), that are listened to for change events.
- // If undefined, it is set to the file input fields inside
- // of the widget element on plugin initialization.
- // Set to null to disable the change listener.
- fileInput: undefined,
- // By default, the file input field is replaced with a clone after
- // each input field change event. This is required for iframe transport
- // queues and allows change events to be fired for the same file
- // selection, but can be disabled by setting the following option to false:
- replaceFileInput: true,
- // The parameter name for the file form data (the request argument name).
- // If undefined or empty, the name property of the file input field is
- // used, or "files[]" if the file input name property is also empty,
- // can be a string or an array of strings:
- paramName: undefined,
- // By default, each file of a selection is uploaded using an individual
- // request for XHR type uploads. Set to false to upload file
- // selections in one request each:
- singleFileUploads: true,
- // To limit the number of files uploaded with one XHR request,
- // set the following option to an integer greater than 0:
- limitMultiFileUploads: undefined,
- // The following option limits the number of files uploaded with one
- // XHR request to keep the request size under or equal to the defined
- // limit in bytes:
- limitMultiFileUploadSize: undefined,
- // Multipart file uploads add a number of bytes to each uploaded file,
- // therefore the following option adds an overhead for each file used
- // in the limitMultiFileUploadSize configuration:
- limitMultiFileUploadSizeOverhead: 512,
- // Set the following option to true to issue all file upload requests
- // in a sequential order:
- sequentialUploads: false,
- // To limit the number of concurrent uploads,
- // set the following option to an integer greater than 0:
- limitConcurrentUploads: undefined,
- // Set the following option to true to force iframe transport uploads:
- forceIframeTransport: false,
- // Set the following option to the location of a redirect url on the
- // origin server, for cross-domain iframe transport uploads:
- redirect: undefined,
- // The parameter name for the redirect url, sent as part of the form
- // data and set to 'redirect' if this option is empty:
- redirectParamName: undefined,
- // Set the following option to the location of a postMessage window,
- // to enable postMessage transport uploads:
- postMessage: undefined,
- // By default, XHR file uploads are sent as multipart/form-data.
- // The iframe transport is always using multipart/form-data.
- // Set to false to enable non-multipart XHR uploads:
- multipart: true,
- // To upload large files in smaller chunks, set the following option
- // to a preferred maximum chunk size. If set to 0, null or undefined,
- // or the browser does not support the required Blob API, files will
- // be uploaded as a whole.
- maxChunkSize: undefined,
- // When a non-multipart upload or a chunked multipart upload has been
- // aborted, this option can be used to resume the upload by setting
- // it to the size of the already uploaded bytes. This option is most
- // useful when modifying the options object inside of the "add" or
- // "send" callbacks, as the options are cloned for each file upload.
- uploadedBytes: undefined,
- // By default, failed (abort or error) file uploads are removed from the
- // global progress calculation. Set the following option to false to
- // prevent recalculating the global progress data:
- recalculateProgress: true,
- // Interval in milliseconds to calculate and trigger progress events:
- progressInterval: 100,
- // Interval in milliseconds to calculate progress bitrate:
- bitrateInterval: 500,
- // By default, uploads are started automatically when adding files:
- autoUpload: true,
-
- // Error and info messages:
- messages: {
- uploadedBytes: 'Uploaded bytes exceed file size'
- },
-
- // Translation function, gets the message key to be translated
- // and an object with context specific data as arguments:
- i18n: function (message, context) {
- message = this.messages[message] || message.toString();
- if (context) {
- $.each(context, function (key, value) {
- message = message.replace('{' + key + '}', value);
- });
- }
- return message;
- },
-
- // Additional form data to be sent along with the file uploads can be set
- // using this option, which accepts an array of objects with name and
- // value properties, a function returning such an array, a FormData
- // object (for XHR file uploads), or a simple object.
- // The form of the first fileInput is given as parameter to the function:
- formData: function (form) {
- return form.serializeArray();
- },
-
- // The add callback is invoked as soon as files are added to the fileupload
- // widget (via file input selection, drag & drop, paste or add API call).
- // If the singleFileUploads option is enabled, this callback will be
- // called once for each file in the selection for XHR file uploads, else
- // once for each file selection.
- //
- // The upload starts when the submit method is invoked on the data parameter.
- // The data object contains a files property holding the added files
- // and allows you to override plugin options as well as define ajax settings.
- //
- // Listeners for this callback can also be bound the following way:
- // .bind('fileuploadadd', func);
- //
- // data.submit() returns a Promise object and allows to attach additional
- // handlers using jQuery's Deferred callbacks:
- // data.submit().done(func).fail(func).always(func);
- add: function (e, data) {
- if (e.isDefaultPrevented()) {
- return false;
- }
- if (data.autoUpload || (data.autoUpload !== false &&
- $(this).fileupload('option', 'autoUpload'))) {
- data.process().done(function () {
- data.submit();
- });
- }
- },
-
- // Other callbacks:
-
- // Callback for the submit event of each file upload:
- // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
-
- // Callback for the start of each file upload request:
- // send: function (e, data) {}, // .bind('fileuploadsend', func);
-
- // Callback for successful uploads:
- // done: function (e, data) {}, // .bind('fileuploaddone', func);
-
- // Callback for failed (abort or error) uploads:
- // fail: function (e, data) {}, // .bind('fileuploadfail', func);
-
- // Callback for completed (success, abort or error) requests:
- // always: function (e, data) {}, // .bind('fileuploadalways', func);
-
- // Callback for upload progress events:
- // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
-
- // Callback for global upload progress events:
- // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
-
- // Callback for uploads start, equivalent to the global ajaxStart event:
- // start: function (e) {}, // .bind('fileuploadstart', func);
-
- // Callback for uploads stop, equivalent to the global ajaxStop event:
- // stop: function (e) {}, // .bind('fileuploadstop', func);
-
- // Callback for change events of the fileInput(s):
- // change: function (e, data) {}, // .bind('fileuploadchange', func);
-
- // Callback for paste events to the pasteZone(s):
- // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
-
- // Callback for drop events of the dropZone(s):
- // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
-
- // Callback for drop events of the dropZone(s) when there are no files:
- // dropnofiles: function (e) {}, // .bind('fileuploaddropnofiles', func);
-
- // Callback for dragover events of the dropZone(s):
- // dragover: function (e) {}, // .bind('fileuploaddragover', func);
-
- // Callback for the start of each chunk upload request:
- // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);
-
- // Callback for successful chunk uploads:
- // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);
-
- // Callback for failed (abort or error) chunk uploads:
- // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);
-
- // Callback for completed (success, abort or error) chunk upload requests:
- // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);
-
- // The plugin options are used as settings object for the ajax calls.
- // The following are jQuery ajax settings required for the file uploads:
- processData: false,
- contentType: false,
- cache: false,
- timeout: 0
- },
-
- // A list of options that require reinitializing event listeners and/or
- // special initialization code:
- _specialOptions: [
- 'fileInput',
- 'dropZone',
- 'pasteZone',
- 'multipart',
- 'forceIframeTransport'
- ],
-
- _blobSlice: $.support.blobSlice && function () {
- var slice = this.slice || this.webkitSlice || this.mozSlice;
- return slice.apply(this, arguments);
- },
-
- _BitrateTimer: function () {
- this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
- this.loaded = 0;
- this.bitrate = 0;
- this.getBitrate = function (now, loaded, interval) {
- var timeDiff = now - this.timestamp;
- if (!this.bitrate || !interval || timeDiff > interval) {
- this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
- this.loaded = loaded;
- this.timestamp = now;
- }
- return this.bitrate;
- };
- },
-
- _isXHRUpload: function (options) {
- return !options.forceIframeTransport &&
- ((!options.multipart && $.support.xhrFileUpload) ||
- $.support.xhrFormDataFileUpload);
- },
-
- _getFormData: function (options) {
- var formData;
- if ($.type(options.formData) === 'function') {
- return options.formData(options.form);
- }
- if ($.isArray(options.formData)) {
- return options.formData;
- }
- if ($.type(options.formData) === 'object') {
- formData = [];
- $.each(options.formData, function (name, value) {
- formData.push({name: name, value: value});
- });
- return formData;
- }
- return [];
- },
-
- _getTotal: function (files) {
- var total = 0;
- $.each(files, function (index, file) {
- total += file.size || 1;
- });
- return total;
- },
-
- _initProgressObject: function (obj) {
- var progress = {
- loaded: 0,
- total: 0,
- bitrate: 0
- };
- if (obj._progress) {
- $.extend(obj._progress, progress);
- } else {
- obj._progress = progress;
- }
- },
-
- _initResponseObject: function (obj) {
- var prop;
- if (obj._response) {
- for (prop in obj._response) {
- if (obj._response.hasOwnProperty(prop)) {
- delete obj._response[prop];
- }
- }
- } else {
- obj._response = {};
- }
- },
-
- _onProgress: function (e, data) {
- if (e.lengthComputable) {
- var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
- loaded;
- if (data._time && data.progressInterval &&
- (now - data._time < data.progressInterval) &&
- e.loaded !== e.total) {
- return;
- }
- data._time = now;
- loaded = Math.floor(
- e.loaded / e.total * (data.chunkSize || data._progress.total)
- ) + (data.uploadedBytes || 0);
- // Add the difference from the previously loaded state
- // to the global loaded counter:
- this._progress.loaded += (loaded - data._progress.loaded);
- this._progress.bitrate = this._bitrateTimer.getBitrate(
- now,
- this._progress.loaded,
- data.bitrateInterval
- );
- data._progress.loaded = data.loaded = loaded;
- data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
- now,
- loaded,
- data.bitrateInterval
- );
- // Trigger a custom progress event with a total data property set
- // to the file size(s) of the current upload and a loaded data
- // property calculated accordingly:
- this._trigger(
- 'progress',
- $.Event('progress', {delegatedEvent: e}),
- data
- );
- // Trigger a global progress event for all current file uploads,
- // including ajax calls queued for sequential file uploads:
- this._trigger(
- 'progressall',
- $.Event('progressall', {delegatedEvent: e}),
- this._progress
- );
- }
- },
-
- _initProgressListener: function (options) {
- var that = this,
- xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
- // Access to the native XHR object is required to add event listeners
- // for the upload progress event:
- if (xhr.upload) {
- $(xhr.upload).bind('progress', function (e) {
- var oe = e.originalEvent;
- // Make sure the progress event properties get copied over:
- e.lengthComputable = oe.lengthComputable;
- e.loaded = oe.loaded;
- e.total = oe.total;
- that._onProgress(e, options);
- });
- options.xhr = function () {
- return xhr;
- };
- }
- },
-
- _isInstanceOf: function (type, obj) {
- // Cross-frame instanceof check
- return Object.prototype.toString.call(obj) === '[object ' + type + ']';
- },
-
- _initXHRData: function (options) {
- var that = this,
- formData,
- file = options.files[0],
- // Ignore non-multipart setting if not supported:
- multipart = options.multipart || !$.support.xhrFileUpload,
- paramName = $.type(options.paramName) === 'array' ?
- options.paramName[0] : options.paramName;
- options.headers = $.extend({}, options.headers);
- if (options.contentRange) {
- options.headers['Content-Range'] = options.contentRange;
- }
- if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
- options.headers['Content-Disposition'] = 'attachment; filename="' +
- encodeURI(file.name) + '"';
- }
- if (!multipart) {
- options.contentType = file.type || 'application/octet-stream';
- options.data = options.blob || file;
- } else if ($.support.xhrFormDataFileUpload) {
- if (options.postMessage) {
- // window.postMessage does not allow sending FormData
- // objects, so we just add the File/Blob objects to
- // the formData array and let the postMessage window
- // create the FormData object out of this array:
- formData = this._getFormData(options);
- if (options.blob) {
- formData.push({
- name: paramName,
- value: options.blob
- });
- } else {
- $.each(options.files, function (index, file) {
- formData.push({
- name: ($.type(options.paramName) === 'array' &&
- options.paramName[index]) || paramName,
- value: file
- });
- });
- }
- } else {
- if (that._isInstanceOf('FormData', options.formData)) {
- formData = options.formData;
- } else {
- formData = new FormData();
- $.each(this._getFormData(options), function (index, field) {
- formData.append(field.name, field.value);
- });
- }
- if (options.blob) {
- formData.append(paramName, options.blob, file.name);
- } else {
- $.each(options.files, function (index, file) {
- // This check allows the tests to run with
- // dummy objects:
- if (that._isInstanceOf('File', file) ||
- that._isInstanceOf('Blob', file)) {
- formData.append(
- ($.type(options.paramName) === 'array' &&
- options.paramName[index]) || paramName,
- file,
- file.uploadName || file.name
- );
- }
- });
- }
- }
- options.data = formData;
- }
- // Blob reference is not needed anymore, free memory:
- options.blob = null;
- },
-
- _initIframeSettings: function (options) {
- var targetHost = $('<a></a>').prop('href', options.url).prop('host');
- // Setting the dataType to iframe enables the iframe transport:
- options.dataType = 'iframe ' + (options.dataType || '');
- // The iframe transport accepts a serialized array as form data:
- options.formData = this._getFormData(options);
- // Add redirect url to form data on cross-domain uploads:
- if (options.redirect && targetHost && targetHost !== location.host) {
- options.formData.push({
- name: options.redirectParamName || 'redirect',
- value: options.redirect
- });
- }
- },
-
- _initDataSettings: function (options) {
- if (this._isXHRUpload(options)) {
- if (!this._chunkedUpload(options, true)) {
- if (!options.data) {
- this._initXHRData(options);
- }
- this._initProgressListener(options);
- }
- if (options.postMessage) {
- // Setting the dataType to postmessage enables the
- // postMessage transport:
- options.dataType = 'postmessage ' + (options.dataType || '');
- }
- } else {
- this._initIframeSettings(options);
- }
- },
-
- _getParamName: function (options) {
- var fileInput = $(options.fileInput),
- paramName = options.paramName;
- if (!paramName) {
- paramName = [];
- fileInput.each(function () {
- var input = $(this),
- name = input.prop('name') || 'files[]',
- i = (input.prop('files') || [1]).length;
- while (i) {
- paramName.push(name);
- i -= 1;
- }
- });
- if (!paramName.length) {
- paramName = [fileInput.prop('name') || 'files[]'];
- }
- } else if (!$.isArray(paramName)) {
- paramName = [paramName];
- }
- return paramName;
- },
-
- _initFormSettings: function (options) {
- // Retrieve missing options from the input field and the
- // associated form, if available:
- if (!options.form || !options.form.length) {
- options.form = $(options.fileInput.prop('form'));
- // If the given file input doesn't have an associated form,
- // use the default widget file input's form:
- if (!options.form.length) {
- options.form = $(this.options.fileInput.prop('form'));
- }
- }
- options.paramName = this._getParamName(options);
- if (!options.url) {
- options.url = options.form.prop('action') || location.href;
- }
- // The HTTP request method must be "POST" or "PUT":
- options.type = (options.type ||
- ($.type(options.form.prop('method')) === 'string' &&
- options.form.prop('method')) || ''
- ).toUpperCase();
- if (options.type !== 'POST' && options.type !== 'PUT' &&
- options.type !== 'PATCH') {
- options.type = 'POST';
- }
- if (!options.formAcceptCharset) {
- options.formAcceptCharset = options.form.attr('accept-charset');
- }
- },
-
- _getAJAXSettings: function (data) {
- var options = $.extend({}, this.options, data);
- this._initFormSettings(options);
- this._initDataSettings(options);
- return options;
- },
-
- // jQuery 1.6 doesn't provide .state(),
- // while jQuery 1.8+ removed .isRejected() and .isResolved():
- _getDeferredState: function (deferred) {
- if (deferred.state) {
- return deferred.state();
- }
- if (deferred.isResolved()) {
- return 'resolved';
- }
- if (deferred.isRejected()) {
- return 'rejected';
- }
- return 'pending';
- },
-
- // Maps jqXHR callbacks to the equivalent
- // methods of the given Promise object:
- _enhancePromise: function (promise) {
- promise.success = promise.done;
- promise.error = promise.fail;
- promise.complete = promise.always;
- return promise;
- },
-
- // Creates and returns a Promise object enhanced with
- // the jqXHR methods abort, success, error and complete:
- _getXHRPromise: function (resolveOrReject, context, args) {
- var dfd = $.Deferred(),
- promise = dfd.promise();
- context = context || this.options.context || promise;
- if (resolveOrReject === true) {
- dfd.resolveWith(context, args);
- } else if (resolveOrReject === false) {
- dfd.rejectWith(context, args);
- }
- promise.abort = dfd.promise;
- return this._enhancePromise(promise);
- },
-
- // Adds convenience methods to the data callback argument:
- _addConvenienceMethods: function (e, data) {
- var that = this,
- getPromise = function (args) {
- return $.Deferred().resolveWith(that, args).promise();
- };
- data.process = function (resolveFunc, rejectFunc) {
- if (resolveFunc || rejectFunc) {
- data._processQueue = this._processQueue =
- (this._processQueue || getPromise([this])).then(
- function () {
- if (data.errorThrown) {
- return $.Deferred()
- .rejectWith(that, [data]).promise();
- }
- return getPromise(arguments);
- }
- ).then(resolveFunc, rejectFunc);
- }
- return this._processQueue || getPromise([this]);
- };
- data.submit = function () {
- if (this.state() !== 'pending') {
- data.jqXHR = this.jqXHR =
- (that._trigger(
- 'submit',
- $.Event('submit', {delegatedEvent: e}),
- this
- ) !== false) && that._onSend(e, this);
- }
- return this.jqXHR || that._getXHRPromise();
- };
- data.abort = function () {
- if (this.jqXHR) {
- return this.jqXHR.abort();
- }
- this.errorThrown = 'abort';
- that._trigger('fail', null, this);
- return that._getXHRPromise(false);
- };
- data.state = function () {
- if (this.jqXHR) {
- return that._getDeferredState(this.jqXHR);
- }
- if (this._processQueue) {
- return that._getDeferredState(this._processQueue);
- }
- };
- data.processing = function () {
- return !this.jqXHR && this._processQueue && that
- ._getDeferredState(this._processQueue) === 'pending';
- };
- data.progress = function () {
- return this._progress;
- };
- data.response = function () {
- return this._response;
- };
- },
-
- // Parses the Range header from the server response
- // and returns the uploaded bytes:
- _getUploadedBytes: function (jqXHR) {
- var range = jqXHR.getResponseHeader('Range'),
- parts = range && range.split('-'),
- upperBytesPos = parts && parts.length > 1 &&
- parseInt(parts[1], 10);
- return upperBytesPos && upperBytesPos + 1;
- },
-
- // Uploads a file in multiple, sequential requests
- // by splitting the file up in multiple blob chunks.
- // If the second parameter is true, only tests if the file
- // should be uploaded in chunks, but does not invoke any
- // upload requests:
- _chunkedUpload: function (options, testOnly) {
- options.uploadedBytes = options.uploadedBytes || 0;
- var that = this,
- file = options.files[0],
- fs = file.size,
- ub = options.uploadedBytes,
- mcs = options.maxChunkSize || fs,
- slice = this._blobSlice,
- dfd = $.Deferred(),
- promise = dfd.promise(),
- jqXHR,
- upload;
-
- // Dynamically adjust the chunk size for Chunking V2 to fit into the 10000 chunk limit
- if (file.size/mcs > 10000) {
- mcs = Math.ceil(file.size/10000)
- }
-
- if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
- options.data) {
- return false;
- }
- if (testOnly) {
- return true;
- }
- if (ub >= fs) {
- file.error = options.i18n('uploadedBytes');
- return this._getXHRPromise(
- false,
- options.context,
- [null, 'error', file.error]
- );
- }
- // The chunk upload method:
- upload = function () {
- // Clone the options object for each chunk upload:
- var o = $.extend({}, options),
- currentLoaded = o._progress.loaded;
- o.blob = slice.call(
- file,
- ub,
- ub + mcs,
- file.type
- );
- // Store the current chunk size, as the blob itself
- // will be dereferenced after data processing:
- o.chunkSize = o.blob.size;
- // Expose the chunk bytes position range:
- o.contentRange = 'bytes ' + ub + '-' +
- (ub + o.chunkSize - 1) + '/' + fs;
- // Process the upload data (the blob and potential form data):
- that._initXHRData(o);
- // Add progress listeners for this chunk upload:
- that._initProgressListener(o);
- jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
- that._getXHRPromise(false, o.context))
- .done(function (result, textStatus, jqXHR) {
- ub = that._getUploadedBytes(jqXHR) ||
- (ub + o.chunkSize);
- // Create a progress event if no final progress event
- // with loaded equaling total has been triggered
- // for this chunk:
- if (currentLoaded + o.chunkSize - o._progress.loaded) {
- that._onProgress($.Event('progress', {
- lengthComputable: true,
- loaded: ub - o.uploadedBytes,
- total: ub - o.uploadedBytes
- }), o);
- }
- options.uploadedBytes = o.uploadedBytes = ub;
- o.result = result;
- o.textStatus = textStatus;
- o.jqXHR = jqXHR;
- that._trigger('chunkdone', null, o);
- that._trigger('chunkalways', null, o);
- if (ub < fs) {
- // File upload not yet complete,
- // continue with the next chunk:
- upload();
- } else {
- dfd.resolveWith(
- o.context,
- [result, textStatus, jqXHR]
- );
- }
- })
- .fail(function (jqXHR, textStatus, errorThrown) {
- o.jqXHR = jqXHR;
- o.textStatus = textStatus;
- o.errorThrown = errorThrown;
- that._trigger('chunkfail', null, o);
- that._trigger('chunkalways', null, o);
- dfd.rejectWith(
- o.context,
- [jqXHR, textStatus, errorThrown]
- );
- });
- };
- this._enhancePromise(promise);
- promise.abort = function () {
- return jqXHR.abort();
- };
- upload();
- return promise;
- },
-
- _beforeSend: function (e, data) {
- if (this._active === 0) {
- // the start callback is triggered when an upload starts
- // and no other uploads are currently running,
- // equivalent to the global ajaxStart event:
- this._trigger('start');
- // Set timer for global bitrate progress calculation:
- this._bitrateTimer = new this._BitrateTimer();
- // Reset the global progress values:
- this._progress.loaded = this._progress.total = 0;
- this._progress.bitrate = 0;
- }
- // Make sure the container objects for the .response() and
- // .progress() methods on the data object are available
- // and reset to their initial state:
- this._initResponseObject(data);
- this._initProgressObject(data);
- data._progress.loaded = data.loaded = data.uploadedBytes || 0;
- data._progress.total = data.total = this._getTotal(data.files) || 1;
- data._progress.bitrate = data.bitrate = 0;
- this._active += 1;
- // Initialize the global progress values:
- this._progress.loaded += data.loaded;
- this._progress.total += data.total;
- },
-
- _onDone: function (result, textStatus, jqXHR, options) {
- var total = options._progress.total,
- response = options._response;
- if (options._progress.loaded < total) {
- // Create a progress event if no final progress event
- // with loaded equaling total has been triggered:
- this._onProgress($.Event('progress', {
- lengthComputable: true,
- loaded: total,
- total: total
- }), options);
- }
- response.result = options.result = result;
- response.textStatus = options.textStatus = textStatus;
- response.jqXHR = options.jqXHR = jqXHR;
- this._trigger('done', null, options);
- },
-
- _onFail: function (jqXHR, textStatus, errorThrown, options) {
- var response = options._response;
- if (options.recalculateProgress) {
- // Remove the failed (error or abort) file upload from
- // the global progress calculation:
- this._progress.loaded -= options._progress.loaded;
- this._progress.total -= options._progress.total;
- }
- response.jqXHR = options.jqXHR = jqXHR;
- response.textStatus = options.textStatus = textStatus;
- response.errorThrown = options.errorThrown = errorThrown;
- this._trigger('fail', null, options);
- },
-
- _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
- // jqXHRorResult, textStatus and jqXHRorError are added to the
- // options object via done and fail callbacks
- this._trigger('always', null, options);
- },
-
- _onSend: function (e, data) {
- if (!data.submit) {
- this._addConvenienceMethods(e, data);
- }
- var that = this,
- jqXHR,
- aborted,
- slot,
- pipe,
- options = that._getAJAXSettings(data),
- send = function () {
- that._sending += 1;
- // Set timer for bitrate progress calculation:
- options._bitrateTimer = new that._BitrateTimer();
- jqXHR = jqXHR || (
- ((aborted || that._trigger(
- 'send',
- $.Event('send', {delegatedEvent: e}),
- options
- ) === false) &&
- that._getXHRPromise(false, options.context, aborted)) ||
- that._chunkedUpload(options) || $.ajax(options)
- ).done(function (result, textStatus, jqXHR) {
- that._onDone(result, textStatus, jqXHR, options);
- }).fail(function (jqXHR, textStatus, errorThrown) {
- that._onFail(jqXHR, textStatus, errorThrown, options);
- }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
- that._onAlways(
- jqXHRorResult,
- textStatus,
- jqXHRorError,
- options
- );
- that._sending -= 1;
- that._active -= 1;
- if (options.limitConcurrentUploads &&
- options.limitConcurrentUploads > that._sending) {
- // Start the next queued upload,
- // that has not been aborted:
- var nextSlot = that._slots.shift();
- while (nextSlot) {
- if (that._getDeferredState(nextSlot) === 'pending') {
- nextSlot.resolve();
- break;
- }
- nextSlot = that._slots.shift();
- }
- }
- if (that._active === 0) {
- // The stop callback is triggered when all uploads have
- // been completed, equivalent to the global ajaxStop event:
- that._trigger('stop');
- }
- });
- return jqXHR;
- };
- this._beforeSend(e, options);
- if (this.options.sequentialUploads ||
- (this.options.limitConcurrentUploads &&
- this.options.limitConcurrentUploads <= this._sending)) {
- if (this.options.limitConcurrentUploads > 1) {
- slot = $.Deferred();
- this._slots.push(slot);
- pipe = slot.then(send);
- } else {
- this._sequence = this._sequence.then(send, send);
- pipe = this._sequence;
- }
- // Return the piped Promise object, enhanced with an abort method,
- // which is delegated to the jqXHR object of the current upload,
- // and jqXHR callbacks mapped to the equivalent Promise methods:
- pipe.abort = function () {
- aborted = [undefined, 'abort', 'abort'];
- if (!jqXHR) {
- if (slot) {
- slot.rejectWith(options.context, aborted);
- }
- return send();
- }
- return jqXHR.abort();
- };
- return this._enhancePromise(pipe);
- }
- return send();
- },
-
- _onAdd: function (e, data) {
- var that = this,
- result = true,
- options = $.extend({}, this.options, data),
- files = data.files,
- filesLength = files.length,
- limit = options.limitMultiFileUploads,
- limitSize = options.limitMultiFileUploadSize,
- overhead = options.limitMultiFileUploadSizeOverhead,
- batchSize = 0,
- paramName = this._getParamName(options),
- paramNameSet,
- paramNameSlice,
- fileSet,
- i,
- j = 0;
- if (!filesLength) {
- return false;
- }
- if (limitSize && files[0].size === undefined) {
- limitSize = undefined;
- }
- if (!(options.singleFileUploads || limit || limitSize) ||
- !this._isXHRUpload(options)) {
- fileSet = [files];
- paramNameSet = [paramName];
- } else if (!(options.singleFileUploads || limitSize) && limit) {
- fileSet = [];
- paramNameSet = [];
- for (i = 0; i < filesLength; i += limit) {
- fileSet.push(files.slice(i, i + limit));
- paramNameSlice = paramName.slice(i, i + limit);
- if (!paramNameSlice.length) {
- paramNameSlice = paramName;
- }
- paramNameSet.push(paramNameSlice);
- }
- } else if (!options.singleFileUploads && limitSize) {
- fileSet = [];
- paramNameSet = [];
- for (i = 0; i < filesLength; i = i + 1) {
- batchSize += files[i].size + overhead;
- if (i + 1 === filesLength ||
- ((batchSize + files[i + 1].size + overhead) > limitSize) ||
- (limit && i + 1 - j >= limit)) {
- fileSet.push(files.slice(j, i + 1));
- paramNameSlice = paramName.slice(j, i + 1);
- if (!paramNameSlice.length) {
- paramNameSlice = paramName;
- }
- paramNameSet.push(paramNameSlice);
- j = i + 1;
- batchSize = 0;
- }
- }
- } else {
- paramNameSet = paramName;
- }
- data.originalFiles = [];
- $.each(files, function (file) {
- if (!file.isDirectory) {
- data.originalFiles.push(file);
- }
- });
- $.each(fileSet || files, function (index, element) {
- var newData = $.extend({}, data);
- newData.files = fileSet ? element : [element];
- newData.paramName = paramNameSet[index];
- that._initResponseObject(newData);
- that._initProgressObject(newData);
- that._addConvenienceMethods(e, newData);
- result = that._trigger(
- 'add',
- $.Event('add', {delegatedEvent: e}),
- newData
- );
- return result;
- });
- return result;
- },
-
- _replaceFileInput: function (data) {
- var input = data.fileInput,
- inputClone = input.clone(true),
- restoreFocus = input.is(document.activeElement);
- // Add a reference for the new cloned file input to the data argument:
- data.fileInputClone = inputClone;
- $('<form></form>').append(inputClone)[0].reset();
- // Detaching allows to insert the fileInput on another form
- // without losing the file input value:
- input.after(inputClone).detach();
- // If the fileInput had focus before it was detached,
- // restore focus to the inputClone.
- if (restoreFocus) {
- inputClone.focus();
- }
- // Avoid memory leaks with the detached file input:
- $.cleanData(input.unbind('remove'));
- // Replace the original file input element in the fileInput
- // elements set with the clone, which has been copied including
- // event handlers:
- this.options.fileInput = this.options.fileInput.map(function (i, el) {
- if (el === input[0]) {
- return inputClone[0];
- }
- return el;
- });
- // If the widget has been initialized on the file input itself,
- // override this.element with the file input clone:
- if (input[0] === this.element[0]) {
- this.element = inputClone;
- }
- },
-
- _handleFileTreeEntry: function (entry, path) {
- var that = this,
- dfd = $.Deferred(),
- errorHandler = function (e) {
- if (e && !e.entry) {
- e.entry = entry;
- }
- // Since $.when returns immediately if one
- // Deferred is rejected, we use resolve instead.
- // This allows valid files and invalid items
- // to be returned together in one set:
- dfd.resolve([e]);
- },
- successHandler = function (entries) {
- that._handleFileTreeEntries(
- entries,
- path + entry.name + '/'
- ).done(function (files) {
- // empty folder
- if (!files.length && entry.isDirectory) {
- dfd.resolve(entry);
- } else {
- dfd.resolve(files);
- }
- }).fail(errorHandler);
- },
- readEntries = function () {
- dirReader.readEntries(function (results) {
- if (!results.length) {
- successHandler(entries);
- } else {
- entries = entries.concat(results);
- readEntries();
- }
- }, errorHandler);
- },
- dirReader, entries = [];
- path = path || '';
- if (entry.isFile) {
- if (entry._file) {
- // Workaround for Chrome bug #149735
- entry._file.relativePath = path;
- dfd.resolve(entry._file);
- } else {
- entry.file(function (file) {
- file.relativePath = path;
- dfd.resolve(file);
- }, errorHandler);
- }
- } else if (entry.isDirectory) {
- dirReader = entry.createReader();
- readEntries();
- } else {
- // Return an empty list for file system items
- // other than files or directories:
- dfd.resolve([]);
- }
- return dfd.promise();
- },
-
- _handleFileTreeEntries: function (entries, path) {
- var that = this;
- return $.when.apply(
- $,
- $.map(entries, function (entry) {
- return that._handleFileTreeEntry(entry, path);
- })
- ).then(function () {
- return Array.prototype.concat.apply(
- [],
- arguments
- );
- });
- },
-
- _getDroppedFiles: function (dataTransfer) {
- dataTransfer = dataTransfer || {};
- var items = dataTransfer.items;
- if (items && items.length && (items[0].webkitGetAsEntry ||
- items[0].getAsEntry)) {
- return this._handleFileTreeEntries(
- $.map(items, function (item) {
- var entry;
- if (item.webkitGetAsEntry) {
- entry = item.webkitGetAsEntry();
- if (entry) {
- // Workaround for Chrome bug #149735:
- entry._file = item.getAsFile();
- }
- return entry;
- }
- return item.getAsEntry();
- })
- );
- }
- return $.Deferred().resolve(
- $.makeArray(dataTransfer.files)
- ).promise();
- },
-
- _getSingleFileInputFiles: function (fileInput) {
- fileInput = $(fileInput);
- var entries = fileInput.prop('webkitEntries') ||
- fileInput.prop('entries'),
- files,
- value;
- if (entries && entries.length) {
- return this._handleFileTreeEntries(entries);
- }
- files = $.makeArray(fileInput.prop('files'));
- if (!files.length) {
- value = fileInput.prop('value');
- if (!value) {
- return $.Deferred().resolve([]).promise();
- }
- // If the files property is not available, the browser does not
- // support the File API and we add a pseudo File object with
- // the input value as name with path information removed:
- files = [{name: value.replace(/^.*\\/, '')}];
- } else if (files[0].name === undefined && files[0].fileName) {
- // File normalization for Safari 4 and Firefox 3:
- $.each(files, function (index, file) {
- file.name = file.fileName;
- file.size = file.fileSize;
- });
- }
- return $.Deferred().resolve(files).promise();
- },
-
- _getFileInputFiles: function (fileInput) {
- if (!(fileInput instanceof $) || fileInput.length === 1) {
- return this._getSingleFileInputFiles(fileInput);
- }
- return $.when.apply(
- $,
- $.map(fileInput, this._getSingleFileInputFiles)
- ).then(function () {
- return Array.prototype.concat.apply(
- [],
- arguments
- );
- });
- },
-
- _onChange: function (e) {
- var that = this,
- data = {
- fileInput: $(e.target),
- form: $(e.target.form)
- };
- this._getFileInputFiles(data.fileInput).always(function (files) {
- data.files = files;
- if (that.options.replaceFileInput) {
- that._replaceFileInput(data);
- }
- if (that._trigger(
- 'change',
- $.Event('change', {delegatedEvent: e}),
- data
- ) !== false) {
- that._onAdd(e, data);
- }
- });
- },
-
- _onPaste: function (e) {
- var items = e.originalEvent && e.originalEvent.clipboardData &&
- e.originalEvent.clipboardData.items,
- data = {files: []};
- if (items && items.length) {
- $.each(items, function (index, item) {
- var file = item.getAsFile && item.getAsFile();
- if (file) {
- data.files.push(file);
- }
- });
- if (this._trigger(
- 'paste',
- $.Event('paste', {delegatedEvent: e}),
- data
- ) !== false) {
- this._onAdd(e, data);
- }
- }
- },
-
- _onDrop: function (e) {
- e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
- var that = this,
- dataTransfer = e.dataTransfer,
- data = {};
- if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
- e.preventDefault();
- this._getDroppedFiles(dataTransfer).always(function (files) {
- data.files = files;
- if (that._trigger(
- 'drop',
- $.Event('drop', {delegatedEvent: e}),
- data
- ) !== false) {
- that._onAdd(e, data);
- }
- });
- } else {
- // "dropnofiles" is triggered to allow proper cleanup of the
- // drag and drop operation, as some browsers trigger "drop"
- // events that have no files even if the "DataTransfer.types" of
- // the "dragover" event included a "Files" item.
- this._trigger(
- 'dropnofiles',
- $.Event('drop', {delegatedEvent: e})
- );
- }
- },
-
- _onDragOver: getDragHandler('dragover'),
-
- _onDragEnter: getDragHandler('dragenter'),
-
- _onDragLeave: getDragHandler('dragleave'),
-
- _initEventHandlers: function () {
- if (this._isXHRUpload(this.options)) {
- this._on(this.options.dropZone, {
- dragover: this._onDragOver,
- drop: this._onDrop,
- // event.preventDefault() on dragenter is required for IE10+:
- dragenter: this._onDragEnter,
- // dragleave is not required, but added for completeness:
- dragleave: this._onDragLeave
- });
- this._on(this.options.pasteZone, {
- paste: this._onPaste
- });
- }
- if ($.support.fileInput) {
- this._on(this.options.fileInput, {
- change: this._onChange
- });
- }
- },
-
- _destroyEventHandlers: function () {
- this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
- this._off(this.options.pasteZone, 'paste');
- this._off(this.options.fileInput, 'change');
- },
-
- _setOption: function (key, value) {
- var reinit = $.inArray(key, this._specialOptions) !== -1;
- if (reinit) {
- this._destroyEventHandlers();
- }
- this._super(key, value);
- if (reinit) {
- this._initSpecialOptions();
- this._initEventHandlers();
- }
- },
-
- _initSpecialOptions: function () {
- var options = this.options;
- if (options.fileInput === undefined) {
- options.fileInput = this.element.is('input[type="file"]') ?
- this.element : this.element.find('input[type="file"]');
- } else if (!(options.fileInput instanceof $)) {
- options.fileInput = $(options.fileInput);
- }
- if (!(options.dropZone instanceof $)) {
- options.dropZone = $(options.dropZone);
- }
- if (!(options.pasteZone instanceof $)) {
- options.pasteZone = $(options.pasteZone);
- }
- },
-
- _getRegExp: function (str) {
- var parts = str.split('/'),
- modifiers = parts.pop();
- parts.shift();
- return new RegExp(parts.join('/'), modifiers);
- },
-
- _isRegExpOption: function (key, value) {
- return key !== 'url' && $.type(value) === 'string' &&
- /^\/.*\/[igm]{0,3}$/.test(value);
- },
-
- _initDataAttributes: function () {
- var that = this,
- options = this.options,
- data = this.element.data();
- // Initialize options set via HTML5 data-attributes:
- $.each(
- this.element[0].attributes,
- function (index, attr) {
- var key = attr.name.toLowerCase(),
- value;
- if (/^data-/.test(key)) {
- // Convert hyphen-ated key to camelCase:
- key = key.slice(5).replace(/-[a-z]/g, function (str) {
- return str.charAt(1).toUpperCase();
- });
- value = data[key];
- if (that._isRegExpOption(key, value)) {
- value = that._getRegExp(value);
- }
- options[key] = value;
- }
- }
- );
- },
-
- _create: function () {
- this._initDataAttributes();
- this._initSpecialOptions();
- this._slots = [];
- this._sequence = this._getXHRPromise(true);
- this._sending = this._active = 0;
- this._initProgressObject(this);
- this._initEventHandlers();
- },
-
- // This method is exposed to the widget API and allows to query
- // the number of active uploads:
- active: function () {
- return this._active;
- },
-
- // This method is exposed to the widget API and allows to query
- // the widget upload progress.
- // It returns an object with loaded, total and bitrate properties
- // for the running uploads:
- progress: function () {
- return this._progress;
- },
-
- // This method is exposed to the widget API and allows adding files
- // using the fileupload API. The data parameter accepts an object which
- // must have a files property and can contain additional options:
- // .fileupload('add', {files: filesList});
- add: function (data) {
- var that = this;
- if (!data || this.options.disabled) {
- return;
- }
- if (data.fileInput && !data.files) {
- this._getFileInputFiles(data.fileInput).always(function (files) {
- data.files = files;
- that._onAdd(null, data);
- });
- } else {
- data.files = $.makeArray(data.files);
- this._onAdd(null, data);
- }
- },
-
- // This method is exposed to the widget API and allows sending files
- // using the fileupload API. The data parameter accepts an object which
- // must have a files or fileInput property and can contain additional options:
- // .fileupload('send', {files: filesList});
- // The method returns a Promise object for the file upload call.
- send: function (data) {
- if (data && !this.options.disabled) {
- if (data.fileInput && !data.files) {
- var that = this,
- dfd = $.Deferred(),
- promise = dfd.promise(),
- jqXHR,
- aborted;
- promise.abort = function () {
- aborted = true;
- if (jqXHR) {
- return jqXHR.abort();
- }
- dfd.reject(null, 'abort', 'abort');
- return promise;
- };
- this._getFileInputFiles(data.fileInput).always(
- function (files) {
- if (aborted) {
- return;
- }
- if (!files.length) {
- dfd.reject();
- return;
- }
- data.files = files;
- jqXHR = that._onSend(null, data);
- jqXHR.then(
- function (result, textStatus, jqXHR) {
- dfd.resolve(result, textStatus, jqXHR);
- },
- function (jqXHR, textStatus, errorThrown) {
- dfd.reject(jqXHR, textStatus, errorThrown);
- }
- );
- }
- );
- return this._enhancePromise(promise);
- }
- data.files = $.makeArray(data.files);
- if (data.files.length) {
- return this._onSend(null, data);
- }
- }
- return this._getXHRPromise(false, data && data.context);
- }
-
- });
-
-}));
diff --git a/apps/files/js/keyboardshortcuts.js b/apps/files/js/keyboardshortcuts.js
deleted file mode 100644
index ebf6232d159..00000000000
--- a/apps/files/js/keyboardshortcuts.js
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2012-2014 ownCloud, Inc.
- * SPDX-FileCopyrightText: 2012 Erik Sargent <esthepiking at gmail dot com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-/*****************************
- * Keyboard shortcuts for Files app
- * ctrl/cmd+n: new folder
- * ctrl/cmd+shift+n: new file
- * esc (while new file context menu is open): close menu
- * up/down: select file/folder
- * enter: open file/folder
- * delete/backspace: delete file/folder
- *****************************/
-(function(Files) {
- var keys = [];
- var keyCodes = {
- shift: 16,
- n: 78,
- cmdFirefox: 224,
- cmdOpera: 17,
- leftCmdWebKit: 91,
- rightCmdWebKit: 93,
- ctrl: 17,
- esc: 27,
- downArrow: 40,
- upArrow: 38,
- enter: 13,
- del: 46
- };
-
- function removeA(arr) {
- var what, a = arguments,
- L = a.length,
- ax;
- while (L > 1 && arr.length) {
- what = a[--L];
- while ((ax = arr.indexOf(what)) !== -1) {
- arr.splice(ax, 1);
- }
- }
- return arr;
- }
-
- function newFile() {
- $("#new").addClass("active");
- $(".popup.popupTop").toggle(true);
- $('#new li[data-type="file"]').trigger('click');
- removeA(keys, keyCodes.n);
- }
-
- function newFolder() {
- $("#new").addClass("active");
- $(".popup.popupTop").toggle(true);
- $('#new li[data-type="folder"]').trigger('click');
- removeA(keys, keyCodes.n);
- }
-
- function esc() {
- $(".files-controls").trigger('click');
- }
-
- function down() {
- var select = -1;
- $(".files-fileList tr").each(function(index) {
- if ($(this).hasClass("mouseOver")) {
- select = index + 1;
- $(this).removeClass("mouseOver");
- }
- });
- if (select === -1) {
- $(".files-fileList tr:first").addClass("mouseOver");
- } else {
- $(".files-fileList tr").each(function(index) {
- if (index === select) {
- $(this).addClass("mouseOver");
- }
- });
- }
- }
-
- function up() {
- var select = -1;
- $(".files-fileList tr").each(function(index) {
- if ($(this).hasClass("mouseOver")) {
- select = index - 1;
- $(this).removeClass("mouseOver");
- }
- });
- if (select === -1) {
- $(".files-fileList tr:last").addClass("mouseOver");
- } else {
- $(".files-fileList tr").each(function(index) {
- if (index === select) {
- $(this).addClass("mouseOver");
- }
- });
- }
- }
-
- function enter() {
- $(".files-fileList tr").each(function(index) {
- if ($(this).hasClass("mouseOver")) {
- $(this).removeClass("mouseOver");
- $(this).find("span.nametext").trigger('click');
- }
- });
- }
-
- function del() {
- $(".files-fileList tr").each(function(index) {
- if ($(this).hasClass("mouseOver")) {
- $(this).removeClass("mouseOver");
- $(this).find("a.action.delete").trigger('click');
- }
- });
- }
-
- function rename() {
- $(".files-fileList tr").each(function(index) {
- if ($(this).hasClass("mouseOver")) {
- $(this).removeClass("mouseOver");
- $(this).find("a[data-action='Rename']").trigger('click');
- }
- });
- }
- Files.bindKeyboardShortcuts = function(document, $) {
- $(document).keydown(function(event) { //check for modifier keys
- if(!$(event.target).is('body')) {
- return;
- }
- var preventDefault = false;
- if ($.inArray(event.keyCode, keys) === -1) {
- keys.push(event.keyCode);
- }
- if (
- $.inArray(keyCodes.n, keys) !== -1 && ($.inArray(keyCodes.cmdFirefox, keys) !== -1 || $.inArray(keyCodes.cmdOpera, keys) !== -1 || $.inArray(keyCodes.leftCmdWebKit, keys) !== -1 || $.inArray(keyCodes.rightCmdWebKit, keys) !== -1 || $.inArray(keyCodes.ctrl, keys) !== -1 || event.ctrlKey)) {
- preventDefault = true; //new file/folder prevent browser from responding
- }
- if (preventDefault) {
- event.preventDefault(); //Prevent web browser from responding
- event.stopPropagation();
- return false;
- }
- });
- $(document).keyup(function(event) {
- // do your event.keyCode checks in here
- if (
- $.inArray(keyCodes.n, keys) !== -1 && ($.inArray(keyCodes.cmdFirefox, keys) !== -1 || $.inArray(keyCodes.cmdOpera, keys) !== -1 || $.inArray(keyCodes.leftCmdWebKit, keys) !== -1 || $.inArray(keyCodes.rightCmdWebKit, keys) !== -1 || $.inArray(keyCodes.ctrl, keys) !== -1 || event.ctrlKey)) {
- if ($.inArray(keyCodes.shift, keys) !== -1) { //16=shift, New File
- newFile();
- } else { //New Folder
- newFolder();
- }
- } else if ($("#new").hasClass("active") && $.inArray(keyCodes.esc, keys) !== -1) { //close new window
- esc();
- } else if ($.inArray(keyCodes.downArrow, keys) !== -1) { //select file
- down();
- } else if ($.inArray(keyCodes.upArrow, keys) !== -1) { //select file
- up();
- } else if (!$("#new").hasClass("active") && $.inArray(keyCodes.enter, keys) !== -1) { //open file
- enter();
- } else if (!$("#new").hasClass("active") && $.inArray(keyCodes.del, keys) !== -1) { //delete file
- del();
- }
- removeA(keys, event.keyCode);
- });
- };
-})((OCA.Files && OCA.Files.Files) || {});
diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js
deleted file mode 100644
index 458a532daaf..00000000000
--- a/apps/files/js/mainfileinfodetailview.js
+++ /dev/null
@@ -1,191 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- /**
- * @class OCA.Files.MainFileInfoDetailView
- * @classdesc
- *
- * Displays main details about a file
- *
- */
- var MainFileInfoDetailView = OCA.Files.DetailFileInfoView.extend(
- /** @lends OCA.Files.MainFileInfoDetailView.prototype */ {
-
- className: 'mainFileInfoView',
-
- /**
- * Associated file list instance, for file actions
- *
- * @type {OCA.Files.FileList}
- */
- _fileList: null,
-
- /**
- * File actions
- *
- * @type {OCA.Files.FileActions}
- */
- _fileActions: null,
-
- /**
- * @type {OCA.Files.SidebarPreviewManager}
- */
- _previewManager: null,
-
- events: {
- 'click a.action-favorite': '_onClickFavorite',
- 'click a.action-default': '_onClickDefaultAction',
- 'click a.permalink': '_onClickPermalink',
- 'focus .permalink-field>input': '_onFocusPermalink'
- },
-
- template: function(data) {
- return OCA.Files.Templates['mainfileinfodetailsview'](data);
- },
-
- initialize: function(options) {
- options = options || {};
- this._fileList = options.fileList;
- this._fileActions = options.fileActions;
- if (!this._fileList) {
- throw 'Missing required parameter "fileList"';
- }
- if (!this._fileActions) {
- throw 'Missing required parameter "fileActions"';
- }
- this._previewManager = new OCA.Files.SidebarPreviewManager(this._fileList);
-
- this._setupClipboard();
- },
-
- _setupClipboard: function() {
- var clipboard = new Clipboard('.permalink');
- clipboard.on('success', function(e) {
- OC.Notification.show(t('files', 'Direct link was copied (only works for people who have access to this file/folder)'), {type: 'success'});
- });
- clipboard.on('error', function(e) {
- var $row = this.$('.permalink-field');
- $row.toggleClass('hidden');
- if (!$row.hasClass('hidden')) {
- $row.find('>input').focus();
- }
- });
- },
-
- _onClickPermalink: function(e) {
- e.preventDefault();
- return;
- },
-
- _onFocusPermalink: function() {
- this.$('.permalink-field>input').select();
- },
-
- _onClickFavorite: function(event) {
- event.preventDefault();
- this._fileActions.triggerAction('Favorite', this.model, this._fileList);
- },
-
- _onClickDefaultAction: function(event) {
- event.preventDefault();
- this._fileActions.triggerAction(null, this.model, this._fileList);
- },
-
- _onModelChanged: function() {
- // simply re-render
- this.render();
- },
-
- _makePermalink: function(fileId) {
- var baseUrl = OC.getProtocol() + '://' + OC.getHost();
- return baseUrl + OC.generateUrl('/f/{fileId}', {fileId: fileId});
- },
-
- setFileInfo: function(fileInfo) {
- if (this.model) {
- this.model.off('change', this._onModelChanged, this);
- }
- this.model = fileInfo;
- if (this.model) {
- this.model.on('change', this._onModelChanged, this);
- }
-
- if (this.model) {
- var properties = [];
- if( !this.model.has('size') ) {
- properties.push(OC.Files.Client.PROPERTY_SIZE);
- properties.push(OC.Files.Client.PROPERTY_GETCONTENTLENGTH);
- }
-
- if( properties.length > 0){
- this.model.reloadProperties(properties);
- }
- }
-
- this.render();
- },
-
- /**
- * Renders this details view
- */
- render: function() {
- this.trigger('pre-render');
-
- if (this.model) {
- var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
- var availableActions = this._fileActions.get(
- this.model.get('mimetype'),
- this.model.get('type'),
- this.model.get('permissions'),
- this.model.get('name')
- );
- var hasFavoriteAction = 'Favorite' in availableActions;
- this.$el.html(this.template({
- type: this.model.isImage()? 'image': '',
- nameLabel: t('files', 'Name'),
- name: this.model.get('displayName') || this.model.get('name'),
- pathLabel: t('files', 'Path'),
- path: this.model.get('path'),
- hasSize: this.model.has('size'),
- sizeLabel: t('files', 'Size'),
- size: OC.Util.humanFileSize(this.model.get('size'), true, false),
- altSize: n('files', '%n byte', '%n bytes', this.model.get('size')),
- dateLabel: t('files', 'Modified'),
- altDate: OC.Util.formatDate(this.model.get('mtime')),
- timestamp: this.model.get('mtime'),
- date: OC.Util.relativeModifiedDate(this.model.get('mtime')),
- hasFavoriteAction: hasFavoriteAction,
- starAltText: isFavorite ? t('files', 'Favorited') : t('files', 'Favorite'),
- starClass: isFavorite ? 'icon-starred' : 'icon-star',
- permalink: this._makePermalink(this.model.get('id')),
- permalinkTitle: t('files', 'Copy direct link (only works for people who have access to this file/folder)')
- }));
-
- // TODO: we really need OC.Previews
- var $iconDiv = this.$el.find('.thumbnail');
- var $container = this.$el.find('.thumbnailContainer');
- if (!this.model.isDirectory()) {
- $iconDiv.addClass('icon-loading icon-32');
- this._previewManager.loadPreview(this.model, $iconDiv, $container);
- } else {
- var iconUrl = this.model.get('icon') || OC.MimeType.getIconUrl('dir');
- if (typeof this.model.get('mountType') !== 'undefined') {
- iconUrl = OC.MimeType.getIconUrl('dir-' + this.model.get('mountType'))
- }
- $iconDiv.css('background-image', 'url("' + iconUrl + '")');
- }
- } else {
- this.$el.empty();
- }
- this.delegateEvents();
-
- this.trigger('post-render');
- }
- });
-
- OCA.Files.MainFileInfoDetailView = MainFileInfoDetailView;
-})();
diff --git a/apps/files/js/merged-index.json b/apps/files/js/merged-index.json
deleted file mode 100644
index 870a807f718..00000000000
--- a/apps/files/js/merged-index.json
+++ /dev/null
@@ -1,28 +0,0 @@
-[
- "app.js",
- "breadcrumb.js",
- "detailfileinfoview.js",
- "detailsview.js",
- "detailtabview.js",
- "file-upload.js",
- "fileactions.js",
- "fileactionsmenu.js",
- "fileinfomodel.js",
- "filelist.js",
- "filemultiselectmenu.js",
- "files.js",
- "filesummary.js",
- "gotoplugin.js",
- "jquery-visibility.js",
- "jquery.fileupload.js",
- "keyboardshortcuts.js",
- "mainfileinfodetailview.js",
- "newfilemenu.js",
- "operationprogressbar.js",
- "recentfilelist.js",
- "semaphore.js",
- "sidebarpreviewmanager.js",
- "sidebarpreviewtext.js",
- "tagsplugin.js",
- "templates.js"
-]
diff --git a/apps/files/js/merged-index.json.license b/apps/files/js/merged-index.json.license
deleted file mode 100644
index 878633bd1b7..00000000000
--- a/apps/files/js/merged-index.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file
diff --git a/apps/files/js/newfilemenu.js b/apps/files/js/newfilemenu.js
deleted file mode 100644
index 7a0d1be555d..00000000000
--- a/apps/files/js/newfilemenu.js
+++ /dev/null
@@ -1,261 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-/* global Files */
-
-(function() {
-
- /**
- * Construct a new NewFileMenu instance
- * @constructs NewFileMenu
- *
- * @memberof OCA.Files
- */
- var NewFileMenu = OC.Backbone.View.extend({
- tagName: 'div',
- // Menu is opened by default because it's rendered on "add-button" click
- className: 'newFileMenu popovermenu bubble menu open menu-left',
-
- events: {
- 'click .menuitem': '_onClickAction'
- },
-
- /**
- * @type OCA.Files.FileList
- */
- fileList: null,
-
- initialize: function(options) {
- var self = this;
- var $uploadEl = $('#file_upload_start');
- if ($uploadEl.length) {
- $uploadEl.on('fileuploadstart', function() {
- self.trigger('actionPerformed', 'upload');
- });
- } else {
- console.warn('Missing upload element "file_upload_start"');
- }
-
- this.fileList = options && options.fileList;
-
- this._menuItems = [{
- id: 'folder',
- displayName: t('files', 'New folder'),
- templateName: t('files', 'New folder'),
- iconClass: 'icon-folder',
- fileType: 'folder',
- actionLabel: t('files', 'Create new folder'),
- actionHandler: function(name) {
- self.fileList.createDirectory(name);
- }
- }];
-
- OC.Plugins.attach('OCA.Files.NewFileMenu', this);
- },
-
- template: function(data) {
- return OCA.Files.Templates['newfilemenu'](data);
- },
-
- /**
- * Event handler whenever an action has been clicked within the menu
- *
- * @param {Object} event event object
- */
- _onClickAction: function(event) {
- var $target = $(event.target);
- if (!$target.hasClass('menuitem')) {
- $target = $target.closest('.menuitem');
- }
- var action = $target.attr('data-action');
- // note: clicking the upload label will automatically
- // set the focus on the "file_upload_start" hidden field
- // which itself triggers the upload dialog.
- // Currently the upload logic is still in file-upload.js and filelist.js
- if (action === 'upload') {
- OC.hideMenus();
- } else {
- var actionItem = _.filter(this._menuItems, function(item) {
- return item.id === action
- }).pop();
- if (typeof actionItem.useInput === 'undefined' || actionItem.useInput === true) {
- event.preventDefault();
- this.$el.find('.menuitem.active').removeClass('active');
- $target.addClass('active');
- this._promptFileName($target);
- } else {
- actionItem.actionHandler();
- OC.hideMenus();
- }
- }
- },
-
- _promptFileName: function($target) {
- var self = this;
-
- if ($target.find('form').length) {
- $target.find('input[type=\'text\']').focus();
- return;
- }
-
- // discard other forms
- this.$el.find('form').remove();
- this.$el.find('.displayname').removeClass('hidden');
-
- $target.find('.displayname').addClass('hidden');
-
- var newName = $target.attr('data-templatename');
- var fileType = $target.attr('data-filetype');
- var actionLabel = $target.attr('data-action-label');
- var $form = $(OCA.Files.Templates['newfilemenu_filename_form']({
- fileName: newName,
- cid: this.cid,
- fileType: fileType,
- actionLabel,
- }));
-
- //this.trigger('actionPerformed', action);
- $target.append($form);
-
- // here comes the OLD code
- var $input = $form.find('input[type=\'text\']');
- var $submit = $form.find('input[type=\'submit\']');
-
- var lastPos;
- var checkInput = function () {
- // Special handling for the setup template directory
- if ($target.attr('data-action') === 'template-init') {
- return true;
- }
-
- var filename = $input.val();
- try {
- if (!Files.isFileNameValid(filename)) {
- // Files.isFileNameValid(filename) throws an exception itself
- } else if (self.fileList.inList(filename)) {
- throw t('files', '{newName} already exists', {newName: filename}, undefined, {
- escape: false
- });
- } else {
- return true;
- }
- } catch (error) {
- $input.attr('title', error);
- $input.addClass('error');
- }
- return false;
- };
-
- // verify filename on typing
- $input.keyup(function() {
- if (checkInput()) {
- $input.removeClass('error');
- }
- });
-
- $submit.click(function(event) {
- event.stopPropagation();
- event.preventDefault();
- $form.submit();
- });
-
- $input.focus();
- // pre select name up to the extension
- lastPos = newName.lastIndexOf('.');
- if (lastPos === -1) {
- lastPos = newName.length;
- }
- $input.selectRange(0, lastPos);
-
- $form.submit(function(event) {
- event.stopPropagation();
- event.preventDefault();
-
- if (checkInput()) {
- var newname = $input.val().trim();
-
- /* Find the right actionHandler that should be called.
- * Actions is retrieved by using `actionSpec.id` */
- var action = _.filter(self._menuItems, function(item) {
- return item.id == $target.attr('data-action');
- }).pop();
- action.actionHandler(newname);
-
- $form.remove();
- $target.find('.displayname').removeClass('hidden');
- OC.hideMenus();
- }
- });
- },
-
- /**
- * Add a new item menu entry in the “New” file menu (in
- * last position). By clicking on the item, the
- * `actionHandler` function is called.
- *
- * @param {Object} actionSpec item’s properties
- */
- addMenuEntry: function(actionSpec) {
- this._menuItems.push({
- id: actionSpec.id,
- displayName: actionSpec.displayName,
- templateName: actionSpec.templateName,
- iconClass: actionSpec.iconClass,
- fileType: actionSpec.fileType,
- useInput: actionSpec.useInput,
- actionLabel: actionSpec.actionLabel,
- actionHandler: actionSpec.actionHandler,
- checkFilename: actionSpec.checkFilename,
- shouldShow: actionSpec.shouldShow,
- });
- },
-
- /**
- * Remove a menu item from the "New" file menu
- * @param {string} actionId
- */
- removeMenuEntry: function(actionId) {
- var index = this._menuItems.findIndex(function (actionSpec) {
- return actionSpec.id === actionId;
- });
- if (index > -1) {
- this._menuItems.splice(index, 1);
- }
- },
-
- /**
- * Renders the menu with the currently set items
- */
- render: function() {
- const menuItems = this._menuItems.filter(item => !item.shouldShow || (item.shouldShow instanceof Function && item.shouldShow() === true))
- this.$el.html(this.template({
- uploadMaxHumanFileSize: 'TODO',
- uploadLabel: t('files', 'Upload file'),
- items: menuItems
- }));
-
- // Trigger upload action also with keyboard navigation on enter
- this.$el.find('[for="file_upload_start"]').on('keyup', function(event) {
- if (event.key === " " || event.key === "Enter") {
- $('#file_upload_start').trigger('click');
- }
- });
- },
-
- /**
- * Displays the menu under the given element
- *
- * @param {Object} $target target element
- */
- showAt: function($target) {
- this.render();
- OC.showMenu($target, this.$el);
- }
- });
-
- OCA.Files.NewFileMenu = NewFileMenu;
-
-})();
diff --git a/apps/files/js/operationprogressbar.js b/apps/files/js/operationprogressbar.js
deleted file mode 100755
index 8f43937b857..00000000000
--- a/apps/files/js/operationprogressbar.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function() {
- var OperationProgressBar = OC.Backbone.View.extend({
- tagName: 'div',
- id: 'uploadprogresswrapper',
- events: {
- 'click button.stop': '_onClickCancel'
- },
-
- render: function() {
- this.$el.html(OCA.Files.Templates['operationprogressbar']({
- textCancelButton: t('Cancel operation')
- }));
- this.setProgressBarText(t('Uploading …'), t('…'));
- },
-
- hideProgressBar: function() {
- var self = this;
- $('#uploadprogresswrapper .stop').fadeOut();
- $('#uploadprogressbar').fadeOut(function() {
- self.$el.trigger(new $.Event('resized'));
- });
- },
-
- hideCancelButton: function() {
- var self = this;
- $('#uploadprogresswrapper .stop').fadeOut(function() {
- self.$el.trigger(new $.Event('resized'));
- });
- },
-
- showProgressBar: function(showCancelButton) {
- if (showCancelButton) {
- showCancelButton = true;
- }
- $('#uploadprogressbar').progressbar({value: 0});
- if(showCancelButton) {
- $('#uploadprogresswrapper .stop').show();
- } else {
- $('#uploadprogresswrapper .stop').hide();
- }
- $('#uploadprogresswrapper .label').show();
- $('#uploadprogressbar').fadeIn();
- this.$el.trigger(new $.Event('resized'));
- },
-
- setProgressBarValue: function(value) {
- $('#uploadprogressbar').progressbar({value: value});
- },
-
- setProgressBarText: function(textDesktop, textMobile, title) {
- var labelHtml = OCA.Files.Templates['operationprogressbarlabel']({textDesktop: textDesktop, textMobile: textMobile});
- $('#uploadprogressbar .ui-progressbar-value').html(labelHtml);
- $('#uploadprogressbar .ui-progressbar-value>em').addClass('inner');
- $('#uploadprogressbar>em').replaceWith(labelHtml);
- $('#uploadprogressbar>em').addClass('outer');
- if (title) {
- $('#uploadprogressbar').attr('title', title);
- $('#uploadprogresswrapper .tooltip-inner').text(title);
- }
- if(textDesktop || textMobile) {
- $('#uploadprogresswrapper .stop').show();
- }
- },
-
- _onClickCancel: function (event) {
- this.trigger('cancel');
- return false;
- }
- });
-
- OCA.Files.OperationProgressBar = OperationProgressBar;
-})(OC, OCA);
diff --git a/apps/files/js/recentfilelist.js b/apps/files/js/recentfilelist.js
deleted file mode 100644
index 248f1c11c62..00000000000
--- a/apps/files/js/recentfilelist.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-// HACK: this piece needs to be loaded AFTER the files app (for unit tests)
-window.addEventListener('DOMContentLoaded', function () {
- (function (OCA) {
- /**
- * @class OCA.Files.RecentFileList
- * @augments OCA.Files.RecentFileList
- *
- * @classdesc Recent file list.
- * Displays the list of recently modified files
- *
- * @param $el container element with existing markup for the .files-controls
- * and a table
- * @param [options] map of options, see other parameters
- */
- var RecentFileList = function ($el, options) {
- options.sorting = {
- mode: 'mtime',
- direction: 'desc'
- };
- this.initialize($el, options);
- this._allowSorting = false;
- };
- RecentFileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
- /** @lends OCA.Files.RecentFileList.prototype */ {
- id: 'recent',
- appName: t('files', 'Recent'),
-
- _clientSideSort: true,
- _allowSelection: false,
-
- /**
- * @private
- */
- initialize: function () {
- OCA.Files.FileList.prototype.initialize.apply(this, arguments);
- if (this.initialized) {
- return;
- }
- OC.Plugins.attach('OCA.Files.RecentFileList', this);
- },
-
- updateEmptyContent: function () {
- var dir = this.getCurrentDirectory();
- if (dir === '/') {
- // root has special permissions
- this.$el.find('.emptyfilelist.emptycontent').toggleClass('hidden', !this.isEmpty);
- this.$el.find('.files-filestable thead th').toggleClass('hidden', this.isEmpty);
- }
- else {
- OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments);
- }
- },
-
- getDirectoryPermissions: function () {
- return OC.PERMISSION_READ | OC.PERMISSION_DELETE;
- },
-
- updateStorageStatistics: function () {
- // no op because it doesn't have
- // storage info like free space / used space
- },
-
- reload: function () {
- this.showMask();
- if (this._reloadCall?.abort) {
- this._reloadCall.abort();
- }
-
- // there is only root
- this._setCurrentDir('/', false);
-
- this._reloadCall = $.ajax({
- url: OC.generateUrl('/apps/files/api/v1/recent'),
- type: 'GET',
- dataType: 'json'
- });
- var callBack = this.reloadCallback.bind(this);
- return this._reloadCall.then(callBack, callBack);
- },
-
- reloadCallback: function (result) {
- delete this._reloadCall;
- this.hideMask();
-
- if (result.files) {
- this.setFiles(result.files.sort(this._sortComparator));
- return true;
- }
- return false;
- }
- });
-
- OCA.Files.RecentFileList = RecentFileList;
- })(OCA);
-});
-
diff --git a/apps/files/js/semaphore.js b/apps/files/js/semaphore.js
deleted file mode 100644
index 3e0d61e922a..00000000000
--- a/apps/files/js/semaphore.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function(){
- var Semaphore = function(max) {
- var counter = 0;
- var waiting = [];
-
- this.acquire = function() {
- if(counter < max) {
- counter++;
- return new Promise(function(resolve) { resolve(); });
- } else {
- return new Promise(function(resolve) { waiting.push(resolve); });
- }
- };
-
- this.release = function() {
- counter--;
- if (waiting.length > 0 && counter < max) {
- counter++;
- var promise = waiting.shift();
- promise();
- }
- };
- };
-
- // needed on public share page to properly register this
- if (!OCA.Files) {
- OCA.Files = {};
- }
- OCA.Files.Semaphore = Semaphore;
-
-})();
diff --git a/apps/files/js/sidebarpreviewmanager.js b/apps/files/js/sidebarpreviewmanager.js
deleted file mode 100644
index de1d1293074..00000000000
--- a/apps/files/js/sidebarpreviewmanager.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function () {
- var SidebarPreviewManager = function (fileList) {
- this._fileList = fileList;
- this._previewHandlers = {};
- OC.Plugins.attach('OCA.Files.SidebarPreviewManager', this);
- };
-
- SidebarPreviewManager.prototype = {
- addPreviewHandler: function (mime, handler) {
- this._previewHandlers[mime] = handler;
- },
-
- getMimeTypePreviewHandler: function(mime) {
- var mimePart = mime.split('/').shift();
- if (this._previewHandlers[mime]) {
- return this._previewHandlers[mime];
- } else if (this._previewHandlers[mimePart]) {
- return this._previewHandlers[mimePart];
- } else {
- return null;
- }
- },
-
- getPreviewHandler: function (mime) {
- var mimetypeHandler = this.getMimeTypePreviewHandler(mime);
- if (mimetypeHandler) {
- return mimetypeHandler;
- } else {
- return this.fallbackPreview.bind(this);
- }
- },
-
- loadPreview: function (model, $thumbnailDiv, $thumbnailContainer) {
- if (model.get('hasPreview') === false && this.getMimeTypePreviewHandler(model.get('mimetype')) === null) {
- var mimeIcon = OC.MimeType.getIconUrl(model.get('mimetype'));
- $thumbnailDiv.removeClass('icon-loading icon-32');
- $thumbnailContainer.removeClass('image'); //fall back to regular view
- $thumbnailDiv.css({
- 'background-image': 'url("' + mimeIcon + '")'
- });
- } else {
- var handler = this.getPreviewHandler(model.get('mimetype'));
- var fallback = this.fallbackPreview.bind(this, model, $thumbnailDiv, $thumbnailContainer);
- handler(model, $thumbnailDiv, $thumbnailContainer, fallback);
- }
- },
-
- // previews for images and mimetype icons
- fallbackPreview: function (model, $thumbnailDiv, $thumbnailContainer) {
- var isImage = model.isImage();
- var maxImageWidth = $thumbnailContainer.parent().width() + 50; // 50px for negative margins
- var maxImageHeight = maxImageWidth / (16 / 9);
-
- var isLandscape = function (img) {
- return img.width > (img.height * 1.2);
- };
-
- var isSmall = function (img) {
- return (img.width * 1.1) < (maxImageWidth * window.devicePixelRatio);
- };
-
- var getTargetHeight = function (img) {
- var targetHeight = img.height / window.devicePixelRatio;
- if (targetHeight <= maxImageHeight) {
- targetHeight = maxImageHeight;
- }
- return targetHeight;
- };
-
- var getTargetRatio = function (img) {
- var ratio = img.width / img.height;
- if (ratio > 16 / 9) {
- return ratio;
- } else {
- return 16 / 9;
- }
- };
-
- this._fileList.lazyLoadPreview({
- fileId: model.get('id'),
- path: model.getFullPath(),
- mime: model.get('mimetype'),
- etag: model.get('etag'),
- y: maxImageHeight,
- x: maxImageWidth,
- a: 1,
- mode: 'cover',
- callback: function (previewUrl, img) {
- $thumbnailDiv.previewImg = previewUrl;
-
- // as long as we only have the mimetype icon, we only save it in case there is no preview
- if (!img) {
- return;
- }
- $thumbnailDiv.removeClass('icon-loading icon-32');
- var targetHeight = getTargetHeight(img);
- $thumbnailContainer.addClass((isLandscape(img) && !isSmall(img)) ? 'landscape' : 'portrait');
- $thumbnailContainer.addClass('large');
-
- // only set background when we have an actual preview
- // when we don't have a preview we show the mime icon in the error handler
- $thumbnailDiv.css({
- 'background-image': 'url("' + previewUrl + '")',
- height: (targetHeight > maxImageHeight) ? 'auto' : targetHeight,
- 'max-height': isSmall(img) ? targetHeight : null
- });
-
- var targetRatio = getTargetRatio(img);
- $thumbnailDiv.find('.stretcher').css({
- 'padding-bottom': (100 / targetRatio) + '%'
- });
- },
- error: function () {
- $thumbnailDiv.removeClass('icon-loading icon-32');
- $thumbnailContainer.removeClass('image'); //fall back to regular view
- $thumbnailDiv.css({
- 'background-image': 'url("' + $thumbnailDiv.previewImg + '")'
- });
- }
- });
- }
- };
-
- OCA.Files.SidebarPreviewManager = SidebarPreviewManager;
-})();
diff --git a/apps/files/js/sidebarpreviewtext.js b/apps/files/js/sidebarpreviewtext.js
deleted file mode 100644
index 55b9153aefe..00000000000
--- a/apps/files/js/sidebarpreviewtext.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-(function () {
- var SidebarPreview = function () {
- };
-
- SidebarPreview.prototype = {
- attach: function (manager) {
- manager.addPreviewHandler('text', this.handlePreview.bind(this));
- },
-
- handlePreview: function (model, $thumbnailDiv, $thumbnailContainer, fallback) {
- var previewWidth = $thumbnailContainer.parent().width() + 50; // 50px for negative margins
- var previewHeight = previewWidth / (16 / 9);
-
- this.getFileContent(model.getFullPath()).then(function (content) {
- $thumbnailDiv.removeClass('icon-loading icon-32');
- $thumbnailContainer.addClass('large');
- $thumbnailContainer.addClass('text');
- var $textPreview = $('<pre></pre>').text(content);
- $thumbnailDiv.children('.stretcher').remove();
- $thumbnailDiv.append($textPreview);
- $thumbnailContainer.css("max-height", previewHeight);
- }, function () {
- fallback();
- });
- },
-
- getFileContent: function (path) {
- return $.ajax({
- url: OC.linkToRemoteBase('files' + path),
- headers: {
- 'Range': 'bytes=0-10240'
- }
- });
- }
- };
-
- OC.Plugins.register('OCA.Files.SidebarPreviewManager', new SidebarPreview());
-})();
diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js
deleted file mode 100644
index 6b4acaaef96..00000000000
--- a/apps/files/js/tagsplugin.js
+++ /dev/null
@@ -1,267 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-/* global Handlebars */
-
-(function (OCA) {
-
- _.extend(OC.Files.Client, {
- PROPERTY_TAGS: '{' + OC.Files.Client.NS_OWNCLOUD + '}tags',
- PROPERTY_FAVORITE: '{' + OC.Files.Client.NS_OWNCLOUD + '}favorite'
- });
-
- /**
- * Returns the icon class for the matching state
- *
- * @param {boolean} state true if starred, false otherwise
- * @return {string} icon class for star image
- */
- function getStarIconClass (state) {
- return state ? 'icon-starred' : 'icon-star';
- }
-
- /**
- * Render the star icon with the given state
- *
- * @param {boolean} state true if starred, false otherwise
- * @return {Object} jQuery object
- */
- function renderStar (state) {
- return OCA.Files.Templates['favorite_mark']({
- isFavorite: state,
- altText: state ? t('files', 'Favorited') : t('files', 'Not favorited'),
- iconClass: getStarIconClass(state)
- });
- }
-
- /**
- * Toggle star icon on favorite mark element
- *
- * @param {Object} $favoriteMarkEl favorite mark element
- * @param {boolean} state true if starred, false otherwise
- */
- function toggleStar ($favoriteMarkEl, state) {
- $favoriteMarkEl.removeClass('icon-star icon-starred').addClass(getStarIconClass(state));
- $favoriteMarkEl.toggleClass('permanent', state);
- }
-
- OCA.Files = OCA.Files || {};
-
- /**
- * Extends the file actions and file list to include a favorite mark icon
- * and a favorite action in the file actions menu; it also adds "data-tags"
- * and "data-favorite" attributes to file elements.
- *
- * @namespace OCA.Files.TagsPlugin
- */
- OCA.Files.TagsPlugin = {
- name: 'Tags',
-
- allowedLists: [
- 'files',
- 'favorites',
- 'systemtags',
- 'shares.self',
- 'shares.others',
- 'shares.link'
- ],
-
- _extendFileActions: function (fileActions) {
- var self = this;
-
- fileActions.registerAction({
- name: 'Favorite',
- displayName: function (context) {
- var $file = context.$file;
- var isFavorite = $file.data('favorite') === true;
-
- if (isFavorite) {
- return t('files', 'Remove from favorites');
- }
-
- // As it is currently not possible to provide a context for
- // the i18n strings "Add to favorites" was used instead of
- // "Favorite" to remove the ambiguity between verb and noun
- // when it is translated.
- return t('files', 'Add to favorites');
- },
- mime: 'all',
- order: -100,
- permissions: OC.PERMISSION_NONE,
- iconClass: function (fileName, context) {
- var $file = context.$file;
- var isFavorite = $file.data('favorite') === true;
-
- if (isFavorite) {
- return 'icon-favorite';
- }
-
- return 'icon-starred';
- },
- actionHandler: function (fileName, context) {
- var $favoriteMarkEl = context.$file.find('.favorite-mark');
- var $file = context.$file;
- var fileInfo = context.fileList.files[$file.index()];
- var dir = context.dir || context.fileList.getCurrentDirectory();
- var tags = $file.attr('data-tags');
- var isFile = $file.attr('data-type') === 'file';
-
- if (_.isUndefined(tags)) {
- tags = '';
- }
- tags = tags.split('|');
- tags = _.without(tags, '');
- var isFavorite = tags.indexOf(OC.TAG_FAVORITE) >= 0;
-
- // Fake Node object for vue compatibility
- const node = {
- type: isFile ? 'file' : 'folder',
- path: (dir + '/' + fileName).replace(/\/\/+/g, '/'),
- root: '/files/' + OC.getCurrentUser().uid
- }
-
- if (isFavorite) {
- // remove tag from list
- tags = _.without(tags, OC.TAG_FAVORITE);
- // vue compatibility
- window._nc_event_bus.emit('files:favorites:removed', node)
- } else {
- tags.push(OC.TAG_FAVORITE);
- // vue compatibility
- window._nc_event_bus.emit('files:favorites:added', node)
- }
-
- // pre-toggle the star
- toggleStar($favoriteMarkEl, !isFavorite);
-
- context.fileInfoModel.trigger('busy', context.fileInfoModel, true);
-
- self.applyFileTags(
- dir + '/' + fileName,
- tags,
- $favoriteMarkEl,
- isFavorite
- ).then(function (result) {
- context.fileInfoModel.trigger('busy', context.fileInfoModel, false);
- // response from server should contain updated tags
- var newTags = result.tags;
- if (_.isUndefined(newTags)) {
- newTags = tags;
- }
- context.fileInfoModel.set({
- 'tags': newTags,
- 'favorite': !isFavorite
- });
- });
- }
- });
- },
-
- _extendFileList: function (fileList) {
- // extend row prototype
- var oldCreateRow = fileList._createRow;
- fileList._createRow = function (fileData) {
- var $tr = oldCreateRow.apply(this, arguments);
- var isFavorite = false;
- if (fileData.tags) {
- $tr.attr('data-tags', fileData.tags.join('|'));
- if (fileData.tags.indexOf(OC.TAG_FAVORITE) >= 0) {
- $tr.attr('data-favorite', true);
- isFavorite = true;
- }
- }
- var $icon = $(renderStar(isFavorite));
- $tr.find('td.filename .thumbnail').append($icon);
- return $tr;
- };
- var oldElementToFile = fileList.elementToFile;
- fileList.elementToFile = function ($el) {
- var fileInfo = oldElementToFile.apply(this, arguments);
- var tags = $el.attr('data-tags');
- if (_.isUndefined(tags)) {
- tags = '';
- }
- tags = tags.split('|');
- tags = _.without(tags, '');
- fileInfo.tags = tags;
- return fileInfo;
- };
-
- var oldGetWebdavProperties = fileList._getWebdavProperties;
- fileList._getWebdavProperties = function () {
- var props = oldGetWebdavProperties.apply(this, arguments);
- props.push(OC.Files.Client.PROPERTY_TAGS);
- props.push(OC.Files.Client.PROPERTY_FAVORITE);
- return props;
- };
-
- fileList.filesClient.addFileInfoParser(function (response) {
- var data = {};
- var props = response.propStat[0].properties;
- var tags = props[OC.Files.Client.PROPERTY_TAGS];
- var favorite = props[OC.Files.Client.PROPERTY_FAVORITE];
- if (tags && tags.length) {
- tags = _.chain(tags).filter(function (xmlvalue) {
- return (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'tag');
- }).map(function (xmlvalue) {
- return xmlvalue.textContent || xmlvalue.text;
- }).value();
- }
- if (tags) {
- data.tags = tags;
- }
- if (favorite && parseInt(favorite, 10) !== 0) {
- data.tags = data.tags || [];
- data.tags.push(OC.TAG_FAVORITE);
- }
- return data;
- });
- },
-
- attach: function (fileList) {
- if (this.allowedLists.indexOf(fileList.id) < 0) {
- return;
- }
- this._extendFileActions(fileList.fileActions);
- this._extendFileList(fileList);
- },
-
- /**
- * Replaces the given files' tags with the specified ones.
- *
- * @param {String} fileName path to the file or folder to tag
- * @param {Array.<String>} tagNames array of tag names
- * @param {Object} $favoriteMarkEl favorite mark element
- * @param {boolean} isFavorite Was the item favorited before
- */
- applyFileTags: function (fileName, tagNames, $favoriteMarkEl, isFavorite) {
- var encodedPath = OC.encodePath(fileName);
- while (encodedPath[0] === '/') {
- encodedPath = encodedPath.substr(1);
- }
- return $.ajax({
- url: OC.generateUrl('/apps/files/api/v1/files/') + encodedPath,
- contentType: 'application/json',
- data: JSON.stringify({
- tags: tagNames || []
- }),
- dataType: 'json',
- type: 'POST'
- }).fail(function (response) {
- var message = '';
- // show message if it is available
- if (response.responseJSON && response.responseJSON.message) {
- message = ': ' + response.responseJSON.message;
- }
- OC.Notification.show(t('files', 'An error occurred while trying to update the tags' + message), {type: 'error'});
- toggleStar($favoriteMarkEl, isFavorite);
- });
- }
- };
-})
-(OCA);
-
-OC.Plugins.register('OCA.Files.FileList', OCA.Files.TagsPlugin);
diff --git a/apps/files/js/templates.js b/apps/files/js/templates.js
deleted file mode 100644
index 3b0aec46ccb..00000000000
--- a/apps/files/js/templates.js
+++ /dev/null
@@ -1,430 +0,0 @@
-(function() {
- var template = Handlebars.template, templates = OCA.Files.Templates = OCA.Files.Templates || {};
-templates['detailsview'] = template({"1":function(container,depth0,helpers,partials,data) {
- var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<ul class=\"tabHeaders\">\n"
- + ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"tabHeaders") : depth0),{"name":"each","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":4,"column":1},"end":{"line":9,"column":10}}})) != null ? stack1 : "")
- + "</ul>\n";
-},"2":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <li class=\"tabHeader\" data-tabid=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"tabId") || (depth0 != null ? lookupProperty(depth0,"tabId") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"tabId","hash":{},"data":data,"loc":{"start":{"line":5,"column":35},"end":{"line":5,"column":44}}}) : helper)))
- + "\" tabindex=\"0\">\n "
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"tabIcon") : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":6,"column":5},"end":{"line":6,"column":65}}})) != null ? stack1 : "")
- + "\n <a href=\"#\" tabindex=\"-1\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"label") || (depth0 != null ? lookupProperty(depth0,"label") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data,"loc":{"start":{"line":7,"column":28},"end":{"line":7,"column":37}}}) : helper)))
- + "</a>\n </li>\n";
-},"3":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<span class=\"icon "
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"tabIcon") || (depth0 != null ? lookupProperty(depth0,"tabIcon") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"tabIcon","hash":{},"data":data,"loc":{"start":{"line":6,"column":38},"end":{"line":6,"column":49}}}) : helper)))
- + "\"></span>";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<div class=\"detailFileInfoContainer\"></div>\n"
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"tabHeaders") : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":0},"end":{"line":11,"column":7}}})) != null ? stack1 : "")
- + "<div class=\"tabsContainer\"></div>\n<a class=\"close icon-close\" href=\"#\"><span class=\"hidden-visually\">"
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"closeLabel") || (depth0 != null ? lookupProperty(depth0,"closeLabel") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"closeLabel","hash":{},"data":data,"loc":{"start":{"line":13,"column":67},"end":{"line":13,"column":81}}}) : helper)))
- + "</span></a>\n";
-},"useData":true});
-templates['favorite_mark'] = template({"1":function(container,depth0,helpers,partials,data) {
- return "permanent";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- }, buffer =
- "<div class=\"favorite-mark ";
- stack1 = ((helper = (helper = lookupProperty(helpers,"isFavorite") || (depth0 != null ? lookupProperty(depth0,"isFavorite") : depth0)) != null ? helper : alias2),(options={"name":"isFavorite","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":65}}}),(typeof helper === alias3 ? helper.call(alias1,options) : helper));
- if (!lookupProperty(helpers,"isFavorite")) { stack1 = container.hooks.blockHelperMissing.call(depth0,stack1,options)}
- if (stack1 != null) { buffer += stack1; }
- return buffer + "\">\n <span class=\"icon "
- + alias4(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":32}}}) : helper)))
- + "\" />\n <span class=\"hidden-visually\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"altText") || (depth0 != null ? lookupProperty(depth0,"altText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altText","hash":{},"data":data,"loc":{"start":{"line":3,"column":31},"end":{"line":3,"column":42}}}) : helper)))
- + "</span>\n</div>\n";
-},"useData":true});
-templates['file_action_trigger'] = template({"1":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <img class=\"svg\" alt=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"altText") || (depth0 != null ? lookupProperty(depth0,"altText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altText","hash":{},"data":data,"loc":{"start":{"line":3,"column":24},"end":{"line":3,"column":35}}}) : helper)))
- + "\" src=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"icon") || (depth0 != null ? lookupProperty(depth0,"icon") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"icon","hash":{},"data":data,"loc":{"start":{"line":3,"column":42},"end":{"line":3,"column":50}}}) : helper)))
- + "\" />\n";
-},"3":function(container,depth0,helpers,partials,data) {
- var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}), lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"iconClass") : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":5,"column":2},"end":{"line":7,"column":9}}})) != null ? stack1 : "")
- + ((stack1 = lookupProperty(helpers,"unless").call(alias1,(depth0 != null ? lookupProperty(depth0,"hasDisplayName") : depth0),{"name":"unless","hash":{},"fn":container.program(6, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":8,"column":2},"end":{"line":10,"column":13}}})) != null ? stack1 : "");
-},"4":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <span class=\"icon "
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":6,"column":21},"end":{"line":6,"column":34}}}) : helper)))
- + "\"></span>\n";
-},"6":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <span class=\"hidden-visually\">"
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"altText") || (depth0 != null ? lookupProperty(depth0,"altText") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"altText","hash":{},"data":data,"loc":{"start":{"line":9,"column":33},"end":{"line":9,"column":44}}}) : helper)))
- + "</span>\n";
-},"8":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<span> "
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":12,"column":27},"end":{"line":12,"column":42}}}) : helper)))
- + "</span>";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<a class=\"action action-"
- + alias4(((helper = (helper = lookupProperty(helpers,"nameLowerCase") || (depth0 != null ? lookupProperty(depth0,"nameLowerCase") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"nameLowerCase","hash":{},"data":data,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":41}}}) : helper)))
- + "\" href=\"#\" data-action=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":1,"column":65},"end":{"line":1,"column":73}}}) : helper)))
- + "\">\n"
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"icon") : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(3, data, 0),"data":data,"loc":{"start":{"line":2,"column":1},"end":{"line":11,"column":8}}})) != null ? stack1 : "")
- + " "
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"displayName") : depth0),{"name":"if","hash":{},"fn":container.program(8, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":12,"column":1},"end":{"line":12,"column":56}}})) != null ? stack1 : "")
- + "\n</a>\n";
-},"useData":true});
-templates['fileactionsmenu'] = template({"1":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <li class=\""
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"inline") : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":13},"end":{"line":3,"column":40}}})) != null ? stack1 : "")
- + " action-"
- + alias4(((helper = (helper = lookupProperty(helpers,"nameLowerCase") || (depth0 != null ? lookupProperty(depth0,"nameLowerCase") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"nameLowerCase","hash":{},"data":data,"loc":{"start":{"line":3,"column":48},"end":{"line":3,"column":65}}}) : helper)))
- + "-container\">\n <a href=\"#\" class=\"menuitem action action-"
- + alias4(((helper = (helper = lookupProperty(helpers,"nameLowerCase") || (depth0 != null ? lookupProperty(depth0,"nameLowerCase") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"nameLowerCase","hash":{},"data":data,"loc":{"start":{"line":4,"column":45},"end":{"line":4,"column":62}}}) : helper)))
- + " permanent\" data-action=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":87},"end":{"line":4,"column":95}}}) : helper)))
- + "\">\n "
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"icon") : depth0),{"name":"if","hash":{},"fn":container.program(4, data, 0),"inverse":container.program(6, data, 0),"data":data,"loc":{"start":{"line":5,"column":4},"end":{"line":12,"column":11}}})) != null ? stack1 : "")
- + " <span>"
- + alias4(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":13,"column":10},"end":{"line":13,"column":25}}}) : helper)))
- + "</span>\n </a>\n </li>\n";
-},"2":function(container,depth0,helpers,partials,data) {
- return "hidden";
-},"4":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<img class=\"icon\" src=\""
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"icon") || (depth0 != null ? lookupProperty(depth0,"icon") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"icon","hash":{},"data":data,"loc":{"start":{"line":5,"column":39},"end":{"line":5,"column":47}}}) : helper)))
- + "\"/>\n";
-},"6":function(container,depth0,helpers,partials,data) {
- var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return ((stack1 = lookupProperty(helpers,"if").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"iconClass") : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.program(9, data, 0),"data":data,"loc":{"start":{"line":7,"column":5},"end":{"line":11,"column":12}}})) != null ? stack1 : "");
-},"7":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <span class=\"icon "
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":8,"column":24},"end":{"line":8,"column":37}}}) : helper)))
- + "\"></span>\n";
-},"9":function(container,depth0,helpers,partials,data) {
- return " <span class=\"no-icon\"></span>\n";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<ul>\n"
- + ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"items") : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":1},"end":{"line":16,"column":10}}})) != null ? stack1 : "")
- + "</ul>\n";
-},"useData":true});
-templates['filemultiselectmenu'] = template({"1":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <li class=\"item-"
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":26}}}) : helper)))
- + "\">\n <a href=\"#\" class=\"menuitem action "
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":38},"end":{"line":4,"column":46}}}) : helper)))
- + " permanent\" data-action=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":71},"end":{"line":4,"column":79}}}) : helper)))
- + "\">\n"
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"iconClass") : depth0),{"name":"if","hash":{},"fn":container.program(2, data, 0),"inverse":container.program(4, data, 0),"data":data,"loc":{"start":{"line":5,"column":4},"end":{"line":9,"column":11}}})) != null ? stack1 : "")
- + " <span class=\"label\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":10,"column":24},"end":{"line":10,"column":39}}}) : helper)))
- + "</span>\n </a>\n </li>\n";
-},"2":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <span class=\"icon "
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":6,"column":23},"end":{"line":6,"column":36}}}) : helper)))
- + "\"></span>\n";
-},"4":function(container,depth0,helpers,partials,data) {
- return " <span class=\"no-icon\"></span>\n";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<ul>\n"
- + ((stack1 = lookupProperty(helpers,"each").call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? lookupProperty(depth0,"items") : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":2,"column":1},"end":{"line":13,"column":10}}})) != null ? stack1 : "")
- + "</ul>\n";
-},"useData":true});
-templates['filesummary'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<span class=\"info\">\n <span class=\"dirinfo\"></span>\n <span class=\"connector\">"
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"connectorLabel") || (depth0 != null ? lookupProperty(depth0,"connectorLabel") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"connectorLabel","hash":{},"data":data,"loc":{"start":{"line":3,"column":25},"end":{"line":3,"column":43}}}) : helper)))
- + "</span>\n <span class=\"fileinfo\"></span>\n <span class=\"hiddeninfo\"></span>\n <span class=\"filter\"></span>\n</span>\n";
-},"useData":true});
-templates['mainfileinfodetailsview'] = template({"1":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <a href=\"#\" class=\"action action-favorite favorite permanent\">\n <span class=\"icon "
- + alias4(((helper = (helper = lookupProperty(helpers,"starClass") || (depth0 != null ? lookupProperty(depth0,"starClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"starClass","hash":{},"data":data,"loc":{"start":{"line":13,"column":22},"end":{"line":13,"column":35}}}) : helper)))
- + "\" title=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"starAltText") || (depth0 != null ? lookupProperty(depth0,"starAltText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"starAltText","hash":{},"data":data,"loc":{"start":{"line":13,"column":44},"end":{"line":13,"column":59}}}) : helper)))
- + "\"></span>\n </a>\n";
-},"3":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<span class=\"size\" title=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"altSize") || (depth0 != null ? lookupProperty(depth0,"altSize") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altSize","hash":{},"data":data,"loc":{"start":{"line":16,"column":43},"end":{"line":16,"column":54}}}) : helper)))
- + "\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"size") || (depth0 != null ? lookupProperty(depth0,"size") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"size","hash":{},"data":data,"loc":{"start":{"line":16,"column":56},"end":{"line":16,"column":64}}}) : helper)))
- + "</span>, ";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<div class=\"thumbnailContainer\"><a href=\"#\" class=\"thumbnail action-default\"><div class=\"stretcher\"></div></a></div>\n<div class=\"file-details-container\">\n <div class=\"fileName\">\n <h3 title=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":13},"end":{"line":4,"column":21}}}) : helper)))
- + "\" class=\"ellipsis\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":4,"column":40},"end":{"line":4,"column":48}}}) : helper)))
- + "</h3>\n <a class=\"permalink\" href=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"permalink") || (depth0 != null ? lookupProperty(depth0,"permalink") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalink","hash":{},"data":data,"loc":{"start":{"line":5,"column":29},"end":{"line":5,"column":42}}}) : helper)))
- + "\" title=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"permalinkTitle") || (depth0 != null ? lookupProperty(depth0,"permalinkTitle") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalinkTitle","hash":{},"data":data,"loc":{"start":{"line":5,"column":51},"end":{"line":5,"column":69}}}) : helper)))
- + "\" data-clipboard-text=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"permalink") || (depth0 != null ? lookupProperty(depth0,"permalink") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalink","hash":{},"data":data,"loc":{"start":{"line":5,"column":92},"end":{"line":5,"column":105}}}) : helper)))
- + "\">\n <span class=\"icon icon-clippy\"></span>\n <span class=\"hidden-visually\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"permalinkTitle") || (depth0 != null ? lookupProperty(depth0,"permalinkTitle") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalinkTitle","hash":{},"data":data,"loc":{"start":{"line":7,"column":33},"end":{"line":7,"column":51}}}) : helper)))
- + "</span>\n </a>\n </div>\n <div class=\"file-details ellipsis\">\n"
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"hasFavoriteAction") : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":11,"column":2},"end":{"line":15,"column":9}}})) != null ? stack1 : "")
- + " "
- + ((stack1 = lookupProperty(helpers,"if").call(alias1,(depth0 != null ? lookupProperty(depth0,"hasSize") : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":80}}})) != null ? stack1 : "")
- + "<span class=\"date live-relative-timestamp\" data-timestamp=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"timestamp") || (depth0 != null ? lookupProperty(depth0,"timestamp") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"timestamp","hash":{},"data":data,"loc":{"start":{"line":16,"column":139},"end":{"line":16,"column":152}}}) : helper)))
- + "\" title=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"altDate") || (depth0 != null ? lookupProperty(depth0,"altDate") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altDate","hash":{},"data":data,"loc":{"start":{"line":16,"column":161},"end":{"line":16,"column":172}}}) : helper)))
- + "\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"date") || (depth0 != null ? lookupProperty(depth0,"date") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"date","hash":{},"data":data,"loc":{"start":{"line":16,"column":174},"end":{"line":16,"column":182}}}) : helper)))
- + "</span>\n </div>\n</div>\n<div class=\"hidden permalink-field\">\n <input type=\"text\" value=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"permalink") || (depth0 != null ? lookupProperty(depth0,"permalink") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalink","hash":{},"data":data,"loc":{"start":{"line":20,"column":27},"end":{"line":20,"column":40}}}) : helper)))
- + "\" placeholder=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"permalinkTitle") || (depth0 != null ? lookupProperty(depth0,"permalinkTitle") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"permalinkTitle","hash":{},"data":data,"loc":{"start":{"line":20,"column":55},"end":{"line":20,"column":73}}}) : helper)))
- + "\" readonly=\"readonly\"/>\n</div>\n";
-},"useData":true});
-templates['newfilemenu'] = template({"1":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return " <li>\n <a href=\"#\" class=\"menuitem\" data-templatename=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"templateName") || (depth0 != null ? lookupProperty(depth0,"templateName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"templateName","hash":{},"data":data,"loc":{"start":{"line":7,"column":51},"end":{"line":7,"column":67}}}) : helper)))
- + "\" data-filetype=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"fileType") || (depth0 != null ? lookupProperty(depth0,"fileType") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileType","hash":{},"data":data,"loc":{"start":{"line":7,"column":84},"end":{"line":7,"column":96}}}) : helper)))
- + "\" data-action=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"id") || (depth0 != null ? lookupProperty(depth0,"id") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"id","hash":{},"data":data,"loc":{"start":{"line":7,"column":111},"end":{"line":7,"column":117}}}) : helper)))
- + "\" data-action-label=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"actionLabel") || (depth0 != null ? lookupProperty(depth0,"actionLabel") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actionLabel","hash":{},"data":data,"loc":{"start":{"line":7,"column":138},"end":{"line":7,"column":153}}}) : helper)))
- + "\"><span class=\"icon "
- + alias4(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":7,"column":173},"end":{"line":7,"column":186}}}) : helper)))
- + " svg\"></span><span class=\"displayname\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"displayName") || (depth0 != null ? lookupProperty(depth0,"displayName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"displayName","hash":{},"data":data,"loc":{"start":{"line":7,"column":225},"end":{"line":7,"column":240}}}) : helper)))
- + "</span></a>\n </li>\n";
-},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<ul>\n <li>\n <label for=\"file_upload_start\" class=\"menuitem\" data-action=\"upload\" title=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"uploadMaxHumanFilesize") || (depth0 != null ? lookupProperty(depth0,"uploadMaxHumanFilesize") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"uploadMaxHumanFilesize","hash":{},"data":data,"loc":{"start":{"line":3,"column":78},"end":{"line":3,"column":104}}}) : helper)))
- + "\" tabindex=\"0\"><span class=\"svg icon icon-upload\"></span><span class=\"displayname\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"uploadLabel") || (depth0 != null ? lookupProperty(depth0,"uploadLabel") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"uploadLabel","hash":{},"data":data,"loc":{"start":{"line":3,"column":187},"end":{"line":3,"column":202}}}) : helper)))
- + "</span></label>\n </li>\n"
- + ((stack1 = lookupProperty(helpers,"each").call(alias1,(depth0 != null ? lookupProperty(depth0,"items") : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":5,"column":1},"end":{"line":9,"column":10}}})) != null ? stack1 : "")
- + "</ul>\n";
-},"useData":true});
-templates['newfilemenu_filename_form'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<form class=\"filenameform\">\n <input id=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"cid") || (depth0 != null ? lookupProperty(depth0,"cid") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"cid","hash":{},"data":data,"loc":{"start":{"line":2,"column":12},"end":{"line":2,"column":19}}}) : helper)))
- + "-input-"
- + alias4(((helper = (helper = lookupProperty(helpers,"fileType") || (depth0 != null ? lookupProperty(depth0,"fileType") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileType","hash":{},"data":data,"loc":{"start":{"line":2,"column":26},"end":{"line":2,"column":38}}}) : helper)))
- + "\" type=\"text\" value=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"fileName") || (depth0 != null ? lookupProperty(depth0,"fileName") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"fileName","hash":{},"data":data,"loc":{"start":{"line":2,"column":59},"end":{"line":2,"column":71}}}) : helper)))
- + "\" autocomplete=\"off\" autocapitalize=\"off\">\n <input type=\"submit\" value=\" \" class=\"icon-confirm\" aria-label=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"actionLabel") || (depth0 != null ? lookupProperty(depth0,"actionLabel") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actionLabel","hash":{},"data":data,"loc":{"start":{"line":3,"column":65},"end":{"line":3,"column":80}}}) : helper)))
- + "\" />\n</form>\n";
-},"useData":true});
-templates['operationprogressbar'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<div id=\"uploadprogressbar\">\n <em class=\"label outer\" style=\"display:none\"></em>\n</div>\n<button class=\"stop icon-close\" style=\"display:none\">\n <span class=\"hidden-visually\">"
- + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"textCancelButton") || (depth0 != null ? lookupProperty(depth0,"textCancelButton") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"textCancelButton","hash":{},"data":data,"loc":{"start":{"line":5,"column":31},"end":{"line":5,"column":51}}}) : helper)))
- + "</span>\n</button>\n";
-},"useData":true});
-templates['operationprogressbarlabel'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<em class=\"label\">\n <span class=\"desktop\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"textDesktop") || (depth0 != null ? lookupProperty(depth0,"textDesktop") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"textDesktop","hash":{},"data":data,"loc":{"start":{"line":2,"column":23},"end":{"line":2,"column":38}}}) : helper)))
- + "</span>\n <span class=\"mobile\">"
- + alias4(((helper = (helper = lookupProperty(helpers,"textMobile") || (depth0 != null ? lookupProperty(depth0,"textMobile") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"textMobile","hash":{},"data":data,"loc":{"start":{"line":3,"column":22},"end":{"line":3,"column":36}}}) : helper)))
- + "</span>\n</em>\n";
-},"useData":true});
-templates['template_addbutton'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
- var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {
- if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
- return parent[propertyName];
- }
- return undefined
- };
-
- return "<a href=\"#\" class=\"button new\" aria-label=\""
- + alias4(((helper = (helper = lookupProperty(helpers,"addLongText") || (depth0 != null ? lookupProperty(depth0,"addLongText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"addLongText","hash":{},"data":data,"loc":{"start":{"line":1,"column":43},"end":{"line":1,"column":58}}}) : helper)))
- + "\">\n <span class=\"icon "
- + alias4(((helper = (helper = lookupProperty(helpers,"iconClass") || (depth0 != null ? lookupProperty(depth0,"iconClass") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"iconClass","hash":{},"data":data,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":32}}}) : helper)))
- + "\"></span>\n <span>"
- + alias4(((helper = (helper = lookupProperty(helpers,"addText") || (depth0 != null ? lookupProperty(depth0,"addText") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"addText","hash":{},"data":data,"loc":{"start":{"line":3,"column":7},"end":{"line":3,"column":18}}}) : helper)))
- + "</span>\n</a>\n";
-},"useData":true});
-})(); \ No newline at end of file
diff --git a/apps/files/js/templates/detailsview.handlebars b/apps/files/js/templates/detailsview.handlebars
deleted file mode 100644
index 4d489e4fa0d..00000000000
--- a/apps/files/js/templates/detailsview.handlebars
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class="detailFileInfoContainer"></div>
-{{#if tabHeaders}}
-<ul class="tabHeaders">
- {{#each tabHeaders}}
- <li class="tabHeader" data-tabid="{{tabId}}" tabindex="0">
- {{#if tabIcon}}<span class="icon {{tabIcon}}"></span>{{/if}}
- <a href="#" tabindex="-1">{{label}}</a>
- </li>
- {{/each}}
-</ul>
-{{/if}}
-<div class="tabsContainer"></div>
-<a class="close icon-close" href="#"><span class="hidden-visually">{{closeLabel}}</span></a>
diff --git a/apps/files/js/templates/favorite_mark.handlebars b/apps/files/js/templates/favorite_mark.handlebars
deleted file mode 100644
index 7e0cb4385a8..00000000000
--- a/apps/files/js/templates/favorite_mark.handlebars
+++ /dev/null
@@ -1,4 +0,0 @@
-<div class="favorite-mark {{#isFavorite}}permanent{{/isFavorite}}">
- <span class="icon {{iconClass}}" />
- <span class="hidden-visually">{{altText}}</span>
-</div>
diff --git a/apps/files/js/templates/file_action_trigger.handlebars b/apps/files/js/templates/file_action_trigger.handlebars
deleted file mode 100644
index e74b3717c92..00000000000
--- a/apps/files/js/templates/file_action_trigger.handlebars
+++ /dev/null
@@ -1,13 +0,0 @@
-<a class="action action-{{nameLowerCase}}" href="#" data-action="{{name}}">
- {{#if icon}}
- <img class="svg" alt="{{altText}}" src="{{icon}}" />
- {{else}}
- {{#if iconClass}}
- <span class="icon {{iconClass}}"></span>
- {{/if}}
- {{#unless hasDisplayName}}
- <span class="hidden-visually">{{altText}}</span>
- {{/unless}}
- {{/if}}
- {{#if displayName}}<span> {{displayName}}</span>{{/if}}
-</a>
diff --git a/apps/files/js/templates/fileactionsmenu.handlebars b/apps/files/js/templates/fileactionsmenu.handlebars
deleted file mode 100644
index b14ec02e5b9..00000000000
--- a/apps/files/js/templates/fileactionsmenu.handlebars
+++ /dev/null
@@ -1,17 +0,0 @@
-<ul>
- {{#each items}}
- <li class="{{#if inline}}hidden{{/if}} action-{{nameLowerCase}}-container">
- <a href="#" class="menuitem action action-{{nameLowerCase}} permanent" data-action="{{name}}">
- {{#if icon}}<img class="icon" src="{{icon}}"/>
- {{else}}
- {{#if iconClass}}
- <span class="icon {{iconClass}}"></span>
- {{else}}
- <span class="no-icon"></span>
- {{/if}}
- {{/if}}
- <span>{{displayName}}</span>
- </a>
- </li>
- {{/each}}
-</ul>
diff --git a/apps/files/js/templates/filemultiselectmenu.handlebars b/apps/files/js/templates/filemultiselectmenu.handlebars
deleted file mode 100644
index 9a723920db9..00000000000
--- a/apps/files/js/templates/filemultiselectmenu.handlebars
+++ /dev/null
@@ -1,14 +0,0 @@
-<ul>
- {{#each items}}
- <li class="item-{{name}}">
- <a href="#" class="menuitem action {{name}} permanent" data-action="{{name}}">
- {{#if iconClass}}
- <span class="icon {{iconClass}}"></span>
- {{else}}
- <span class="no-icon"></span>
- {{/if}}
- <span class="label">{{displayName}}</span>
- </a>
- </li>
- {{/each}}
-</ul>
diff --git a/apps/files/js/templates/filesummary.handlebars b/apps/files/js/templates/filesummary.handlebars
deleted file mode 100644
index f975a2a7737..00000000000
--- a/apps/files/js/templates/filesummary.handlebars
+++ /dev/null
@@ -1,7 +0,0 @@
-<span class="info">
- <span class="dirinfo"></span>
- <span class="connector">{{connectorLabel}}</span>
- <span class="fileinfo"></span>
- <span class="hiddeninfo"></span>
- <span class="filter"></span>
-</span>
diff --git a/apps/files/js/templates/mainfileinfodetailsview.handlebars b/apps/files/js/templates/mainfileinfodetailsview.handlebars
deleted file mode 100644
index db836ca5991..00000000000
--- a/apps/files/js/templates/mainfileinfodetailsview.handlebars
+++ /dev/null
@@ -1,21 +0,0 @@
-<div class="thumbnailContainer"><a href="#" class="thumbnail action-default"><div class="stretcher"></div></a></div>
-<div class="file-details-container">
- <div class="fileName">
- <h3 title="{{name}}" class="ellipsis">{{name}}</h3>
- <a class="permalink" href="{{permalink}}" title="{{permalinkTitle}}" data-clipboard-text="{{permalink}}">
- <span class="icon icon-clippy"></span>
- <span class="hidden-visually">{{permalinkTitle}}</span>
- </a>
- </div>
- <div class="file-details ellipsis">
- {{#if hasFavoriteAction}}
- <a href="#" class="action action-favorite favorite permanent">
- <span class="icon {{starClass}}" title="{{starAltText}}"></span>
- </a>
- {{/if}}
- {{#if hasSize}}<span class="size" title="{{altSize}}">{{size}}</span>, {{/if}}<span class="date live-relative-timestamp" data-timestamp="{{timestamp}}" title="{{altDate}}">{{date}}</span>
- </div>
-</div>
-<div class="hidden permalink-field">
- <input type="text" value="{{permalink}}" placeholder="{{permalinkTitle}}" readonly="readonly"/>
-</div>
diff --git a/apps/files/js/templates/newfilemenu.handlebars b/apps/files/js/templates/newfilemenu.handlebars
deleted file mode 100644
index 756595212d9..00000000000
--- a/apps/files/js/templates/newfilemenu.handlebars
+++ /dev/null
@@ -1,10 +0,0 @@
-<ul>
- <li>
- <label for="file_upload_start" class="menuitem" data-action="upload" title="{{uploadMaxHumanFilesize}}" tabindex="0"><span class="svg icon icon-upload"></span><span class="displayname">{{uploadLabel}}</span></label>
- </li>
- {{#each items}}
- <li>
- <a href="#" class="menuitem" data-templatename="{{templateName}}" data-filetype="{{fileType}}" data-action="{{id}}" data-action-label="{{actionLabel}}"><span class="icon {{iconClass}} svg"></span><span class="displayname">{{displayName}}</span></a>
- </li>
- {{/each}}
-</ul>
diff --git a/apps/files/js/templates/newfilemenu_filename_form.handlebars b/apps/files/js/templates/newfilemenu_filename_form.handlebars
deleted file mode 100644
index 2d5af97ee76..00000000000
--- a/apps/files/js/templates/newfilemenu_filename_form.handlebars
+++ /dev/null
@@ -1,4 +0,0 @@
-<form class="filenameform">
- <input id="{{cid}}-input-{{fileType}}" type="text" value="{{fileName}}" autocomplete="off" autocapitalize="off">
- <input type="submit" value=" " class="icon-confirm" aria-label="{{actionLabel}}" />
-</form>
diff --git a/apps/files/js/templates/operationprogressbar.handlebars b/apps/files/js/templates/operationprogressbar.handlebars
deleted file mode 100644
index c02ac623fab..00000000000
--- a/apps/files/js/templates/operationprogressbar.handlebars
+++ /dev/null
@@ -1,6 +0,0 @@
-<div id="uploadprogressbar">
- <em class="label outer" style="display:none"></em>
-</div>
-<button class="stop icon-close" style="display:none">
- <span class="hidden-visually">{{textCancelButton}}</span>
-</button>
diff --git a/apps/files/js/templates/operationprogressbarlabel.handlebars b/apps/files/js/templates/operationprogressbarlabel.handlebars
deleted file mode 100644
index 24ac66994da..00000000000
--- a/apps/files/js/templates/operationprogressbarlabel.handlebars
+++ /dev/null
@@ -1,4 +0,0 @@
-<em class="label">
- <span class="desktop">{{textDesktop}}</span>
- <span class="mobile">{{textMobile}}</span>
-</em>
diff --git a/apps/files/js/templates/template_addbutton.handlebars b/apps/files/js/templates/template_addbutton.handlebars
deleted file mode 100644
index 478465d93aa..00000000000
--- a/apps/files/js/templates/template_addbutton.handlebars
+++ /dev/null
@@ -1,4 +0,0 @@
-<a href="#" class="button new" aria-label="{{addLongText}}">
- <span class="icon {{iconClass}}"></span>
- <span>{{addText}}</span>
-</a>
diff --git a/apps/files/js/upload.js b/apps/files/js/upload.js
deleted file mode 100644
index 90020e3dbab..00000000000
--- a/apps/files/js/upload.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2013-2014 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-function Upload(fileSelector) {
- if ($.support.xhrFileUpload) {
- return new XHRUpload(fileSelector.target.files);
- } else {
- return new FormUpload(fileSelector);
- }
-}
-Upload.target = OC.filePath('files', 'ajax', 'upload.php');
diff --git a/apps/files/lib/Controller/ViewController.php b/apps/files/lib/Controller/ViewController.php
index c402a4c5bed..b5c61aaa5f4 100644
--- a/apps/files/lib/Controller/ViewController.php
+++ b/apps/files/lib/Controller/ViewController.php
@@ -139,7 +139,6 @@ class ViewController extends Controller {
// Load the files we need
\OCP\Util::addInitScript('files', 'init');
- \OCP\Util::addStyle('files', 'merged');
\OCP\Util::addScript('files', 'main');
$userId = $this->userSession->getUser()->getUID();
diff --git a/apps/files/lib/Listener/LoadSidebarListener.php b/apps/files/lib/Listener/LoadSidebarListener.php
index 4113e3f9f9d..78b48ab1ce0 100644
--- a/apps/files/lib/Listener/LoadSidebarListener.php
+++ b/apps/files/lib/Listener/LoadSidebarListener.php
@@ -22,8 +22,5 @@ class LoadSidebarListener implements IEventListener {
}
Util::addScript(Application::APP_ID, 'sidebar');
- // needed by the Sidebar legacy tabs
- // TODO: remove when all tabs migrated to the new api
- Util::addScript('files', 'fileinfomodel');
}
}
diff --git a/apps/files/src/main-personal-settings.js b/apps/files/src/main-personal-settings.js
index 63221b3ceb0..dce190f0160 100644
--- a/apps/files/src/main-personal-settings.js
+++ b/apps/files/src/main-personal-settings.js
@@ -12,8 +12,6 @@ import PersonalSettings from './components/PersonalSettings.vue'
__webpack_nonce__ = getCSPNonce()
Vue.prototype.t = t
-
-if (!window.TESTING) {
- const View = Vue.extend(PersonalSettings)
- new View().$mount('#files-personal-settings')
-}
+const View = Vue.extend(PersonalSettings)
+const instance = new View()
+instance.$mount('#files-personal-settings')
diff --git a/apps/files/templates/fileexists.html b/apps/files/templates/fileexists.html
deleted file mode 100644
index de213803e0e..00000000000
--- a/apps/files/templates/fileexists.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
- - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
- - SPDX-License-Identifier: AGPL-3.0-only
- -->
-<div id="{dialog_name}" title="{title}" class="fileexists">
- <span class="why">{why}<!-- Which files do you want to keep --></span><br/>
- <span class="what">{what}<!-- If you select both versions, the copied file will have a number added to its name. --></span><br/>
- <br/>
- <table>
- <th><input id="checkbox-allnewfiles" class="allnewfiles checkbox" type="checkbox" /><label for="checkbox-allnewfiles">{allnewfiles}<span class="count"></span></label></th>
- <th><input id="checkbox-allexistingfiles" class="allexistingfiles checkbox" type="checkbox" /><label for="checkbox-allexistingfiles">{allexistingfiles}<span class="count"></span></label></th>
- </table>
- <div class="conflicts">
- <div class="template">
- <div class="filename"></div>
- <div class="replacement">
- <input type="checkbox" class="checkbox u-left"/>
- <label>
- <span class="svg icon"></span>
- <div class="mtime"></div>
- <div class="size"></div>
- </label>
- </div>
- <div class="original">
- <input type="checkbox" class="checkbox u-left" />
- <label>
- <span class="svg icon"></span>
- <div class="mtime"></div>
- <div class="size"></div>
- <div class="message"></div>
- </label>
- </div>
- </div>
- </div>
-</div>
diff --git a/apps/files/tests/js/breadcrumbSpec.js b/apps/files/tests/js/breadcrumbSpec.js
deleted file mode 100644
index 5633cbd99c2..00000000000
--- a/apps/files/tests/js/breadcrumbSpec.js
+++ /dev/null
@@ -1,648 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.BreadCrumb tests', function() {
- var BreadCrumb = OCA.Files.BreadCrumb;
-
- describe('Rendering', function() {
- var bc;
- beforeEach(function() {
- bc = new BreadCrumb({
- getCrumbUrl: function(part, index) {
- // for testing purposes
- return part.dir + '#' + index;
- }
- });
- });
- afterEach(function() {
- bc = null;
- });
- it('Renders its own container', function() {
- bc.render();
- expect(bc.$el.find("ul").hasClass('breadcrumb')).toEqual(true);
- });
- it('Renders root by default', function() {
- var $crumbs;
- bc.render();
- $crumbs = bc.$el.find('.crumb');
- // menu and home
- expect($crumbs.length).toEqual(2);
- expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
- expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
- expect($crumbs.eq(0).data('dir')).not.toBeDefined();
- expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
- expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
- expect($crumbs.eq(1).data('dir')).toEqual('/');
- });
- it('Renders root when switching to root', function() {
- var $crumbs;
- bc.setDirectory('/somedir');
- bc.setDirectory('/');
- $crumbs = bc.$el.find('.crumb');
- expect($crumbs.length).toEqual(2);
- expect($crumbs.eq(1).data('dir')).toEqual('/');
- });
- it('Renders single path section', function() {
- var $crumbs;
- bc.setDirectory('/somedir');
- $crumbs = bc.$el.find('.crumb');
- expect($crumbs.length).toEqual(3);
- expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
- expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
- expect($crumbs.eq(0).data('dir')).not.toBeDefined();
-
- expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
- expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
- expect($crumbs.eq(1).data('dir')).toEqual('/');
-
- expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir#2');
- expect($crumbs.eq(2).find('img').length).toEqual(0);
- expect($crumbs.eq(2).data('dir')).toEqual('/somedir');
- });
- it('Renders multiple path sections and special chars', function() {
- var $crumbs;
- bc.setDirectory('/somedir/with space/abc');
- $crumbs = bc.$el.find('.crumb');
- expect($crumbs.length).toEqual(5);
- expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
- expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
- expect($crumbs.eq(0).data('dir')).not.toBeDefined();
-
- expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
- expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
- expect($crumbs.eq(1).data('dir')).toEqual('/');
-
- expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir#2');
- expect($crumbs.eq(2).find('img').length).toEqual(0);
- expect($crumbs.eq(2).data('dir')).toEqual('/somedir');
-
- expect($crumbs.eq(3).find('a').attr('href')).toEqual('/somedir/with space#3');
- expect($crumbs.eq(3).find('img').length).toEqual(0);
- expect($crumbs.eq(3).data('dir')).toEqual('/somedir/with space');
-
- expect($crumbs.eq(4).find('a').attr('href')).toEqual('/somedir/with space/abc#4');
- expect($crumbs.eq(4).find('img').length).toEqual(0);
- expect($crumbs.eq(4).data('dir')).toEqual('/somedir/with space/abc');
- });
- it('Renders backslashes as regular directory separator', function() {
- var $crumbs;
- bc.setDirectory('/somedir\\with/mixed\\separators');
- $crumbs = bc.$el.find('.crumb');
- expect($crumbs.length).toEqual(6);
- expect($crumbs.eq(0).find('a').hasClass('icon-more')).toEqual(true);
- expect($crumbs.eq(0).find('div.popovermenu').length).toEqual(1);
- expect($crumbs.eq(0).data('dir')).not.toBeDefined();
-
- expect($crumbs.eq(1).find('a').attr('href')).toEqual('/#1');
- expect($crumbs.eq(1).find('a').hasClass('icon-home')).toEqual(true);
- expect($crumbs.eq(1).data('dir')).toEqual('/');
-
- expect($crumbs.eq(2).find('a').attr('href')).toEqual('/somedir#2');
- expect($crumbs.eq(2).find('img').length).toEqual(0);
- expect($crumbs.eq(2).data('dir')).toEqual('/somedir');
-
- expect($crumbs.eq(3).find('a').attr('href')).toEqual('/somedir/with#3');
- expect($crumbs.eq(3).find('img').length).toEqual(0);
- expect($crumbs.eq(3).data('dir')).toEqual('/somedir/with');
-
- expect($crumbs.eq(4).find('a').attr('href')).toEqual('/somedir/with/mixed#4');
- expect($crumbs.eq(4).find('img').length).toEqual(0);
- expect($crumbs.eq(4).data('dir')).toEqual('/somedir/with/mixed');
-
- expect($crumbs.eq(5).find('a').attr('href')).toEqual('/somedir/with/mixed/separators#5');
- expect($crumbs.eq(5).find('img').length).toEqual(0);
- expect($crumbs.eq(5).data('dir')).toEqual('/somedir/with/mixed/separators');
- });
- });
- describe('Events', function() {
- it('Calls onClick handler when clicking on a crumb', function() {
- var handler = sinon.stub();
- var bc = new BreadCrumb({
- onClick: handler
- });
- bc.setDirectory('/one/two/three/four');
- // Click on crumb does not work, only link
- bc.$el.find('.crumb:eq(4)').click();
- expect(handler.calledOnce).toEqual(false);
-
- handler.reset();
- // Click on crumb link works
- bc.$el.find('.crumb:eq(1) a').click();
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).thisValue).toEqual(bc.$el.find('.crumb > a').get(1));
- });
- it('Calls onDrop handler when dropping on a crumb', function() {
- var droppableStub = sinon.stub($.fn, 'droppable');
- var handler = sinon.stub();
- var bc = new BreadCrumb({
- onDrop: handler
- });
- bc.setDirectory('/one/two/three/four');
- expect(droppableStub.calledOnce).toEqual(true);
-
- expect(droppableStub.getCall(0).args[0].drop).toBeDefined();
- // simulate drop
- droppableStub.getCall(0).args[0].drop({dummy: true});
-
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0]).toEqual({dummy: true});
-
- droppableStub.restore();
- });
- });
-
- describe('Menu tests', function() {
- var bc, dummyDir, $crumbmenuLink, $popovermenu;
-
- beforeEach(function() {
- dummyDir = '/one/two/three/four/five'
-
- bc = new BreadCrumb();
- // append dummy navigation and controls
- // as they are currently used for measurements
- $('#testArea').append(
- '<div class="files-controls"></div>'
- );
- $('.files-controls').append(bc.$el);
-
- bc.setDirectory(dummyDir);
-
- $('li.crumb').each(function(index){
- $(this).css('width', 50);
- $(this).css('padding', 0);
- $(this).css('margin', 0);
- });
- $('div.crumbhome').css('width', 51);
- $('div.crumbmenu').css('width', 51);
-
- $('.files-controls').width(1000);
- bc._resize();
-
- // Shrink to show popovermenu
- $('.files-controls').width(300);
- bc._resize();
-
- $crumbmenuLink = bc.$el.find('.crumbmenu > a');
- $popovermenu = $crumbmenuLink.next('.popovermenu');
- });
- afterEach(function() {
- bc = null;
- });
-
- it('Shows only items not in the breadcrumb', function() {
- var hiddenCrumbs = bc.$el.find('.crumb:not(.crumbmenu).hidden');
- expect($popovermenu.find('li:not(.in-breadcrumb)').length).toEqual(hiddenCrumbs.length);
- });
- });
-
- describe('Resizing', function() {
- var bc, dummyDir, widths, paddings, margins;
-
- // cit() will skip tests if running on PhantomJS because it does not
- // have proper support for flexboxes.
- var cit = window.isPhantom?xit:it;
-
- beforeEach(function() {
- dummyDir = '/short name/longer name/looooooooooooonger/' +
- 'even longer long long long longer long/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/last one';
-
- bc = new BreadCrumb();
- // append dummy navigation and controls
- // as they are currently used for measurements
- $('#testArea').append(
- '<div class="files-controls"></div>'
- );
- $('.files-controls').append(bc.$el);
-
- // triggers resize implicitly
- bc.setDirectory(dummyDir);
-
- // using hard-coded widths (pre-measured) to avoid getting different
- // results on different browsers due to font engine differences
- // 51px is default size for menu and home
- widths = [51, 51, 106, 112, 160, 257, 251, 91];
- // using hard-coded paddings and margins to avoid depending on the
- // current CSS values used in the server
- paddings = [0, 0, 0, 0, 0, 0, 0, 0];
- margins = [0, 0, 0, 0, 0, 0, 0, 0];
-
- $('li.crumb').each(function(index){
- $(this).css('width', widths[index]);
- $(this).css('padding', paddings[index]);
- $(this).css('margin', margins[index]);
- });
- });
- afterEach(function() {
- bc = null;
- });
- it('Hides breadcrumbs to fit available width', function() {
- var $crumbs;
-
- $('.files-controls').width(500);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Second, third, fourth and fifth crumb are hidden and everything
- // else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Hides breadcrumbs to fit available width', function() {
- var $crumbs;
-
- $('.files-controls').width(700);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Third and fourth crumb are hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Hides breadcrumbs to fit available width taking paddings into account', function() {
- var $crumbs;
-
- // Each element is 20px wider
- paddings = [10, 10, 10, 10, 10, 10, 10, 10];
-
- $('li.crumb').each(function(index){
- $(this).css('padding', paddings[index]);
- });
-
- $('.files-controls').width(700);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Second, third and fourth crumb are hidden and everything else is
- // visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Hides breadcrumbs to fit available width taking margins into account', function() {
- var $crumbs;
-
- // Each element is 20px wider
- margins = [10, 10, 10, 10, 10, 10, 10, 10];
-
- $('li.crumb').each(function(index){
- $(this).css('margin', margins[index]);
- });
-
- $('.files-controls').width(700);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Second, third and fourth crumb are hidden and everything else is
- // visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Hides breadcrumbs to fit available width left by siblings', function() {
- var $crumbs;
-
- $('.files-controls').width(700);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Third and fourth crumb are hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
-
- // Visible sibling widths add up to 200px
- var $previousSibling = $('<div class="otherSibling"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $previousSibling.css('width', '50px');
- $previousSibling.css('min-width', '50px');
- $('.files-controls').prepend($previousSibling);
-
- var $creatableActions = $('<div class="actions creatable"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $creatableActions.css('width', '100px');
- $creatableActions.css('min-width', '100px');
- $('.files-controls').append($creatableActions);
-
- var $nextHiddenSibling = $('<div class="otherSibling hidden"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $nextHiddenSibling.css('width', '200px');
- $nextHiddenSibling.css('min-width', '200px');
- $('.files-controls').append($nextHiddenSibling);
-
- var $nextSibling = $('<div class="otherSibling"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $nextSibling.css('width', '50px');
- $nextSibling.css('min-width', '50px');
- $('.files-controls').append($nextSibling);
-
- bc._resize();
-
- // Second, third, fourth and fifth crumb are hidden and everything
- // else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Hides breadcrumbs to fit available width left by siblings with paddings and margins', function() {
- var $crumbs;
-
- $('.files-controls').width(700);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Third and fourth crumb are hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
-
- // Visible sibling widths add up to 200px
- var $previousSibling = $('<div class="otherSibling"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $previousSibling.css('width', '10px');
- $previousSibling.css('min-width', '10px');
- $previousSibling.css('margin', '20px');
- $('.files-controls').prepend($previousSibling);
-
- var $creatableActions = $('<div class="actions creatable"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $creatableActions.css('width', '20px');
- $creatableActions.css('min-width', '20px');
- $creatableActions.css('margin-left', '40px');
- $creatableActions.css('padding-right', '40px');
- $('.files-controls').append($creatableActions);
-
- var $nextHiddenSibling = $('<div class="otherSibling hidden"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $nextHiddenSibling.css('width', '200px');
- $nextHiddenSibling.css('min-width', '200px');
- $('.files-controls').append($nextHiddenSibling);
-
- var $nextSibling = $('<div class="otherSibling"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $nextSibling.css('width', '10px');
- $nextSibling.css('min-width', '10px');
- $nextSibling.css('padding', '20px');
- $('.files-controls').append($nextSibling);
-
- bc._resize();
-
- // Second, third, fourth and fifth crumb are hidden and everything
- // else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Updates the breadcrumbs when reducing available width', function() {
- var $crumbs;
-
- // enough space
- $('.files-controls').width(1800);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Menu is hidden
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(true);
-
- // simulate decrease
- $('.files-controls').width(950);
- bc._resize();
-
- // Third crumb is hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Updates the breadcrumbs when reducing available width taking into account the menu width', function() {
- var $crumbs;
-
- // enough space
- $('.files-controls').width(1800);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Menu is hidden
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
-
- // simulate decrease
- // 650 is enough for all the crumbs except the third and fourth
- // ones, but not enough for the menu and all the crumbs except the
- // third and fourth ones; the second one has to be hidden too.
- $('.files-controls').width(650);
- bc._resize();
-
- // Second, third and fourth crumb are hidden and everything else is
- // visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Updates the breadcrumbs when increasing available width', function() {
- var $crumbs;
-
- // limited space
- $('.files-controls').width(850);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Third and fourth crumb are hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
-
- // simulate increase
- $('.files-controls').width(1000);
- bc._resize();
-
- // Third crumb is hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- it('Updates the breadcrumbs when increasing available width taking into account the menu width', function() {
- var $crumbs;
-
- // limited space
- $('.files-controls').width(850);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Third and fourth crumb are hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
-
- // simulate increase
- // 1030 is enough for all the crumbs if the menu is hidden.
- $('.files-controls').width(1030);
- bc._resize();
-
- // Menu is hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- cit('Updates the breadcrumbs when increasing available width with an expanding sibling', function() {
- var $crumbs;
-
- // The sibling expands to fill all the width left by the breadcrumbs
- var $nextSibling = $('<div class="sibling"></div>');
- // Set both the width and the min-width to even differences in width
- // handling in the browsers used to run the tests.
- $nextSibling.css('width', '10px');
- $nextSibling.css('min-width', '10px');
- $nextSibling.css('display', 'flex');
- $nextSibling.css('flex', '1 1');
- var $nextSiblingChild = $('<div class="siblingChild"></div>');
- $nextSiblingChild.css('margin-left', 'auto');
- $nextSibling.append($nextSiblingChild);
- $('.files-controls').append($nextSibling);
-
- // limited space
- $('.files-controls').width(850);
- bc._resize();
-
- $crumbs = bc.$el.find('.crumb');
-
- // Third and fourth crumb are hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
-
- // simulate increase
- $('.files-controls').width(1000);
- bc._resize();
-
- // Third crumb is hidden and everything else is visible
- expect($crumbs.eq(0).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(1).hasClass('hidden')).toEqual(false);
-
- expect($crumbs.eq(2).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(3).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(4).hasClass('hidden')).toEqual(true);
- expect($crumbs.eq(5).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(6).hasClass('hidden')).toEqual(false);
- expect($crumbs.eq(7).hasClass('hidden')).toEqual(false);
- });
- });
-});
diff --git a/apps/files/tests/js/detailsviewSpec.js b/apps/files/tests/js/detailsviewSpec.js
deleted file mode 100644
index 2ade0876e8e..00000000000
--- a/apps/files/tests/js/detailsviewSpec.js
+++ /dev/null
@@ -1,240 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.DetailsView tests', function() {
- var detailsView;
-
- beforeEach(function() {
- detailsView = new OCA.Files.DetailsView();
- });
- afterEach(function() {
- detailsView.remove();
- detailsView = undefined;
- });
- it('renders itself empty when nothing registered', function() {
- detailsView.render();
- expect(detailsView.$el.find('.detailFileInfoContainer').length).toEqual(1);
- expect(detailsView.$el.find('.tabsContainer').length).toEqual(1);
- });
- describe('file info detail view', function() {
- it('returns registered view', function() {
- var testView = new OCA.Files.DetailFileInfoView();
- var testView2 = new OCA.Files.DetailFileInfoView();
- detailsView.addDetailView(testView);
- detailsView.addDetailView(testView2);
-
- detailViews = detailsView.getDetailViews();
-
- expect(detailViews).toContain(testView);
- expect(detailViews).toContain(testView2);
-
- // Modify array and check that registered detail views are not
- // modified
- detailViews.pop();
- detailViews.pop();
-
- detailViews = detailsView.getDetailViews();
-
- expect(detailViews).toContain(testView);
- expect(detailViews).toContain(testView2);
- });
- it('renders registered view', function() {
- var testView = new OCA.Files.DetailFileInfoView();
- var testView2 = new OCA.Files.DetailFileInfoView();
- detailsView.addDetailView(testView);
- detailsView.addDetailView(testView2);
- detailsView.render();
-
- expect(detailsView.$el.find('.detailFileInfoContainer .detailFileInfoView').length).toEqual(2);
- });
- it('updates registered tabs when fileinfo is updated', function() {
- var viewRenderStub = sinon.stub(OCA.Files.DetailFileInfoView.prototype, 'render');
- var testView = new OCA.Files.DetailFileInfoView();
- var testView2 = new OCA.Files.DetailFileInfoView();
- detailsView.addDetailView(testView);
- detailsView.addDetailView(testView2);
- detailsView.render();
-
- var fileInfo = {id: 5, name: 'test.txt'};
- viewRenderStub.reset();
- detailsView.setFileInfo(fileInfo);
-
- expect(testView.getFileInfo()).toEqual(fileInfo);
- expect(testView2.getFileInfo()).toEqual(fileInfo);
-
- expect(viewRenderStub.callCount).toEqual(2);
- viewRenderStub.restore();
- });
- });
- describe('tabs', function() {
- var testView, testView2;
-
- beforeEach(function() {
- testView = new OCA.Files.DetailTabView({id: 'test1'});
- testView2 = new OCA.Files.DetailTabView({id: 'test2'});
- detailsView.addTabView(testView);
- detailsView.addTabView(testView2);
- detailsView.render();
- });
- it('initially renders only the selected tab', function() {
- expect(detailsView.$el.find('.tab').length).toEqual(1);
- expect(detailsView.$el.find('.tab').attr('id')).toEqual('test1');
- });
- it('updates tab model and rerenders on-demand as soon as it gets selected', function() {
- var tab1RenderStub = sinon.stub(testView, 'render');
- var tab2RenderStub = sinon.stub(testView2, 'render');
- var fileInfo1 = new OCA.Files.FileInfoModel({id: 5, name: 'test.txt'});
- var fileInfo2 = new OCA.Files.FileInfoModel({id: 8, name: 'test2.txt'});
-
- detailsView.setFileInfo(fileInfo1);
-
- // first tab renders, not the second one
- expect(tab1RenderStub.calledOnce).toEqual(true);
- expect(tab2RenderStub.notCalled).toEqual(true);
-
- // info got set only to the first visible tab
- expect(testView.getFileInfo()).toEqual(fileInfo1);
- expect(testView2.getFileInfo()).toBeUndefined();
-
- // select second tab for first render
- detailsView.$el.find('.tabHeader').eq(1).click();
-
- // second tab got rendered
- expect(tab2RenderStub.calledOnce).toEqual(true);
- expect(testView2.getFileInfo()).toEqual(fileInfo1);
-
- // select the first tab again
- detailsView.$el.find('.tabHeader').eq(0).click();
-
- // no re-render
- expect(tab1RenderStub.calledOnce).toEqual(true);
- expect(tab2RenderStub.calledOnce).toEqual(true);
-
- tab1RenderStub.reset();
- tab2RenderStub.reset();
-
- // switch to another file
- detailsView.setFileInfo(fileInfo2);
-
- // only the visible tab was updated and rerendered
- expect(tab1RenderStub.calledOnce).toEqual(true);
- expect(testView.getFileInfo()).toEqual(fileInfo2);
-
- // second/invisible tab still has old info, not rerendered
- expect(tab2RenderStub.notCalled).toEqual(true);
- expect(testView2.getFileInfo()).toEqual(fileInfo1);
-
- // reselect the second one
- detailsView.$el.find('.tabHeader').eq(1).click();
-
- // second tab becomes visible, updated and rendered
- expect(testView2.getFileInfo()).toEqual(fileInfo2);
- expect(tab2RenderStub.calledOnce).toEqual(true);
-
- tab1RenderStub.restore();
- tab2RenderStub.restore();
- });
- it('selects the first tab by default', function() {
- expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(true);
- expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(false);
- expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(false);
- expect(detailsView.$el.find('.tab').eq(1).length).toEqual(0);
- });
- it('switches the current tab when clicking on tab header', function() {
- detailsView.$el.find('.tabHeader').eq(1).click();
- expect(detailsView.$el.find('.tabHeader').eq(0).hasClass('selected')).toEqual(false);
- expect(detailsView.$el.find('.tabHeader').eq(1).hasClass('selected')).toEqual(true);
- expect(detailsView.$el.find('.tab').eq(0).hasClass('hidden')).toEqual(true);
- expect(detailsView.$el.find('.tab').eq(1).hasClass('hidden')).toEqual(false);
- });
- describe('tab visibility', function() {
- beforeEach(function() {
- detailsView.remove();
- detailsView = new OCA.Files.DetailsView();
- });
- it('does not display tab headers when only one tab exists', function() {
- testView = new OCA.Files.DetailTabView({id: 'test1'});
- detailsView.addTabView(testView);
- detailsView.render();
-
- expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true);
- expect(detailsView.$el.find('.tabHeader').length).toEqual(1);
- });
- it('does not display tab that do not pass visibility check', function() {
- testView = new OCA.Files.DetailTabView({id: 'test1'});
- testView.canDisplay = sinon.stub().returns(false);
- testView2 = new OCA.Files.DetailTabView({id: 'test2'});
- var testView3 = new OCA.Files.DetailTabView({id: 'test3'});
- detailsView.addTabView(testView);
- detailsView.addTabView(testView2);
- detailsView.addTabView(testView3);
-
- var fileInfo = {id: 5, name: 'test.txt'};
- detailsView.setFileInfo(fileInfo);
-
- expect(testView.canDisplay.calledOnce).toEqual(true);
- expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true);
-
- expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(false);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test3]').hasClass('hidden')).toEqual(false);
- });
- it('does not show tab headers if only one header is visible due to visibility check', function() {
- testView = new OCA.Files.DetailTabView({id: 'test1'});
- testView.canDisplay = sinon.stub().returns(false);
- testView2 = new OCA.Files.DetailTabView({id: 'test2'});
- detailsView.addTabView(testView);
- detailsView.addTabView(testView2);
-
- var fileInfo = {id: 5, name: 'test.txt'};
- detailsView.setFileInfo(fileInfo);
-
- expect(testView.canDisplay.calledOnce).toEqual(true);
- expect(testView.canDisplay.calledWith(fileInfo)).toEqual(true);
-
- expect(detailsView.$el.find('.tabHeaders').hasClass('hidden')).toEqual(true);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('hidden')).toEqual(true);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('hidden')).toEqual(false);
- });
- it('deselects the current tab if a model update does not pass the visibility check', function() {
- testView = new OCA.Files.DetailTabView({id: 'test1'});
- testView.canDisplay = function(fileInfo) {
- return fileInfo.mimetype !== 'httpd/unix-directory';
- };
- testView2 = new OCA.Files.DetailTabView({id: 'test2'});
- detailsView.addTabView(testView);
- detailsView.addTabView(testView2);
-
- var fileInfo = {id: 5, name: 'test.txt', mimetype: 'text/plain'};
- detailsView.setFileInfo(fileInfo);
-
- expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(true);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(false);
-
- detailsView.setFileInfo({id: 10, name: 'folder', mimetype: 'httpd/unix-directory'});
-
- expect(detailsView.$el.find('.tabHeader[data-tabid=test1]').hasClass('selected')).toEqual(false);
- expect(detailsView.$el.find('.tabHeader[data-tabid=test2]').hasClass('selected')).toEqual(true);
- });
- });
- it('sorts by order and then label', function() {
- detailsView.remove();
- detailsView = new OCA.Files.DetailsView();
- detailsView.addTabView(new OCA.Files.DetailTabView({id: 'abc', order: 20}));
- detailsView.addTabView(new OCA.Files.DetailTabView({id: 'def', order: 10}));
- detailsView.addTabView(new OCA.Files.DetailTabView({id: 'jkl'}));
- detailsView.addTabView(new OCA.Files.DetailTabView({id: 'ghi'}));
- detailsView.render();
-
- var tabs = detailsView.$el.find('.tabHeader').map(function() {
- return $(this).attr('data-tabid');
- }).toArray();
-
- expect(tabs).toEqual(['ghi', 'jkl', 'def', 'abc']);
- });
- });
-});
diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js
deleted file mode 100644
index 4a95bb7aeb5..00000000000
--- a/apps/files/tests/js/fileactionsSpec.js
+++ /dev/null
@@ -1,739 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.FileActions tests', function() {
- var fileList, fileActions, clock;
-
- beforeEach(function() {
- clock = sinon.useFakeTimers();
- // init horrible parameters
- var $body = $('#testArea');
- $body.append('<input type="hidden" id="permissions" value="31"></input>');
- $body.append('<table class="files-filestable list-container view-grid"><tbody class="files-fileList"></tbody></table>');
- // dummy files table
- fileActions = new OCA.Files.FileActions();
- fileActions.registerAction({
- name: 'Testdropdown',
- displayName: 'Testdropdowndisplay',
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- icon: function () {
- return OC.imagePath('core', 'actions/download');
- }
- });
-
- fileActions.registerAction({
- name: 'Testinline',
- displayName: 'Testinlinedisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.registerAction({
- name: 'Testdefault',
- displayName: 'Testdefaultdisplay',
- mime: 'all',
- permissions: OC.PERMISSION_READ
- });
- fileActions.setDefault('all', 'Testdefault');
- fileList = new OCA.Files.FileList($body, {
- fileActions: fileActions
- });
- fileList.changeDirectory('/subdir', false, true);
- });
- afterEach(function() {
- fileActions = null;
- fileList.destroy();
- fileList = undefined;
- clock.restore();
- $('#permissions, .files-filestable').remove();
- });
- it('calling clear() clears file actions', function() {
- fileActions.clear();
- expect(fileActions.actions).toEqual({});
- expect(fileActions.defaults).toEqual({});
- expect(fileActions.icons).toEqual({});
- expect(fileActions.currentFile).toBe(null);
- });
- describe('displaying actions', function() {
- var $tr;
-
- beforeEach(function() {
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456',
- permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE
- };
-
- // note: FileActions.display() is called implicitly
- $tr = fileList.add(fileData);
- });
- it('renders inline file actions', function() {
- // actions defined after call
- expect($tr.find('.action.action-testinline').length).toEqual(1);
- expect($tr.find('.action.action-testinline').attr('data-action')).toEqual('Testinline');
- });
- it('does not render dropdown actions', function() {
- expect($tr.find('.action.action-testdropdown').length).toEqual(0);
- });
- it('does not render default action', function() {
- expect($tr.find('.action.action-testdefault').length).toEqual(0);
- });
- it('replaces file actions when displayed twice', function() {
- fileActions.display($tr.find('td.filename'), true, fileList);
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action.action-testinline').length).toEqual(1);
- });
- it('renders actions menu trigger', function() {
- expect($tr.find('.action.action-menu').length).toEqual(1);
- expect($tr.find('.action.action-menu').attr('data-action')).toEqual('menu');
- });
- it('only renders actions relevant to the mime type', function() {
- fileActions.registerAction({
- name: 'Match',
- displayName: 'MatchDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'text/plain',
- permissions: OC.PERMISSION_READ
- });
- fileActions.registerAction({
- name: 'Nomatch',
- displayName: 'NoMatchDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'application/octet-stream',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.display($tr.find('td.filename'), true, fileList);
- expect($tr.find('.action.action-match').length).toEqual(1);
- expect($tr.find('.action.action-nomatch').length).toEqual(0);
- });
- it('only renders actions relevant to the permissions', function() {
- fileActions.registerAction({
- name: 'Match',
- displayName: 'MatchDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'text/plain',
- permissions: OC.PERMISSION_UPDATE
- });
- fileActions.registerAction({
- name: 'Nomatch',
- displayName: 'NoMatchDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'text/plain',
- permissions: OC.PERMISSION_DELETE
- });
-
- fileActions.display($tr.find('td.filename'), true, fileList);
- expect($tr.find('.action.action-match').length).toEqual(1);
- expect($tr.find('.action.action-nomatch').length).toEqual(0);
- });
- it('display inline icon with image path', function() {
- fileActions.registerAction({
- name: 'Icon',
- displayName: 'IconDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- icon: OC.imagePath('core', 'actions/icon'),
- permissions: OC.PERMISSION_READ
- });
- fileActions.registerAction({
- name: 'NoIcon',
- displayName: 'NoIconDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action.action-icon').length).toEqual(1);
- expect($tr.find('.action.action-icon').find('img').length).toEqual(1);
- expect($tr.find('.action.action-icon').find('img').eq(0).attr('src')).toEqual(OC.imagePath('core', 'actions/icon'));
-
- expect($tr.find('.action.action-noicon').length).toEqual(1);
- expect($tr.find('.action.action-noicon').find('img').length).toEqual(0);
- });
- it('display alt text on inline icon with image path', function() {
- fileActions.registerAction({
- name: 'IconAltText',
- displayName: 'IconAltTextDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- icon: OC.imagePath('core', 'actions/iconAltText'),
- altText: 'alt icon text',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.registerAction({
- name: 'IconNoAltText',
- displayName: 'IconNoAltTextDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- icon: OC.imagePath('core', 'actions/iconNoAltText'),
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action.action-iconalttext').length).toEqual(1);
- expect($tr.find('.action.action-iconalttext').find('img').length).toEqual(1);
- expect($tr.find('.action.action-iconalttext').find('img').eq(0).attr('alt')).toEqual('alt icon text');
-
- expect($tr.find('.action.action-iconnoalttext').length).toEqual(1);
- expect($tr.find('.action.action-iconnoalttext').find('img').length).toEqual(1);
- expect($tr.find('.action.action-iconnoalttext').find('img').eq(0).attr('alt')).toEqual('');
- });
- it('display inline icon with iconClass', function() {
- fileActions.registerAction({
- name: 'Icon',
- displayName: 'IconDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- iconClass: 'icon-test',
- permissions: OC.PERMISSION_READ
- });
- fileActions.registerAction({
- name: 'NoIcon',
- displayName: 'NoIconDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action.action-icon').length).toEqual(1);
- expect($tr.find('.action.action-icon').find('.icon').length).toEqual(1);
- expect($tr.find('.action.action-icon').find('.icon').hasClass('icon-test')).toEqual(true);
-
- expect($tr.find('.action.action-noicon').length).toEqual(1);
- expect($tr.find('.action.action-noicon').find('.icon').length).toEqual(0);
- });
- it('display alt text on inline icon with iconClass when no display name exists', function() {
- fileActions.registerAction({
- name: 'IconAltText',
- displayName: '',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- iconClass: 'icon-alttext',
- altText: 'alt icon text',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.registerAction({
- name: 'IconNoAltText',
- displayName: 'IconNoAltTextDisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- altText: 'useless alt text',
- iconClass: 'icon-noalttext',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action.action-iconalttext').length).toEqual(1);
- expect($tr.find('.action.action-iconalttext').find('.icon').length).toEqual(1);
- expect($tr.find('.action.action-iconalttext').find('.hidden-visually').text()).toEqual('alt icon text');
-
- expect($tr.find('.action.action-iconnoalttext').length).toEqual(1);
- expect($tr.find('.action.action-iconnoalttext').find('.icon').length).toEqual(1);
- expect($tr.find('.action.action-iconnoalttext').find('.hidden-visually').length).toEqual(0);
- });
- });
- describe('action handler', function() {
- var actionStub, $tr, clock;
-
- beforeEach(function() {
- clock = sinon.useFakeTimers();
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- actionStub = sinon.stub();
- fileActions.registerAction({
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- icon: OC.imagePath('core', 'actions/test'),
- permissions: OC.PERMISSION_READ,
- actionHandler: actionStub
- });
- $tr = fileList.add(fileData);
- });
- afterEach(function() {
- OC.hideMenus();
- // jump past animations
- clock.tick(1000);
- clock.restore();
- });
- it('passes context to action handler', function() {
- var notifyUpdateListenersSpy = sinon.spy(fileList.fileActions, '_notifyUpdateListeners');
- expect($tr.length).toEqual(1);
- expect($tr.find('.action-test').length).toEqual(1);
- $tr.find('.action-test').click();
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
- var context = actionStub.getCall(0).args[1];
- expect(context.$file.is($tr)).toEqual(true);
- expect(context.fileList).toBeDefined();
- expect(context.fileActions).toBeDefined();
- expect(context.dir).toEqual('/subdir');
- expect(context.fileInfoModel.get('name')).toEqual('testName.txt');
-
- expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
- expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
- expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
- expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
- expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
- action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
- fileName: 'testName.txt',
- context: context
- });
- expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
- expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
- action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
- fileName: 'testName.txt',
- context: context
- });
-
- // when data-path is defined
- actionStub.reset();
- $tr.attr('data-path', '/somepath');
- $tr.find('.action-test').click();
- context = actionStub.getCall(0).args[1];
- expect(context.dir).toEqual('/somepath');
- });
- it('also triggers action handler when calling triggerAction()', function() {
- var notifyUpdateListenersSpy = sinon.spy(fileList.fileActions, '_notifyUpdateListeners');
- var model = new OCA.Files.FileInfoModel({
- id: 1,
- name: 'Test.txt',
- path: '/subdir',
- mime: 'text/plain',
- permissions: 31
- });
- fileActions.triggerAction('Test', model, fileList);
-
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
- expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList);
- expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions);
- expect(actionStub.getCall(0).args[1].fileInfoModel).toEqual(model);
-
- expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
- expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
- expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
- expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
- expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
- action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
- fileName: 'Test.txt',
- context: {
- fileActions: fileActions,
- fileInfoModel: model,
- dir: '/subdir',
- fileList: fileList,
- $file: fileList.findFileEl('Test.txt')
- }
- });
- expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
- expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
- action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
- fileName: 'Test.txt',
- context: {
- fileActions: fileActions,
- fileInfoModel: model,
- dir: '/subdir',
- fileList: fileList,
- $file: fileList.findFileEl('Test.txt')
- }
- });
- });
- it('triggers listener events when invoked directly', function() {
- var context = {fileActions: new OCA.Files.FileActions()}
- var notifyUpdateListenersSpy = sinon.spy(context.fileActions, '_notifyUpdateListeners');
- var testAction = fileActions.get('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'];
-
- testAction('Test.txt', context);
-
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
- expect(actionStub.getCall(0).args[1]).toBe(context);
-
- expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
- expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
- expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
- expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
- expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
- action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
- fileName: 'Test.txt',
- context: context
- });
- expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
- expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
- action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
- fileName: 'Test.txt',
- context: context
- });
- }),
- describe('actions menu', function() {
- it('shows actions menu inside row when clicking the menu trigger', function() {
- expect($tr.find('td.filename .fileActionsMenu').length).toEqual(0);
- $tr.find('.action-menu').click();
- expect($tr.find('td.filename .fileActionsMenu').length).toEqual(1);
- });
- it('shows highlight on current row', function() {
- $tr.find('.action-menu').click();
- expect($tr.hasClass('mouseOver')).toEqual(true);
- });
- it('cleans up after hiding', function() {
- var slideUpStub = sinon.stub($.fn, 'slideUp');
- $tr.find('.action-menu').click();
- expect($tr.find('.fileActionsMenu').length).toEqual(1);
- OC.hideMenus();
- // sliding animation
- expect(slideUpStub.calledOnce).toEqual(true);
- slideUpStub.getCall(0).args[1]();
- expect($tr.hasClass('mouseOver')).toEqual(false);
- expect($tr.find('.fileActionsMenu').length).toEqual(0);
- });
- });
- });
- describe('custom rendering', function() {
- var $tr;
- beforeEach(function() {
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- $tr = fileList.add(fileData);
- });
- it('regular function', function() {
- var actionStub = sinon.stub();
- fileActions.registerAction({
- name: 'Test',
- displayName: '',
- mime: 'all',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- render: function(actionSpec, isDefault, context) {
- expect(actionSpec.name).toEqual('Test');
- expect(actionSpec.displayName).toEqual('');
- expect(actionSpec.permissions).toEqual(OC.PERMISSION_READ);
- expect(actionSpec.mime).toEqual('all');
- expect(isDefault).toEqual(false);
-
- expect(context.fileList).toEqual(fileList);
- expect(context.$file[0]).toEqual($tr[0]);
-
- var $customEl = $('<a class="action action-test" href="#"><span>blabli</span><span>blabla</span></a>');
- $tr.find('td:first').append($customEl);
- return $customEl;
- },
- actionHandler: actionStub
- });
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- var $actionEl = $tr.find('td:first .action-test');
- expect($actionEl.length).toEqual(1);
- expect($actionEl.hasClass('action')).toEqual(true);
-
- $actionEl.click();
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
- });
- });
- describe('merging', function() {
- var $tr;
- beforeEach(function() {
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- path: '/anotherpath/there',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- $tr = fileList.add(fileData);
- });
- afterEach(function() {
- $tr = null;
- });
- it('copies all actions to target file actions', function() {
- var actions1 = new OCA.Files.FileActions();
- var actions2 = new OCA.Files.FileActions();
- var actionStub1 = sinon.stub();
- var actionStub2 = sinon.stub();
- actions1.registerAction({
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub1
- });
- actions2.registerAction({
- name: 'Test2',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub2
- });
- actions2.merge(actions1);
-
- actions2.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action-test').length).toEqual(1);
- expect($tr.find('.action-test2').length).toEqual(1);
-
- $tr.find('.action-test').click();
- expect(actionStub1.calledOnce).toEqual(true);
- expect(actionStub2.notCalled).toEqual(true);
-
- actionStub1.reset();
-
- $tr.find('.action-test2').click();
- expect(actionStub1.notCalled).toEqual(true);
- expect(actionStub2.calledOnce).toEqual(true);
- });
- it('overrides existing actions on merge', function() {
- var actions1 = new OCA.Files.FileActions();
- var actions2 = new OCA.Files.FileActions();
- var actionStub1 = sinon.stub();
- var actionStub2 = sinon.stub();
- actions1.registerAction({
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub1
- });
- actions2.registerAction({
- name: 'Test', // override
- mime: 'all',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub2
- });
- actions1.merge(actions2);
-
- actions1.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action-test').length).toEqual(1);
-
- $tr.find('.action-test').click();
- expect(actionStub1.notCalled).toEqual(true);
- expect(actionStub2.calledOnce).toEqual(true);
- });
- it('overrides existing action when calling register after merge', function() {
- var actions1 = new OCA.Files.FileActions();
- var actions2 = new OCA.Files.FileActions();
- var actionStub1 = sinon.stub();
- var actionStub2 = sinon.stub();
- actions1.registerAction({
- mime: 'all',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub1
- });
-
- actions1.merge(actions2);
-
- // late override
- actions1.registerAction({
- mime: 'all',
- name: 'Test', // override
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub2
- });
-
- actions1.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action-test').length).toEqual(1);
-
- $tr.find('.action-test').click();
- expect(actionStub1.notCalled).toEqual(true);
- expect(actionStub2.calledOnce).toEqual(true);
- });
- it('leaves original file actions untouched (clean copy)', function() {
- var actions1 = new OCA.Files.FileActions();
- var actions2 = new OCA.Files.FileActions();
- var actionStub1 = sinon.stub();
- var actionStub2 = sinon.stub();
- actions1.registerAction({
- mime: 'all',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub1
- });
-
- // copy the Test action to actions2
- actions2.merge(actions1);
-
- // late override
- actions2.registerAction({
- mime: 'all',
- name: 'Test', // override
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub2
- });
-
- // check if original actions still call the correct handler
- actions1.display($tr.find('td.filename'), true, fileList);
-
- expect($tr.find('.action-test').length).toEqual(1);
-
- $tr.find('.action-test').click();
- expect(actionStub1.calledOnce).toEqual(true);
- expect(actionStub2.notCalled).toEqual(true);
- });
- });
- describe('events', function() {
- var clock;
- beforeEach(function() {
- clock = sinon.useFakeTimers();
- });
- afterEach(function() {
- clock.restore();
- });
- it('notifies update event handlers once after multiple changes', function() {
- var actionStub = sinon.stub();
- var handler = sinon.stub();
- fileActions.on('registerAction', handler);
- fileActions.registerAction({
- mime: 'all',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub
- });
- fileActions.registerAction({
- mime: 'all',
- name: 'Test2',
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub
- });
- expect(handler.calledTwice).toEqual(true);
- });
- it('does not notifies update event handlers after unregistering', function() {
- var actionStub = sinon.stub();
- var handler = sinon.stub();
- fileActions.on('registerAction', handler);
- fileActions.off('registerAction', handler);
- fileActions.registerAction({
- mime: 'all',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub
- });
- fileActions.registerAction({
- mime: 'all',
- name: 'Test2',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_READ,
- icon: OC.imagePath('core', 'actions/test'),
- actionHandler: actionStub
- });
- expect(handler.notCalled).toEqual(true);
- });
- });
- describe('default actions', function() {
- describe('download', function() {
- it('redirects to URL and sets busy state to list', function() {
- var handleDownloadStub = sinon.stub(OCA.Files.Files, 'handleDownload');
- var busyStub = sinon.stub(fileList, 'showFileBusyState');
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456',
- permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE
- };
-
- // note: FileActions.display() is called implicitly
- fileList.add(fileData);
-
- var model = fileList.getModelForFile('testName.txt');
-
- fileActions.registerDefaultActions();
- fileActions.triggerAction('Download', model, fileList);
-
- expect(busyStub.calledOnce).toEqual(true);
- expect(busyStub.calledWith('testName.txt', true)).toEqual(true);
- expect(handleDownloadStub.calledOnce).toEqual(true);
- expect(handleDownloadStub.getCall(0).args[0]).toEqual(
- OC.getRootPath() + '/remote.php/webdav/subdir/testName.txt'
- );
- busyStub.reset();
- handleDownloadStub.yield();
-
- expect(busyStub.calledOnce).toEqual(true);
- expect(busyStub.calledWith('testName.txt', false)).toEqual(true);
-
- busyStub.restore();
- handleDownloadStub.restore();
- });
- });
- });
- describe('download spinner', function() {
- var FileActions = OCA.Files.FileActions;
- var $el;
-
- beforeEach(function() {
- $el = $('<a href="#"><span class="icon icon-download"></span><span>Download</span></a>');
- });
-
- it('replaces download icon with spinner', function() {
- FileActions.updateFileActionSpinner($el, true);
- expect($el.find('.icon.icon-loading-small').length).toEqual(1);
- expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(true);
- });
- it('replaces spinner back with download icon with spinner', function() {
- FileActions.updateFileActionSpinner($el, true);
- FileActions.updateFileActionSpinner($el, false);
- expect($el.find('.icon.icon-loading-small').length).toEqual(0);
- expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(false);
- });
- });
-});
diff --git a/apps/files/tests/js/fileactionsmenuSpec.js b/apps/files/tests/js/fileactionsmenuSpec.js
deleted file mode 100644
index 1ed8dc15a98..00000000000
--- a/apps/files/tests/js/fileactionsmenuSpec.js
+++ /dev/null
@@ -1,344 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.FileActionsMenu tests', function() {
- var fileList, fileActions, menu, actionStub, menuContext, $tr;
-
- beforeEach(function() {
- // init horrible parameters
- var $body = $('#testArea');
- $body.append('<input type="hidden" id="permissions" value="31"></input>');
- // dummy files table
- actionStub = sinon.stub();
- fileActions = new OCA.Files.FileActions();
- fileList = new OCA.Files.FileList($body, {
- fileActions: fileActions
- });
- fileList.changeDirectory('/subdir', false, true);
-
- fileActions.registerAction({
- name: 'Testdropdown',
- displayName: 'Testdropdowndisplay',
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- icon: function () {
- return OC.imagePath('core', 'actions/download');
- },
- actionHandler: actionStub
- });
-
- fileActions.registerAction({
- name: 'Testdropdownnoicon',
- displayName: 'Testdropdowndisplaynoicon',
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- actionHandler: actionStub
- });
-
- fileActions.registerAction({
- name: 'Testinline',
- displayName: 'Testinlinedisplay',
- type: OCA.Files.FileActions.TYPE_INLINE,
- mime: 'all',
- permissions: OC.PERMISSION_READ
- });
-
- fileActions.registerAction({
- name: 'Testdefault',
- displayName: 'Testdefaultdisplay',
- mime: 'all',
- permissions: OC.PERMISSION_READ
- });
- fileActions.setDefault('all', 'Testdefault');
-
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- $tr = fileList.add(fileData);
-
- menuContext = {
- $file: $tr,
- fileList: fileList,
- fileActions: fileActions,
- dir: fileList.getCurrentDirectory()
- };
- menu = new OCA.Files.FileActionsMenu();
- menu.show(menuContext);
- });
- afterEach(function() {
- fileActions = null;
- fileList.destroy();
- fileList = undefined;
- menu.remove();
- $('#permissions, .files-filestable').remove();
- });
-
- describe('rendering', function() {
- it('renders dropdown actions in menu', function() {
- var $action = menu.$el.find('a[data-action=Testdropdown]');
- expect($action.length).toEqual(1);
- expect($action.find('img').attr('src'))
- .toEqual(OC.imagePath('core', 'actions/download'));
- expect($action.find('.no-icon').length).toEqual(0);
-
- $action = menu.$el.find('a[data-action=Testdropdownnoicon]');
- expect($action.length).toEqual(1);
- expect($action.find('img').length).toEqual(0);
- expect($action.find('.no-icon').length).toEqual(1);
- });
- it('does not render default actions', function() {
- expect(menu.$el.find('a[data-action=Testdefault]').length).toEqual(0);
- });
- it('render inline actions', function() {
- expect(menu.$el.find('a[data-action=Testinline]').length).toEqual(1);
- });
- it('render inline actions but it is hidden', function() {
- expect(menu.$el.find('a[data-action=Testinline]').parent().hasClass('hidden')).toEqual(true);
- });
- it('only renders actions relevant to the mime type', function() {
- fileActions.registerAction({
- name: 'Match',
- displayName: 'MatchDisplay',
- mime: 'text/plain',
- permissions: OC.PERMISSION_READ
- });
- fileActions.registerAction({
- name: 'Nomatch',
- displayName: 'NoMatchDisplay',
- mime: 'application/octet-stream',
- permissions: OC.PERMISSION_READ
- });
-
- menu.render();
- expect(menu.$el.find('a[data-action=Match]').length).toEqual(1);
- expect(menu.$el.find('a[data-action=NoMatch]').length).toEqual(0);
- });
- it('only renders actions relevant to the permissions', function() {
- fileActions.registerAction({
- name: 'Match',
- displayName: 'MatchDisplay',
- mime: 'text/plain',
- permissions: OC.PERMISSION_UPDATE
- });
- fileActions.registerAction({
- name: 'Nomatch',
- displayName: 'NoMatchDisplay',
- mime: 'text/plain',
- permissions: OC.PERMISSION_DELETE
- });
-
- menu.render();
- expect(menu.$el.find('a[data-action=Match]').length).toEqual(1);
- expect(menu.$el.find('a[data-action=NoMatch]').length).toEqual(0);
- });
- it('sorts by order attribute, then name', function() {
- fileActions.registerAction({
- name: 'Baction',
- displayName: 'Baction',
- order: 2,
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL
- });
- fileActions.registerAction({
- name: 'Zaction',
- displayName: 'Zaction',
- order: 1,
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL
- });
- fileActions.registerAction({
- name: 'Yaction',
- displayName: 'Yaction',
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL
- });
- fileActions.registerAction({
- name: 'Waction',
- displayName: 'Waction',
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL
- });
-
- menu.render();
- var zactionIndex = menu.$el.find('a[data-action=Zaction]').closest('li').index();
- var bactionIndex = menu.$el.find('a[data-action=Baction]').closest('li').index();
- expect(zactionIndex).toBeLessThan(bactionIndex);
-
- var wactionIndex = menu.$el.find('a[data-action=Waction]').closest('li').index();
- var yactionIndex = menu.$el.find('a[data-action=Yaction]').closest('li').index();
- expect(wactionIndex).toBeLessThan(yactionIndex);
- });
- it('calls displayName function', function() {
- var displayNameStub = sinon.stub().returns('Test');
-
- fileActions.registerAction({
- name: 'Something',
- displayName: displayNameStub,
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL
- });
-
- menu.render();
-
- expect(displayNameStub.calledOnce).toEqual(true);
- expect(displayNameStub.calledWith(menuContext)).toEqual(true);
- expect(menu.$el.find('a[data-action=Something] span:not(.icon)').text()).toEqual('Test');
- });
- it('uses plain iconClass', function() {
- fileActions.registerAction({
- name: 'Something',
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL,
- iconClass: 'test'
- });
-
- menu.render();
-
- expect(menu.$el.find('a[data-action=Something]').children('span.icon').hasClass('test')).toEqual(true);
- });
- it('calls iconClass function', function() {
- var iconClassStub = sinon.stub().returns('test');
-
- fileActions.registerAction({
- name: 'Something',
- mime: 'text/plain',
- permissions: OC.PERMISSION_ALL,
- iconClass: iconClassStub
- });
-
- menu.render();
-
- expect(iconClassStub.calledOnce).toEqual(true);
- expect(iconClassStub.calledWith(menuContext.$file.attr('data-file'), menuContext)).toEqual(true);
- expect(menu.$el.find('a[data-action=Something]').children('span.icon').hasClass('test')).toEqual(true);
- });
- });
-
- describe('action handler', function() {
- it('calls action handler when clicking menu item', function() {
- var $action = menu.$el.find('a[data-action=Testdropdown]');
- $action.click();
-
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
- expect(actionStub.getCall(0).args[1].$file[0]).toEqual($tr[0]);
- expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList);
- expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions);
- expect(actionStub.getCall(0).args[1].dir).toEqual('/subdir');
- });
- });
- describe('default actions from registerDefaultActions', function() {
- beforeEach(function() {
- fileActions.clear();
- fileActions.registerDefaultActions();
- });
- it('redirects to download URL when clicking download', function() {
- var redirectStub = sinon.stub(OC, 'redirect');
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- var $tr = fileList.add(fileData);
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- var menuContext = {
- $file: $tr,
- fileList: fileList,
- fileActions: fileActions,
- fileInfoModel: new OCA.Files.FileInfoModel(fileData),
- dir: fileList.getCurrentDirectory()
- };
- menu = new OCA.Files.FileActionsMenu();
- menu.show(menuContext);
-
- menu.$el.find('.action-download').click();
-
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(
- OC.getRootPath() +
- '/remote.php/webdav/subdir/testName.txt'
- );
- redirectStub.restore();
- });
- it('takes the file\'s path into account when clicking download', function() {
- var redirectStub = sinon.stub(OC, 'redirect');
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- path: '/anotherpath/there',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- var $tr = fileList.add(fileData);
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- var menuContext = {
- $file: $tr,
- fileList: fileList,
- fileActions: fileActions,
- fileInfoModel: new OCA.Files.FileInfoModel(fileData),
- dir: '/anotherpath/there'
- };
- menu = new OCA.Files.FileActionsMenu();
- menu.show(menuContext);
-
- menu.$el.find('.action-download').click();
-
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(
- OC.getRootPath() + '/remote.php/webdav/anotherpath/there/testName.txt'
- );
- redirectStub.restore();
- });
- it('deletes file when clicking delete', function() {
- var deleteStub = sinon.stub(fileList, 'do_delete');
- var fileData = {
- id: 18,
- type: 'file',
- name: 'testName.txt',
- path: '/somepath/dir',
- mimetype: 'text/plain',
- size: '1234',
- etag: 'a01234c',
- mtime: '123456'
- };
- var $tr = fileList.add(fileData);
- fileActions.display($tr.find('td.filename'), true, fileList);
-
- var menuContext = {
- $file: $tr,
- fileList: fileList,
- fileActions: fileActions,
- fileInfoModel: new OCA.Files.FileInfoModel(fileData),
- dir: '/somepath/dir'
- };
- menu = new OCA.Files.FileActionsMenu();
- menu.show(menuContext);
-
- menu.$el.find('.action-delete').click();
-
- expect(deleteStub.calledOnce).toEqual(true);
- expect(deleteStub.getCall(0).args[0]).toEqual('testName.txt');
- expect(deleteStub.getCall(0).args[1]).toEqual('/somepath/dir');
- deleteStub.restore();
- });
- });
-});
-
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
deleted file mode 100644
index 4a2a7b7941d..00000000000
--- a/apps/files/tests/js/filelistSpec.js
+++ /dev/null
@@ -1,3405 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.FileList tests', function() {
- var FileInfo = OC.Files.FileInfo;
- var testFiles, testRoot, notificationStub, fileList, pageSizeStub;
- var bcResizeStub;
- var filesClient;
- var filesConfig;
- var redirectStub;
-
- /**
- * Generate test file data
- */
- function generateFiles(startIndex, endIndex) {
- var files = [];
- var name;
- for (var i = startIndex; i <= endIndex; i++) {
- name = 'File with index ';
- if (i < 10) {
- // do not rely on localeCompare here
- // and make the sorting predictable
- // cross-browser
- name += '0';
- }
- name += i + '.txt';
- files.push(new FileInfo({
- id: i,
- type: 'file',
- name: name,
- mimetype: 'text/plain',
- size: i * 2,
- etag: 'abc'
- }));
- }
- return files;
- }
-
- beforeEach(function() {
- filesConfig = new OC.Backbone.Model({
- showhidden: true
- });
-
- filesClient = new OC.Files.Client({
- host: 'localhost',
- port: 80,
- // FIXME: uncomment after fixing the test OC.getRootPath()
- //root: OC.getRootPath() + '/remote.php/webdav',
- root: '/remote.php/webdav',
- useHTTPS: false
- });
- redirectStub = sinon.stub(OC, 'redirect');
- notificationStub = sinon.stub(OC.Notification, 'show');
- // prevent resize algo to mess up breadcrumb order while
- // testing
- bcResizeStub = sinon.stub(OCA.Files.BreadCrumb.prototype, '_resize');
-
- // init parameters and test table elements
- $('#testArea').append(
- '<div id="app-content-files">' +
- // init horrible parameters
- '<input type="hidden" id="permissions" value="31"/>' +
- // dummy controls
- '<div class="files-controls">' +
- ' <div class="actions creatable"></div>' +
- ' <div class="notCreatable"></div>' +
- '</div>' +
- // uploader
- '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
- // dummy table
- // TODO: at some point this will be rendered by the fileList class itself!
- '<table class="files-filestable list-container view-grid">' +
- '<thead><tr>' +
- '<th class="hidden column-name">' +
- '<input type="checkbox" id="select_all_files" class="select-all checkbox">' +
- '<a class="name columntitle" href="#" onclick="event.preventDefault()" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
- '<span class="selectedActions hidden">' +
- '<a class="actions-selected" href="#" onclick="event.preventDefault()"><span class="icon icon-more"></span><span>Actions</span></a>' +
- '</th>' +
- '<th class="hidden column-size"><a class="columntitle" href="#" onclick="event.preventDefault()" data-sort="size"><span class="sort-indicator"></span></a></th>' +
- '<th class="hidden column-mtime"><a class="columntitle" href="#" onclick="event.preventDefault()" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
- '</tr></thead>' +
- '<tbody class="files-fileList"></tbody>' +
- '<tfoot></tfoot>' +
- '</table>' +
- // TODO: move to handlebars template
- '<div class="emptyfilelist emptycontent"><h2>Empty content message</h2><p class="uploadmessage">Upload message</p></div>' +
- '<div class="nofilterresults hidden"></div>' +
- '</div>'
- );
-
- testRoot = new FileInfo({
- // root entry
- id: 99,
- type: 'dir',
- name: '/subdir',
- mimetype: 'httpd/unix-directory',
- size: 1200000,
- etag: 'a0b0c0d0',
- permissions: OC.PERMISSION_ALL
- });
- testFiles = [new FileInfo({
- id: 1,
- type: 'file',
- name: 'One.txt',
- mimetype: 'text/plain',
- mtime: 123456789,
- size: 12,
- etag: 'abc',
- permissions: OC.PERMISSION_ALL
- }), new FileInfo({
- id: 2,
- type: 'file',
- name: 'Two.jpg',
- mimetype: 'image/jpeg',
- mtime: 234567890,
- size: 12049,
- etag: 'def',
- permissions: OC.PERMISSION_ALL
- }), new FileInfo({
- id: 3,
- type: 'file',
- name: 'Three.pdf',
- mimetype: 'application/pdf',
- mtime: 234560000,
- size: 58009,
- etag: '123',
- permissions: OC.PERMISSION_ALL
- }), new FileInfo({
- id: 4,
- type: 'dir',
- name: 'somedir',
- mimetype: 'httpd/unix-directory',
- mtime: 134560000,
- size: 250,
- etag: '456',
- permissions: OC.PERMISSION_ALL
- })];
- pageSizeStub = sinon.stub(OCA.Files.FileList.prototype, 'pageSize').returns(20);
- fileList = new OCA.Files.FileList($('#app-content-files'), {
- filesClient: filesClient,
- config: filesConfig,
- dir: '/subdir',
- enableUpload: true,
- multiSelectMenu: [{
- name: 'copyMove',
- displayName: t('files', 'Move or copy'),
- iconClass: 'icon-external',
- },
- {
- name: 'download',
- displayName: t('files', 'Download'),
- iconClass: 'icon-download',
- },
- {
- name: 'delete',
- displayName: t('files', 'Delete'),
- iconClass: 'icon-delete',
- }]
- });
- });
- afterEach(function() {
- testFiles = undefined;
- if (fileList) {
- fileList.destroy();
- }
- fileList = undefined;
-
- notificationStub.restore();
- bcResizeStub.restore();
- pageSizeStub.restore();
- redirectStub.restore();
- });
- describe('Getters', function() {
- it('Returns the current directory', function() {
- fileList.changeDirectory('/one/two/three', false, true);
- expect(fileList.getCurrentDirectory()).toEqual('/one/two/three');
- });
- it('Returns the directory permissions as int', function() {
- $('#permissions').val('23');
- expect(fileList.getDirectoryPermissions()).toEqual(23);
- });
- });
- describe('Adding files', function() {
- it('generates file element with correct attributes when calling add() with file data', function() {
- var fileData = new FileInfo({
- id: 18,
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: 1234,
- etag: 'a01234c',
- mtime: 123456
- });
- var $tr = fileList.add(fileData);
-
- expect($tr).toBeDefined();
- expect($tr[0].tagName.toLowerCase()).toEqual('tr');
- expect($tr.attr('data-id')).toEqual('18');
- expect($tr.attr('data-type')).toEqual('file');
- expect($tr.attr('data-file')).toEqual('testName.txt');
- expect($tr.attr('data-size')).toEqual('1234');
- expect($tr.attr('data-etag')).toEqual('a01234c');
- expect($tr.attr('data-permissions')).toEqual('31');
- expect($tr.attr('data-mime')).toEqual('text/plain');
- expect($tr.attr('data-mtime')).toEqual('123456');
- expect($tr.attr('data-e2eencrypted')).toEqual('false');
- expect($tr.find('a.name').attr('href'))
- .toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/testName.txt');
- expect($tr.find('.nametext').text().trim()).toEqual('testName.txt');
-
- expect($tr.find('.filesize').text()).toEqual('1 KB');
- expect($tr.find('.date').text()).not.toEqual('?');
- expect(fileList.findFileEl('testName.txt')[0]).toEqual($tr[0]);
- });
- it('generates file element with url for default action when one is defined', function() {
- var actionStub = sinon.stub();
- fileList.setFiles(testFiles);
- fileList.fileActions.registerAction({
- mime: 'text/plain',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_ALL,
- icon: function() {
- // Specify icon for history button
- return OC.imagePath('core','actions/history');
- },
- actionHandler: actionStub
- });
- fileList.fileActions.setDefault('text/plain', 'Test');
- var fileData = new FileInfo({
- id: 18,
- name: 'testName.txt',
- mimetype: 'text/plain',
- size: 1234,
- etag: 'a01234c',
- mtime: 123456
- });
- var $tr = fileList.add(fileData);
- expect($tr.find('a.name').attr('href'))
- .toEqual(OC.getRootPath() + '/index.php/apps/files?dir=&openfile=18');
- });
- it('generates dir element with correct attributes when calling add() with dir data', function() {
- var fileData = new FileInfo({
- id: 19,
- name: 'testFolder',
- mimetype: 'httpd/unix-directory',
- size: 1234,
- etag: 'a01234c',
- mtime: 123456
- });
- var $tr = fileList.add(fileData);
-
- expect($tr).toBeDefined();
- expect($tr[0].tagName.toLowerCase()).toEqual('tr');
- expect($tr.attr('data-id')).toEqual('19');
- expect($tr.attr('data-type')).toEqual('dir');
- expect($tr.attr('data-file')).toEqual('testFolder');
- expect($tr.attr('data-size')).toEqual('1234');
- expect($tr.attr('data-etag')).toEqual('a01234c');
- expect($tr.attr('data-permissions')).toEqual('31');
- expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
- expect($tr.attr('data-mtime')).toEqual('123456');
- expect($tr.attr('data-e2eencrypted')).toEqual('false');
-
- expect($tr.find('.filesize').text()).toEqual('1 KB');
- expect($tr.find('.date').text()).not.toEqual('?');
-
- expect(fileList.findFileEl('testFolder')[0]).toEqual($tr[0]);
- });
- it('generates file element with default attributes when calling add() with minimal data', function() {
- var fileData = {
- type: 'file',
- name: 'testFile.txt'
- };
-
- var $tr = fileList.add(fileData);
-
- expect($tr).toBeDefined();
- expect($tr[0].tagName.toLowerCase()).toEqual('tr');
- expect($tr.attr('data-id')).toBeUndefined();
- expect($tr.attr('data-type')).toEqual('file');
- expect($tr.attr('data-file')).toEqual('testFile.txt');
- expect($tr.attr('data-size')).toBeUndefined();
- expect($tr.attr('data-etag')).toBeUndefined();
- expect($tr.attr('data-permissions')).toEqual('31');
- expect($tr.attr('data-mime')).toBeUndefined();
- expect($tr.attr('data-e2eencrypted')).toEqual('false');
-
- expect($tr.find('.filesize').text()).toEqual('Pending');
- expect($tr.find('.date').text()).not.toEqual('?');
- });
- it('generates dir element with default attributes when calling add() with minimal data', function() {
- var fileData = {
- type: 'dir',
- name: 'testFolder'
- };
- var $tr = fileList.add(fileData);
-
- expect($tr).toBeDefined();
- expect($tr[0].tagName.toLowerCase()).toEqual('tr');
- expect($tr.attr('data-id')).toBeUndefined();
- expect($tr.attr('data-type')).toEqual('dir');
- expect($tr.attr('data-file')).toEqual('testFolder');
- expect($tr.attr('data-size')).toBeUndefined();
- expect($tr.attr('data-etag')).toBeUndefined();
- expect($tr.attr('data-permissions')).toEqual('31');
- expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
- expect($tr.attr('data-e2eencrypted')).toEqual('false');
-
- expect($tr.find('.filesize').text()).toEqual('Pending');
- expect($tr.find('.date').text()).not.toEqual('?');
- });
- it('generates dir element with true e2eencrypted attribute when calling add() with minimal data including isEncrypted', function() {
- var fileData = {
- type: 'dir',
- name: 'testFolder',
- isEncrypted: true
- };
- var $tr = fileList.add(fileData);
- expect($tr.attr('data-e2eencrypted')).toEqual('true');
- });
- it('generates file element with no permissions when permissions are explicitly none', function() {
- var fileData = {
- type: 'dir',
- name: 'testFolder',
- permissions: OC.PERMISSION_NONE
- };
- var $tr = fileList.add(fileData);
- expect($tr.attr('data-permissions')).toEqual('0');
- });
- it('generates file element with zero size when size is explicitly zero', function() {
- var fileData = {
- type: 'dir',
- name: 'testFolder',
- size: '0'
- };
- var $tr = fileList.add(fileData);
- expect($tr.find('.filesize').text()).toEqual('0 KB');
- });
- it('generates file element with unknown date when mtime invalid', function() {
- var fileData = {
- type: 'dir',
- name: 'testFolder',
- mtime: -1
- };
- var $tr = fileList.add(fileData);
- expect($tr.find('.date .modified').text()).toEqual('?');
- });
- it('adds new file to the end of the list', function() {
- var $tr;
- var fileData = {
- type: 'file',
- name: 'ZZZ.txt'
- };
- fileList.setFiles(testFiles);
- $tr = fileList.add(fileData);
- expect($tr.index()).toEqual(4);
- });
- it('inserts files in a sorted manner when insert option is enabled', function() {
- for (var i = 0; i < testFiles.length; i++) {
- fileList.add(testFiles[i]);
- }
- expect(fileList.files[0].name).toEqual('somedir');
- expect(fileList.files[1].name).toEqual('One.txt');
- expect(fileList.files[2].name).toEqual('Three.pdf');
- expect(fileList.files[3].name).toEqual('Two.jpg');
- });
- it('inserts new file at correct position', function() {
- var $tr;
- var fileData = {
- type: 'file',
- name: 'P comes after O.txt'
- };
- for (var i = 0; i < testFiles.length; i++) {
- fileList.add(testFiles[i]);
- }
- $tr = fileList.add(fileData);
- // after "One.txt"
- expect($tr.index()).toEqual(2);
- expect(fileList.files[2]).toEqual(fileData);
- });
- it('inserts new folder at correct position in insert mode', function() {
- var $tr;
- var fileData = {
- type: 'dir',
- name: 'somedir2 comes after somedir'
- };
- for (var i = 0; i < testFiles.length; i++) {
- fileList.add(testFiles[i]);
- }
- $tr = fileList.add(fileData);
- expect($tr.index()).toEqual(1);
- expect(fileList.files[1]).toEqual(fileData);
- });
- it('inserts new file at the end correctly', function() {
- var $tr;
- var fileData = {
- type: 'file',
- name: 'zzz.txt'
- };
- for (var i = 0; i < testFiles.length; i++) {
- fileList.add(testFiles[i]);
- }
- $tr = fileList.add(fileData);
- expect($tr.index()).toEqual(4);
- expect(fileList.files[4]).toEqual(fileData);
- });
- it('removes empty content message and shows summary when adding first file', function() {
- var $summary;
- var fileData = {
- type: 'file',
- name: 'first file.txt',
- size: 12
- };
- fileList.setFiles([]);
- expect(fileList.isEmpty).toEqual(true);
- fileList.add(fileData);
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(false);
- // yes, ugly...
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.connector').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
- expect($summary.find('.filesize').text()).toEqual('12 B');
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(false);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(true);
- expect(fileList.isEmpty).toEqual(false);
- });
- it('correctly adds the extension markup and show hidden files completely in gray', function() {
- var $tr;
- var testDataAndExpectedResult = [
- {file: {type: 'file', name: 'ZZZ.txt'}, extension: '.txt'},
- {file: {type: 'file', name: 'ZZZ.tar.gz'}, extension: '.gz'},
- {file: {type: 'file', name: 'test.with.some.dots.in.it.txt'}, extension: '.txt'},
- // we render hidden files completely in gray
- {file: {type: 'file', name: '.test.with.some.dots.in.it.txt'}, extension: '.test.with.some.dots.in.it.txt'},
- {file: {type: 'file', name: '.hidden'}, extension: '.hidden'},
- ];
- fileList.setFiles(testFiles);
-
- for(var i = 0; i < testDataAndExpectedResult.length; i++) {
- var testSet = testDataAndExpectedResult[i];
- var fileData = testSet['file'];
- $tr = fileList.add(fileData);
- expect($tr.find('.nametext .extension').text()).toEqual(testSet['extension']);
- }
- });
- });
- describe('Hidden files', function() {
- it('sets the class hidden-file for hidden files', function() {
- var fileData = {
- type: 'dir',
- name: '.testFolder'
- };
- var $tr = fileList.add(fileData);
-
- expect($tr).toBeDefined();
- expect($tr.hasClass('hidden-file')).toEqual(true);
- });
- it('does not set the class hidden-file for visible files', function() {
- var fileData = {
- type: 'dir',
- name: 'testFolder'
- };
- var $tr = fileList.add(fileData);
-
- expect($tr).toBeDefined();
- expect($tr.hasClass('hidden-file')).toEqual(false);
- });
- it('toggles the list\'s class when toggling hidden files', function() {
- expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(false);
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
- expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(true);
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true })
- expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(false);
- });
- });
- describe('Removing files from the list', function() {
- it('Removes file from list when calling remove() and updates summary', function() {
- var $summary;
- var $removedEl;
- fileList.setFiles(testFiles);
- $removedEl = fileList.remove('One.txt');
- expect($removedEl).toBeDefined();
- expect($removedEl.attr('data-file')).toEqual('One.txt');
- expect($('.files-fileList tr').length).toEqual(3);
- expect(fileList.files.length).toEqual(3);
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
-
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('2 files');
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
- expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
- expect($summary.find('.filesize').text()).toEqual('69 KB');
- expect(fileList.isEmpty).toEqual(false);
- });
- it('Shows empty content when removing last file', function() {
- var $summary;
- fileList.setFiles([testFiles[0]]);
- fileList.remove('One.txt');
- expect($('.files-fileList tr').length).toEqual(0);
- expect(fileList.files.length).toEqual(0);
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
-
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(true);
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
- expect(fileList.isEmpty).toEqual(true);
- });
- });
- describe('Deleting files', function() {
- var deferredDelete;
- var deleteStub;
-
- beforeEach(function() {
- deferredDelete = $.Deferred();
- deleteStub = sinon.stub(filesClient, 'remove');
- });
- afterEach(function() {
- deleteStub.restore();
- });
-
- function doDelete() {
- // note: normally called from FileActions
- return fileList.do_delete(['One.txt', 'Two.jpg']).then(function(){
-
- expect(deleteStub.calledTwice).toEqual(true);
- expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
- expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
- });
- }
- it('calls delete.php, removes the deleted entries and updates summary', function(done) {
- var $summary;
- fileList.setFiles(testFiles);
- deferredDelete1 = $.Deferred();
- deferredDelete2 = $.Deferred();
- deleteStub.onCall(0).callsFake(function(src){
- expect(deleteStub.calledOnce).toEqual(true);
- expect(src).toEqual('/subdir/One.txt');
- return deferredDelete1.promise();
- });
- deleteStub.onCall(1).callsFake(function(src){
- expect(deleteStub.calledTwice).toEqual(true);
- expect(src).toEqual('/subdir/Two.jpg');
- return deferredDelete2.promise();
- });
-
- var promise = fileList.do_delete(['One.txt', 'Two.jpg']);
- deferredDelete1.resolve(200);
- deferredDelete2.resolve(200);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
- expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
- expect(fileList.findFileEl('Three.pdf').length).toEqual(1);
- expect(fileList.$fileList.find('tr').length).toEqual(2);
-
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
- expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
- expect($summary.find('.filesize').text()).toEqual('57 KB');
- expect(fileList.isEmpty).toEqual(false);
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(false);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(true);
-
- expect(notificationStub.notCalled).toEqual(true);
- }).then(done, done);
- });
- it('shows busy state on files to be deleted', function(done) {
- fileList.setFiles(testFiles);
- deferredDelete1 = $.Deferred();
- deferredDelete2 = $.Deferred();
- deleteStub.onCall(0).callsFake(function(src){
- expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(true);
- expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
-
- expect(deleteStub.calledOnce).toEqual(true);
- expect(src).toEqual('/subdir/One.txt');
- return deferredDelete1.promise();
- });
- deleteStub.onCall(1).callsFake(function(src){
- expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(true);
- expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
-
- expect(deleteStub.calledTwice).toEqual(true);
- expect(src).toEqual('/subdir/Two.jpg');
- return deferredDelete2.promise();
- });
- var promise = fileList.do_delete(['One.txt', 'Two.jpg']).then(function(){
- expect(deleteStub.calledTwice).toEqual(true);
- });
- deferredDelete1.resolve(200);
- deferredDelete2.resolve(200);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(false);
- expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(false);
- }).then(done, done);
- });
- it('shows busy state on all files when deleting all', function(done) {
- fileList.setFiles(testFiles);
- var deferredDeleteArray = [];
- var count = 0;
- for (var i = 0; i < 4; i++) {
- (function(i, fn){
- deferredDeleteArray.push($.Deferred());
- deleteStub.onCall(i).callsFake(function(src){
- expect(fileList.findFileEl(fn).hasClass('busy')).toEqual(true);
- count++;
- return deferredDeleteArray[i].promise();
- });
- })(i, testFiles[i].name);
- }
- var promise = fileList.do_delete();
- for (var i = 0; i < 4; i++) {
- deferredDeleteArray[i].resolve(200);
- }
- return promise.then(function(){
- expect(count).toEqual(4);
- }).then(done, done);
- });
- it('updates summary when deleting last file', function(done) {
- var $summary;
- fileList.setFiles([testFiles[0], testFiles[1]]);
- deleteStub.returns(deferredDelete.promise());
- deferredDelete.resolve(200);
-
- return doDelete().then(function(){
- expect(fileList.$fileList.find('tr').length).toEqual(0);
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(true);
- expect(fileList.isEmpty).toEqual(true);
- expect(fileList.files.length).toEqual(0);
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
- }).then(done, done);
- });
- it('bring back deleted item when delete call failed', function(done) {
- fileList.setFiles(testFiles);
- deleteStub.returns(deferredDelete.promise());
- var promise = doDelete();
- deferredDelete.reject(403);
- return promise.then(function(){
- // files are still in the list
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
- expect(fileList.$fileList.find('tr').length).toEqual(4);
-
- expect(notificationStub.calledTwice).toEqual(true);
- }).then(done, done);
- });
- it('remove file from list if delete call returned 404 not found', function(done) {
- fileList.setFiles(testFiles);
- deleteStub.returns(deferredDelete.promise());
- var promise = doDelete();
- deferredDelete.reject(404);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
- expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
- expect(fileList.$fileList.find('tr').length).toEqual(2);
-
- expect(notificationStub.notCalled).toEqual(true);
- }).then(done, done);
- });
- });
- describe('Renaming files', function() {
- var deferredRename;
- var renameStub;
-
- beforeEach(function() {
- deferredRename = $.Deferred();
- renameStub = sinon.stub(filesClient, 'move').returns(deferredRename.promise());
-
- for (var i = 0; i < testFiles.length; i++) {
- var file = testFiles[i];
- file.path = '/some/subdir';
- fileList.add(file, {silent: true});
- }
- });
- afterEach(function() {
- renameStub.restore();
- });
-
- function doCancelRename() {
- var $input;
-
- // trigger rename prompt
- fileList.rename('One.txt');
- $input = fileList.$fileList.find('input.filename');
- // keep same name
- $input.val('One.txt');
- // trigger submit because triggering blur doesn't work in all browsers
- $input.closest('form').trigger('submit');
-
- expect(renameStub.notCalled).toEqual(true);
- }
- function doRename() {
- var $input;
-
- // trigger rename prompt
- fileList.rename('One.txt');
- $input = fileList.$fileList.find('input.filename');
- $input.val('Tu_after_three.txt');
- // trigger submit because triggering blur doesn't work in all browsers
- $input.closest('form').trigger('submit');
-
- expect(renameStub.calledOnce).toEqual(true);
- expect(renameStub.getCall(0).args[0]).toEqual('/some/subdir/One.txt');
- expect(renameStub.getCall(0).args[1]).toEqual('/some/subdir/Tu_after_three.txt');
- }
- it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() {
- doRename();
-
- deferredRename.resolve(201);
-
- // element stays renamed
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
- expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(1);
- expect(fileList.findFileEl('Tu_after_three.txt').index()).toEqual(2); // after Two.jpg
-
- expect(notificationStub.notCalled).toEqual(true);
- });
- it('Reverts file entry if rename ajax call failed', function() {
- doRename();
-
- deferredRename.reject(403);
-
- // element was reverted
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- expect(fileList.findFileEl('One.txt').index()).toEqual(1); // after somedir
- expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(0);
-
- expect(notificationStub.calledOnce).toEqual(true);
- });
- it('Correctly updates file link after rename', function() {
- var $tr;
- doRename();
-
- deferredRename.resolve(201);
-
- $tr = fileList.findFileEl('Tu_after_three.txt');
- expect($tr.find('a.name').attr('href'))
- .toEqual(OC.getRootPath() + '/remote.php/webdav/some/subdir/Tu_after_three.txt');
- });
- it('Triggers "fileActionsReady" event after rename', function() {
- var handler = sinon.stub();
- fileList.$fileList.on('fileActionsReady', handler);
- doRename();
- expect(handler.notCalled).toEqual(true);
-
- deferredRename.resolve(201);
-
- expect(handler.calledOnce).toEqual(true);
- expect(fileList.$fileList.find('.test').length).toEqual(0);
- });
- it('Leaves the summary alone when reinserting renamed element', function() {
- var $summary = $('.files-filestable .summary');
- doRename();
-
- deferredRename.resolve(201);
-
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('3 files');
- });
- it('Leaves the summary alone when cancel renaming', function() {
- var $summary = $('.files-filestable .summary');
- doCancelRename();
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('3 files');
- });
- it('Shows busy state while rename in progress', function() {
- var $tr;
- doRename();
-
- // element is renamed before the request finishes
- $tr = fileList.findFileEl('Tu_after_three.txt');
- expect($tr.length).toEqual(1);
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
- // file actions are hidden
- expect($tr.hasClass('busy')).toEqual(true);
-
- // input and form are gone
- expect(fileList.$fileList.find('input.filename').length).toEqual(0);
- expect(fileList.$fileList.find('form').length).toEqual(0);
- });
- it('Validates the file name', function() {
- var $input, $tr;
-
- $tr = fileList.findFileEl('One.txt');
- expect($tr.find('a.name').css('display')).not.toEqual('none');
-
- // trigger rename prompt
- fileList.rename('One.txt');
-
- expect($tr.find('a.name .thumbnail-wrapper').css('display')).not.toEqual('none');
- expect($tr.find('a.name .nametext').css('display')).toEqual('none');
-
- $input = fileList.$fileList.find('input.filename');
- $input.val('Two.jpg');
-
- // simulate key to trigger validation
- $input.trigger(new $.Event('keyup', {keyCode: 97}));
-
- // input is still there with error
- expect(fileList.$fileList.find('input.filename').length).toEqual(1);
- expect(fileList.$fileList.find('input.filename').hasClass('error')).toEqual(true);
-
- // trigger submit does not send server request
- $input.closest('form').trigger('submit');
- expect(renameStub.notCalled).toEqual(true);
-
- // simulate escape key
- $input.trigger(new $.Event('keyup', {keyCode: 27}));
-
- // element is added back with the correct name
- $tr = fileList.findFileEl('One.txt');
- expect($tr.length).toEqual(1);
- expect($tr.find('a .nametext').text().trim()).toEqual('One.txt');
- expect($tr.find('a.name').css('display')).not.toEqual('none');
-
- $tr = fileList.findFileEl('Two.jpg');
- expect($tr.length).toEqual(1);
- expect($tr.find('a .nametext').text().trim()).toEqual('Two.jpg');
- expect($tr.find('a.name').css('display')).not.toEqual('none');
-
- // input and form are gone
- expect(fileList.$fileList.find('input.filename').length).toEqual(0);
- expect(fileList.$fileList.find('form').length).toEqual(0);
- });
- it('Restores thumbnail when rename was cancelled', function(done) {
- doRename();
-
- expect(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail').parent().attr('class'))
- .toContain('icon-loading-small');
-
- deferredRename.reject(409);
-
- return Promise.resolve().then(function() {
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
- .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
- }).then(done, done);
- });
- });
- describe('Moving files', function() {
- var deferredMove;
- var moveStub;
-
- beforeEach(function() {
- deferredMove = $.Deferred();
- moveStub = sinon.stub(filesClient, 'move');
-
- fileList.setFiles(testFiles);
- });
- afterEach(function() {
- moveStub.restore();
- });
-
- it('Moves single file to target folder', function(done) {
- var promise = fileList.move('One.txt', '/somedir');
-
- moveStub.callsFake(function(src, dst){
- expect(moveStub.calledOnce).toEqual(true);
- expect(src).toEqual('/subdir/One.txt');
- expect(dst).toEqual('/somedir/One.txt');
- return deferredMove.promise();
- });
-
- deferredMove.resolve(201);
-
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
-
- // folder size has increased
- expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
- expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
-
- expect(notificationStub.notCalled).toEqual(true);
- }).then(done, done);
- });
- it('Moves list of files to target folder', function(done) {
- var deferredMove1 = $.Deferred();
- var deferredMove2 = $.Deferred();
- moveStub.onCall(0).callsFake(function(src, dst){
- expect(moveStub.calledOnce).toEqual(true);
- expect(src).toEqual('/subdir/One.txt');
- expect(dst).toEqual('/somedir/One.txt');
- return deferredMove1.promise();
- });
- moveStub.onCall(1).callsFake(function(src, dst){
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
-
- // folder size has increased during move
- expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
- expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
-
- expect(src).toEqual('/subdir/Two.jpg');
- expect(dst).toEqual('/somedir/Two.jpg');
- return deferredMove2.promise();
- });
-
- var promise = fileList.move(['One.txt', 'Two.jpg'], '/somedir');
- deferredMove1.resolve(201);
- deferredMove2.resolve(201);
- return promise.then(function(){
- expect(moveStub.calledTwice).toEqual(true);
-
- expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
-
- // folder size has increased
- expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
- expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
-
- expect(notificationStub.notCalled).toEqual(true);
- }).then(done, done);
- });
- it('Shows notification if a file could not be moved', function(done) {
- moveStub.callsFake(function(){
- expect(moveStub.calledOnce).toEqual(true);
- return deferredMove.promise();
- });
- var promise = fileList.move('One.txt', '/somedir');
- deferredMove.reject(409);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- expect(notificationStub.calledOnce).toEqual(true);
- expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
- }).then(done, done);
- });
- it('Restores thumbnail if a file could not be moved', function(done) {
- moveStub.callsFake(function(){
- expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
- .toContain('icon-loading-small');
- expect(moveStub.calledOnce).toEqual(true);
- return deferredMove.promise();
- });
- var promise = fileList.move('One.txt', '/somedir');
- deferredMove.reject(409);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- expect(notificationStub.calledOnce).toEqual(true);
- expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
- expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
- .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
- }).then(done, done);
- });
- });
-
- describe('Copying files', function() {
- var deferredCopy;
- var copyStub;
-
- beforeEach(function() {
- deferredCopy = $.Deferred();
- copyStub = sinon.stub(filesClient, 'copy');
-
- fileList.setFiles(testFiles);
- });
- afterEach(function() {
- copyStub.restore();
- });
-
- it('Copies single file to target folder', function(done) {
- copyStub.callsFake(function(){
- expect(copyStub.calledOnce).toEqual(true);
- expect(copyStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
- expect(copyStub.getCall(0).args[1]).toEqual('/somedir/One.txt');
-
- return deferredCopy.promise();
- });
-
- var promise = fileList.copy('One.txt', '/somedir');
- deferredCopy.resolve(201);
- return promise.then(function(){
- // File is still here
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
-
- // folder size has increased
- expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
- expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
-
- // Copying sents a notification to tell that we've successfully copied file
- expect(notificationStub.notCalled).toEqual(false);
- }).then(done, done);
- });
- it('Copies list of files to target folder', function(done) {
- var deferredCopy1 = $.Deferred();
- var deferredCopy2 = $.Deferred();
- copyStub.onCall(0).callsFake(function(src, dst){
- expect(src).toEqual('/subdir/One.txt');
- expect(dst).toEqual('/somedir/One.txt');
- return deferredCopy1.promise();
- });
- copyStub.onCall(1).callsFake(function(src, dst){
- // folder size has increased during copy
- expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
- expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
-
- expect(src).toEqual('/subdir/Two.jpg');
- expect(dst).toEqual('/somedir/Two.jpg');
- return deferredCopy2.promise();
- });
-
- var promise = fileList.copy(['One.txt', 'Two.jpg'], '/somedir');
- deferredCopy1.resolve(201);
- deferredCopy2.resolve(201);
-
- return promise.then(function(){
- expect(copyStub.calledTwice).toEqual(true);
- expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
-
- // folder size has increased
- expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
- expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
-
- expect(notificationStub.notCalled).toEqual(false);
- }).then(done, done);
- });
- it('Shows notification if a file could not be copied', function(done) {
- copyStub.callsFake(function(){
- expect(copyStub.calledOnce).toEqual(true);
- return deferredCopy.promise();
- });
-
- var promise = fileList.copy('One.txt', '/somedir');
- deferredCopy.reject(409);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- expect(notificationStub.calledOnce).toEqual(true);
- expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
- }).then(done, done);
- });
- it('Restores thumbnail if a file could not be copied', function(done) {
- copyStub.callsFake(function(){
- expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
- .toContain('icon-loading-small');
- expect(copyStub.calledOnce).toEqual(true);
- return deferredCopy.promise();
- });
-
- var promise = fileList.copy('One.txt', '/somedir');
- deferredCopy.reject(409);
- return promise.then(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
-
- expect(notificationStub.calledOnce).toEqual(true);
- expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
-
- expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
- .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
- }).then(done, done);
- });
- });
-
- describe('Update file', function() {
- it('does not change summary', function() {
- var $summary = $('.files-filestable .summary');
- var fileData = new FileInfo({
- type: 'file',
- name: 'test file',
- });
- var $tr = fileList.add(fileData);
-
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
-
- var model = fileList.getModelForFile('test file');
- model.set({size: '100'});
-
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- });
- })
- describe('List rendering', function() {
- it('renders a list of files using add()', function() {
- expect(fileList.files.length).toEqual(0);
- expect(fileList.files).toEqual([]);
- fileList.setFiles(testFiles);
- expect($('.files-fileList tr').length).toEqual(4);
- expect(fileList.files.length).toEqual(4);
- expect(fileList.files).toEqual(testFiles);
- });
- it('updates summary using the file sizes', function() {
- var $summary;
- fileList.setFiles(testFiles);
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('3 files');
- expect($summary.find('.filesize').text()).toEqual('69 KB');
- });
- it('shows headers, summary and hide empty content message after setting files', function(){
- fileList.setFiles(testFiles);
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(false);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(true);
- expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(false);
- });
- it('hides headers, summary and show empty content message after setting empty file list', function(){
- fileList.setFiles([]);
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
- expect($('.emptyfilelist.emptycontent .uploadmessage').hasClass('hidden')).toEqual(false);
- expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
- });
- it('hides headers, upload message, and summary when list is empty and user has no creation permission', function(){
- $('#permissions').val(0);
- fileList.setFiles([]);
- expect($('.files-filestable thead th').hasClass('hidden')).toEqual(true);
- expect($('.emptyfilelist.emptycontent').hasClass('hidden')).toEqual(false);
- expect($('.emptyfilelist.emptycontent .uploadmessage').hasClass('hidden')).toEqual(true);
- expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
- });
- it('calling findFileEl() can find existing file element', function() {
- fileList.setFiles(testFiles);
- expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
- });
- it('calling findFileEl() returns empty when file not found in file', function() {
- fileList.setFiles(testFiles);
- expect(fileList.findFileEl('unexist.dat').length).toEqual(0);
- });
- it('only add file if in same current directory', function() {
- fileList.changeDirectory('/current dir', false, true);
- var fileData = {
- type: 'file',
- name: 'testFile.txt',
- directory: '/current dir'
- };
- fileList.add(fileData);
- expect(fileList.findFileEl('testFile.txt').length).toEqual(1);
- });
- it('triggers "fileActionsReady" event after update', function() {
- var handler = sinon.stub();
- fileList.$fileList.on('fileActionsReady', handler);
- fileList.setFiles(testFiles);
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].$files.length).toEqual(testFiles.length);
- });
- it('triggers "fileActionsReady" event after single add', function() {
- var handler = sinon.stub();
- var $tr;
- fileList.setFiles(testFiles);
- fileList.$fileList.on('fileActionsReady', handler);
- $tr = fileList.add({name: 'test.txt'});
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].$files.is($tr)).toEqual(true);
- });
- it('triggers "fileActionsReady" event after next page load with the newly appended files', function() {
- var handler = sinon.stub();
- fileList.setFiles(generateFiles(0, 64));
- fileList.$fileList.on('fileActionsReady', handler);
- fileList._nextPage();
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize());
- });
- it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
- var handler = sinon.stub();
- fileList.setFiles(testFiles);
- fileList.$fileList.on('fileActionsReady', handler);
- fileList.add({name: 'test.txt'}, {silent: true});
- expect(handler.notCalled).toEqual(true);
- });
- it('triggers "updated" event after update', function() {
- var handler = sinon.stub();
- fileList.$fileList.on('updated', handler);
- fileList.setFiles(testFiles);
- expect(handler.calledOnce).toEqual(true);
- });
- it('does not update summary when removing non-existing files', function() {
- var $summary;
- // single file
- fileList.setFiles([testFiles[0]]);
- $summary = $('.files-filestable .summary');
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- fileList.remove('unexist.txt');
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- });
- });
- describe('Filtered list rendering', function() {
- it('filters the list of files using filter()', function() {
- expect(fileList.files.length).toEqual(0);
- expect(fileList.files).toEqual([]);
- fileList.setFiles(testFiles);
- var $summary = $('.files-filestable .summary');
- var $nofilterresults = fileList.$el.find(".nofilterresults");
- expect($nofilterresults.length).toEqual(1);
- expect($summary.hasClass('hidden')).toEqual(false);
-
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(4);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- fileList.setFilter('e');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(3);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('2 files');
- expect($summary.find('.filter').text()).toEqual(" match \"e\"");
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- fileList.setFilter('ee');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(1);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- expect($summary.find('.filter').text()).toEqual(" matches \"ee\"");
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- fileList.setFilter('eee');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(true);
- expect($nofilterresults.hasClass('hidden')).toEqual(false);
-
- fileList.setFilter('ee');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(1);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- expect($summary.find('.filter').text()).toEqual(" matches \"ee\"");
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- fileList.setFilter('e');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(3);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('2 files');
- expect($summary.find('.filter').text()).toEqual(" match \"e\"");
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- fileList.setFilter('');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(4);
- expect(fileList.files.length).toEqual(4);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').text()).toEqual('1 folder');
- expect($summary.find('.fileinfo').text()).toEqual('3 files');
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
- });
- it('filters the list of non-rendered rows using filter()', function() {
- var $summary = $('.files-filestable .summary');
- var $nofilterresults = fileList.$el.find(".nofilterresults");
- fileList.setFiles(generateFiles(0, 64));
-
- fileList.setFilter('63');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(1);
- expect($summary.hasClass('hidden')).toEqual(false);
- expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
- expect($summary.find('.fileinfo').text()).toEqual('1 file');
- expect($summary.find('.filter').text()).toEqual(" matches \"63\"");
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
- });
- it('hides the emptyfiles notice when using filter()', function() {
- expect(fileList.files.length).toEqual(0);
- expect(fileList.files).toEqual([]);
- fileList.setFiles([]);
- var $summary = $('.files-filestable .summary');
- var $emptycontent = fileList.$el.find(".emptyfilelist.emptycontent");
- var $nofilterresults = fileList.$el.find(".nofilterresults");
- expect($emptycontent.length).toEqual(1);
- expect($nofilterresults.length).toEqual(1);
-
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
- expect(fileList.files.length).toEqual(0);
- expect($summary.hasClass('hidden')).toEqual(true);
- expect($emptycontent.hasClass('hidden')).toEqual(false);
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- fileList.setFilter('e');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
- expect(fileList.files.length).toEqual(0);
- expect($summary.hasClass('hidden')).toEqual(true);
- expect($emptycontent.hasClass('hidden')).toEqual(true);
- expect($nofilterresults.hasClass('hidden')).toEqual(false);
-
- fileList.setFilter('');
- expect($('.files-fileList tr:not(.hidden)').length).toEqual(0);
- expect(fileList.files.length).toEqual(0);
- expect($summary.hasClass('hidden')).toEqual(true);
- expect($emptycontent.hasClass('hidden')).toEqual(false);
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
- });
- it('does not show the emptyfiles or nofilterresults notice when the mask is active', function() {
- expect(fileList.files.length).toEqual(0);
- expect(fileList.files).toEqual([]);
- fileList.showMask();
- fileList.setFiles(testFiles);
- var $emptycontent = fileList.$el.find(".emptyfilelist.emptycontent");
- var $nofilterresults = fileList.$el.find(".nofilterresults");
- expect($emptycontent.length).toEqual(1);
- expect($nofilterresults.length).toEqual(1);
-
- expect($emptycontent.hasClass('hidden')).toEqual(true);
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
-
- /*
- fileList.setFilter('e');
- expect($emptycontent.hasClass('hidden')).toEqual(true);
- expect($nofilterresults.hasClass('hidden')).toEqual(false);
- */
-
- fileList.setFilter('');
- expect($emptycontent.hasClass('hidden')).toEqual(true);
- expect($nofilterresults.hasClass('hidden')).toEqual(true);
- });
- });
- describe('Rendering next page on scroll', function() {
- beforeEach(function() {
- fileList.setFiles(generateFiles(0, 64));
- });
- it('renders only the first page', function() {
- expect(fileList.files.length).toEqual(65);
- expect($('.files-fileList tr').length).toEqual(20);
- });
- it('renders the full first page despite hidden rows', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
- var files = _.map(generateFiles(0, 23), function(data) {
- return _.extend(data, {
- name: '.' + data.name
- });
- });
- // only hidden files + one visible
- files.push(testFiles[0]);
- fileList.setFiles(files);
- expect(fileList.files.length).toEqual(25);
- // render 24 hidden elements + the visible one
- expect($('.files-fileList tr').length).toEqual(25);
- });
- it('renders the full first page despite hidden rows', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
- var files = _.map(generateFiles(0, 23), function(data) {
- return _.extend(data, {
- name: '.' + data.name
- });
- });
- // only hidden files + one visible
- files.push(testFiles[0]);
- fileList.setFiles(files);
- expect(fileList.files.length).toEqual(25);
- // render 20 first hidden elements as visible
- expect($('.files-fileList tr').length).toEqual(20);
- });
- it('renders the second page when scrolling down (trigger nextPage)', function() {
- // TODO: can't simulate scrolling here, so calling nextPage directly
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(40);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(60);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(65);
- fileList._nextPage(true);
- // stays at 65
- expect($('.files-fileList tr').length).toEqual(65);
- });
- it('inserts into the DOM if insertion point is in the visible page ', function() {
- fileList.add({
- id: 2000,
- type: 'file',
- name: 'File with index 15b.txt'
- });
- expect($('.files-fileList tr').length).toEqual(21);
- expect(fileList.findFileEl('File with index 15b.txt').index()).toEqual(16);
- });
- it('does not inserts into the DOM if insertion point is not the visible page ', function() {
- fileList.add({
- id: 2000,
- type: 'file',
- name: 'File with index 28b.txt'
- });
- expect($('.files-fileList tr').length).toEqual(20);
- expect(fileList.findFileEl('File with index 28b.txt').length).toEqual(0);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(40);
- expect(fileList.findFileEl('File with index 28b.txt').index()).toEqual(29);
- });
- it('appends into the DOM when inserting a file after the last visible element', function() {
- fileList.add({
- id: 2000,
- type: 'file',
- name: 'File with index 19b.txt'
- });
- expect($('.files-fileList tr').length).toEqual(21);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(41);
- });
- it('appends into the DOM when inserting a file on the last page when visible', function() {
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(40);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(60);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(65);
- fileList._nextPage(true);
- fileList.add({
- id: 2000,
- type: 'file',
- name: 'File with index 88.txt'
- });
- expect($('.files-fileList tr').length).toEqual(66);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(66);
- });
- it('shows additional page when appending a page of files and scrolling down', function() {
- var newFiles = generateFiles(66, 81);
- for (var i = 0; i < newFiles.length; i++) {
- fileList.add(newFiles[i]);
- }
- expect($('.files-fileList tr').length).toEqual(20);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(40);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(60);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(80);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(81);
- fileList._nextPage(true);
- expect($('.files-fileList tr').length).toEqual(81);
- });
- it('automatically renders next page when there are not enough elements visible', function() {
- // delete the 15 first elements
- for (var i = 0; i < 15; i++) {
- fileList.remove(fileList.files[0].name);
- }
- // still makes sure that there are 20 elements visible, if any
- expect($('.files-fileList tr').length).toEqual(25);
- });
- });
- describe('file previews', function() {
- var previewLoadStub;
-
- beforeEach(function() {
- previewLoadStub = sinon.stub(OCA.Files.FileList.prototype, 'lazyLoadPreview');
- });
- afterEach(function() {
- previewLoadStub.restore();
- });
- it('renders default file icon when none provided and no mime type is set', function() {
- var fileData = {
- name: 'testFile.txt'
- };
- var $tr = fileList.add(fileData);
- var $imgDiv = $tr.find('td.filename .thumbnail');
- expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
- // tries to load preview
- expect(previewLoadStub.calledOnce).toEqual(true);
- });
- it('renders default icon for folder when none provided', function() {
- var fileData = {
- name: 'test dir',
- mimetype: 'httpd/unix-directory'
- };
-
- var $tr = fileList.add(fileData);
- var $imgDiv = $tr.find('td.filename .thumbnail');
- expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/folder.svg');
- // no preview since it's a directory
- expect(previewLoadStub.notCalled).toEqual(true);
- });
- it('renders provided icon for file when provided', function() {
- var fileData = new FileInfo({
- type: 'file',
- name: 'test file',
- icon: OC.getRootPath() + '/core/img/filetypes/application-pdf.svg',
- mimetype: 'application/pdf'
- });
- var $tr = fileList.add(fileData);
- var $imgDiv = $tr.find('td.filename .thumbnail');
- expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/application-pdf.svg');
- // try loading preview
- expect(previewLoadStub.calledOnce).toEqual(true);
- });
- it('renders provided icon for file when provided', function() {
- var fileData = new FileInfo({
- name: 'somefile.pdf',
- icon: OC.getRootPath() + '/core/img/filetypes/application-pdf.svg'
- });
-
- var $tr = fileList.add(fileData);
- var $imgDiv = $tr.find('td.filename .thumbnail');
- expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/application-pdf.svg');
- // try loading preview
- expect(previewLoadStub.calledOnce).toEqual(true);
- });
- it('renders provided icon for folder when provided', function() {
- var fileData = new FileInfo({
- name: 'some folder',
- mimetype: 'httpd/unix-directory',
- icon: OC.getRootPath() + '/core/img/filetypes/folder-alt.svg'
- });
-
- var $tr = fileList.add(fileData);
- var $imgDiv = $tr.find('td.filename .thumbnail');
- expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-alt.svg');
- // do not load preview for folders
- expect(previewLoadStub.notCalled).toEqual(true);
- });
- it('renders preview when no icon was provided', function() {
- var fileData = {
- type: 'file',
- name: 'test file'
- };
- var $tr = fileList.add(fileData);
- var $td = $tr.find('td.filename');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail')))
- .toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
- expect(previewLoadStub.calledOnce).toEqual(true);
- // third argument is callback
- previewLoadStub.getCall(0).args[0].callback(OC.getRootPath() + '/somepath.png');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/somepath.png');
- });
- it('does not render preview for directories', function() {
- var fileData = {
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- name: 'test dir'
- };
- var $tr = fileList.add(fileData);
- var $td = $tr.find('td.filename');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder.svg');
- expect(previewLoadStub.notCalled).toEqual(true);
- });
- it('render encrypted folder icon for encrypted root', function() {
- var fileData = {
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- name: 'test dir',
- isEncrypted: true
- };
- var $tr = fileList.add(fileData);
- var $td = $tr.find('td.filename');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
- expect(previewLoadStub.notCalled).toEqual(true);
- });
- it('render encrypted folder icon for encrypted subdir', function() {
- var fileData = {
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- name: 'test dir',
- isEncrypted: true
- };
- var $tr = fileList.add(fileData);
- var $td = $tr.find('td.filename');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
- expect(previewLoadStub.notCalled).toEqual(true);
- // default icon override
- expect($tr.attr('data-icon')).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
- });
- it('render external storage icon for external storage root', function() {
- var fileData = {
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- name: 'test dir',
- mountType: 'external-root'
- };
- var $tr = fileList.add(fileData);
- var $td = $tr.find('td.filename');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
- expect(previewLoadStub.notCalled).toEqual(true);
- });
- it('render external storage icon for external storage subdir', function() {
- var fileData = {
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- name: 'test dir',
- mountType: 'external'
- };
- var $tr = fileList.add(fileData);
- var $td = $tr.find('td.filename');
- expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
- expect(previewLoadStub.notCalled).toEqual(true);
- // default icon override
- expect($tr.attr('data-icon')).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
- });
-
- });
- describe('viewer mode', function() {
- it('enabling viewer mode hides files table and action buttons', function() {
- fileList.setViewerMode(true);
- expect($('.files-filestable').hasClass('hidden')).toEqual(true);
- expect($('.actions').hasClass('hidden')).toEqual(true);
- expect($('.notCreatable').hasClass('hidden')).toEqual(true);
- });
- it('disabling viewer mode restores files table and action buttons', function() {
- fileList.setViewerMode(true);
- fileList.setViewerMode(false);
- expect($('.files-filestable').hasClass('hidden')).toEqual(false);
- expect($('.actions').hasClass('hidden')).toEqual(false);
- expect($('.notCreatable').hasClass('hidden')).toEqual(true);
- });
- it('disabling viewer mode restores files table and action buttons with correct permissions', function() {
- $('#permissions').val(0);
- fileList.setViewerMode(true);
- fileList.setViewerMode(false);
- expect($('.files-filestable').hasClass('hidden')).toEqual(false);
- expect($('.actions').hasClass('hidden')).toEqual(true);
- expect($('.notCreatable').hasClass('hidden')).toEqual(false);
- });
- it('toggling viewer mode triggers event', function() {
- var handler = sinon.stub();
- fileList.$el.on('changeViewerMode', handler);
- fileList.setViewerMode(true);
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(true);
-
- handler.reset();
- fileList.setViewerMode(false);
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(false);
- });
- });
- describe('loading file list', function() {
- var deferredList;
- var getFolderContentsStub;
-
- beforeEach(function() {
- deferredList = $.Deferred();
- getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
- });
- afterEach(function() {
- getFolderContentsStub.restore();
- });
- it('fetches file list from server and renders it when reload() is called', function(done) {
- var reloading = fileList.reload();
- expect(getFolderContentsStub.calledOnce).toEqual(true);
- expect(getFolderContentsStub.calledWith('/subdir')).toEqual(true);
- deferredList.resolve(200, [testRoot].concat(testFiles));
- return reloading.then(function() {
- expect($('.files-fileList tr').length).toEqual(4);
- expect(fileList.findFileEl('One.txt').length).toEqual(1);
- }).then(done, done);
- });
- it('switches dir and fetches file list when calling changeDirectory()', function() {
- fileList.changeDirectory('/anothersubdir');
- expect(fileList.getCurrentDirectory()).toEqual('/anothersubdir');
- expect(getFolderContentsStub.calledOnce).toEqual(true);
- expect(getFolderContentsStub.calledWith('/anothersubdir')).toEqual(true);
- });
- it('converts backslashes to slashes when calling changeDirectory()', function() {
- fileList.changeDirectory('/another\\subdir');
- expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
- });
- it('switches to root dir when current directory is invalid', function() {
- _.each([
- '..',
- '/..',
- '../',
- '/../',
- '/../abc',
- '/abc/..',
- '/abc/../',
- '/../abc/',
- '/foo%0Abar/',
- '/foo%00bar/',
- '/another\\subdir/../foo\\../bar\\..\\file/..\\folder/../'
- ], function(path) {
- fileList.changeDirectory(decodeURI(path));
- expect(fileList.getCurrentDirectory()).toEqual('/');
- });
- });
- it('allows paths with dotdot at the beginning or end', function() {
- _.each([
- '/..abc',
- '/def..',
- '/...',
- '/abc../def'
- ], function(path) {
- fileList.changeDirectory(path);
- expect(fileList.getCurrentDirectory()).toEqual(path);
- });
- });
- it('switches to root dir when current directory does not exist', function(done) {
- var changing = fileList.changeDirectory('/unexist');
-
- deferredList.reject(404);
-
- return changing.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- }).then(done, done);
- });
- it('switches to root dir when current directory returns 400', function(done) {
- var changing = fileList.changeDirectory('/unexist');
-
- deferredList.reject(400);
-
- return changing.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- }).then(done, done);
- });
- it('switches to root dir when current directory returns 405', function(done) {
- var changing = fileList.changeDirectory('/unexist');
-
- deferredList.reject(405);
-
- return changing.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- }).then(done, done);
- });
- it('switches to root dir when current directory is forbidden', function(done) {
- var changing = fileList.changeDirectory('/unexist');
-
- deferredList.reject(403);
-
- return changing.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- }).then(done, done);
- });
- it('switches to root dir when current directory is unavailable', function(done) {
- var changing = fileList.changeDirectory('/unexist');
-
- deferredList.reject(500);
-
- return changing.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- }).then(done, done);
- });
- it('shows mask before loading file list then hides it at the end', function(done) {
- var showMaskStub = sinon.stub(fileList, 'showMask');
- var hideMaskStub = sinon.stub(fileList, 'hideMask');
-
- var changing = fileList.changeDirectory('/anothersubdir');
- expect(showMaskStub.calledOnce).toEqual(true);
- expect(hideMaskStub.calledOnce).toEqual(false);
- deferredList.resolve(200, [testRoot].concat(testFiles));
-
- return changing.then(function() {
- expect(showMaskStub.calledOnce).toEqual(true);
- expect(hideMaskStub.calledOnce).toEqual(true);
- showMaskStub.restore();
- hideMaskStub.restore();
- }).then(done, done);
- });
- it('triggers "changeDirectory" event when changing directory', function(done) {
- var handler = sinon.stub();
- $('#app-content-files').on('changeDirectory', handler);
- var changing = fileList.changeDirectory('/somedir');
-
- deferredList.resolve(200, [testRoot].concat(testFiles));
-
- return changing.then(function() {
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
- }).then(done, done);
- });
- it('triggers "afterChangeDirectory" event with fileid after changing directory', function(done) {
- var handler = sinon.stub();
- $('#app-content-files').on('afterChangeDirectory', handler);
- var changing = fileList.changeDirectory('/somedir');
-
- deferredList.resolve(200, [testRoot].concat(testFiles));
-
- return changing.then(function() {
- expect(handler.calledOnce).toEqual(true);
- expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
- expect(handler.getCall(0).args[0].fileId).toEqual(99);
- }).then(done, done);
- });
- it('changes the directory when receiving "urlChanged" event', function() {
- $('#app-content-files').trigger(new $.Event('urlChanged', {view: 'files', dir: '/somedir'}));
- expect(fileList.getCurrentDirectory()).toEqual('/somedir');
- });
- it('refreshes breadcrumb after update', function() {
- var setDirSpy = sinon.spy(fileList.breadcrumb, 'setDirectory');
- fileList.changeDirectory('/anothersubdir');
- deferredList.resolve(200, [testRoot].concat(testFiles));
- // twice because setDirectory gets called by _setCurrentDir which
- // gets called directly by changeDirectory and via reload()
- expect(fileList.breadcrumb.setDirectory.calledTwice).toEqual(true);
- expect(fileList.breadcrumb.setDirectory.calledWith('/anothersubdir')).toEqual(true);
- setDirSpy.restore();
- getFolderContentsStub.restore();
- });
- it('prepends a slash to directory if none was given', function() {
- fileList.changeDirectory('');
- expect(fileList.getCurrentDirectory()).toEqual('/');
- fileList.changeDirectory('noslash');
- expect(fileList.getCurrentDirectory()).toEqual('/noslash');
- });
- });
- describe('breadcrumb events', function() {
- var deferredList;
- var getFolderContentsStub;
-
- beforeEach(function() {
- deferredList = $.Deferred();
- getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
- });
- afterEach(function() {
- getFolderContentsStub.restore();
- });
- it('clicking on root breadcrumb changes directory to root', function() {
- fileList.changeDirectory('/subdir/two/three with space/four/five');
- deferredList.resolve(200, [testRoot].concat(testFiles));
- var changeDirStub = sinon.stub(fileList, 'changeDirectory');
- fileList.breadcrumb.$el.find('.crumb:eq(1) > a').trigger({type: 'click', which: 1});
-
- expect(changeDirStub.calledOnce).toEqual(true);
- expect(changeDirStub.getCall(0).args[0]).toEqual('/');
- changeDirStub.restore();
- });
- it('clicking on breadcrumb changes directory', function() {
- fileList.changeDirectory('/subdir/two/three with space/four/five');
- deferredList.resolve(200, [testRoot].concat(testFiles));
- var changeDirStub = sinon.stub(fileList, 'changeDirectory');
- fileList.breadcrumb.$el.find('.crumb:eq(4) > a').trigger({type: 'click', which: 1});
-
- expect(changeDirStub.calledOnce).toEqual(true);
- expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
- changeDirStub.restore();
- });
- it('dropping files on breadcrumb calls move operation', function() {
- var testDir = '/subdir/two/three with space/four/five';
- var moveStub = sinon.stub(filesClient, 'move');
- var deferredMove1 = $.Deferred();
- var deferredMove2 = $.Deferred();
- moveStub.onCall(0).returns(deferredMove1.promise());
- moveStub.onCall(1).returns(deferredMove2.promise());
- fileList.changeDirectory(testDir);
- deferredList.resolve(200, [testRoot].concat(testFiles));
- var $crumb = fileList.breadcrumb.$el.find('.crumb:eq(4)');
- // no idea what this is but is required by the handler
- var ui = {
- helper: {
- find: sinon.stub()
- }
- };
- // returns a list of tr that were dragged
- ui.helper.find.returns([
- $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
- $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
- ]);
- // simulate drop event
- var result = fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui).then(function(){
- expect(moveStub.callCount).toEqual(2);
- expect(moveStub.getCall(0).args[0]).toEqual(testDir + '/One.txt');
- expect(moveStub.getCall(0).args[1]).toEqual('/subdir/two/three with space/One.txt');
- expect(moveStub.getCall(1).args[0]).toEqual(testDir + '/Two.jpg');
- expect(moveStub.getCall(1).args[1]).toEqual('/subdir/two/three with space/Two.jpg');
- moveStub.restore();
- });
- deferredMove1.resolve(201);
- deferredMove2.resolve(201);
- return result;
- });
- it('dropping files on same dir breadcrumb does nothing', function() {
- var testDir = '/subdir/two/three with space/four/five';
- var moveStub = sinon.stub(filesClient, 'move').returns($.Deferred().promise());
- fileList.changeDirectory(testDir);
- deferredList.resolve(200, [testRoot].concat(testFiles));
- var $crumb = fileList.breadcrumb.$el.find('.crumb:last');
- // no idea what this is but is required by the handler
- var ui = {
- helper: {
- find: sinon.stub()
- }
- };
- // returns a list of tr that were dragged
- ui.helper.find.returns([
- $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
- $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
- ]);
- // simulate drop event
- fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
-
- // no extra server request
- expect(moveStub.notCalled).toEqual(true);
- });
- });
- describe('Download Url', function() {
- it('returns correct download URL for single files', function() {
- expect(fileList.getDownloadUrl('some file.txt'))
- .toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/some%20file.txt');
- expect(fileList.getDownloadUrl('some file.txt', '/anotherpath/abc'))
- .toEqual(OC.getRootPath() + '/remote.php/webdav/anotherpath/abc/some%20file.txt');
- fileList.changeDirectory('/', false, true);
- expect(fileList.getDownloadUrl('some file.txt'))
- .toEqual(OC.getRootPath() + '/remote.php/webdav/some%20file.txt');
- });
- it('returns correct download URL for multiple files', function() {
- expect(fileList.getDownloadUrl(['a b c.txt', 'd e f.txt']))
- .toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
- });
- it('returns the correct ajax URL', function() {
- expect(fileList.getAjaxUrl('test', {a:1, b:'x y'}))
- .toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
- });
- });
- describe('File selection', function() {
- beforeEach(function() {
- fileList.setFiles(testFiles);
- });
- it('Selects a file when clicking its checkbox', function() {
- var $tr = fileList.findFileEl('One.txt');
- expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
- $tr.find('td.selection input:checkbox').click();
-
- expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
- });
- it('Selects a range when clicking on one file then Shift clicking on another one', function() {
- var $tr = fileList.findFileEl('One.txt');
- var $tr2 = fileList.findFileEl('Three.pdf');
- var e;
- $tr.find('td.selection input:checkbox').click();
- e = new $.Event('click');
- e.shiftKey = true;
- $tr2.find('td.filename .name').trigger(e);
-
- expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
- expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
- expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
- var selection = _.pluck(fileList.getSelectedFiles(), 'name');
- expect(selection.length).toEqual(3);
- expect(selection).toContain('One.txt');
- expect(selection).toContain('Two.jpg');
- expect(selection).toContain('Three.pdf');
- });
- it('Selects a range when clicking on one file then Shift clicking on another one that is above the first one', function() {
- var $tr = fileList.findFileEl('One.txt');
- var $tr2 = fileList.findFileEl('Three.pdf');
- var e;
- $tr2.find('td.selection input:checkbox').click();
- e = new $.Event('click');
- e.shiftKey = true;
- $tr.find('td.filename .name').trigger(e);
-
- expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
- expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
- expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
- var selection = _.pluck(fileList.getSelectedFiles(), 'name');
- expect(selection.length).toEqual(3);
- expect(selection).toContain('One.txt');
- expect(selection).toContain('Two.jpg');
- expect(selection).toContain('Three.pdf');
- });
- it('Selecting all files will automatically check "select all" checkbox', function() {
- expect($('.select-all').prop('checked')).toEqual(false);
- $('.files-fileList tr td.selection input:checkbox').click();
- expect($('.select-all').prop('checked')).toEqual(true);
- });
- it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() {
- fileList.setFiles(generateFiles(0, 41));
- expect($('.select-all').prop('checked')).toEqual(false);
- $('.files-fileList tr td.selection input:checkbox').click();
- expect($('.select-all').prop('checked')).toEqual(false);
- });
- it('Selecting all files also selects hidden files when invisible', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
- var $tr = fileList.add(new FileInfo({
- name: '.hidden',
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- size: 150
- }));
- $('.select-all').click();
- expect($tr.find('td.selection input:checkbox').prop('checked')).toEqual(true);
- expect(_.pluck(fileList.getSelectedFiles(), 'name')).toContain('.hidden');
- });
- it('Clicking "select all" will select/deselect all files', function() {
- fileList.setFiles(generateFiles(0, 41));
- $('.select-all').click();
- expect($('.select-all').prop('checked')).toEqual(true);
- $('.files-fileList tr input:checkbox').each(function() {
- expect($(this).prop('checked')).toEqual(true);
- });
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
-
- $('.select-all').click();
- expect($('.select-all').prop('checked')).toEqual(false);
-
- $('.files-fileList tr input:checkbox').each(function() {
- expect($(this).prop('checked')).toEqual(false);
- });
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(0);
- });
- it('Clicking "select all" then deselecting a file will uncheck "select all"', function() {
- $('.select-all').click();
- expect($('.select-all').prop('checked')).toEqual(true);
-
- var $tr = fileList.findFileEl('One.txt');
- $tr.find('input:checkbox').click();
-
- expect($('.select-all').prop('checked')).toEqual(false);
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
- });
- it('Updates the selection summary when doing a few manipulations with "Select all"', function() {
- $('.select-all').click();
- expect($('.select-all').prop('checked')).toEqual(true);
-
- var $tr = fileList.findFileEl('One.txt');
- // unselect one
- $tr.find('input:checkbox').click();
-
- expect($('.select-all').prop('checked')).toEqual(false);
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
-
- // select all
- $('.select-all').click();
- expect($('.select-all').prop('checked')).toEqual(true);
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
-
- // unselect one
- $tr.find('input:checkbox').click();
- expect($('.select-all').prop('checked')).toEqual(false);
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
-
- // re-select it
- $tr.find('input:checkbox').click();
- expect($('.select-all').prop('checked')).toEqual(true);
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
- });
- it('Auto-selects files on next page when "select all" is checked', function() {
- fileList.setFiles(generateFiles(0, 41));
- $('.select-all').click();
-
- expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(20);
- fileList._nextPage(true);
- expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(40);
- fileList._nextPage(true);
- expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(42);
- expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
- });
- it('Selecting files updates selection summary', function() {
- var $summary = $('.column-name a.name>span:first');
- expect($summary.text()).toEqual('Name');
- fileList.findFileEl('One.txt').find('input:checkbox').click();
- fileList.findFileEl('Three.pdf').find('input:checkbox').click();
- fileList.findFileEl('somedir').find('input:checkbox').click();
- expect($summary.text()).toEqual('1 folder and 2 files');
- });
- it('Unselecting files hides selection summary', function() {
- var $summary = $('.column-name a.name>span:first');
- fileList.findFileEl('One.txt').find('input:checkbox').click().click();
- expect($summary.text()).toEqual('Name');
- });
- it('Displays the number of hidden files in selection summary if hidden files are invisible', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
- var $tr = fileList.add(new FileInfo({
- name: '.hidden',
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- size: 150
- }));
- $('.select-all').click();
- var $summary = $('.column-name a.name>span:first');
- expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)');
- });
- it('Does not displays the number of hidden files in selection summary if hidden files are visible', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
- var $tr = fileList.add(new FileInfo({
- name: '.hidden',
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- size: 150
- }));
- $('.select-all').click();
- var $summary = $('.column-name a.name>span:first');
- expect($summary.text()).toEqual('2 folders and 3 files');
- });
- it('Toggling hidden file visibility updates selection summary', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
- var $tr = fileList.add(new FileInfo({
- name: '.hidden',
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- size: 150
- }));
- $('.select-all').click();
- var $summary = $('.column-name a.name>span:first');
- expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)');
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
- expect($summary.text()).toEqual('2 folders and 3 files');
- });
- it('Select/deselect files shows/hides file actions', function() {
- var $actions = $('.column-name .selectedActions');
- var $checkbox = fileList.findFileEl('One.txt').find('input:checkbox');
- expect($actions.hasClass('hidden')).toEqual(true);
- $checkbox.click();
- expect($actions.hasClass('hidden')).toEqual(false);
- $checkbox.click();
- expect($actions.hasClass('hidden')).toEqual(true);
- });
- it('Selection is cleared when switching dirs', function() {
- $('.select-all').click();
- var deferredList = $.Deferred();
- var getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
-
- fileList.changeDirectory('/');
-
- deferredList.resolve(200, [testRoot].concat(testFiles));
-
- expect($('.select-all').prop('checked')).toEqual(false);
- expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual([]);
-
- getFolderContentsStub.restore();
- });
- it('getSelectedFiles returns the selected files even when they are on the next page', function() {
- var selectedFiles;
- fileList.setFiles(generateFiles(0, 41));
- $('.select-all').click();
- // unselect one to not have the "allFiles" case
- fileList.$fileList.find('tr input:checkbox:first').click();
-
- // only 20 files visible, must still return all the selected ones
- selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
-
- expect(selectedFiles.length).toEqual(41);
- });
- describe('clearing the selection', function() {
- it('clears selected files selected individually calling setFiles()', function() {
- var selectedFiles;
-
- fileList.setFiles(generateFiles(0, 41));
- fileList.$fileList.find('tr:eq(5) input:checkbox:first').click();
- fileList.$fileList.find('tr:eq(7) input:checkbox:first').click();
-
- selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
- expect(selectedFiles.length).toEqual(2);
-
- fileList.setFiles(generateFiles(0, 2));
-
- selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
- expect(selectedFiles.length).toEqual(0);
- });
- it('clears selected files selected with select all when calling setFiles()', function() {
- var selectedFiles;
-
- fileList.setFiles(generateFiles(0, 41));
- $('.select-all').click();
-
- selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
- expect(selectedFiles.length).toEqual(42);
-
- fileList.setFiles(generateFiles(0, 2));
-
- selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
- expect(selectedFiles.length).toEqual(0);
- });
- });
- describe('Selection overlay', function() {
- it('show doesnt show the copy/move action if one or more files are not copiable/movable', function () {
- fileList.setFiles(testFiles);
- $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false);
- expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Move or copy');
- testFiles[0].permissions = OC.PERMISSION_READ;
- $('.select-all').click();
- fileList.setFiles(testFiles);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false);
- expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Copy');
- testFiles[0].permissions = OC.PERMISSION_NONE;
- $('.select-all').click();
- fileList.setFiles(testFiles);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(true);
- });
- it('show doesnt show the download action if one or more files are not downloadable', function () {
- fileList.setFiles(testFiles);
- $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(false);
- testFiles[0].permissions = OC.PERMISSION_UPDATE;
- $('.select-all').click();
- fileList.setFiles(testFiles);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(true);
- });
- it('show doesnt show the delete action if one or more files are not deletable', function () {
- fileList.setFiles(testFiles);
- $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(false);
- testFiles[0].permissions = OC.PERMISSION_READ;
- $('.select-all').click();
- fileList.setFiles(testFiles);
- $('.select-all').click();
- expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(true);
- });
- });
- describe('Actions', function() {
- beforeEach(function() {
- fileList.findFileEl('One.txt').find('input:checkbox').click();
- fileList.findFileEl('Three.pdf').find('input:checkbox').click();
- fileList.findFileEl('somedir').find('input:checkbox').click();
- });
- it('getSelectedFiles returns the selected file data', function() {
- var files = fileList.getSelectedFiles();
- expect(files.length).toEqual(3);
- expect(files[0]).toEqual({
- id: 1,
- name: 'One.txt',
- mimetype: 'text/plain',
- mtime: 123456789,
- type: 'file',
- size: 12,
- etag: 'abc',
- quotaAvailableBytes: '-1',
- permissions: OC.PERMISSION_ALL,
- hasPreview: true,
- isEncrypted: false
- });
- expect(files[1]).toEqual({
- id: 3,
- type: 'file',
- name: 'Three.pdf',
- mimetype: 'application/pdf',
- mtime: 234560000,
- size: 58009,
- quotaAvailableBytes: '-1',
- etag: '123',
- permissions: OC.PERMISSION_ALL,
- hasPreview: true,
- isEncrypted: false
- });
- expect(files[2]).toEqual({
- id: 4,
- type: 'dir',
- name: 'somedir',
- mimetype: 'httpd/unix-directory',
- mtime: 134560000,
- size: 250,
- quotaAvailableBytes: '-1',
- etag: '456',
- permissions: OC.PERMISSION_ALL,
- hasPreview: true,
- isEncrypted: false
- });
- expect(files[0].id).toEqual(1);
- expect(files[0].name).toEqual('One.txt');
- expect(files[1].id).toEqual(3);
- expect(files[1].name).toEqual('Three.pdf');
- expect(files[2].id).toEqual(4);
- expect(files[2].name).toEqual('somedir');
- });
- it('Removing a file removes it from the selection', function() {
- fileList.remove('Three.pdf');
- var files = fileList.getSelectedFiles();
- expect(files.length).toEqual(2);
- expect(files[0]).toEqual({
- id: 1,
- name: 'One.txt',
- mimetype: 'text/plain',
- mtime: 123456789,
- type: 'file',
- size: 12,
- quotaAvailableBytes: '-1',
- etag: 'abc',
- permissions: OC.PERMISSION_ALL,
- hasPreview: true,
- isEncrypted: false
- });
- expect(files[1]).toEqual({
- id: 4,
- type: 'dir',
- name: 'somedir',
- mimetype: 'httpd/unix-directory',
- mtime: 134560000,
- size: 250,
- quotaAvailableBytes: '-1',
- etag: '456',
- permissions: OC.PERMISSION_ALL,
- hasPreview: true,
- isEncrypted: false
- });
- });
- describe('Download', function() {
- beforeEach(function() {
- fileList.$el.find('.actions-selected').click();
- });
-
- it('Opens download URL when clicking "Download"', function() {
- $('.selectedActions .filesSelectMenu .download').click();
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D');
- redirectStub.restore();
- });
- it('Downloads root folder when all selected in root folder', function() {
- fileList.changeDirectory('/', false, true);
- $('.select-all').click();
- $('.selectedActions .filesSelectMenu .download').click();
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2F&files=');
- });
- it('Downloads parent folder when all selected in subfolder', function() {
- $('.select-all').click();
- $('.selectedActions .filesSelectMenu .download').click();
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir');
- });
-
- afterEach(function() {
- fileList.$el.find('.actions-selected').click();
- });
- });
-
- describe('Delete', function() {
- var deleteStub, deferredDelete;
- beforeEach(function() {
- deferredDelete = $.Deferred();
- deleteStub = sinon.stub(filesClient, 'remove');
- fileList.$el.find('.actions-selected').click();
- });
-
- afterEach(function() {
- fileList.$el.find('.actions-selected').click();
- deleteStub.restore();
- });
-
- it('Deletes selected files when "Delete" clicked', function(done) {
- var deferred = $.Deferred();
-
- deleteStub.returns(deferredDelete.promise());
- deleteStub.onCall(2).callsFake(function(){
- expect(deleteStub.callCount).toEqual(3);
- expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
- expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Three.pdf');
- expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/somedir');
- return deferredDelete.promise();
- });
-
- stub = sinon.stub(fileList._operationProgressBar, 'hideProgressBar').callsFake(function(){
- expect(fileList.findFileEl('One.txt').length).toEqual(0);
- expect(fileList.findFileEl('Three.pdf').length).toEqual(0);
- expect(fileList.findFileEl('somedir').length).toEqual(0);
- expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
- done();
- deferred.resolve();
- });
- $('.selectedActions .filesSelectMenu .delete').click();
- deferredDelete.resolve(204);
- return deferred.promise();
- });
- it('Deletes all files when all selected when "Delete" clicked', function(done) {
- var deferred = $.Deferred();
-
- deleteStub.returns(deferredDelete.promise());
- deleteStub.onCall(3).callsFake(function(){
- expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
- expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
- expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/Three.pdf');
- expect(deleteStub.getCall(3).args[0]).toEqual('/subdir/somedir');
- return deferredDelete.promise();
- });
-
- stub = sinon.stub(fileList._operationProgressBar, 'hideProgressBar').callsFake(function(){
- expect(fileList.isEmpty).toEqual(true);
- expect(deleteStub.callCount).toEqual(4);
- done();
- deferred.resolve();
- });
- $('.select-all').click();
- $('.selectedActions .filesSelectMenu .delete').click();
- deferredDelete.resolve(204);
-
- return deferred.promise();
- });
- });
- });
- it('resets the file selection on reload', function() {
- fileList.$el.find('.select-all').click();
- fileList.reload();
- expect(fileList.$el.find('.select-all').prop('checked')).toEqual(false);
- expect(fileList.getSelectedFiles()).toEqual([]);
- });
- describe('Disabled selection', function() {
- beforeEach(function() {
- fileList._allowSelection = false;
- fileList.setFiles(testFiles);
- });
- it('Does not render checkboxes', function() {
- expect(fileList.$fileList.find('.selectCheckBox').length).toEqual(0);
- });
- it('Does not select a file with Ctrl or Shift if selection is not allowed', function() {
- var $tr = fileList.findFileEl('One.txt');
- var $tr2 = fileList.findFileEl('Three.pdf');
- var e;
- e = new $.Event('click');
- e.ctrlKey = true;
- $tr.find('td.filename .name').trigger(e);
-
- // click on second entry, does not clear the selection
- e = new $.Event('click');
- e.ctrlKey = true;
- $tr2.find('td.filename .name').trigger(e);
-
- expect(fileList.getSelectedFiles().length).toEqual(0);
-
- // deselect now
- e = new $.Event('click');
- e.shiftKey = true;
- $tr2.find('td.filename .name').trigger(e);
- expect(fileList.getSelectedFiles().length).toEqual(0);
- });
- });
- });
- describe('File actions', function() {
- it('Clicking on a file name will trigger default action', function() {
- var actionStub = sinon.stub();
- fileList.setFiles(testFiles);
- fileList.fileActions.registerAction({
- mime: 'text/plain',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_ALL,
- icon: function() {
- // Specify icon for history button
- return OC.imagePath('core','actions/history');
- },
- actionHandler: actionStub
- });
- fileList.fileActions.setDefault('text/plain', 'Test');
- var $tr = fileList.findFileEl('One.txt');
- $tr.find('td.filename .nametext').click();
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('One.txt');
- var context = actionStub.getCall(0).args[1];
- expect(context.$file.is($tr)).toEqual(true);
- expect(context.fileList).toBeDefined();
- expect(context.fileActions).toBeDefined();
- expect(context.dir).toEqual('/subdir');
- });
- it('Clicking on an empty space of the file row will trigger the "Details" action', function() {
- var detailsActionStub = sinon.stub();
- fileList.setFiles(testFiles);
- // Override the "Details" action set internally by the FileList for
- // easier testing.
- fileList.fileActions.registerAction({
- mime: 'all',
- name: 'Details',
- permissions: OC.PERMISSION_NONE,
- actionHandler: detailsActionStub
- });
- // Ensure that the action works even if fileActions.currentFile is
- // not set.
- fileList.fileActions.currentFile = null;
- var $tr = fileList.findFileEl('One.txt');
- $tr.find('td.filesize').click();
- expect(detailsActionStub.calledOnce).toEqual(true);
- expect(detailsActionStub.getCall(0).args[0]).toEqual('One.txt');
- var context = detailsActionStub.getCall(0).args[1];
- expect(context.$file.is($tr)).toEqual(true);
- expect(context.fileList).toBe(fileList);
- expect(context.fileActions).toBe(fileList.fileActions);
- expect(context.dir).toEqual('/subdir');
- });
- it('redisplays actions when new actions have been registered', function() {
- var actionStub = sinon.stub();
- var readyHandler = sinon.stub();
- var clock = sinon.useFakeTimers();
- var debounceStub = sinon.stub(_, 'debounce').callsFake(function(callback) {
- return function() {
- // defer instead of debounce, to make it work with clock
- _.defer(callback);
- };
- });
-
- // need to reinit the list to make the debounce call
- fileList.destroy();
- fileList = new OCA.Files.FileList($('#app-content-files'));
-
- fileList.setFiles(testFiles);
-
- fileList.$fileList.on('fileActionsReady', readyHandler);
-
- fileList.fileActions.registerAction({
- mime: 'text/plain',
- name: 'Test',
- type: OCA.Files.FileActions.TYPE_INLINE,
- permissions: OC.PERMISSION_ALL,
- icon: function() {
- // Specify icon for history button
- return OC.imagePath('core','actions/history');
- },
- actionHandler: actionStub
- });
- var $tr = fileList.findFileEl('One.txt');
- expect($tr.find('.action-test').length).toEqual(0);
- expect(readyHandler.notCalled).toEqual(true);
-
- // update is delayed
- clock.tick(100);
- expect($tr.find('.action-test').length).toEqual(1);
- expect(readyHandler.calledOnce).toEqual(true);
-
- clock.restore();
- debounceStub.restore();
- });
- });
- describe('Sorting files', function() {
-
- var getCurrentUserStub;
-
- beforeEach(function() {
- getCurrentUserStub = sinon.stub(OC, 'getCurrentUser').returns({
- uid: 1,
- displayName: 'user1'
- });
- });
-
- afterEach(function() {
- getCurrentUserStub.restore();
- });
-
- it('Toggles the sort indicator when clicking on a column header', function() {
- var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS;
- var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS;
- var request;
- var sortingUrl = OC.generateUrl('/apps/files/api/v1/sorting');
- fileList.$el.find('.column-size .columntitle').click();
- // moves triangle to size column, check indicator on name is hidden
- expect(
- fileList.$el.find('.column-name .sort-indicator').hasClass('hidden')
- ).toEqual(true);
- // check indicator on size is visible and defaults to descending
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
- ).toEqual(false);
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
- ).toEqual(true);
- // check if changes are persisted
- expect(fakeServer.requests.length).toEqual(1);
- request = fakeServer.requests[0];
- expect(request.url).toEqual(sortingUrl);
-
- // click again on size column, reverses direction
- fileList.$el.find('.column-size .columntitle').click();
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
- ).toEqual(false);
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
- ).toEqual(true);
- // check if changes are persisted
- expect(fakeServer.requests.length).toEqual(2);
- request = fakeServer.requests[1];
- expect(request.url).toEqual(sortingUrl);
-
- // click again on size column, reverses direction
- fileList.$el.find('.column-size .columntitle').click();
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
- ).toEqual(false);
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
- ).toEqual(true);
- expect(fakeServer.requests.length).toEqual(3);
- request = fakeServer.requests[2];
- expect(request.url).toEqual(sortingUrl);
-
- // click on mtime column, moves indicator there
- fileList.$el.find('.column-mtime .columntitle').click();
- expect(
- fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
- ).toEqual(true);
- expect(
- fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden')
- ).toEqual(false);
- expect(
- fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS)
- ).toEqual(true);
- expect(fakeServer.requests.length).toEqual(4);
- request = fakeServer.requests[3];
- expect(request.url).toEqual(sortingUrl);
- });
- it('Uses correct sort comparator when inserting files', function() {
- testFiles.sort(OCA.Files.FileList.Comparators.size);
- testFiles.reverse(); //default is descending
- fileList.setFiles(testFiles);
- fileList.$el.find('.column-size .columntitle').click();
- var newFileData = new FileInfo({
- id: 999,
- name: 'new file.txt',
- mimetype: 'text/plain',
- size: 40001,
- etag: '999'
- });
- fileList.add(newFileData);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
- expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
- expect(fileList.findFileEl('somedir').index()).toEqual(3);
- expect(fileList.findFileEl('One.txt').index()).toEqual(4);
- expect(fileList.files.length).toEqual(5);
- expect(fileList.$fileList.find('tr').length).toEqual(5);
- });
- it('Uses correct reversed sort comparator when inserting files', function() {
- testFiles.sort(OCA.Files.FileList.Comparators.size);
- fileList.setFiles(testFiles);
- fileList.$el.find('.column-size .columntitle').click();
-
- // reverse sort
- fileList.$el.find('.column-size .columntitle').click();
- var newFileData = new FileInfo({
- id: 999,
- name: 'new file.txt',
- mimetype: 'text/plain',
- size: 40001,
- etag: '999'
- });
- fileList.add(newFileData);
- expect(fileList.findFileEl('One.txt').index()).toEqual(0);
- expect(fileList.findFileEl('somedir').index()).toEqual(1);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
- expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
- expect(fileList.files.length).toEqual(5);
- expect(fileList.$fileList.find('tr').length).toEqual(5);
- });
- it('does not sort when clicking on header whenever multiselect is enabled', function() {
- var sortStub = sinon.stub(OCA.Files.FileList.prototype, 'setSort');
-
- fileList.setFiles(testFiles);
- fileList.findFileEl('One.txt').find('input:checkbox:first').click();
-
- fileList.$el.find('.column-size .columntitle').click();
-
- expect(sortStub.notCalled).toEqual(true);
-
- // can sort again after deselecting
- fileList.findFileEl('One.txt').find('input:checkbox:first').click();
-
- fileList.$el.find('.column-size .columntitle').click();
-
- expect(sortStub.calledOnce).toEqual(true);
-
- sortStub.restore();
- });
-
- describe('if no user logged in', function() {
- beforeEach(function() {
- getCurrentUserStub.returns({
- uid: null,
- displayName: 'Guest'
- });
- });
-
- it('shouldn\'t send an update sort order request', function() {
- OC.currentUser = false;
- fileList.$el.find('.column-size .columntitle').click();
- // check if there was no request
- expect(fakeServer.requests.length).toEqual(0);
- });
- });
-
- describe('with favorites', function() {
- it('shows favorite files on top', function() {
- testFiles.push(new FileInfo({
- id: 5,
- type: 'file',
- name: 'ZZY Before last file in ascending order',
- mimetype: 'text/plain',
- mtime: 999999998,
- size: 9999998,
- // Tags would be added by TagsPlugin
- tags: [OC.TAG_FAVORITE],
- }), new FileInfo({
- id: 6,
- type: 'file',
- name: 'ZZZ Last file in ascending order',
- mimetype: 'text/plain',
- mtime: 999999999,
- size: 9999999,
- // Tags would be added by TagsPlugin
- tags: [OC.TAG_FAVORITE],
- }));
-
- fileList.setFiles(testFiles);
-
- // Sort by name in ascending order (default sorting is by name
- // in ascending order, but setFiles does not trigger a sort, so
- // the files must be sorted before being set or a sort must be
- // triggered afterwards by clicking on the header).
- fileList.$el.find('.column-name .columntitle').click();
- fileList.$el.find('.column-name .columntitle').click();
-
- expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
- expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
- expect(fileList.findFileEl('somedir').index()).toEqual(2);
- expect(fileList.findFileEl('One.txt').index()).toEqual(3);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(5);
-
- // Sort by size in ascending order
- fileList.$el.find('.column-size .columntitle').click();
- fileList.$el.find('.column-size .columntitle').click();
-
- expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
- expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
- expect(fileList.findFileEl('One.txt').index()).toEqual(2);
- expect(fileList.findFileEl('somedir').index()).toEqual(3);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(4);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(5);
-
- // Sort by modification time in ascending order
- fileList.$el.find('.column-mtime .columntitle').click();
- fileList.$el.find('.column-mtime .columntitle').click();
-
- expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
- expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
- expect(fileList.findFileEl('One.txt').index()).toEqual(2);
- expect(fileList.findFileEl('somedir').index()).toEqual(3);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(5);
- });
- it('shows favorite files on top also when using descending order', function() {
- testFiles.push(new FileInfo({
- id: 5,
- type: 'file',
- name: 'AAB Before last file in descending order',
- mimetype: 'text/plain',
- mtime: 2,
- size: 2,
- // Tags would be added by TagsPlugin
- tags: [OC.TAG_FAVORITE],
- }), new FileInfo({
- id: 6,
- type: 'file',
- name: 'AAA Last file in descending order',
- mimetype: 'text/plain',
- mtime: 1,
- size: 1,
- // Tags would be added by TagsPlugin
- tags: [OC.TAG_FAVORITE],
- }));
-
- fileList.setFiles(testFiles);
-
- // Sort by name in descending order
- fileList.$el.find('.column-name .columntitle').click();
-
- expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
- expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(3);
- expect(fileList.findFileEl('One.txt').index()).toEqual(4);
- expect(fileList.findFileEl('somedir').index()).toEqual(5);
-
- // Sort by size in descending order
- fileList.$el.find('.column-size .columntitle').click();
-
- expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
- expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(2);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(3);
- expect(fileList.findFileEl('somedir').index()).toEqual(4);
- expect(fileList.findFileEl('One.txt').index()).toEqual(5);
-
- // Sort by modification time in descending order
- fileList.$el.find('.column-mtime .columntitle').click();
-
- expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
- expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
- expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
- expect(fileList.findFileEl('Three.pdf').index()).toEqual(3);
- expect(fileList.findFileEl('somedir').index()).toEqual(4);
- expect(fileList.findFileEl('One.txt').index()).toEqual(5);
- });
- });
- });
- describe('create file', function() {
- var deferredCreate;
- var deferredInfo;
- var createStub;
- var getFileInfoStub;
-
- beforeEach(function() {
- deferredCreate = $.Deferred();
- deferredInfo = $.Deferred();
- createStub = sinon.stub(filesClient, 'putFileContents')
- .returns(deferredCreate.promise());
- getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
- .returns(deferredInfo.promise());
- });
- afterEach(function() {
- createStub.restore();
- getFileInfoStub.restore();
- });
-
- it('creates file with given name and adds it to the list', function(done) {
- var creating = fileList.createFile('test.txt');
-
- expect(createStub.calledOnce).toEqual(true);
- expect(createStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
- expect(createStub.getCall(0).args[2]).toEqual({
- contentType: 'text/plain',
- overwrite: true
- });
-
- deferredCreate.resolve(200);
-
- expect(getFileInfoStub.calledOnce).toEqual(true);
- expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
-
- deferredInfo.resolve(
- 200,
- new FileInfo({
- path: '/subdir',
- name: 'test.txt',
- mimetype: 'text/plain'
- })
- );
-
- return creating.then(function() {
- var $tr = fileList.findFileEl('test.txt');
- expect($tr.length).toEqual(1);
- expect($tr.attr('data-mime')).toEqual('text/plain');
- }).then(done, done);
- });
- // TODO: error cases
- // TODO: unique name cases
- });
- describe('create folder', function() {
- var deferredCreate;
- var deferredInfo;
- var createStub;
- var getFileInfoStub;
-
- beforeEach(function() {
- deferredCreate = $.Deferred();
- deferredInfo = $.Deferred();
- createStub = sinon.stub(filesClient, 'createDirectory')
- .returns(deferredCreate.promise());
- getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
- .returns(deferredInfo.promise());
- });
- afterEach(function() {
- createStub.restore();
- getFileInfoStub.restore();
- });
-
- it('creates folder with given name and adds it to the list', function(done) {
- var creating = fileList.createDirectory('sub dir');
-
- expect(createStub.calledOnce).toEqual(true);
- expect(createStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
-
- deferredCreate.resolve(200);
-
- expect(getFileInfoStub.calledOnce).toEqual(true);
- expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
-
- deferredInfo.resolve(
- 200,
- new FileInfo({
- path: '/subdir',
- name: 'sub dir',
- mimetype: 'httpd/unix-directory'
- })
- );
-
- return creating.then(function() {
- var $tr = fileList.findFileEl('sub dir');
- expect($tr.length).toEqual(1);
- expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
- }).then(done, done);
- });
- // TODO: error cases
- // TODO: unique name cases
- });
- describe('addAndFetchFileInfo', function() {
- var getFileInfoStub;
- var getFileInfoDeferred;
-
- beforeEach(function() {
- getFileInfoDeferred = $.Deferred();
- getFileInfoStub = sinon.stub(OC.Files.Client.prototype, 'getFileInfo');
- getFileInfoStub.returns(getFileInfoDeferred.promise());
- });
- afterEach(function() {
- getFileInfoStub.restore();
- });
- it('does not fetch if the given folder is not the current one', function() {
- var promise = fileList.addAndFetchFileInfo('testfile.txt', '/another');
- expect(getFileInfoStub.notCalled).toEqual(true);
-
- expect(promise.state()).toEqual('resolved');
- });
- it('fetches info when folder is the current one', function(done) {
- fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
-
- return Promise.resolve().then(function() {
- expect(getFileInfoStub.calledOnce).toEqual(true);
- expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/testfile.txt');
- }).then(done, done);
- });
- it('adds file data to list when fetching is done', function(done) {
- var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
- getFileInfoDeferred.resolve(200, {
- name: 'testfile.txt',
- size: 100
- });
-
- return adding.then(function() {
- expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
- }).then(done, done);
- });
- it('replaces file data to list when fetching is done', function(done) {
- var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
- fileList.add({
- name: 'testfile.txt',
- size: 95
- });
- getFileInfoDeferred.resolve(200, {
- name: 'testfile.txt',
- size: 100
- });
- expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('95');
- return adding.then(function() {
- expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
- }).then(done, done);
- });
- it('resolves promise with file data when fetching is done', function(done) {
- var promise = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
- getFileInfoDeferred.resolve(200, {
- name: 'testfile.txt',
- size: 100
- });
- expect(promise.state()).toEqual('pending');
- return promise.then(function(status, data) {
- expect(promise.state()).toEqual('resolved');
- expect(status).toEqual(200);
- expect(data.name).toEqual('testfile.txt');
- expect(data.size).toEqual(100);
- }).then(done, done);
- });
- });
- /**
- * Test upload mostly by testing the code inside the event handlers
- * that were registered on the magic upload object
- */
- describe('file upload', function() {
- var uploadData;
- var uploader;
-
- beforeEach(function() {
- fileList.setFiles(testFiles);
- uploader = fileList._uploader;
- // simulate data structure from jquery.upload
- uploadData = {
- files: [{
- name: 'upload.txt'
- }]
- };
- });
-
- afterEach(function() {
- uploader = null;
- uploadData = null;
- });
-
- describe('enableupload', function() {
- it('sets up uploader when enableUpload is true', function() {
- expect(fileList._uploader).toBeDefined();
- });
- it('does not sets up uploader when enableUpload is false', function() {
- fileList.destroy();
- fileList = new OCA.Files.FileList($('#app-content-files'), {
- filesClient: filesClient
- });
- expect(fileList._uploader).toBeFalsy();
- });
- });
-
- describe('adding files for upload', function() {
- /**
- * Simulate add event on the given target
- *
- * @return event object including the result
- */
- function addFile(data) {
- uploader.trigger('add', {}, data || {});
- }
-
- it('sets target dir to the current directory', function() {
- addFile(uploadData);
- expect(uploadData.targetDir).toEqual('/subdir');
- });
- });
- describe('dropping external files', function() {
-
- /**
- * Simulate drop event on the given target
- *
- * @param $target target element to drop on
- * @return event object including the result
- */
- function dropOn($target, data) {
- var eventData = {
- delegatedEvent: {
- target: $target
- },
- preventDefault: function () {
- },
- stopPropagation: function() {
- }
- };
- uploader.trigger('drop', eventData, data || {});
- return !!data.targetDir;
- }
- it('drop on a tr or crumb outside file list does not trigger upload', function() {
- var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
- var ev;
- $('#testArea').append($anotherTable);
- ev = dropOn($anotherTable.find('tr'), uploadData);
- expect(ev).toEqual(false);
-
- ev = dropOn($anotherTable.find('.crumb'), uploadData);
- expect(ev).toEqual(false);
- });
- it('drop on an element outside file list container does not trigger upload', function() {
- var $anotherEl = $('<div>outside</div>');
- var ev;
- $('#testArea').append($anotherEl);
- ev = dropOn($anotherEl, uploadData);
-
- expect(ev).toEqual(false);
- });
- it('drop on an element inside the table triggers upload', function() {
- var ev;
- ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
-
- expect(ev).not.toEqual(false);
- expect(uploadData.targetDir).toEqual('/subdir');
- });
- it('drop on an element on the table container triggers upload', function() {
- var ev;
- ev = dropOn($('#app-content-files'), uploadData);
-
- expect(ev).not.toEqual(false);
- expect(uploadData.targetDir).toEqual('/subdir');
- });
- it('drop on an element inside the table does not trigger upload if no upload permission', function() {
- $('#permissions').val(0);
- var ev;
- ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
-
- expect(ev).toEqual(false);
- expect(notificationStub.calledOnce).toEqual(true);
- });
- it('drop on an folder does not trigger upload if no upload permission on that folder', function() {
- var $tr = fileList.findFileEl('somedir');
- var ev;
- $tr.data('permissions', OC.PERMISSION_READ);
- ev = dropOn($tr, uploadData);
-
- expect(ev).toEqual(false);
- expect(notificationStub.calledOnce).toEqual(true);
- });
- it('drop on a file row inside the table triggers upload to current folder', function() {
- var ev;
- ev = dropOn(fileList.findFileEl('One.txt').find('td:first'), uploadData);
-
- expect(ev).not.toEqual(false);
- expect(uploadData.targetDir).toEqual('/subdir');
- });
- it('drop on a folder row inside the table triggers upload to target folder', function() {
- var ev;
- ev = dropOn(fileList.findFileEl('somedir').find('td:eq(2)'), uploadData);
-
- expect(ev).not.toEqual(false);
- expect(uploadData.targetDir).toEqual('/subdir/somedir');
- });
- it('drop on a breadcrumb inside the table triggers upload to target folder', function() {
- var ev;
- fileList.changeDirectory('a/b/c/d');
- ev = dropOn(fileList.$el.find('.crumb:eq(3)'), uploadData);
-
- expect(ev).not.toEqual(false);
- expect(uploadData.targetDir).toEqual('/a/b');
- });
- it('renders upload indicator element for folders only', function() {
- fileList.add({
- name: 'afolder',
- type: 'dir',
- mime: 'httpd/unix-directory'
- });
- fileList.add({
- name: 'afile.txt',
- type: 'file',
- mime: 'text/plain'
- });
-
- expect(fileList.findFileEl('afolder').find('.uploadtext').length).toEqual(1);
- expect(fileList.findFileEl('afile.txt').find('.uploadtext').length).toEqual(0);
- });
- });
-
- describe('after folder creation due to folder upload', function() {
- it('fetches folder info', function() {
- var fetchInfoStub = sinon.stub(fileList, 'addAndFetchFileInfo');
-
- uploader.trigger('createdfolder', '/subdir/newfolder');
-
- expect(fetchInfoStub.calledOnce).toEqual(true);
- expect(fetchInfoStub.getCall(0).args[0]).toEqual('newfolder');
- expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
-
- fetchInfoStub.restore();
- });
- });
-
- describe('after upload', function() {
- var fetchInfoStub;
-
- beforeEach(function() {
- fetchInfoStub = sinon.stub(fileList, 'addAndFetchFileInfo');
-
- });
- afterEach(function() {
- fetchInfoStub.restore();
- });
-
-
- function createUpload(name, dir) {
- var jqXHR = {
- status: 200
- };
- return {
- getFileName: sinon.stub().returns(name),
- getFullPath: sinon.stub().returns(dir),
- data: {
- jqXHR: jqXHR
- }
- };
- }
-
- /**
- * Simulate add event on the given target
- *
- * @return event object including the result
- */
- function addFile(data) {
- var ev = new $.Event('done', {
- jqXHR: {status: 200}
- });
- var deferred = $.Deferred();
- fetchInfoStub.returns(deferred.promise());
- uploader.trigger('done', ev, data || {});
- return deferred;
- }
-
- it('fetches file info', function() {
- addFile(createUpload('upload.txt', '/subdir'));
- expect(fetchInfoStub.calledOnce).toEqual(true);
- expect(fetchInfoStub.getCall(0).args[0]).toEqual('upload.txt');
- expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
- });
- it('highlights all uploaded files after all fetches are done', function(done) {
- var highlightStub = sinon.stub(fileList, 'highlightFiles');
- var def1 = addFile(createUpload('upload.txt', '/subdir'));
- var def2 = addFile(createUpload('upload2.txt', '/subdir'));
- var def3 = addFile(createUpload('upload3.txt', '/another'));
- uploader.trigger('stop', {});
-
- expect(highlightStub.notCalled).toEqual(true);
- def1.resolve();
- expect(highlightStub.notCalled).toEqual(true);
- def2.resolve();
- def3.resolve();
- setTimeout(function() {
- expect(highlightStub.callCount).toEqual(1);
- expect(highlightStub.getCall(0).args[0]).toEqual(['upload.txt', 'upload2.txt']);
-
- highlightStub.restore();
-
- done();
- }, 5);
- });
- it('queries storage stats after all fetches are done', function(done) {
- var statStub = sinon.stub(fileList, 'updateStorageStatistics');
- var highlightStub = sinon.stub(fileList, 'highlightFiles');
- var def1 = addFile(createUpload('upload.txt', '/subdir'));
- var def2 = addFile(createUpload('upload2.txt', '/subdir'));
- var def3 = addFile(createUpload('upload3.txt', '/another'));
- uploader.trigger('stop', {});
-
- expect(statStub.notCalled).toEqual(true);
- def1.resolve();
- expect(statStub.notCalled).toEqual(true);
- def2.resolve();
- def3.resolve();
- setTimeout(function() {
- expect(statStub.calledOnce).toEqual(true);
-
- highlightStub.restore();
-
- done();
- }, 3);
- });
- });
- });
- describe('Handling errors', function () {
- var deferredList;
- var getFolderContentsStub;
- var reloading;
-
- beforeEach(function() {
- deferredList = $.Deferred();
- getFolderContentsStub =
- sinon.stub(filesClient, 'getFolderContents');
- getFolderContentsStub.onCall(0).returns(deferredList.promise());
- getFolderContentsStub.onCall(1).returns($.Deferred().promise());
- reloading = fileList.reload();
- });
- afterEach(function() {
- getFolderContentsStub.restore();
- fileList = undefined;
- });
- it('redirects to root folder in case of forbidden access', function (done) {
- deferredList.reject(403);
-
- return reloading.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- expect(getFolderContentsStub.calledTwice).toEqual(true);
- }).then(done, done);
- });
- it('redirects to root folder and shows notification in case of internal server error', function (done) {
- expect(notificationStub.notCalled).toEqual(true);
- deferredList.reject(500);
-
- return reloading.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- expect(getFolderContentsStub.calledTwice).toEqual(true);
- expect(notificationStub.calledOnce).toEqual(true);
- }).then(done, done);
- });
- it('redirects to root folder and shows notification in case of storage not available', function (done) {
- expect(notificationStub.notCalled).toEqual(true);
- deferredList.reject(503, 'Storage is temporarily not available');
-
- return reloading.then(function() {
- expect(fileList.getCurrentDirectory()).toEqual('/');
- expect(getFolderContentsStub.calledTwice).toEqual(true);
- expect(notificationStub.calledOnce).toEqual(true);
- }).then(done, done);
- });
- });
- describe('showFileBusyState', function() {
- var $tr;
-
- beforeEach(function() {
- fileList.setFiles(testFiles);
- $tr = fileList.findFileEl('Two.jpg');
- });
- it('shows spinner on busy rows', function() {
- fileList.showFileBusyState('Two.jpg', true);
- expect($tr.hasClass('busy')).toEqual(true);
- expect($tr.find('.thumbnail').parent().attr('class'))
- .toContain('icon-loading-small');
-
- fileList.showFileBusyState('Two.jpg', false);
- expect($tr.hasClass('busy')).toEqual(false);
- expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail')))
- .toEqual(OC.imagePath('core', 'filetypes/image.svg'));
- });
- it('accepts multiple input formats', function() {
- _.each([
- 'Two.jpg',
- ['Two.jpg'],
- $tr,
- [$tr]
- ], function(testCase) {
- fileList.showFileBusyState(testCase, true);
- expect($tr.hasClass('busy')).toEqual(true);
- fileList.showFileBusyState(testCase, false);
- expect($tr.hasClass('busy')).toEqual(false);
- });
- });
- });
- describe('elementToFile', function() {
- var $tr;
-
- beforeEach(function() {
- fileList.setFiles(testFiles);
- $tr = fileList.findFileEl('One.txt');
- });
-
- it('converts data attributes to file info structure', function() {
- var fileInfo = fileList.elementToFile($tr);
- expect(fileInfo.id).toEqual(1);
- expect(fileInfo.name).toEqual('One.txt');
- expect(fileInfo.mtime).toEqual(123456789);
- expect(fileInfo.etag).toEqual('abc');
- expect(fileInfo.permissions).toEqual(OC.PERMISSION_ALL);
- expect(fileInfo.size).toEqual(12);
- expect(fileInfo.mimetype).toEqual('text/plain');
- expect(fileInfo.type).toEqual('file');
- expect(fileInfo.path).not.toBeDefined();
- expect(fileInfo.isEncrypted).toEqual(false);
- });
- it('sets isEncrypted attribute if data includes true e2eencrypted', function() {
- testFiles[3].isEncrypted = true;
-
- fileList.setFiles(testFiles);
- $tr = fileList.findFileEl('somedir');
-
- var fileInfo = fileList.elementToFile($tr);
- expect(fileInfo.isEncrypted).toEqual(true);
- });
- it('adds path attribute if available', function() {
- $tr.attr('data-path', '/subdir');
- var fileInfo = fileList.elementToFile($tr);
- expect(fileInfo.path).toEqual('/subdir');
- });
- });
- describe('new file menu', function() {
- var newFileMenuStub;
-
- beforeEach(function() {
- newFileMenuStub = sinon.stub(OCA.Files.NewFileMenu.prototype, 'showAt');
- });
- afterEach(function() {
- newFileMenuStub.restore();
- })
- it('renders new button when no legacy upload button exists', function() {
- expect(fileList.$el.find('.button.upload').length).toEqual(0);
- expect(fileList.$el.find('.button.new').length).toEqual(1);
- });
- it('does not render new button when no legacy upload button exists (public page)', function() {
- fileList.destroy();
- $('.files-controls').append('<input type="button" class="button upload" />');
- fileList = new OCA.Files.FileList($('#app-content-files'));
- expect(fileList.$el.find('.button.upload').length).toEqual(1);
- expect(fileList.$el.find('.button.new').length).toEqual(0);
- });
- it('opens the new file menu when clicking on the "New" button', function() {
- var $button = fileList.$el.find('.button.new');
- $button.click();
- expect(newFileMenuStub.calledOnce).toEqual(true);
- });
- it('does not open the new file menu when button is disabled', function() {
- var $button = fileList.$el.find('.button.new');
- $button.addClass('disabled');
- $button.click();
- expect(newFileMenuStub.notCalled).toEqual(true);
- });
- });
- describe('mount type detection', function() {
- function testMountType(dirInfoId, dirInfoMountType, inputMountType, expectedMountType) {
- var $tr;
- fileList.dirInfo.id = dirInfoId;
- fileList.dirInfo.mountType = dirInfoMountType;
- $tr = fileList.add({
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- name: 'test dir',
- mountType: inputMountType
- });
-
- expect($tr.attr('data-mounttype')).toEqual(expectedMountType);
- }
-
- it('leaves mount type as is if no parent exists', function() {
- testMountType(null, null, 'external', 'external');
- testMountType(null, null, 'shared', 'shared');
- });
- it('detects share root if parent exists', function() {
- testMountType(123, null, 'shared', 'shared-root');
- testMountType(123, 'shared', 'shared', 'shared');
- testMountType(123, 'shared-root', 'shared', 'shared');
- });
- it('detects external storage root if parent exists', function() {
- testMountType(123, null, 'external', 'external-root');
- testMountType(123, 'external', 'external', 'external');
- testMountType(123, 'external-root', 'external', 'external');
- });
- });
- describe('file list should not refresh if url does not change', function() {
- var fileListStub;
-
- beforeEach(function() {
- fileListStub = sinon.stub(OCA.Files.FileList.prototype, 'changeDirectory');
- fileList._currentDirectory = '/subdir';
- });
- afterEach(function() {
- fileListStub.restore();
- });
- it('File list must not be refreshed', function() {
- $('#app-content-files').trigger(new $.Event('urlChanged', {dir: '/subdir'}));
- expect(fileListStub.notCalled).toEqual(true);
- });
- it('File list must be refreshed', function() {
- $('#app-content-files').trigger(new $.Event('urlChanged', {dir: '/'}));
- expect(fileListStub.notCalled).toEqual(false);
- });
- });
-});
diff --git a/apps/files/tests/js/filesSpec.js b/apps/files/tests/js/filesSpec.js
deleted file mode 100644
index 0ae15412fd0..00000000000
--- a/apps/files/tests/js/filesSpec.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.Files tests', function() {
- var Files = OCA.Files.Files;
-
- describe('File name validation', function() {
- it('Validates correct file names', function() {
- var fileNames = [
- 'boringname',
- 'something.with.extension',
- 'now with spaces',
- '.a',
- '..a',
- '.dotfile',
- 'single\'quote',
- ' spaces before',
- 'spaces after ',
- 'allowed chars including the crazy ones $%&_-^@!,()[]{}=;#',
- '汉字也能用',
- 'und Ümläüte sind auch willkommen'
- ];
- for ( var i = 0; i < fileNames.length; i++ ) {
- var error = false;
- try {
- expect(Files.isFileNameValid(fileNames[i])).toEqual(true);
- }
- catch (e) {
- error = e;
- }
- expect(error).toEqual(false);
- }
- });
- it('Detects invalid file names', function() {
- var fileNames = [
- '',
- ' ',
- '.',
- '..',
- ' ..',
- '.. ',
- '. ',
- ' .',
- 'foo.part',
- 'bar.filepart'
- ];
- for ( var i = 0; i < fileNames.length; i++ ) {
- var threwException = false;
- try {
- Files.isFileNameValid(fileNames[i]);
- console.error('Invalid file name not detected:', fileNames[i]);
- }
- catch (e) {
- threwException = true;
- }
- expect(threwException).toEqual(true);
- }
- });
- });
- describe('getDownloadUrl', function() {
- it('returns the ajax download URL when filename and dir specified', function() {
- var url = Files.getDownloadUrl('test file.txt', '/subdir');
- expect(url).toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/test%20file.txt');
- });
- it('returns the webdav download URL when filename and root dir specified', function() {
- var url = Files.getDownloadUrl('test file.txt', '/');
- expect(url).toEqual(OC.getRootPath() + '/remote.php/webdav/test%20file.txt');
- });
- it('returns the ajax download URL when multiple files specified', function() {
- var url = Files.getDownloadUrl(['test file.txt', 'abc.txt'], '/subdir');
- expect(url).toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22test%20file.txt%22%2C%22abc.txt%22%5D');
- });
- });
- describe('handleDownload', function() {
- var redirectStub;
- var cookieStub;
- var clock;
- var testUrl;
-
- beforeEach(function() {
- testUrl = 'http://example.com/owncloud/path/download.php';
- redirectStub = sinon.stub(OC, 'redirect');
- cookieStub = sinon.stub(OC.Util, 'isCookieSetToValue');
- clock = sinon.useFakeTimers();
- });
- afterEach(function() {
- redirectStub.restore();
- cookieStub.restore();
- clock.restore();
- });
-
- it('appends secret to url when no existing parameters', function() {
- Files.handleDownload(testUrl);
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(testUrl + '?downloadStartSecret=');
- });
- it('appends secret to url with existing parameters', function() {
- Files.handleDownload(testUrl + '?test=1');
- expect(redirectStub.calledOnce).toEqual(true);
- expect(redirectStub.getCall(0).args[0]).toContain(testUrl + '?test=1&downloadStartSecret=');
- });
- it('sets cookie and calls callback when cookie appears', function() {
- var callbackStub = sinon.stub();
- var token;
- Files.handleDownload(testUrl, callbackStub);
- expect(redirectStub.calledOnce).toEqual(true);
- token = OC.parseQueryString(redirectStub.getCall(0).args[0]).downloadStartSecret;
- expect(token).toBeDefined();
-
- expect(cookieStub.calledOnce).toEqual(true);
- cookieStub.returns(false);
- clock.tick(600);
-
- expect(cookieStub.calledTwice).toEqual(true);
- expect(cookieStub.getCall(1).args[0]).toEqual('ocDownloadStarted');
- expect(cookieStub.getCall(1).args[1]).toEqual(token);
- expect(callbackStub.notCalled).toEqual(true);
-
- cookieStub.returns(true);
- clock.tick(2000);
-
- expect(cookieStub.callCount).toEqual(3);
- expect(callbackStub.calledOnce).toEqual(true);
- });
- });
-});
diff --git a/apps/files/tests/js/filesummarySpec.js b/apps/files/tests/js/filesummarySpec.js
deleted file mode 100644
index da35ff11c14..00000000000
--- a/apps/files/tests/js/filesummarySpec.js
+++ /dev/null
@@ -1,227 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.FileSummary tests', function() {
- var FileSummary = OCA.Files.FileSummary;
- var $container;
-
- beforeEach(function() {
- $container = $('<table><tr></tr></table>').find('tr');
- });
- afterEach(function() {
- $container = null;
- });
-
- it('renders summary as text', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 5,
- totalFiles: 2,
- totalSize: 256000
- });
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('5 folders');
- expect($container.find('.fileinfo').text()).toEqual('2 files');
- expect($container.find('.filesize').text()).toEqual('250 KB');
- });
- it('hides summary when no files or folders', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 0,
- totalFiles: 0,
- totalSize: 0
- });
- expect($container.hasClass('hidden')).toEqual(true);
- });
- it('increases summary when adding files', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 5,
- totalFiles: 2,
- totalSize: 256000
- });
- s.add({type: 'file', size: 256000});
- s.add({type: 'dir', size: 100});
- s.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('6 folders');
- expect($container.find('.fileinfo').text()).toEqual('3 files');
- expect($container.find('.filesize').text()).toEqual('500 KB');
- expect(s.summary.totalDirs).toEqual(6);
- expect(s.summary.totalFiles).toEqual(3);
- expect(s.summary.totalSize).toEqual(512100);
- });
- it('decreases summary when removing files', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 5,
- totalFiles: 2,
- totalSize: 256000
- });
- s.remove({type: 'file', size: 128000});
- s.remove({type: 'dir', size: 100});
- s.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('4 folders');
- expect($container.find('.fileinfo').text()).toEqual('1 file');
- expect($container.find('.filesize').text()).toEqual('125 KB');
- expect(s.summary.totalDirs).toEqual(4);
- expect(s.summary.totalFiles).toEqual(1);
- expect(s.summary.totalSize).toEqual(127900);
- });
-
- it('renders filtered summary as text', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 5,
- totalFiles: 2,
- totalSize: 256000,
- filter: 'foo'
- });
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('5 folders');
- expect($container.find('.fileinfo').text()).toEqual('2 files');
- expect($container.find('.filter').text()).toEqual(' match "foo"');
- expect($container.find('.filesize').text()).toEqual('250 KB');
- });
- it('hides filtered summary when no files or folders', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 0,
- totalFiles: 0,
- totalSize: 0,
- filter: 'foo'
- });
- expect($container.hasClass('hidden')).toEqual(true);
- });
- it('increases filtered summary when adding files', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 5,
- totalFiles: 2,
- totalSize: 256000,
- filter: 'foo'
- });
- s.add({name: 'bar.txt', type: 'file', size: 256000});
- s.add({name: 'foo.txt', type: 'file', size: 256001});
- s.add({name: 'bar', type: 'dir', size: 100});
- s.add({name: 'foo', type: 'dir', size: 102});
- s.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('6 folders');
- expect($container.find('.fileinfo').text()).toEqual('3 files');
- expect($container.find('.filter').text()).toEqual(' match "foo"');
- expect($container.find('.filesize').text()).toEqual('500 KB');
- expect(s.summary.totalDirs).toEqual(6);
- expect(s.summary.totalFiles).toEqual(3);
- expect(s.summary.totalSize).toEqual(512103);
- });
- it('decreases filtered summary when removing files', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 5,
- totalFiles: 2,
- totalSize: 256000,
- filter: 'foo'
- });
- s.remove({name: 'bar.txt', type: 'file', size: 128000});
- s.remove({name: 'foo.txt', type: 'file', size: 127999});
- s.remove({name: 'bar', type: 'dir', size: 100});
- s.remove({name: 'foo', type: 'dir', size: 98});
- s.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('4 folders');
- expect($container.find('.fileinfo').text()).toEqual('1 file');
- expect($container.find('.filter').text()).toEqual(' match "foo"');
- expect($container.find('.filesize').text()).toEqual('125 KB');
- expect(s.summary.totalDirs).toEqual(4);
- expect(s.summary.totalFiles).toEqual(1);
- expect(s.summary.totalSize).toEqual(127903);
- });
- it('properly sum up pending folder sizes after adding', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 0,
- totalFiles: 0,
- totalSize: 0
- });
- s.add({type: 'dir', size: -1});
- s.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('1 folder');
- expect($container.find('.fileinfo').hasClass('hidden')).toEqual(true);
- expect($container.find('.filesize').text()).toEqual('Pending');
- expect(s.summary.totalDirs).toEqual(1);
- expect(s.summary.totalFiles).toEqual(0);
- expect(s.summary.totalSize).toEqual(0);
- });
- it('properly sum up pending folder sizes after remove', function() {
- var s = new FileSummary($container);
- s.setSummary({
- totalDirs: 0,
- totalFiles: 0,
- totalSize: 0
- });
- s.add({type: 'dir', size: -1});
- s.remove({type: 'dir', size: -1});
- s.update();
- expect($container.hasClass('hidden')).toEqual(true);
- expect(s.summary.totalDirs).toEqual(0);
- expect(s.summary.totalFiles).toEqual(0);
- expect(s.summary.totalSize).toEqual(0);
- });
- describe('hidden files', function() {
- var config;
- var summary;
-
- beforeEach(function() {
- config = new OC.Backbone.Model();
- summary = new FileSummary($container, {
- config: config
- });
- });
-
- it('renders hidden count section when hidden files are hidden', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
-
- summary.add({name: 'abc', type: 'file', size: 256000});
- summary.add({name: 'def', type: 'dir', size: 100});
- summary.add({name: '.hidden', type: 'dir', size: 512000});
- summary.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('2 folders');
- expect($container.find('.fileinfo').text()).toEqual('1 file');
- expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(false);
- expect($container.find('.hiddeninfo').text()).toEqual(' (including 1 hidden)');
- expect($container.find('.filesize').text()).toEqual('750 KB');
- });
- it('does not render hidden count section when hidden files exist but are visible', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: true });
-
- summary.add({name: 'abc', type: 'file', size: 256000});
- summary.add({name: 'def', type: 'dir', size: 100});
- summary.add({name: '.hidden', type: 'dir', size: 512000});
- summary.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('2 folders');
- expect($container.find('.fileinfo').text()).toEqual('1 file');
- expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(true);
- expect($container.find('.filesize').text()).toEqual('750 KB');
- });
- it('does not render hidden count section when no hidden files exist', function() {
- window._nc_event_bus.emit('files:config:updated', { key: 'show_hidden', value: false });
-
- summary.add({name: 'abc', type: 'file', size: 256000});
- summary.add({name: 'def', type: 'dir', size: 100});
- summary.update();
- expect($container.hasClass('hidden')).toEqual(false);
- expect($container.find('.dirinfo').text()).toEqual('1 folder');
- expect($container.find('.fileinfo').text()).toEqual('1 file');
- expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(true);
- expect($container.find('.filesize').text()).toEqual('250 KB');
- });
- });
-});
diff --git a/apps/files/tests/js/mainfileinfodetailviewSpec.js b/apps/files/tests/js/mainfileinfodetailviewSpec.js
deleted file mode 100644
index 1816e35b050..00000000000
--- a/apps/files/tests/js/mainfileinfodetailviewSpec.js
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.MainFileInfoDetailView tests', function() {
- var view, tooltipStub, fileActions, fileList, testFileInfo;
-
- beforeEach(function() {
- tooltipStub = sinon.stub($.fn, 'tooltip');
- fileActions = new OCA.Files.FileActions();
- fileList = new OCA.Files.FileList($('<table></table>'), {
- fileActions: fileActions
- });
- view = new OCA.Files.MainFileInfoDetailView({
- fileList: fileList,
- fileActions: fileActions
- });
- testFileInfo = new OCA.Files.FileInfoModel({
- id: 5,
- name: 'One.txt',
- mimetype: 'text/plain',
- permissions: 31,
- path: '/subdir',
- size: 123456789,
- etag: 'abcdefg',
- mtime: Date.UTC(2015, 6, 17, 1, 2, 0, 0)
- });
- });
- afterEach(function() {
- view.remove();
- view = undefined;
- tooltipStub.restore();
-
- });
- describe('rendering', function() {
- it('displays basic info', function() {
- var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3));
- var dateExpected = OC.Util.formatDate(Date(Date.UTC(2015, 6, 17, 1, 2, 0, 0)));
- view.setFileInfo(testFileInfo);
- expect(view.$el.find('.fileName h3').text()).toEqual('One.txt');
- expect(view.$el.find('.fileName h3').attr('title')).toEqual('One.txt');
- expect(view.$el.find('.size').text()).toEqual('117.7 MB');
- expect(view.$el.find('.size').attr('title')).toEqual('123456789 bytes');
- expect(view.$el.find('.date').text()).toEqual('seconds ago');
- expect(view.$el.find('.date').attr('title')).toEqual(dateExpected);
- clock.restore();
- });
- it('displays permalink', function() {
- view.setFileInfo(testFileInfo);
- expect(view.$el.find('.permalink').attr('href'))
- .toEqual(OC.getProtocol() + '://' + OC.getHost() + OC.generateUrl('/f/5'));
- });
- it('displays favorite icon', function() {
- fileActions.registerAction({
- name: 'Favorite',
- mime: 'all',
- permissions: OC.PERMISSION_NONE
- });
-
- testFileInfo.set('tags', [OC.TAG_FAVORITE]);
- view.setFileInfo(testFileInfo);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(true);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(false);
-
- testFileInfo.set('tags', []);
- view.setFileInfo(testFileInfo);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
- });
- it('does not display favorite icon if favorite action is not available', function() {
- testFileInfo.set('tags', [OC.TAG_FAVORITE]);
- view.setFileInfo(testFileInfo);
- expect(view.$el.find('.action-favorite').length).toEqual(0);
-
- testFileInfo.set('tags', []);
- view.setFileInfo(testFileInfo);
- expect(view.$el.find('.action-favorite').length).toEqual(0);
- });
- it('displays mime icon', function() {
- // File
- var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
- testFileInfo.set('mimetype', 'text/calendar');
- view.setFileInfo(testFileInfo);
-
- expect(lazyLoadPreviewStub.calledOnce).toEqual(true);
- var previewArgs = lazyLoadPreviewStub.getCall(0).args;
- expect(previewArgs[0].mime).toEqual('text/calendar');
- expect(previewArgs[0].path).toEqual('/subdir/One.txt');
- expect(previewArgs[0].etag).toEqual('abcdefg');
-
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
-
- // returns mime icon first without img parameter
- previewArgs[0].callback(
- OC.imagePath('core', 'filetypes/text-calendar.svg')
- );
-
- // still loading
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
-
- // preview loading failed, no prview
- previewArgs[0].error();
-
- // loading stopped, the mimetype icon gets displayed
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
- expect(view.$el.find('.thumbnail').css('background-image'))
- .toContain('filetypes/text-calendar.svg');
-
- // Folder
- testFileInfo.set('mimetype', 'httpd/unix-directory');
- view.setFileInfo(testFileInfo);
-
- expect(view.$el.find('.thumbnail').css('background-image'))
- .toContain('filetypes/folder.svg');
-
- lazyLoadPreviewStub.restore();
- });
- it('uses icon from model if present in model', function() {
- var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
- testFileInfo.set('mimetype', 'httpd/unix-directory');
- testFileInfo.set('icon', OC.MimeType.getIconUrl('dir-external'));
- view.setFileInfo(testFileInfo);
-
- expect(lazyLoadPreviewStub.notCalled).toEqual(true);
-
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
- expect(view.$el.find('.thumbnail').css('background-image'))
- .toContain('filetypes/folder-external.svg');
-
- lazyLoadPreviewStub.restore();
- });
- it('displays thumbnail', function() {
- var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
-
- testFileInfo.set('mimetype', 'text/plain');
- view.setFileInfo(testFileInfo);
-
- expect(lazyLoadPreviewStub.calledOnce).toEqual(true);
- var previewArgs = lazyLoadPreviewStub.getCall(0).args;
- expect(previewArgs[0].mime).toEqual('text/plain');
- expect(previewArgs[0].path).toEqual('/subdir/One.txt');
- expect(previewArgs[0].etag).toEqual('abcdefg');
-
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
-
- // returns mime icon first without img parameter
- previewArgs[0].callback(
- OC.imagePath('core', 'filetypes/text-plain.svg')
- );
-
- // still loading
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(true);
-
- // return an actual (simulated) image
- previewArgs[0].callback(
- 'testimage', {
- width: 100,
- height: 200
- }
- );
-
- // loading stopped, image got displayed
- expect(view.$el.find('.thumbnail').css('background-image'))
- .toContain('testimage');
-
- expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
-
- lazyLoadPreviewStub.restore();
- });
- it('does not show size if no size available', function() {
- testFileInfo.unset('size');
- view.setFileInfo(testFileInfo);
-
- expect(view.$el.find('.size').length).toEqual(0);
- });
- it('renders displayName instead of name if available', function() {
- testFileInfo.set('displayName', 'hello.txt');
- view.setFileInfo(testFileInfo);
-
- expect(view.$el.find('.fileName h3').text()).toEqual('hello.txt');
- expect(view.$el.find('.fileName h3').attr('title')).toEqual('hello.txt');
- });
- it('rerenders when changes are made on the model', function() {
- // Show the "Favorite" icon
- fileActions.registerAction({
- name: 'Favorite',
- mime: 'all',
- permissions: OC.PERMISSION_NONE
- });
-
- view.setFileInfo(testFileInfo);
-
- testFileInfo.set('tags', [OC.TAG_FAVORITE]);
-
- expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(true);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(false);
-
- testFileInfo.set('tags', []);
-
- expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
- });
- it('unbinds change listener from model', function() {
- // Show the "Favorite" icon
- fileActions.registerAction({
- name: 'Favorite',
- mime: 'all',
- permissions: OC.PERMISSION_NONE
- });
-
- view.setFileInfo(testFileInfo);
- view.setFileInfo(new OCA.Files.FileInfoModel({
- id: 999,
- name: 'test.txt',
- path: '/'
- }));
-
- // set value on old model
- testFileInfo.set('tags', [OC.TAG_FAVORITE]);
-
- // no change
- expect(view.$el.find('.action-favorite > span').hasClass('icon-starred')).toEqual(false);
- expect(view.$el.find('.action-favorite > span').hasClass('icon-star')).toEqual(true);
- });
- });
- describe('events', function() {
- it('triggers default action when clicking on the thumbnail', function() {
- var actionHandler = sinon.stub();
-
- fileActions.registerAction({
- name: 'Something',
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- actionHandler: actionHandler
- });
- fileActions.setDefault('text/plain', 'Something');
-
- view.setFileInfo(testFileInfo);
-
- view.$el.find('.thumbnail').click();
-
- expect(actionHandler.calledOnce).toEqual(true);
- expect(actionHandler.getCall(0).args[0]).toEqual('One.txt');
- expect(actionHandler.getCall(0).args[1].fileList).toEqual(fileList);
- expect(actionHandler.getCall(0).args[1].fileActions).toEqual(fileActions);
- expect(actionHandler.getCall(0).args[1].fileInfoModel).toEqual(testFileInfo);
- });
- it('triggers "Favorite" action when clicking on the star', function() {
- var actionHandler = sinon.stub();
-
- fileActions.registerAction({
- name: 'Favorite',
- mime: 'all',
- permissions: OC.PERMISSION_READ,
- actionHandler: actionHandler
- });
-
- view.setFileInfo(testFileInfo);
-
- view.$el.find('.action-favorite').click();
-
- expect(actionHandler.calledOnce).toEqual(true);
- expect(actionHandler.getCall(0).args[0]).toEqual('One.txt');
- expect(actionHandler.getCall(0).args[1].fileList).toEqual(fileList);
- expect(actionHandler.getCall(0).args[1].fileActions).toEqual(fileActions);
- expect(actionHandler.getCall(0).args[1].fileInfoModel).toEqual(testFileInfo);
- });
- });
-});
diff --git a/apps/files/tests/js/newfilemenuSpec.js b/apps/files/tests/js/newfilemenuSpec.js
deleted file mode 100644
index a642ad6d4db..00000000000
--- a/apps/files/tests/js/newfilemenuSpec.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.NewFileMenu', function() {
- var FileList = OCA.Files.FileList;
- var menu, fileList, $uploadField, $trigger;
-
- beforeEach(function() {
- // dummy upload button
- var $container = $('<div id="app-content-files"></div>');
- $uploadField = $('<input id="file_upload_start"></input>');
- $trigger = $('<a href="#">Menu</a>');
- $container.append($uploadField).append($trigger);
- $('#testArea').append($container);
-
- fileList = new FileList($container);
- menu = new OCA.Files.NewFileMenu({
- fileList: fileList
- });
- menu.showAt($trigger);
- });
- afterEach(function() {
- OC.hideMenus();
- fileList = null;
- menu = null;
- });
-
- describe('rendering', function() {
- it('renders menu items', function() {
- var $items = menu.$el.find('.menuitem');
- expect($items.length).toEqual(2);
- // label points to the file_upload_start item
- var $item = $items.eq(0);
- expect($item.is('label')).toEqual(true);
- expect($item.attr('for')).toEqual('file_upload_start');
- });
- });
- describe('New file/folder', function() {
- var $input;
- var createDirectoryStub;
-
- beforeEach(function() {
- createDirectoryStub = sinon.stub(FileList.prototype, 'createDirectory');
- menu.$el.find('.menuitem').eq(1).click();
- $input = menu.$el.find('form.filenameform input');
- });
- afterEach(function() {
- createDirectoryStub.restore();
- });
-
- it('sets default text in field', function() {
- // text + submit
- expect($input.length).toEqual(2);
- expect($input.val()).toEqual('New folder');
- });
- it('prevents entering invalid file names', function() {
- $input.val('..');
- $input.trigger(new $.Event('keyup', {keyCode: 13}));
- $input.closest('form').submit();
-
- expect(createDirectoryStub.notCalled).toEqual(true);
- });
- it('prevents entering file names that already exist', function() {
- var inListStub = sinon.stub(fileList, 'inList').returns(true);
- $input.val('existing.txt');
- $input.trigger(new $.Event('keyup', {keyCode: 13}));
- $input.closest('form').submit();
-
- expect(createDirectoryStub.notCalled).toEqual(true);
- inListStub.restore();
- });
- it('creates directory when clicking on create directory field', function() {
- $input = menu.$el.find('form.filenameform input');
- $input.val('some folder');
- $input.trigger(new $.Event('keyup', {keyCode: 13}));
- $input.closest('form').submit();
-
- expect(createDirectoryStub.calledOnce).toEqual(true);
- expect(createDirectoryStub.getCall(0).args[0]).toEqual('some folder');
- });
- });
- describe('custom entries', function() {
- var oldPlugins;
- var plugin;
- var actionStub;
-
- beforeEach(function() {
- oldPlugins = _.extend({}, OC.Plugins._plugins);
- actionStub = sinon.stub();
- plugin = {
- attach: function(menu) {
- menu.addMenuEntry({
- id: 'file',
- displayName: t('files_texteditor', 'Text file'),
- templateName: t('files_texteditor', 'New text file.txt'),
- iconClass: 'icon-filetype-text',
- fileType: 'file',
- actionHandler: actionStub
- });
- }
- };
-
- OC.Plugins.register('OCA.Files.NewFileMenu', plugin);
- menu = new OCA.Files.NewFileMenu({
- fileList: fileList
- });
- menu.showAt($trigger);
- });
- afterEach(function() {
- OC.Plugins._plugins = oldPlugins;
- });
- it('renders custom menu items', function() {
- expect(menu.$el.find('.menuitem').length).toEqual(3);
- expect(menu.$el.find('.menuitem[data-action=file]').length).toEqual(1);
- });
- it('calls action handler when clicking on custom item', function() {
- menu.$el.find('.menuitem').eq(2).click();
- var $input = menu.$el.find('form.filenameform input');
- $input.val('some name');
- $input.trigger(new $.Event('keyup', {keyCode: 13}));
- $input.closest('form').submit();
-
- expect(actionStub.calledOnce).toEqual(true);
- expect(actionStub.getCall(0).args[0]).toEqual('some name');
- });
- it('switching fields removes the previous form', function() {
- menu.$el.find('.menuitem').eq(2).click();
- expect(menu.$el.find('form').length).toEqual(1);
- });
- });
-});
diff --git a/apps/files/tests/js/tagspluginspec.js b/apps/files/tests/js/tagspluginspec.js
deleted file mode 100644
index 302c2f8a0f7..00000000000
--- a/apps/files/tests/js/tagspluginspec.js
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-describe('OCA.Files.TagsPlugin tests', function() {
- var fileList;
- var testFiles;
-
- beforeEach(function() {
- var $content = $('<div id="app-content"></div>');
- $('#testArea').append($content);
- // dummy file list
- var $div = $(
- '<div>' +
- '<table class="files-filestable">' +
- '<thead></thead>' +
- '<tbody class="files-fileList"></tbody>' +
- '</table>' +
- '</div>');
- $('#app-content').append($div);
-
- fileList = new OCA.Files.FileList($div);
- OCA.Files.TagsPlugin.attach(fileList);
-
- testFiles = [{
- id: 1,
- type: 'file',
- name: 'One.txt',
- path: '/subdir',
- mimetype: 'text/plain',
- size: 12,
- permissions: OC.PERMISSION_ALL,
- etag: 'abc',
- shareOwner: 'User One',
- isShareMountPoint: false,
- tags: ['tag1', 'tag2']
- }];
- });
- afterEach(function() {
- fileList.destroy();
- fileList = null;
- });
-
- describe('Favorites icon', function() {
- it('renders favorite icon and extra data', function() {
- var $favoriteMark, $tr;
- fileList.setFiles(testFiles);
- $tr = fileList.$el.find('tbody tr:first');
- $favoriteMark = $tr.find('.favorite-mark');
- expect($favoriteMark.length).toEqual(1);
- expect($favoriteMark.hasClass('permanent')).toEqual(false);
-
- expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2']);
- expect($tr.attr('data-favorite')).not.toBeDefined();
- });
- it('renders permanent favorite icon and extra data', function() {
- var $favoriteMark, $tr;
- testFiles[0].tags.push(OC.TAG_FAVORITE);
- fileList.setFiles(testFiles);
- $tr = fileList.$el.find('tbody tr:first');
- $favoriteMark = $tr.find('.favorite-mark');
- expect($favoriteMark.length).toEqual(1);
- expect($favoriteMark.hasClass('permanent')).toEqual(true);
-
- expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', OC.TAG_FAVORITE]);
- expect($tr.attr('data-favorite')).toEqual('true');
- });
- });
- describe('Applying tags', function() {
- it('through FileActionsMenu sends request to server and updates icon', function(done) {
- var request;
- fileList.setFiles(testFiles);
- var $tr = fileList.findFileEl('One.txt');
- var $favoriteMark = $tr.find('.favorite-mark');
- var $showMenuAction = $tr.find('.action-menu');
- $showMenuAction.click();
- var $favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
- $favoriteActionInMenu.click();
-
- expect(fakeServer.requests.length).toEqual(1);
- request = fakeServer.requests[0];
- expect(JSON.parse(request.requestBody)).toEqual({
- tags: ['tag1', 'tag2', OC.TAG_FAVORITE]
- });
- request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
- tags: ['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]
- }));
-
- setTimeout(function () {
- // re-read the element as it was re-inserted
- $tr = fileList.findFileEl('One.txt');
- $favoriteMark = $tr.find('.favorite-mark');
- $showMenuAction = $tr.find('.action-menu');
-
- expect($tr.attr('data-favorite')).toEqual('true');
- expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
- expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
- expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(false);
- expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(true);
-
- // show again the menu and get the new action, as the menu was
- // closed and removed (and with it, the previous action) when that
- // action was clicked
- $showMenuAction.click();
- $favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
- $favoriteActionInMenu.click();
-
- setTimeout(function() {
- expect(fakeServer.requests.length).toEqual(2);
- request = fakeServer.requests[1];
- expect(JSON.parse(request.requestBody)).toEqual({
- tags: ['tag1', 'tag2', 'tag3']
- });
-
- request.respond(200, {'Content-Type': 'application/json'}, JSON.stringify({
- tags: ['tag1', 'tag2', 'tag3']
- }));
-
- setTimeout(function() {
- // re-read the element as it was re-inserted
- $tr = fileList.findFileEl('One.txt');
- $favoriteMark = $tr.find('.favorite-mark');
-
- expect($tr.attr('data-favorite')).toBeFalsy();
- expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
- expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);
- expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(true);
- expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(false);
-
- done();
- }, 1);
- }, 1);
- }, 1);
- });
- });
- describe('elementToFile', function() {
- it('returns tags', function() {
- fileList.setFiles(testFiles);
- var $tr = fileList.findFileEl('One.txt');
- var data = fileList.elementToFile($tr);
- expect(data.tags).toEqual(['tag1', 'tag2']);
- });
- it('returns empty array when no tags present', function() {
- delete testFiles[0].tags;
- fileList.setFiles(testFiles);
- var $tr = fileList.findFileEl('One.txt');
- var data = fileList.elementToFile($tr);
- expect(data.tags).toEqual([]);
- });
- });
-});
diff --git a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
index 784dc4e5e15..bea48f28dee 100644
--- a/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
+++ b/apps/files_sharing/lib/DefaultPublicShareTemplateProvider.php
@@ -101,7 +101,6 @@ class DefaultPublicShareTemplateProvider implements IPublicShareTemplateProvider
\OCP\Util::addInitScript(Application::APP_ID, 'init');
\OCP\Util::addInitScript(Application::APP_ID, 'init-public');
\OCP\Util::addScript('files', 'main');
- \OCP\Util::addStyle('files', 'merged');
// Add file-request script if needed
$attributes = $share->getAttributes();
diff --git a/apps/settings/templates/settings/frame.php b/apps/settings/templates/settings/frame.php
index 27da91152f3..30d74efdc7d 100644
--- a/apps/settings/templates/settings/frame.php
+++ b/apps/settings/templates/settings/frame.php
@@ -8,7 +8,6 @@ style('settings', 'settings');
script('settings', 'settings');
\OCP\Util::addScript('settings', 'legacy-admin');
script('core', 'setupchecks');
-script('files', 'jquery.fileupload');
?>