updated
This commit is contained in:
parent
3dd3e01f4c
commit
6f83300f92
|
@ -1,11 +1,10 @@
|
||||||
{
|
{
|
||||||
"server":{
|
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
"baseURL": "",
|
"baseURL": "",
|
||||||
"address": "",
|
"address": "",
|
||||||
"log": "stdout",
|
"log": "stdout",
|
||||||
"database": "./database.db",
|
"database": "./database.db",
|
||||||
"root": "/srv"
|
"root": "/srv"
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@ package search
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var typeRegexp = regexp.MustCompile(`type:(\S+)`)
|
var typeRegexp = regexp.MustCompile(`type:(\S+)`)
|
||||||
|
@ -26,22 +26,21 @@ var compressedFile = []string{
|
||||||
}
|
}
|
||||||
|
|
||||||
type searchOptions struct {
|
type searchOptions struct {
|
||||||
Conditions map[string]bool
|
Conditions map[string]bool
|
||||||
Size int
|
Size int
|
||||||
Terms []string
|
Terms []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseSearch(value string) *searchOptions {
|
func ParseSearch(value string) *searchOptions {
|
||||||
opts := &searchOptions{
|
opts := &searchOptions{
|
||||||
Conditions: map[string]bool{
|
Conditions: map[string]bool{
|
||||||
"exact": strings.Contains(value, "case:exact"),
|
"exact": strings.Contains(value, "case:exact"),
|
||||||
},
|
},
|
||||||
Terms: []string{},
|
Terms: []string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes the options from the value
|
// removes the options from the value
|
||||||
value = strings.Replace(value, "case:exact", "", -1)
|
value = strings.Replace(value, "case:exact", "", -1)
|
||||||
value = strings.Replace(value, "case:exact", "", -1)
|
|
||||||
value = strings.TrimSpace(value)
|
value = strings.TrimSpace(value)
|
||||||
|
|
||||||
types := typeRegexp.FindAllStringSubmatch(value, -1)
|
types := typeRegexp.FindAllStringSubmatch(value, -1)
|
||||||
|
@ -51,15 +50,21 @@ func ParseSearch(value string) *searchOptions {
|
||||||
}
|
}
|
||||||
filter := filterType[1]
|
filter := filterType[1]
|
||||||
switch filter {
|
switch filter {
|
||||||
case "image" : opts.Conditions["image"] = true
|
case "image":
|
||||||
case "audio", "music" : opts.Conditions["audio"] = true
|
opts.Conditions["image"] = true
|
||||||
case "video" : opts.Conditions["video"] = true
|
case "audio", "music":
|
||||||
case "doc" : opts.Conditions["doc"] = true
|
opts.Conditions["audio"] = true
|
||||||
case "archive" : opts.Conditions["archive"] = true
|
case "video":
|
||||||
case "folder" : opts.Conditions["dir"] = true
|
opts.Conditions["video"] = true
|
||||||
case "file" : opts.Conditions["dir"] = false
|
case "doc":
|
||||||
|
opts.Conditions["doc"] = true
|
||||||
|
case "archive":
|
||||||
|
opts.Conditions["archive"] = true
|
||||||
|
case "folder":
|
||||||
|
opts.Conditions["dir"] = true
|
||||||
|
case "file":
|
||||||
|
opts.Conditions["dir"] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(filter) < 8 {
|
if len(filter) < 8 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -74,8 +79,8 @@ func ParseSearch(value string) *searchOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(types) > 0 {
|
if len(types) > 0 {
|
||||||
// Remove the fields from the search value.
|
// Remove the fields from the search value, including added space
|
||||||
value = typeRegexp.ReplaceAllString(value, "")
|
value = typeRegexp.ReplaceAllString(value+" ", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
if value == "" {
|
if value == "" {
|
||||||
|
@ -91,9 +96,8 @@ func ParseSearch(value string) *searchOptions {
|
||||||
opts.Terms = []string{unique}
|
opts.Terms = []string{unique}
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
re := regexp.MustCompile(` +`)
|
value = strings.TrimSpace(value)
|
||||||
value = re.ReplaceAllString(value, " ")
|
opts.Terms = strings.Split(value, "|")
|
||||||
opts.Terms = strings.Split(value, " ")
|
|
||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,4 +116,4 @@ func updateSize(given string) int {
|
||||||
} else {
|
} else {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,11 @@
|
||||||
<p v-show="isEmpty && isRunning" id="renew">
|
<p v-show="isEmpty && isRunning" id="renew">
|
||||||
<i class="material-icons spin">autorenew</i>
|
<i class="material-icons spin">autorenew</i>
|
||||||
</p>
|
</p>
|
||||||
<p v-show="isEmpty && !isRunning">{{ text }}</p>
|
<div v-show="isEmpty && !isRunning">
|
||||||
|
<div class="searchPrompt" v-show="isEmpty && !isRunning">
|
||||||
|
<p>No results found in indexed search.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template v-if="isEmpty">
|
<template v-if="isEmpty">
|
||||||
<template v-if="value.length === 0">
|
<template v-if="value.length === 0">
|
||||||
<div class="boxes">
|
<div class="boxes">
|
||||||
|
@ -50,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isMobile && active" id="result-desktop" ref="result">
|
<div v-if="!isMobile && active" id="result-desktop" ref="result">
|
||||||
<div id="result-list">
|
<div id="result-list">
|
||||||
<div class="button" style="width: 100%">
|
<div class="button fluid">
|
||||||
Search Context: {{ getContext(this.$route.path) }}
|
Search Context: {{ getContext(this.$route.path) }}
|
||||||
</div>
|
</div>
|
||||||
<ul v-show="results.length > 0">
|
<ul v-show="results.length > 0">
|
||||||
|
@ -72,9 +76,18 @@
|
||||||
<p v-show="isEmpty && isRunning" id="renew">
|
<p v-show="isEmpty && isRunning" id="renew">
|
||||||
<i class="material-icons spin">autorenew</i>
|
<i class="material-icons spin">autorenew</i>
|
||||||
</p>
|
</p>
|
||||||
<p v-show="isEmpty && !isRunning">{{ text }}</p>
|
<div class="searchPrompt" v-show="isEmpty && !isRunning">
|
||||||
|
<p>No results found in indexed search.</p>
|
||||||
|
<div class="helpButton" @click="toggleHelp()">Toggle Search Help</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="helpText" v-if="showHelp">
|
||||||
|
Search additional terms separated by <code>|</code>, for example <code>"test|not"</code> searches for both terms independently
|
||||||
|
<p>Note: searching files by size may have significantly longer search times since it cannot rely on the index alone.
|
||||||
|
The search looks for only files that match all other conditions first, then checks the filesize and returns matching results.</p>
|
||||||
|
</div>
|
||||||
<template>
|
<template>
|
||||||
<div v-show="results.length == 0" class="boxes">
|
<div class="boxes">
|
||||||
<ButtonGroup :buttons="folderSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
|
<ButtonGroup :buttons="folderSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
|
||||||
<ButtonGroup :buttons="typeSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
|
<ButtonGroup :buttons="typeSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
|
||||||
<ButtonGroup :buttons="sizeSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
|
<ButtonGroup :buttons="sizeSelect" @button-clicked="init" @remove-button-clicked="removeInit" />
|
||||||
|
@ -86,6 +99,26 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.helpText{
|
||||||
|
padding:1em
|
||||||
|
}
|
||||||
|
.helpButton {
|
||||||
|
text-align: center;
|
||||||
|
background: var(--background);
|
||||||
|
background-color: lightgray;
|
||||||
|
padding: .25em;
|
||||||
|
border-radius: .25em;
|
||||||
|
}
|
||||||
|
.searchPrompt {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ButtonGroup from "./ButtonGroup.vue";
|
import ButtonGroup from "./ButtonGroup.vue";
|
||||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||||
|
@ -110,6 +143,7 @@ export default {
|
||||||
name: "search",
|
name: "search",
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
|
showHelp: false,
|
||||||
folderSelect: [
|
folderSelect: [
|
||||||
{ label: "Only Folders", value: "type:folder" },
|
{ label: "Only Folders", value: "type:folder" },
|
||||||
{ label: "Only Files", value: "type:file" },
|
{ label: "Only Files", value: "type:file" },
|
||||||
|
@ -182,7 +216,10 @@ export default {
|
||||||
},
|
},
|
||||||
isRunning() {
|
isRunning() {
|
||||||
return this.ongoing;
|
return this.ongoing;
|
||||||
}
|
},
|
||||||
|
searchHelp() {
|
||||||
|
return this.showHelp
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
window.addEventListener("resize", this.handleResize);
|
window.addEventListener("resize", this.handleResize);
|
||||||
|
@ -271,6 +308,9 @@ export default {
|
||||||
}
|
}
|
||||||
this.ongoing = false;
|
this.ongoing = false;
|
||||||
},
|
},
|
||||||
|
toggleHelp(){
|
||||||
|
this.showHelp = !this.showHelp
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
|
@ -16,6 +16,10 @@
|
||||||
<i class="material-icons">note_add</i>
|
<i class="material-icons">note_add</i>
|
||||||
<span>{{ $t("sidebar.newFile") }}</span>
|
<span>{{ $t("sidebar.newFile") }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button id="upload-button" @click="upload($event)" class="action" :aria-label="$t('sidebar.upload')" >
|
||||||
|
<i class="material-icons">file_upload</i>
|
||||||
|
<span>Upload file</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="action" @click="toSettings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
|
<button class="action" @click="toSettings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
|
||||||
|
@ -62,6 +66,7 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters } from "vuex";
|
import { mapState, mapGetters } from "vuex";
|
||||||
|
import * as upload from "@/utils/upload";
|
||||||
import * as auth from "@/utils/auth";
|
import * as auth from "@/utils/auth";
|
||||||
import {
|
import {
|
||||||
version,
|
version,
|
||||||
|
@ -132,6 +137,50 @@ export default {
|
||||||
help() {
|
help() {
|
||||||
this.$store.commit("showHover", "help");
|
this.$store.commit("showHover", "help");
|
||||||
},
|
},
|
||||||
|
upload: function () {
|
||||||
|
if (
|
||||||
|
typeof window.DataTransferItem !== "undefined" &&
|
||||||
|
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
|
||||||
|
) {
|
||||||
|
this.$store.commit("showHover", "upload");
|
||||||
|
} else {
|
||||||
|
document.getElementById("upload-input").click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
uploadInput(event) {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
|
||||||
|
let files = event.currentTarget.files;
|
||||||
|
let folder_upload =
|
||||||
|
files[0].webkitRelativePath !== undefined && files[0].webkitRelativePath !== "";
|
||||||
|
|
||||||
|
if (folder_upload) {
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
let file = files[i];
|
||||||
|
files[i].fullPath = file.webkitRelativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = this.$route.path.endsWith("/")
|
||||||
|
? this.$route.path
|
||||||
|
: this.$route.path + "/";
|
||||||
|
let conflict = upload.checkConflict(files, this.req.items);
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
this.$store.commit("showHover", {
|
||||||
|
prompt: "replace",
|
||||||
|
confirm: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
upload.handleFiles(files, path, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload.handleFiles(files, path);
|
||||||
|
},
|
||||||
logout: auth.logout,
|
logout: auth.logout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,13 @@
|
||||||
:label="$t('buttons.selectMultiple')"
|
:label="$t('buttons.selectMultiple')"
|
||||||
@action="toggleMultipleSelection"
|
@action="toggleMultipleSelection"
|
||||||
/>
|
/>
|
||||||
|
<action
|
||||||
|
v-if="headerButtons.download"
|
||||||
|
icon="file_download"
|
||||||
|
:label="$t('buttons.download')"
|
||||||
|
@action="download"
|
||||||
|
:counter="selectedCount"
|
||||||
|
/>
|
||||||
<action
|
<action
|
||||||
v-if="headerButtons.share"
|
v-if="headerButtons.share"
|
||||||
icon="share"
|
icon="share"
|
||||||
|
@ -303,8 +310,8 @@ export default {
|
||||||
headerButtons() {
|
headerButtons() {
|
||||||
return {
|
return {
|
||||||
select: this.selectedCount > 0,
|
select: this.selectedCount > 0,
|
||||||
upload: this.user.perm.create,
|
upload: this.user.perm.create && this.selectedCount > 0,
|
||||||
download: this.user.perm.download,
|
download: this.user.perm.download && this.selectedCount > 0,
|
||||||
shell: this.user.perm.execute && enableExec,
|
shell: this.user.perm.execute && enableExec,
|
||||||
delete: this.selectedCount > 0 && this.user.perm.delete,
|
delete: this.selectedCount > 0 && this.user.perm.delete,
|
||||||
rename: this.selectedCount === 1 && this.user.perm.rename,
|
rename: this.selectedCount === 1 && this.user.perm.rename,
|
||||||
|
|
Loading…
Reference in New Issue