Merge pull request #1353 from ramiresviana/fixes-8

Some fixes
This commit is contained in:
Oleg Lobanov 2021-03-30 09:43:27 +02:00 committed by GitHub
commit 84e3a98303
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 240 additions and 57 deletions

View File

@ -170,7 +170,7 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
case strings.HasPrefix(mimetype, "image"): case strings.HasPrefix(mimetype, "image"):
i.Type = "image" i.Type = "image"
return nil return nil
case (strings.HasPrefix(mimetype, "text") || (len(buffer) > 0 && !isBinary(buffer))) && i.Size <= 10*1024*1024: // 10 MB case (strings.HasPrefix(mimetype, "text") || !isBinary(buffer)) && i.Size <= 10*1024*1024: // 10 MB
i.Type = "text" i.Type = "text"
if !modify { if !modify {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -29,14 +29,6 @@ export default {
type: Number, type: Number,
default: () => 200, default: () => 200,
}, },
maxScale: {
type: Number,
default: () => 4,
},
minScale: {
type: Number,
default: () => 0.25,
},
classList: { classList: {
type: Array, type: Array,
default: () => [], default: () => [],
@ -45,10 +37,6 @@ export default {
type: Number, type: Number,
default: () => 0.25, default: () => 0.25,
}, },
autofill: {
type: Boolean,
default: () => false,
},
}, },
data() { data() {
return { return {
@ -64,6 +52,8 @@ export default {
center: { x: 0, y: 0 }, center: { x: 0, y: 0 },
relative: { x: 0, y: 0 }, relative: { x: 0, y: 0 },
}, },
maxScale: 4,
minScale: 0.25,
}; };
}, },
mounted() { mounted() {
@ -88,6 +78,10 @@ export default {
}, },
watch: { watch: {
src: function () { src: function () {
if (!this.decodeUTIF()) {
this.$refs.imgex.src = this.src;
}
this.scale = 1; this.scale = 1;
this.setZoom(); this.setZoom();
this.setCenter(); this.setCenter();
@ -122,6 +116,21 @@ export default {
img.classList.add("image-ex-img-ready"); img.classList.add("image-ex-img-ready");
document.addEventListener("mouseup", this.onMouseUp); document.addEventListener("mouseup", this.onMouseUp);
let realSize = img.naturalWidth;
let displaySize = img.offsetWidth;
// Image is in portrait orientation
if (img.naturalHeight > img.naturalWidth) {
realSize = img.naturalHeight;
displaySize = img.offsetHeight;
}
// Scale needed to display the image on full size
const fullScale = realSize / displaySize;
// Full size plus additional zoom
this.maxScale = fullScale + 4;
}, },
onMouseUp() { onMouseUp() {
this.inDrag = false; this.inDrag = false;
@ -251,7 +260,7 @@ export default {
} }
}, },
wheelMove(event) { wheelMove(event) {
this.scale += (event.wheelDeltaY / 100) * this.zoomStep; this.scale += -Math.sign(event.deltaY) * this.zoomStep;
this.setZoom(); this.setZoom();
}, },
setZoom() { setZoom() {

View File

@ -56,6 +56,12 @@ export default {
let prompt = this.$refs.currentComponent; let prompt = this.$refs.currentComponent;
// Esc!
if (event.keyCode === 27) {
event.stopImmediatePropagation();
this.$store.commit("closeHovers");
}
// Enter // Enter
if (event.keyCode == 13) { if (event.keyCode == 13) {
switch (this.show) { switch (this.show) {

View File

@ -34,7 +34,7 @@
} }
.share__box__info { .share__box__info {
flex: 1 1 auto; flex: 1 1 18em;
} }
.share__box__element { .share__box__element {

View File

@ -110,4 +110,60 @@
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
} }
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-cyrillic-ext.woff2) format('woff2');
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-cyrillic.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-greek-ext.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-greek.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-vietnamese.woff2) format('woff2');
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local('Roboto Bold'), local('Roboto-Bold'), url(../assets/fonts/roboto/bold-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
@import "~material-design-icons/iconfont/material-icons.css"; @import "~material-design-icons/iconfont/material-icons.css";

View File

@ -25,6 +25,7 @@
transition: .1s ease background, .1s ease opacity; transition: .1s ease background, .1s ease opacity;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
user-select: none;
} }
#listing .item div:last-of-type { #listing .item div:last-of-type {

View File

@ -109,6 +109,7 @@
#previewer { #previewer {
background-color: rgba(0, 0, 0, 0.9); background-color: rgba(0, 0, 0, 0.9);
padding-top: 4em;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@ -142,7 +143,6 @@
} }
#previewer .preview { #previewer .preview {
margin-top: 4em;
text-align: center; text-align: center;
height: calc(100vh - 4em); height: calc(100vh - 4em);
} }
@ -209,7 +209,7 @@
#editor-container { #editor-container {
background-color: #fafafa; background-color: #fafafa;
position: fixed; position: fixed;
margin-top: 4em; padding-top: 4em;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<header-bar v-if="error || !req.type" showMenu showLogo /> <header-bar v-if="error || req.type == null" showMenu showLogo />
<breadcrumbs base="/files" /> <breadcrumbs base="/files" />
@ -124,15 +124,6 @@ export default {
} }
}, },
keyEvent(event) { keyEvent(event) {
if (this.show !== null) {
// Esc!
if (event.keyCode === 27) {
this.$store.commit("closeHovers");
}
return;
}
// F1! // F1!
if (event.keyCode === 112) { if (event.keyCode === 112) {
event.preventDefault(); event.preventDefault();

View File

@ -10,26 +10,22 @@
{{ $t("settings.profileSettings") }} {{ $t("settings.profileSettings") }}
</li></router-link </li></router-link
> >
<router-link to="/settings/shares" <router-link to="/settings/shares" v-if="user.perm.share"
><li :class="{ active: $route.path === '/settings/shares' }"> ><li :class="{ active: $route.path === '/settings/shares' }">
{{ $t("settings.shareManagement") }} {{ $t("settings.shareManagement") }}
</li></router-link </li></router-link
> >
<router-link to="/settings/global" <router-link to="/settings/global" v-if="user.perm.admin"
><li ><li :class="{ active: $route.path === '/settings/global' }">
:class="{ active: $route.path === '/settings/global' }"
v-if="user.perm.admin"
>
{{ $t("settings.globalSettings") }} {{ $t("settings.globalSettings") }}
</li></router-link </li></router-link
> >
<router-link to="/settings/users" <router-link to="/settings/users" v-if="user.perm.admin"
><li ><li
:class="{ :class="{
active: active:
$route.path === '/settings/users' || $route.name === 'User', $route.path === '/settings/users' || $route.name === 'User',
}" }"
v-if="user.perm.admin"
> >
{{ $t("settings.userManagement") }} {{ $t("settings.userManagement") }}
</li></router-link </li></router-link

View File

@ -6,6 +6,7 @@
<template #actions> <template #actions>
<action <action
v-if="user.perm.modify"
id="save-button" id="save-button"
icon="save" icon="save"
:label="$t('buttons.save')" :label="$t('buttons.save')"

View File

@ -25,18 +25,21 @@
/> />
<action <action
v-if="headerButtons.copy" v-if="headerButtons.copy"
id="copy-button"
icon="content_copy" icon="content_copy"
:label="$t('buttons.copyFile')" :label="$t('buttons.copyFile')"
show="copy" show="copy"
/> />
<action <action
v-if="headerButtons.move" v-if="headerButtons.move"
id="move-button"
icon="forward" icon="forward"
:label="$t('buttons.moveFile')" :label="$t('buttons.moveFile')"
show="move" show="move"
/> />
<action <action
v-if="headerButtons.delete" v-if="headerButtons.delete"
id="delete-button"
icon="delete" icon="delete"
:label="$t('buttons.delete')" :label="$t('buttons.delete')"
show="delete" show="delete"
@ -55,13 +58,16 @@
@action="switchView" @action="switchView"
/> />
<action <action
v-if="headerButtons.download"
icon="file_download" icon="file_download"
:label="$t('buttons.download')" :label="$t('buttons.download')"
@action="download" @action="download"
:counter="selectedCount" :counter="selectedCount"
/> />
<action <action
v-if="headerButtons.upload"
icon="file_upload" icon="file_upload"
id="upload-button"
:label="$t('buttons.upload')" :label="$t('buttons.upload')"
@action="upload" @action="upload"
/> />
@ -135,7 +141,7 @@
multiple multiple
/> />
</div> </div>
<div v-else id="listing" :class="user.viewMode"> <div v-else id="listing" ref="listing" :class="user.viewMode">
<div> <div>
<div class="item header"> <div class="item header">
<div></div> <div></div>
@ -253,6 +259,7 @@ import { users, files as api } from "@/api";
import { enableExec } from "@/utils/constants"; import { enableExec } from "@/utils/constants";
import * as upload from "@/utils/upload"; import * as upload from "@/utils/upload";
import css from "@/utils/css"; import css from "@/utils/css";
import throttle from "lodash.throttle";
import HeaderBar from "@/components/header/HeaderBar"; import HeaderBar from "@/components/header/HeaderBar";
import Action from "@/components/header/Action"; import Action from "@/components/header/Action";
@ -350,15 +357,31 @@ export default {
return this.width <= 736; return this.width <= 736;
}, },
}, },
watch: {
req: function () {
// Reset the show value
this.showLimit = 50;
// Fill and fit the window with listing items
this.fillWindow(true);
},
},
mounted: function () { mounted: function () {
// Check the columns size for the first time. // Check the columns size for the first time.
this.resizeEvent(); this.colunmsResize();
// How much every listing item affects the window height
this.setItemWeight();
// Fill and fit the window with listing items
this.fillWindow(true);
// Add the needed event listeners to the window and document. // Add the needed event listeners to the window and document.
window.addEventListener("keydown", this.keyEvent); window.addEventListener("keydown", this.keyEvent);
window.addEventListener("resize", this.resizeEvent);
window.addEventListener("scroll", this.scrollEvent); window.addEventListener("scroll", this.scrollEvent);
window.addEventListener("resize", this.windowsResize); window.addEventListener("resize", this.windowsResize);
if (!this.user.perm.create) return;
document.addEventListener("dragover", this.preventDefault); document.addEventListener("dragover", this.preventDefault);
document.addEventListener("dragenter", this.dragEnter); document.addEventListener("dragenter", this.dragEnter);
document.addEventListener("dragleave", this.dragLeave); document.addEventListener("dragleave", this.dragLeave);
@ -367,9 +390,10 @@ export default {
beforeDestroy() { beforeDestroy() {
// Remove event listeners before destroying this page. // Remove event listeners before destroying this page.
window.removeEventListener("keydown", this.keyEvent); window.removeEventListener("keydown", this.keyEvent);
window.removeEventListener("resize", this.resizeEvent);
window.removeEventListener("scroll", this.scrollEvent); window.removeEventListener("scroll", this.scrollEvent);
window.removeEventListener("resize", this.windowsResize); window.removeEventListener("resize", this.windowsResize);
if (!this.user.perm.create) return;
document.removeEventListener("dragover", this.preventDefault); document.removeEventListener("dragover", this.preventDefault);
document.removeEventListener("dragenter", this.dragEnter); document.removeEventListener("dragenter", this.dragEnter);
document.removeEventListener("dragleave", this.dragLeave); document.removeEventListener("dragleave", this.dragLeave);
@ -543,7 +567,7 @@ export default {
action(overwrite, rename); action(overwrite, rename);
}, },
resizeEvent() { colunmsResize() {
// Update the columns size based on the window width. // Update the columns size based on the window width.
let columns = Math.floor( let columns = Math.floor(
document.querySelector("main").offsetWidth / 300 document.querySelector("main").offsetWidth / 300
@ -552,11 +576,27 @@ export default {
if (columns === 0) columns = 1; if (columns === 0) columns = 1;
items.style.width = `calc(${100 / columns}% - 1em)`; items.style.width = `calc(${100 / columns}% - 1em)`;
}, },
scrollEvent() { scrollEvent: throttle(function () {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) { const totalItems = this.req.numDirs + this.req.numFiles;
this.showLimit += 50;
// All items are displayed
if (this.showLimit >= totalItems) return;
const currentPos = window.innerHeight + window.scrollY;
// Trigger at the 75% of the window height
const triggerPos = document.body.offsetHeight - window.innerHeight * 0.25;
if (currentPos > triggerPos) {
// Quantity of items needed to fill 2x of the window height
const showQuantity = Math.ceil(
(window.innerHeight * 2) / this.itemWeight
);
// Increase the number of displayed items
this.showLimit += showQuantity;
} }
}, }, 100),
dragEnter() { dragEnter() {
this.dragCounter++; this.dragCounter++;
@ -707,9 +747,19 @@ export default {
this.$store.commit("multiple", !this.multiple); this.$store.commit("multiple", !this.multiple);
this.$store.commit("closeHovers"); this.$store.commit("closeHovers");
}, },
windowsResize() { windowsResize: throttle(function () {
this.colunmsResize();
this.width = window.innerWidth; this.width = window.innerWidth;
},
// Listing element is not displayed
if (this.$refs.listing == null) return;
// How much every listing item affects the window height
this.setItemWeight();
// Fill but not fit the window
this.fillWindow();
}, 100),
download() { download() {
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) { if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
api.download(null, this.req.items[this.selected[0]].url); api.download(null, this.req.items[this.selected[0]].url);
@ -745,7 +795,12 @@ export default {
try { try {
await users.update(data, ["viewMode"]); await users.update(data, ["viewMode"]);
this.$store.commit("updateUser", data);
// Await ensures correct value for setItemWeight()
await this.$store.commit("updateUser", data);
this.setItemWeight();
this.fillWindow();
} catch (e) { } catch (e) {
this.$showError(e); this.$showError(e);
} }
@ -757,6 +812,32 @@ export default {
document.getElementById("upload-input").click(); document.getElementById("upload-input").click();
} }
}, },
setItemWeight() {
let itemQuantity = this.req.numDirs + this.req.numFiles;
if (itemQuantity > this.showLimit) itemQuantity = this.showLimit;
// How much every listing item affects the window height
this.itemWeight = this.$refs.listing.offsetHeight / itemQuantity;
},
fillWindow(fit = false) {
const totalItems = this.req.numDirs + this.req.numFiles;
// More items are displayed than the total
if (this.showLimit >= totalItems && !fit) return;
const windowHeight = window.innerHeight;
// Quantity of items needed to fill 2x of the window height
const showQuantity = Math.ceil(
(windowHeight + windowHeight * 2) / this.itemWeight
);
// Less items to display than current
if (this.showLimit > showQuantity && !fit) return;
// Set the number of displayed items
this.showLimit = showQuantity > totalItems ? totalItems : showQuantity;
},
}, },
}; };
</script> </script>

View File

@ -17,12 +17,14 @@
<template #actions> <template #actions>
<action <action
:disabled="loading" :disabled="loading"
v-if="user.perm.rename"
icon="mode_edit" icon="mode_edit"
:label="$t('buttons.rename')" :label="$t('buttons.rename')"
show="rename" show="rename"
/> />
<action <action
:disabled="loading" :disabled="loading"
v-if="user.perm.delete"
icon="delete" icon="delete"
:label="$t('buttons.delete')" :label="$t('buttons.delete')"
@action="deleteFile" @action="deleteFile"
@ -30,6 +32,7 @@
/> />
<action <action
:disabled="loading" :disabled="loading"
v-if="user.perm.download"
icon="file_download" icon="file_download"
:label="$t('buttons.download')" :label="$t('buttons.download')"
@action="download" @action="download"
@ -54,8 +57,22 @@
<template v-if="!loading"> <template v-if="!loading">
<div class="preview"> <div class="preview">
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage> <ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage>
<audio v-else-if="req.type == 'audio'" :src="raw" controls></audio> <audio
<video v-else-if="req.type == 'video'" :src="raw" controls> v-else-if="req.type == 'audio'"
ref="player"
:src="raw"
controls
:autoplay="autoPlay"
@play="autoPlay = true"
></audio>
<video
v-else-if="req.type == 'video'"
ref="player"
:src="raw"
controls
:autoplay="autoPlay"
@play="autoPlay = true"
>
<track <track
kind="captions" kind="captions"
v-for="(sub, index) in subtitles" v-for="(sub, index) in subtitles"
@ -136,6 +153,7 @@ export default {
showNav: true, showNav: true,
navTimeout: null, navTimeout: null,
hoverNav: false, hoverNav: false,
autoPlay: false,
}; };
}, },
computed: { computed: {
@ -230,6 +248,14 @@ export default {
} }
}, },
async updatePreview() { async updatePreview() {
if (
this.$refs.player &&
this.$refs.player.paused &&
!this.$refs.player.ended
) {
this.autoPlay = false;
}
if (this.req.subtitles) { if (this.req.subtitles) {
this.subtitles = this.req.subtitles.map( this.subtitles = this.req.subtitles.map(
(sub) => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true` (sub) => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`

View File

@ -181,14 +181,14 @@ func rawDirHandler(w http.ResponseWriter, r *http.Request, d *data, file *files.
commonDir := fileutils.CommonPrefix(filepath.Separator, filenames...) commonDir := fileutils.CommonPrefix(filepath.Separator, filenames...)
var name string name := filepath.Base(commonDir)
if len(filenames) > 1 { if name == "." || name == "" || name == string(filepath.Separator) {
name = "_" + filepath.Base(commonDir)
} else {
name = file.Name name = file.Name
} }
if name == "." || name == "" { // Prefix used to distinguish a filelist generated
name = "archive" // archive from the full directory archive
if len(filenames) > 1 {
name = "_" + name
} }
name += extension name += extension
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name)) w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))

View File

@ -118,6 +118,11 @@ func resourcePostHandler(fileCache FileCache) handleFunc {
return http.StatusConflict, nil return http.StatusConflict, nil
} }
// Permission for overwriting the file
if !d.user.Perm.Modify {
return http.StatusForbidden, nil
}
err = delThumbs(r.Context(), fileCache, file) err = delThumbs(r.Context(), fileCache, file)
if err != nil { if err != nil {
return errToStatus(err), err return errToStatus(err), err
@ -125,7 +130,10 @@ func resourcePostHandler(fileCache FileCache) handleFunc {
} }
err = d.RunHook(func() error { err = d.RunHook(func() error {
info, _ := writeFile(d.user.Fs, r.URL.Path, r.Body) info, writeErr := writeFile(d.user.Fs, r.URL.Path, r.Body)
if writeErr != nil {
return writeErr
}
etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size()) etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
w.Header().Set("ETag", etag) w.Header().Set("ETag", etag)
@ -155,7 +163,10 @@ var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
} }
err := d.RunHook(func() error { err := d.RunHook(func() error {
info, _ := writeFile(d.user.Fs, r.URL.Path, r.Body) info, writeErr := writeFile(d.user.Fs, r.URL.Path, r.Body)
if writeErr != nil {
return writeErr
}
etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size()) etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
w.Header().Set("ETag", etag) w.Header().Set("ETag", etag)
@ -198,6 +209,11 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
dst = addVersionSuffix(dst, d.user.Fs) dst = addVersionSuffix(dst, d.user.Fs)
} }
// Permission for overwriting the file
if override && !d.user.Perm.Modify {
return http.StatusForbidden, nil
}
err = d.RunHook(func() error { err = d.RunHook(func() error {
switch action { switch action {
// TODO: use enum // TODO: use enum