Browse Source

Updates v1.121

- Replaced most Tocas-UI based page to Semantic for future migration to Formantic
- Fixed filename with hash cannot upload bug (#91)
- Fixed issue for unauthorized file access (#92)
- Optimized read only directory mounting (#90)
- Optimized low memory upload mode quota cutoff response time
- Added advance file sharing options (to selected users or groups)
Toby Chui 3 years ago
parent
commit
7911ed8b9e

+ 16 - 1
src/file_system.go

@@ -479,7 +479,6 @@ func system_fs_handleLowMemoryUpload(w http.ResponseWriter, r *http.Request) {
 			msg := strings.TrimSpace(string(message))
 			if msg == "done" {
 				//Start the merging process
-				log.Println(userinfo.Username + " uploaded a file: " + filepath.Base(targetUploadLocation))
 				break
 			} else {
 				//Unknown operations
@@ -508,6 +507,18 @@ func system_fs_handleLowMemoryUpload(w http.ResponseWriter, r *http.Request) {
 				//Clear the tmp files
 				os.RemoveAll(uploadFolder)
 				return
+			} else if !userinfo.StorageQuota.HaveSpace(totalFileSize) {
+				//Quota exceeded
+				c.WriteMessage(1, []byte(`{\"error\":\"User Storage Quota Exceeded\"}`))
+
+				//Close the connection
+				c.WriteControl(8, []byte{}, time.Now().Add(time.Second))
+				time.Sleep(1 * time.Second)
+				c.Close()
+
+				//Clear the tmp files
+				os.RemoveAll(uploadFolder)
+
 			}
 			blockCounter++
 
@@ -554,9 +565,13 @@ func system_fs_handleLowMemoryUpload(w http.ResponseWriter, r *http.Request) {
 
 	if !userinfo.StorageQuota.HaveSpace(fi.Size()) {
 		c.WriteMessage(1, []byte(`{\"error\":\"User Storage Quota Exceeded\"}`))
+		os.RemoveAll(targetUploadLocation)
 		return
 	}
 
+	//Log the upload filename
+	log.Println(userinfo.Username + " uploaded a file: " + filepath.Base(targetUploadLocation))
+
 	//Set owner of the new uploaded file
 	userinfo.SetOwnerOfFile(targetUploadLocation)
 

+ 2 - 6
src/go.mod

@@ -13,9 +13,7 @@ require (
 	github.com/frankban/quicktest v1.10.0 // indirect
 	github.com/gabriel-vasile/mimetype v1.1.0
 	github.com/go-git/go-git/v5 v5.2.0
-	github.com/go-ldap/ldap v3.0.3+incompatible // indirect
-	github.com/go-ldap/ldap/v3 v3.4.2 // indirect
-	github.com/google/uuid v1.3.0 // indirect
+	github.com/go-ldap/ldap v3.0.3+incompatible
 	github.com/gopherjs/gopherjs v0.0.0-20220221023154-0b2280d3ff96 // indirect
 	github.com/gorilla/sessions v1.2.0
 	github.com/gorilla/websocket v1.4.2
@@ -28,12 +26,11 @@ require (
 	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
 	github.com/nwaples/rardecode v1.1.0 // indirect
 	github.com/oliamb/cutter v0.2.2
-	github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb // indirect
+	github.com/oov/psd v0.0.0-20220121172623-5db5eafcecbb
 	github.com/pierrec/lz4 v2.5.2+incompatible // indirect
 	github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
 	github.com/satori/go.uuid v1.2.0
 	github.com/spf13/afero v1.6.0
-	github.com/stretchr/testify v1.7.0 // indirect
 	github.com/tidwall/pretty v1.0.2
 	github.com/ulikunitz/xz v0.5.10 // indirect
 	github.com/valyala/fasttemplate v1.1.0
@@ -46,5 +43,4 @@ require (
 	golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 // indirect
 	gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
 	gopkg.in/sourcemap.v1 v1.0.5 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
 )

+ 0 - 16
src/go.sum

@@ -12,7 +12,6 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
 cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
 cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
 cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
@@ -39,8 +38,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
 cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
-github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
@@ -148,8 +145,6 @@ github.com/gabriel-vasile/mimetype v1.1.0/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pm
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
-github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
 github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
 github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
@@ -167,8 +162,6 @@ github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
 github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
 github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
 github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
-github.com/go-ldap/ldap/v3 v3.4.2 h1:zFZKcXKLqZpFMrMQGHeHWKXbDTdNCmhGY9AK41zPh+8=
-github.com/go-ldap/ldap/v3 v3.4.2/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
@@ -208,7 +201,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -226,7 +218,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
 github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -252,8 +243,6 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -470,7 +459,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
-github.com/spf13/afero v1.3.1 h1:GPTpEAuNr98px18yNQ66JllNil98wfRZ/5Ukny8FeQA=
 github.com/spf13/afero v1.3.1/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
 github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
@@ -554,7 +542,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
@@ -831,7 +818,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
 google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -911,7 +897,6 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
@@ -945,7 +930,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 1 - 1
src/main.flags.go

@@ -28,7 +28,7 @@ var subserviceBasePort = 12810            //Next subservice port
 
 // =========== SYSTEM BUILD INFORMATION ==============
 var build_version = "development"                      //System build flag, this can be either {development / production / stable}
-var internal_version = "0.1.120"                       //Internal build version, [fork_id].[major_release_no].[minor_release_no]
+var internal_version = "0.1.121"                       //Internal build version, [fork_id].[major_release_no].[minor_release_no]
 var deviceUUID string                                  //The device uuid of this host
 var deviceVendor = "IMUSLAB.INC"                       //Vendor of the system
 var deviceVendorURL = "http://imuslab.com"             //Vendor contact information

+ 24 - 2
src/mod/filesystem/filesystem.go

@@ -120,10 +120,18 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 			}
 
 		}
+
 		//Create the fsdb for this handler
-		fsdb, err := db.NewDatabase(filepath.ToSlash(filepath.Join(filepath.Clean(option.Path), "aofs.db")), false)
+		var fsdb *db.Database = nil
+
+		dbp, err := db.NewDatabase(filepath.ToSlash(filepath.Join(filepath.Clean(option.Path), "aofs.db")), false)
 		if err != nil {
-			return &FileSystemHandler{}, errors.New("Unable to create fsdb inside the target path. Is the directory read only?")
+			if option.Access != "readonly" {
+				log.Println("[Filesystem] Invalid config: Trying to mount a read only path as read-write mount point. Changing " + option.Name + " mount point to READONLY.")
+				option.Access = "readonly"
+			}
+		} else {
+			fsdb = dbp
 		}
 
 		return &FileSystemHandler{
@@ -164,6 +172,10 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 
 //Create a file ownership record
 func (fsh *FileSystemHandler) CreateFileRecord(realpath string, owner string) error {
+	if fsh.FilesystemDatabase == nil {
+		//Not supported file system type
+		return errors.New("Not supported filesystem type")
+	}
 	rpabs, _ := filepath.Abs(realpath)
 	fsrabs, _ := filepath.Abs(fsh.Path)
 	reldir, err := filepath.Rel(fsrabs, rpabs)
@@ -178,6 +190,11 @@ func (fsh *FileSystemHandler) CreateFileRecord(realpath string, owner string) er
 
 //Read the owner of a file
 func (fsh *FileSystemHandler) GetFileRecord(realpath string) (string, error) {
+	if fsh.FilesystemDatabase == nil {
+		//Not supported file system type
+		return "", errors.New("Not supported filesystem type")
+	}
+
 	rpabs, _ := filepath.Abs(realpath)
 	fsrabs, _ := filepath.Abs(fsh.Path)
 	reldir, err := filepath.Rel(fsrabs, rpabs)
@@ -196,6 +213,11 @@ func (fsh *FileSystemHandler) GetFileRecord(realpath string) (string, error) {
 
 //Delete a file ownership record
 func (fsh *FileSystemHandler) DeleteFileRecord(realpath string) error {
+	if fsh.FilesystemDatabase == nil {
+		//Not supported file system type
+		return errors.New("Not supported filesystem type")
+	}
+
 	rpabs, _ := filepath.Abs(realpath)
 	fsrabs, _ := filepath.Abs(fsh.Path)
 	reldir, err := filepath.Rel(fsrabs, rpabs)

+ 1 - 2
src/mod/share/share.go

@@ -222,8 +222,7 @@ func (s *Manager) HandleShareAccess(w http.ResponseWriter, r *http.Request) {
 			}
 
 			//Check if username in the allowed user list
-			if !inArray(shareOption.Accessibles, thisuserinfo.Username) {
-				//Serve permission denied page
+			if !inArray(shareOption.Accessibles, thisuserinfo.Username) && shareOption.Owner != thisuserinfo.Username {
 				//Serve permission denied page
 				if directDownload || directServe {
 					w.WriteHeader(http.StatusForbidden)

+ 3 - 0
src/mod/share/shareEntry/shareOptions.go

@@ -18,6 +18,9 @@ func (s *ShareOption) IsAccessibleBy(username string, usergroup []string) bool {
 		if stringInSlice(username, s.Accessibles) {
 			//User's name is in the allowed group
 			return true
+		} else if s.Owner == username {
+			//This user own this file
+			return true
 		}
 	}
 	return false

+ 2 - 2
src/mod/user/directoryHandler.go

@@ -102,9 +102,9 @@ func (u *User) VirtualPathToRealPath(vpath string) (string, error) {
 
 			//Handle general cases
 			if storage.Hierarchy == "user" {
-				return filepath.ToSlash(filepath.Clean(storage.Path) + "/users/" + u.Username + subpath), nil
+				return filepath.ToSlash(filepath.Join(filepath.Clean(storage.Path), "/users/", u.Username, subpath)), nil
 			} else if storage.Hierarchy == "public" {
-				return filepath.ToSlash(filepath.Clean(storage.Path) + subpath), nil
+				return filepath.ToSlash(filepath.Join(filepath.Clean(storage.Path), subpath)), nil
 			} else if storage.Hierarchy == "share" {
 				return (*u.parent.shareEntryTable).ResolveShareVrootPath(subpath, u.Username, u.GetUserPermissionGroupNames())
 			} else {

+ 1 - 1
src/module.util.go

@@ -48,7 +48,7 @@ func util_init() {
 		SupportFW:    false,
 		SupportEmb:   true,
 		LaunchEmb:    "SystemAO/utilities/audio.html",
-		InitEmbSize:  []int{600, 175},
+		InitEmbSize:  []int{533, 144},
 		SupportedExt: []string{".mp3", ".wav", ".ogg", ".flac"},
 	})
 

+ 1 - 1
src/system/storage.json.example

@@ -45,4 +45,4 @@
     "hierarchy":"public",
     "automount":false
   }
-]
+]

+ 25 - 21
src/web/Manga Cafe/viewComic.html

@@ -5,8 +5,8 @@
 <head>
 	<meta charset="UTF-8">
 	<script src="../script/jquery.min.js"></script>
-	<link rel="stylesheet" href="../script/tocas/tocas.css">
-	<script type='text/javascript' src="../script/tocas/tocas.js"></script>
+	<link rel="stylesheet" href="../script/semantic/semantic.min.css">
+	<script type='text/javascript' src="../script/semantic/semantic.min.js"></script>
 	<script type='text/javascript' src="../script/ao_module.js"></script>
 	<style>
 	body, html {
@@ -14,6 +14,10 @@
 		margin: 0;
 		background-color:#2b2b2b;
 	}
+
+	#settingMenu .ui.toggle.checkbox input:checked ~ .box,#settingMenu  .ui.toggle.checkbox input:checked ~ label{
+		color: rgb(189, 189, 189) !important;
+	}
 	</style>
 	<script>
 		//Scrolling Controller
@@ -55,16 +59,16 @@
 </head>
 <body>
 <div id="loading" style="position: fixed;top: 0;left: 0;background:rgba(0,0,0,0.5);color:white;width:100%;height:100%;z-index:100">
-	<div class="ts active dimmer">
-        <div class="ts text loader">Loading</div>
+	<div class="ui active dimmer">
+        <div class="ui text loader">Loading</div>
     </div>
 </div>
-<div class="ts attached inverted message" onClick="ToggleChapters();">
+<div class="ui attached inverted message" style="margin-top: 0em;" onClick="ToggleChapters();">
     <div class="header"><i class="bookmark icon"></i>You are already reached the top of this manga.</div>
     <p>Click here to select more chapters.</p>
 </div>
 
-<div id="MangaList" class="ts list">
+<div id="MangaList" class="ui list">
 	<div class="item" style="width: 100%;display: flex;align-items: flex-start;"><img src="" style="width: 100%;height: auto;"></img></div>
 </div>
 
@@ -89,19 +93,19 @@
     display:block;
     z-index:1501;
 	display:none;" onClick="">
-	<div id="settingMenu" class="ts very narrow container" style="top:15%;z-index:1502;">
-		<div class="ts container" style="color:white;">
-		<div class="ts inverted segment" style="background:rgba(61,61,61,0.8);">
+	<div id="settingMenu" class="ui very narrow container" style="top:15%;z-index:1502;">
+		<div class="ui container" style="color:white;">
+		<div class="ui inverted segment" style="background:rgba(61,61,61,0.8);">
 			<p style="font-size:120%;"><i class='setting icon'></i>Settings</p>
-			<div class="ts toggle checkbox">
+			<div class="ui toggle checkbox">
 				<input type="checkbox" id="lefthand">
 				<label for="lefthand" style="color:white;"><i class="hand paper icon" style="transform: scaleX(-1);-moz-transform: scaleX(-1);-webkit-transform: scaleX(-1);-ms-transform: scaleX(-1);"></i>Left hand mode <br> Click the left section of the view for downward auto scrolling instead of right section.</label>
 			</div><br><br>
-			<div class="ts toggle checkbox">
+			<div class="ui toggle checkbox">
 				<input type="checkbox" id="progress">
 				<label for="progress" style="color:white;"><i class="save icon"></i>Save progress <br> Save the current progress and return to that page automatically in the future.</label>
 			</div><br><br>
-			<div class="ts toggle checkbox">
+			<div class="ui toggle checkbox">
 				<input type="checkbox" id="pcm">
 				<label for="pcm" style="color:white;"><i class="save icon"></i>PC Mode <br> Scale down the manga a bit to make you feel better on a big PC screen.</label>
 			</div>
@@ -112,27 +116,27 @@
 
 <!-- Page Info DIV-->
 <div align="right" style="position: fixed;bottom: 0;right: 0;background:rgba(0,0,0,0.5);color:white;z-index:1000;">
-<div id="PageInfo" class="ts fluid text container">
+<div id="PageInfo" class="ui fluid text container">
 Loading Page Data...
 </div>
 </div>
 <!-- Next Chapter Message-->
-<div class="ts inverted message" onclick="NextChapter();" style="cursor: pointer;">
+<div class="ui inverted message" onclick="NextChapter();" style="cursor: pointer;">
     <div class="header"><i class="bookmark icon"></i>You have finished this chapter.</div>
     <p id="reminder">Click this box to proceed to next chapter.</p>
 </div>
 
 <!-- Start of Chapter Selector-->
-<div id="ChapterSelector" class="ts inverted message" style="color:white;display:none;">
-<div class="ts grid" id="chapterlist">
+<div id="ChapterSelector" class="ui inverted message" style="color:white;display:none;">
+<div class="ui stackable grid" id="chapterlist">
     
 </div>
 </div>
 <!-- END of Chapter Selector-->
 <div align="center" style="width:100%">
-<div class="ts buttons" >
-    <button class="ts basic button" onClick="window.location.href='index.html'" style="color:white;"><i class="chevron left icon"></i>Back</button>
-    <button class="ts basic button" onClick="ToggleChapters();" style="color:white;"><i class="book icon"></i>Chapters</button>
+<div class="ui buttons" >
+    <button class="ui inverted basic button" onClick="window.location.href='index.html'" style="color:white !important;"><i class="chevron left icon"></i>Back</button>
+    <button class="ui inverted basic button" onClick="ToggleChapters();" style="color:white !important;"><i class="book icon"></i>Chapters</button>
 </div>
 </div>
 
@@ -202,7 +206,7 @@ Loading Page Data...
 				chapterWide = "sixteen";
 			}
 			if (chapterName == data.title[1]){
-				$("#chapterlist").append(`<div class="${chapterWide} wide column" style="color:white;"><a class="ts fluid basic disabled button" style="color:white;">${chapterName}</a></div>`);
+				$("#chapterlist").append(`<div class="${chapterWide} wide column compact" style="color:white; padding: 0.3em !important;"><a class="ui fluid inverted basic disabled button" style="color:white;">${chapterName}</a></div>`);
 				nextChapterID = counter;
 				nextOneIsPreRender = true;
 			}else{
@@ -210,7 +214,7 @@ Loading Page Data...
 					nextOneIsPreRender = false;
 					prerender = chapter;
 				}
-				$("#chapterlist").append(`<div class="${chapterWide} wide column" style="color:white;"><a class="ts fluid basic button" style="color:white;" onClick="ChapterRedirect('${chapter}');">${chapterName}</a></div>`);
+				$("#chapterlist").append(`<div class="${chapterWide} wide column compact" style="color:white; padding: 0.3em !important;"><a class="ui fluid inverted basic button" style="color:white;" onClick="ChapterRedirect('${chapter}');">${chapterName}</a></div>`);
 			}
 			counter++;
 		});

+ 0 - 0
src/web/PDF Viewer/index.html


+ 1 - 1
src/web/PDF Viewer/viewer.js

@@ -251,7 +251,7 @@ if (inputFiles == null){
   //Leave the openURL to be nofile.pdf
 }else{
   //Open the file using media server
-  openURL = (ao_module_getAORootFromScriptPath() + "media/?file=" + inputFiles[0].filepath);
+  openURL = (ao_module_getAORootFromScriptPath() + "media/?file=" + encodeURIComponent(inputFiles[0].filepath));
 }
 
 var defaultOptions = {

+ 1 - 1
src/web/SystemAO/boot/bootflags.html

@@ -84,7 +84,7 @@
                     <i class="checkmark icon"></i> Startup paramter Updated
                 </div>
                 <ul class="list">
-                    <p>Some functions might require a reboot to take effects.</p>
+                    <p>These settings will only be available in current runtime session. Restart your server to restore original settings.</p>
                 </ul>
             </div>
         </div>

+ 6 - 6
src/web/SystemAO/file_system/defaultOpener.html

@@ -3,9 +3,9 @@
         <title>Select Default Opening WebApp</title>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
-        <link rel="stylesheet" href="../../script/tocas/tocas.css">
+        <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
         <link rel="stylesheet" href="../../script/ao.css">
-        <script type="text/javascript" src="../../script/tocas/tocas.js"></script>
+        <script type="text/javascript" src="../../script/semantic/semantic.min.js"></script>
         <script type="text/javascript" src="../../script/jquery.min.js"></script>
         <script type="text/javascript" src="../../script/ao_module.js"></script>
         <script type="text/javascript" src="../../script/applocale.js"></script>
@@ -59,16 +59,16 @@
         </style>
     </head>
     <body>
-        <div class="ts container" style="padding-top:12px;">
+        <div class="ui container" style="padding-top:12px;">
             <p><span locale="message/select">Select a default WebApp to open</span> <span id="fext"></span> <span locale="message/files">files.</span></p>
             <div id="moduleList" class="moduleSelector">
                 
             </div>
-            <button id="showallbtn" class="ts fluid tiny button" onclick="showAllModules();" locale="button/showAll">Show All</button>
+            <button id="showallbtn" class="ui fluid tiny button" onclick="showAllModules();" locale="button/showAll">Show All</button>
         </div>
         <div class="bottomControls" align="right">
-            <button class="ts small button" onclick="cancel();" locale="button/cancel">Cancel</button>
-            <button id="confirmBtn" class="ts small primary disabled button" onclick="confirmSelection()" locale="button/confirm">Confirm</button>
+            <button class="ui small button" onclick="cancel();" locale="button/cancel">Cancel</button>
+            <button id="confirmBtn" class="ui small primary disabled button" onclick="confirmSelection()" locale="button/confirm">Confirm</button>
         </div>
         <script>
             var targetFiles = "";

+ 67 - 22
src/web/SystemAO/file_system/file_explorer.html

@@ -922,10 +922,17 @@
         <div id="shareFile" class="popup wide whiteTheme" style="display:none;">
             <div class="popupheader whiteTheme">
                 <i class="external icon"></i> <span locale="opr/share/title">Share File</span>
-                <div class="popupcloser" onclick="hideAllPopupWindows();">
+                <div class="popupcloser" onclick="hideShare();">
                     <i class="remove icon"></i>
                 </div>
             </div>
+            <div>
+                <iframe id="shareFileEmbedded" style="border: 0px solid transparent; width: 100%; height: 70vh;" allowtransparency="true">
+
+                </iframe>
+            </div>
+            
+            <!--
             <div class="popupcontent" style="padding:12px;">
                 <div class="ts stackable grid">
                     <div class="eight wide column">
@@ -985,15 +992,19 @@
                         </div>
                     </div>
                 </div>
-                
-                <div class="ts divider"></div>
-                <div id="sharingRemoveBtn" class="popupbuttons negative whiteTheme allowHover" onclick="removeSharing()">
-                    <i class="remove icon"></i> <span locale="opr/share/remove">Remove Sharing</span>
-                </div> 
-                <div class="popupbuttons whiteTheme allowHover" onclick="hideShare()">
-                    <i class="checkmark icon"></i> <span locale="opr/share/ok">OK</span>
-                </div> 
+                  -->
+                <div style="padding:12px;">
+                    <div class="ts divider"></div>
+                    <div id="sharingRemoveBtn" class="popupbuttons negative whiteTheme allowHover" onclick="removeSharing()">
+                        <i class="remove icon"></i> <span locale="opr/share/remove">Remove Sharing</span>
+                    </div> 
+                    <div class="popupbuttons whiteTheme allowHover" onclick="hideShare();">
+                        <i class="checkmark icon"></i> <span locale="opr/share/ok">OK</span>
+                    </div> 
+                </div>
             </div>
+           
+            
         </div>
 
         <!-- Open With Dialog-->
@@ -1897,7 +1908,7 @@
                     }
                 }
 
-                thumbRenderWebSocket = new WebSocket(protocol + window.location.hostname + ":" + port + "/system/file_system/handleCacheRender?folder=" + path);
+                thumbRenderWebSocket = new WebSocket(protocol + window.location.hostname + ":" + port + "/system/file_system/handleCacheRender?folder=" + encodeURIComponent(path));
 
                 thumbRenderWebSocket.onopen = function(e) {
 
@@ -1934,16 +1945,20 @@
             }
 
             function getThumbnailExtensionFromBase64String(base64String){
-                let tid = base64String.charAt(0);
                 let ext = "jpg";
-                if (tid == "i"){
-                    ext = "png";
-                }else if (tid == "R"){
-                    ext = "gif";
-                }else if (tid == "U"){
-                    ext = "webp";
+                if (typeof base64String === 'string' || base64String instanceof String){
+                    let tid = base64String.charAt(0);
+                    if (tid == "i"){
+                        ext = "png";
+                    }else if (tid == "R"){
+                        ext = "gif";
+                    }else if (tid == "U"){
+                        ext = "webp";
+                    }
+                }else{
+                    //Not string
                 }
-
+                
                 return ext;
             }
 
@@ -3697,6 +3712,9 @@
                 if (isMobile){
                     refreshList();
                 }
+
+                //Resize the share iframe
+                resizeShareIframe()
             });
 
             function handleShareFilebuttonClick(event, object){
@@ -3712,12 +3730,19 @@
                 shareFile();
             }
 
+            function resizeShareIframe(){
+                $("#shareFileEmbedded").css("height", $("#shareFile").height() - 126 + "px");
+            }
             
             function shareFile(){
                 var selectedFiles = [];
+                var selectedFileObjects = [];
                 $(".fileObject.selected").each(function(){
                     var thisFilepath = $(this).attr("filepath");
+                    var thisFilename = $(this).attr("filename");
                     selectedFiles.push(thisFilepath);
+                    selectedFileObjects.push({"filepath": thisFilepath, "filename": thisFilename});
+
                 });
 
                 if (selectedFiles.length != 1){
@@ -3728,6 +3753,7 @@
 
                 //OK! Continue to generate link
                 var selectedFile = selectedFiles[0];
+                var selectedFileObject = selectedFileObjects[0];
                 shareEditingObject = selectedFile;
                 $.ajax({
                     url: "../../system/file_system/share/new",
@@ -3737,6 +3763,8 @@
                             msgbox("remove",applocale.getString("message/" + data.error,data.error), 5000);
                         }else{
                             //Build the predicted share endpoint
+                            
+                            /*
                             let protocol = "https://";
                             if (location.protocol !== 'https:') {
                                 protocol = "http://";
@@ -3765,6 +3793,15 @@
                             }else if (tagetCheckbox == "samegroup"){
                                 $("#samegroup")[0].checked = true;
                             }
+                            */ 
+
+                            selectedFileObject["QRCode"] = true;
+                            selectedFileObject["ActionButtons"] = false;
+                            var payload = encodeURIComponent(JSON.stringify([selectedFileObject]));
+                            var requestURL = "file_share.html#" + payload;
+                            console.log(selectedFileObject, requestURL);
+                            $("#shareFileEmbedded").attr("src", requestURL);
+                            resizeShareIframe();
 
                             //Show the share file interface
                             $(".popup").fadeOut('fast');
@@ -3784,9 +3821,10 @@
                 if (shareEditingObject == ""){
                     return
                 }
+
                 //The target file to remove
                 var selectedFile = shareEditingObject;
-
+                $("#shareFileEmbedded").attr("src", "");
                 $.ajax({
                     url: "../../system/file_system/share/delete",
                     data: {path: selectedFile},
@@ -3796,7 +3834,6 @@
                         $(".shareoption").parent().addClass("disabled");
                         $("#sharelink").text("(Sharing Removed)");
                         $("#sharelink").removeAttr("href");
-                        $("#sharingRemoveBtn").hide();
                         //Reload the current filelist and hide the share interface
                         listDirectory(currentPath);
 
@@ -3806,6 +3843,9 @@
                     }
                 });
 
+                $("#shareFile").fadeOut(100);
+                $(".popupWrapper").fadeOut(100);
+                msgbox("checkmark", applocale.getString("message/share/removed", "File share removed"))
             }
 
             function updateShareSettings(object){
@@ -3831,6 +3871,8 @@
 
             function hideShare(){
                 hideAllPopupWindows();
+                $("#shareFileEmbedded").attr("src", "");
+               
             }
 
             function toggleSidebar(useAnimation=true){
@@ -4598,7 +4640,7 @@
                     //console.log("Firefox Mode: ", uploadDir, "Target Dir", targetDir)
                 }
 
-                let socket = new WebSocket(protocol + window.location.hostname + ":" + port + "/system/file_system/lowmemUpload?filename=" + filename + "&path=" + uploadDir);
+                let socket = new WebSocket(protocol + window.location.hostname + ":" + port + "/system/file_system/lowmemUpload?filename=" + encodeURIComponent(filename) + "&path=" + encodeURIComponent(uploadDir));
                 let currentSendingIndex = 0;
                 let chunks = Math.ceil(file.size/uploadFileChunkSize,uploadFileChunkSize);
                 
@@ -4773,7 +4815,7 @@
                     //The upload paramter supplied targetDir
                     uploadCurrentPath = targetDir;
                 }
-                let url = '../../system/file_system/upload?path=' + uploadCurrentPath
+                let url = '../../system/file_system/upload?path=' + encodeURIComponent(uploadCurrentPath)
                 //console.log("Uploading To => ", uploadCurrentPath, targetDir, currentPath);
                 let formData = new FormData()
                 let xhr = new XMLHttpRequest()
@@ -5192,6 +5234,9 @@
             $(".popup").fadeOut(100);
             $(".popupWrapper").fadeOut(100);
             $('body').css("overflow","");
+            if($("#shareFile").is(":visible")){
+                $("#shareFileEmbedded").attr("src", "");
+            }
         }
 
         function showPopupWrapper(){

+ 7 - 2
src/web/SystemAO/file_system/file_selector.html

@@ -292,11 +292,16 @@
             }
 
             function newFolder(){
+                var newFileOffset = 0;
+                if ($("#newfilenameInput").is(":visible")){
+                    newFileOffset += 40;
+                }
+
                 $("#newFolderInput").toggle();
                 if($("#newFolderInput").is(":visible")){
-                    $("#sidebarPadder").css("height", "90px");
+                    $("#sidebarPadder").css("height", 90 + newFileOffset + "px");
                 }else{
-                    $("#sidebarPadder").css("height", "46px");
+                    $("#sidebarPadder").css("height", 46 + newFileOffset + "px");
                 }
                 updateFileListTopLocation();
             }

+ 288 - 54
src/web/SystemAO/file_system/file_share.html

@@ -1,25 +1,66 @@
 <html>
     <head>
         <title locale="title/title">File Share</title>
+        <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
+        <script src="../../script/jquery.min.js"></script>
+        <script src="../../script/semantic/semantic.min.js"></script>
+        <script src="../../script/ao_module.js"></script>
+        <script type="text/javascript" src="../../script/applocale.js"></script>
+        <script type="text/javascript" src="../../script/qrcode.min.js"></script>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
         <style>
-            body{
+            body:not(.darkTheme){
                 background: rgba(255,255,255,01) !important;
             }
+
+            body.darkTheme{
+                background-color: #242330 !important;
+            }
+
+            .sub.header.darkTheme{
+                color: rgba(255, 255, 255, 0.623) !important;
+            }
+
+            .darkTheme:not(a){
+                color: #eeeeee;
+            }
+
+            .yellow.message.darkTheme{
+                box-shadow: none !important;
+                border: 0px solid transparent !important;
+                background-color: #5e550e !important;
+            }
+
+            .dropDown.darkTheme:not(.icon){
+                background-color: #413f57 !important;
+                border: 1px solid white;
+            }
+
+            .dropdown.darkThem  .ui.label{
+                color: rgb(175, 175, 175);
+                background-color: #2f2d3d !important;
+            }
+
+            .dropdown.darkThem  .ui.label:hover{
+                color: white;
+                opacity: 0.8;
+            }
+
+
         </style>
     </head>
     <body>
-        <div class="ui stackable grid">
+        <div id="main" class="ui stackable grid">
             <div class="eight wide column">
-                <div class="width: 100%; ">
+                <div style="width: 100%;" class="qrcode" align="center">
                     <div style="display: block; margin-left: auto; margin-right: auto;" align="center">
                         <div id="qrcode" style="border: 10px solid white; background-color: white;">
                             <h1><br><i class="ui loading spinner icon"></i><br></h1>
                         </div>
                     </div>
-                    <div style="width: 100%">
-                        <a id="sharelink" href="" target="_blank" style="margin-top:8px; font-size: 120%; padding-left: 20px; padding-right: 20px; word-break: break-all; overflow-wrap: anywhere;"></a>
+                    <div style="width: 80%; text-align: center;">
+                    <a id="sharelink" href="" target="_blank" style="margin-top:8px; font-size: 120%; padding-left: 20px; padding-right: 20px; word-break: break-all; overflow-wrap: anywhere;"></a>
                     </div>
                     
                 </div>
@@ -68,8 +109,57 @@
                                     </div>
                                 </label>
                             </div>
+                            <br><br>
+                            <div class="ui accordion" id="advanceShare">
+                                <div class="title">
+                                    <i class="dropdown icon"></i>
+                                    <span locale="share/setting/advance/title">Advance Share Options</span>
+                                </div>
+                                <div class="content">
+                                    <div class="ui radio checkbox">
+                                        <input id="users" type="radio" class="shareoption" value="users" name="shareopt" onchange="updateSharePermission(this);">
+                                        <label for="users">
+                                            <div class="ui header">
+                                                <div class="content whiteTheme">
+                                                    <i class="clipboard check icon"></i> <span locale="share/setting/advance/users">Selected Users</span>
+                                                    <div class="sub header whiteTheme" locale="share/setting/advance/usersDesc">Selected users with matching user name</div>
+                                                </div>
+                                            </div>
+                                        </label>
+                                    </div>
+                                    <div id="userselector">
+                                        <p style="margin-top: 1em;" locale="share/setting/advance/usersInstruct"">Select target users from the list below</p>
+                                        <select id="targetUsersList" class="ui fluid search dropdown" multiple="">
+                                            <option value="">Users</option>
+                                        </select>
+                                        <div id="noUserWarning" class="ui yellow message"  style="display:none;">
+                                            <i class="caret up icon"></i> <span locale="share/setting/advance/addUserToSave">Add at least one target share user to save changes</span>
+                                        </div>
+                                    </div>
+                                    <div class="ui radio checkbox" style="margin-top: 1em;">
+                                        <input id="groups" type="radio" class="shareoption" value="groups" name="shareopt" onchange="updateSharePermission(this);">
+                                        <label for="groups">
+                                            <div class="ui header">
+                                                <div class="content whiteTheme">
+                                                    <i class="sitemap icon"></i> <span locale="share/setting/advance/groups">Selected Groups</span>
+                                                    <div class="sub header whiteTheme" locale="share/setting/advance/groupsDesc">All the users that has access to any one of the selected group(s)</div>
+                                                </div>
+                                            </div>
+                                        </label>
+                                    </div>
+                                    <div id="groupselector">
+                                        <p style="margin-top: 1em;" locale="share/setting/advance/groupsInstruct">Select target groups from the list below</p>
+                                        <select id="targetGroupList" class="ui fluid search dropdown" multiple="">
+                                            <option value="">Groups</option>
+                                        </select>
+                                        <div id="noGroupWarning" class="ui yellow message" style="display:none;">
+                                            <i class="caret up icon"></i> <span locale="share/setting/advance/addGroupToSave">Add at least one target share group to save changes</span>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
                         <br><br>
-                        <div id="udpateNotification" style="display:none;" class="ui green inverted segment">
+                        <div id="udpateNotification" style="display:none; position: fixed; bottom: 1em; right: 1em;" class="ui green inverted segment">
                                 <i class=" checkmark icon"></i> <span locale="share/setting/updated">Share Setting Updated</span>
                         </div>
                         </div>
@@ -77,8 +167,8 @@
                 </div>
             </div>
         </div>
-        <div class="ui divider"></div>
-        <div style="width: 100%; padding-right: 12px;" align="right">
+        <div class="ui divider actionButton"></div>
+        <div class="actionButton" style="width: 100%; padding-right: 12px;" align="right">
             <div class="ui button popupbuttons whiteTheme allowHover" onclick="copyLinkToClipboard(this)">
                 <i class="copy icon"></i> <span locale="button/copy">Copy</span>
             </div> 
@@ -107,17 +197,121 @@
                 To remove a share, pass in "remove" for the share mode
                 Supported Share Mode keywords {anyone/signedin/samegroup/remove}
 
+                Other supported flags
+                {
+                    QRCode: {true/false} //Show or Hide the QR Code and link
+                    ActionButtons: {true/false} //Show or Hide the action button on the bottom
+                }
+
             */
-            var shareEditingUUID = "";
+            var sharCurrentEditingUUID = "";
             var shareingFileData = {};
+            var darkTheme = false;
             var initialized = false;
             var fileSharingURL = "";
+            var relpath = "../../";
             
+            function applyDarkThemeMode(){
+                $(".whiteTheme").removeClass("whiteTheme").addClass("darkTheme");
+                $("*:not(button)").addClass("darkTheme");
+                $("body").addClass("darkTheme");
+                $(".ui.button").addClass("inverted");
+                darkTheme = true;
+            }
+
+            function removeQRCodeInDarkTheme(){
+                if (darkTheme){
+                    $("#qrcode").css({
+                        "border":"0px solid transparent",
+                        "background-color":"#242330",
+                    })
+                }
+            }
+
+            if ($(parent) && $(parent.document).find("body").hasClass("darkTheme")){
+                //Switch to darkTheme mode immediately
+                applyDarkThemeMode();
+            }   
 
+            PageReady();
             function PageReady(){
                 if (initialized){
                     return;
                 }
+
+                //Load User & Group List
+                $.get(relpath + "../system/users/list", function(data){
+                    var groups = {};
+                    $("#targetUsersList").html(`<option value="">Users</option>`);
+                    data.forEach(user => {
+                        $("#targetUsersList").append(`<option value="${user[0]}">${user[0]}</option>`);
+                        var userGroups = user[1];
+                        userGroups.forEach(thisGroup => {
+                            groups[thisGroup] = true;
+                        });
+                    });
+                    $("#targetGroupList").html(`<option value="">Groups</option>`);
+                    for (var [key, value] of Object.entries(groups)) {
+                        $("#targetGroupList").append(`<option value="${key}">${key}</option>`);
+                    }
+
+                    //Initiate the share details
+                    initFileDetails(shareingFileData, function(shareUUID){
+                        //Set the mode of share if it is defined
+                        if (shareingFileData.shareMode !== undefined && shareingFileData.shareMode == "remove"){
+                            //Remove the share UUID
+                            removeSharing();
+                            return;
+                        }
+                        if (shareingFileData.shareMode !== undefined){
+                            //As the share mode is defined by the caller, hide the setting interface
+                            $("#shareSettingOptions").hide();
+                            $("#sharelink").parent().css({
+                                "width": "100%",
+                                "text-align": "left",
+                            });
+                            $.ajax({
+                                url: relpath + "../system/file_system/share/edit",
+                                data: {uuid: sharCurrentEditingUUID, mode: shareingFileData.shareMode},
+                                success: function(data){
+                                    if (data.error !== undefined){
+                                        alert(data.error);
+                                        return;
+                                    }
+                                    
+                                    //Update the checkbox
+                                    $(".shareoption").each(function(){
+                                        if ($(this)[0].value != shareingFileData.shareMode){
+                                            $(this)[0].checked = false;
+                                        }else{
+                                            $(this)[0].checked = true;
+                                        }
+                                    
+                                    });
+                                }
+                            });
+                        }else{
+                            //Default: show the setting to allow user adjustment
+                            $("#main").css({
+                                "margin-top":"0em",
+                                "padding": "4px"
+                            });
+                            $("#shareSettingOptions").show();
+                        }
+
+                        $(".accordion").accordion();
+                    });
+                });
+
+                //Load theme style
+                $.get(relpath + "../system/file_system/preference?key=file_explorer/theme",function(data){
+                    if (data == "darkTheme"){
+                        applyDarkThemeMode();
+                    }
+                });
+
+
+
                 initialized = true;
                 //Do localization
                 applocale.init(relpath + "../SystemAO/locale/file_share.json", function(){
@@ -125,6 +319,7 @@
                 });
 
                 $(".checkbox").checkbox();
+                $(".dropdown").dropdown();
                 var inputFile = ao_module_loadInputFiles();
                 if (inputFile == null){
                     //No file selected
@@ -138,44 +333,20 @@
 
                 //Make sure one file is choicen each time
                 inputFile = inputFile[0]; 
+                console.log("inputFile", inputFile);
                 shareingFileData = inputFile;
 
-                initFileDetails(shareingFileData, function(shareUUID){
-                    //Set the mode of share if it is defined
-                    if (shareingFileData.shareMode !== undefined && shareingFileData.shareMode == "remove"){
-                        //Remove the share UUID
-                        removeSharing();
-                        return;
-                    }
-                    if (shareingFileData.shareMode !== undefined){
-                        //As the share mode is defined by the caller, hide the setting interface
-                        $("#shareSettingOptions").hide();
-                        $.ajax({
-                            url: relpath + "../system/file_system/share/edit",
-                            data: {uuid: shareEditingUUID, mode: shareingFileData.shareMode},
-                            success: function(data){
-                                if (data.error !== undefined){
-                                    alert(data.error);
-                                    return;
-                                }
-                                
-                                //Update the checkbox
-                                $(".shareoption").each(function(){
-                                    if ($(this)[0].value != shareingFileData.shareMode){
-                                        $(this)[0].checked = false;
-                                    }else{
-                                        $(this)[0].checked = true;
-                                    }
-                                
-                                });
-                            }
-                        });
-                    }else{
-                        //Default: show the setting to allow user adjustment
-                        $("#shareSettingOptions").show();
-                    }
-                });
+                //Filter out the nessary display flags
+                if (shareingFileData.QRCode !== undefined && shareingFileData.QRCode == false){
+                    $(".qrcode").hide();
+                    $("#shareSettingOptions").attr("class", "sixteen wide column");
+                    $("#shareSettingOptions").css("padding", "1em");
+                    $(".eight.wide").hide();
+                }
 
+                if (shareingFileData.ActionButtons !== undefined && shareingFileData.ActionButtons == false){
+                    $(".actionButton").hide();
+                }
             }
 
             function initFileDetails(shareingFileData, callback=undefined){
@@ -186,17 +357,43 @@
                         if (data.error !== undefined){
                             alert(data.error);
                         }else{
+                            console.log(data);
                             updateShareLinkInfo(data.UUID);
-                            shareEditingUUID = data.UUID;
+                            sharCurrentEditingUUID = data.UUID;
                             $(".shareoption").each(function(){
                                 if ($(this)[0].value != data.Permission){
                                     $(this)[0].checked = false;
                                 }else{
                                     $(this)[0].checked = true;
+                                    if (data.Permission == "users"){
+                                        $("#advanceShare").accordion("open", 0);
+                                        $("#targetUsersList").dropdown("set selected", data.Accessibles);
+                                        $("#targetUsersList").parent().removeClass("disabled");
+                                        $("#targetGroupList").parent().addClass("disabled");
+                                    }else if (data.Permission == "groups"){
+                                        $("#advanceShare").accordion("open", 0);
+                                        $("#targetGroupList").dropdown("set selected", data.Accessibles);
+                                        $("#targetUsersList").parent().addClass("disabled");
+                                        $("#targetGroupList").parent().removeClass("disabled");
+                                    }else{
+                                        $("#targetGroupList").parent().addClass("disabled");
+                                        $("#targetUsersList").parent().addClass("disabled");
+                                    }
                                 }
                                
                             });
 
+                          
+                            $("#targetUsersList").on("change", function(evt){
+                                updateSharePermissionByType("users");
+                            });
+
+                            $("#targetGroupList").on("change", function(evt){
+                                updateSharePermissionByType("groups");
+                            });
+               
+                            
+
                             //If the file is from desktop, set share icon
                             if (ao_module_virtualDesktop == true){
                                 var fileDir = shareingFileData.filepath.split("/");
@@ -217,7 +414,7 @@
             }
 
             function removeSharing(){
-                if (shareEditingUUID == ""){
+                if (sharCurrentEditingUUID == ""){
                     return
                 }
 
@@ -235,7 +432,7 @@
                             $("#sharelink").text("");
                             $("#sharelink").attr("href", "#");
                             $("#qrcode").html(`<br><br><h1><i class="green checkmark icon"></i> ${applocale.getString("message/removed", "Share Removed")}</h1>`);
-
+                            removeQRCodeInDarkTheme();
                             //If the file is located on desktop and it is web desktop mode
                             if (ao_module_virtualDesktop == true){
                                 var fileDir = shareingFileData.filepath.split("/");
@@ -253,18 +450,54 @@
 
             function updateSharePermission(object){
                 var newPermission = $(object).attr('value');
+                updateSharePermissionByType(newPermission);
+            }   
+
+            function updateSharePermissionByType(newPermission){
+                if (newPermission == "users"){
+                    //Build the user list
+                    $("#targetUsersList").parent().removeClass("disabled");
+                    $("#targetGroupList").parent().addClass("disabled");
+                    var selectedUsers = $("#targetUsersList").val();
+                    $("#noGroupWarning").slideUp("fast");
+                    if (selectedUsers.length == 0){
+                        //Show tips message
+                        $("#noUserWarning").slideDown("fast");
+                        return;
+                    }else{
+                        $("#noUserWarning").slideUp("fast");
+                    }
+                    //Rewrite it to permission handling description
+                    newPermission = "users:" + selectedUsers.join(",");
+                }else if (newPermission == "groups"){
+                    //Build the group list
+                    $("#targetUsersList").parent().addClass("disabled");
+                    $("#targetGroupList").parent().removeClass("disabled");
+                    var selectedGroups = $("#targetGroupList").val();
+                    $("#noUserWarning").slideUp("fast");
+                    if (selectedGroups.length == 0){
+                        //Show tips message
+                        $("#noGroupWarning").slideDown("fast");
+                        return;
+                    }else{
+                        $("#noGroupWarning").slideUp("fast");
+                    }
+                    //Rewrite it to permission handling description
+                    newPermission = "groups:" + selectedGroups.join(",");
+                }
+
                 $.ajax({
                     url: relpath + "../system/file_system/share/edit",
-                    data: {uuid: shareEditingUUID, mode: newPermission},
+                    data: {uuid: sharCurrentEditingUUID, mode: newPermission},
                     success: function(data){
                         if (data.error !== undefined){
                             alert(data.error);
                             return;
                         }
-                        $("#udpateNotification").slideDown("fast").delay(3000).slideUp("fast");
+                        $("#udpateNotification").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
                     }
                 });
-            }   
+            }
 
             function updateShareLinkInfo(uuid){
                 $("#qrcode").html("");
@@ -278,7 +511,7 @@
                     port = "";
                 }
                 var shareURL = protocol + window.location.hostname + port + "/share/" + uuid;
-                shareEditingUUID = uuid;
+                sharCurrentEditingUUID = uuid;
                 fileSharingURL = shareURL;
                 new QRCode(document.getElementById("qrcode"), shareURL);
                 $("#sharelink").text(shareURL);
@@ -295,6 +528,7 @@
                 This function try to load jQuery and ao_module from the script folder.
                 Also loading the semantic js and the css main body
             */
+           /*
             //The possible location for desktop.system, standard webapp module, SystemAO interfaces and iui sub-interfaces
             let possibleLocations = ["script/", "../script/", "../../script/", "../../../script/"];
             let loopCount = Math.min(possibleLocations.length, JSON.parse(JSON.stringify(window.location.toString())).split("/").length - 3);
@@ -355,7 +589,7 @@
 
             //Load jQuery first
             if (typeof(window.jQuery) == "undefined"){
-                //jQuery not found. Laod it
+                //jQuery not found. Load it
                 for (var i = 0; i < loopCount; i++){
                     var relpath = possibleLocations[i];
                     tryLoad(relpath, "jquery.min.js", function(relpath, filename){
@@ -369,12 +603,11 @@
                             injectOtherJavaScriptLibrary(relpath);
                         });
                         
-                        
                     });
                 }
             }else{
                 //jQuery exists. Load ao_module
-                dynamicLoadAoModule();
+                injectOtherJavaScriptLibrary();
             }
 
             function doAfterJqueryLoaded(callback){
@@ -388,6 +621,7 @@
                     }
                 }), 300;
             }
+            */
 
             function copyLinkToClipboard(btn){
                 //Copy text

+ 4 - 4
src/web/SystemAO/file_system/zip_extractor.html

@@ -3,9 +3,9 @@
         <title>Zip Extractor</title>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
-        <link rel="stylesheet" href="../../script/tocas/tocas.css">
-        <script type="text/javascript" src="../../script/tocas/tocas.js"></script>
+        <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
         <script type="text/javascript" src="../../script/jquery.min.js"></script>
+        <script type="text/javascript" src="../../script/semantic/semantic.min.js"></script>
         <script type="text/javascript" src="../../script/ao_module.js"></script>
         <style>
             body{
@@ -15,8 +15,8 @@
     </head>
     <body>
         <br>
-        <div class="ts active inverted dimmer">
-            <div class="ts text loader">Loading Archives</div>
+        <div class="ui active inverted dimmer">
+            <div class="ui text loader">Loading Archives</div>
         </div>
         <script>
             ao_module_setFixedWindowSize();

+ 3 - 0
src/web/SystemAO/locale/file_explorer.json

@@ -123,6 +123,7 @@
                 "message/rename/success": "重新命名成功",
                 "message/copy/success": " 個檔案已複製",
                 "message/move/success": " 個檔案已移動",
+                "message/share/removed": "已停止檔案分享",
                 "message/paste/nothing": "沒有可貼上的檔案",
                 "message/remove/success": " 個檔案已被成功刪除",
                 "message/recycle/success": " 個檔案已被成功移動到資源回收箱",
@@ -323,6 +324,7 @@
                 "message/rename/success": "重新命名成功",
                 "message/copy/success": " 個檔案已複製",
                 "message/move/success": " 個檔案已移動",
+                "message/share/removed": "已停止檔案分享",
                 "message/paste/nothing": "沒有可貼上的檔案",
                 "message/remove/success": " 個檔案已被成功刪除",
                 "message/recycle/success": " 個檔案已被成功移動到資源回收箱",
@@ -520,6 +522,7 @@
                 "message/rename/success": "重新命名成功",
                 "message/copy/success": " 个文件已复制",
                 "message/move/success": " 个文件已移动",
+                "message/share/removed": "已停止文件分享",
                 "message/paste/nothing": "没有可粘贴的文件",
                 "message/remove/success": " 个文件已被成功删除",
                 "message/recycle/success": " 个文件已被成功移动到资源回收箱",

+ 30 - 0
src/web/SystemAO/locale/file_share.json

@@ -22,6 +22,16 @@
                 "share/setting/sameGroup/desc":"任何擁有相同使用者權限的使用者皆可下載此檔案",
                 "share/setting/updated":"存取權限已更新",
 
+                "share/setting/advance/title":"進階分享選項",
+                "share/setting/advance/users":"指定使用者",
+                "share/setting/advance/usersDesc":"指定已登入之使用者帳戶名稱",
+                "share/setting/advance/usersInstruct":"於下方列表中選擇指定分享的用戶",
+                "share/setting/advance/addUserToSave":"新增最少一名使用者以儲存變更",
+                "share/setting/advance/groups":"指定權限群組",
+                "share/setting/advance/groupsDesc":"使用者只要擁有下述指定之任何一個群組權限皆可存取",
+                "share/setting/advance/groupsInstruct":"於下方列表中選擇指定分享的群組",
+                "share/setting/advance/addGroupToSave":"新增最少一個權限群組以儲存變更",
+
                 "message/removed": "分享已移除",
                 "qr/loading": "載入中"
                
@@ -53,6 +63,16 @@
                 "share/setting/sameGroup/desc":"任何擁有相同使用者權限的使用者皆可下載此檔案",
                 "share/setting/updated":"存取權限已更新",
 
+                "share/setting/advance/title":"進階分享選項",
+                "share/setting/advance/users":"指定使用者",
+                "share/setting/advance/usersDesc":"指定已登入之使用者帳戶名稱",
+                "share/setting/advance/usersInstruct":"於下方列表中選擇指定分享的用戶",
+                "share/setting/advance/addUserToSave":"新增最少一名使用者以儲存變更",
+                "share/setting/advance/groups":"指定權限群組",
+                "share/setting/advance/groupsDesc":"使用者只要擁有下述指定之任何一個群組權限皆可存取",
+                "share/setting/advance/groupsInstruct":"於下方列表中選擇指定分享的群組",
+                "share/setting/advance/addGroupToSave":"新增最少一個權限群組以儲存變更",
+
                 "message/removed": "分享已移除",
                 "qr/loading": "載入中"
             },
@@ -82,6 +102,16 @@
                 "share/setting/sameGroup/desc":"任何拥有相同使用者权限的使用者皆可下载此文件",
                 "share/setting/updated":"存取权限已更新",
 
+                "share/setting/advance/title":"进阶分享选项",
+                "share/setting/advance/users":"指定使用者",
+                "share/setting/advance/usersDesc":"指定已登入之使用者帐户名称",
+                "share/setting/advance/usersInstruct":"于下方列表中选择指定分享的用户",
+                "share/setting/advance/addUserToSave":"新增最少一名使用者以储存变更",
+                "share/setting/advance/groups":"指定权限群组",
+                "share/setting/advance/groupsDesc":"使用者只要拥有下述指定之任何一个群组权限皆可存取",
+                "share/setting/advance/groupsInstruct":"于下方列表中选择指定分享的群组",
+                "share/setting/advance/addGroupToSave":"新增最少一个权限群组以储存变更",
+                
                 "message/removed": "分享已移除",
                 "qr/loading": "载入中" 
             },

+ 20 - 3
src/web/SystemAO/users/editgroup.html

@@ -3,7 +3,6 @@
         <title>Edit Group</title>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
-        <link rel="stylesheet" href="../../script/tocas/tocas.css">
         <link rel="stylesheet" href="../../script/semantic/semantic.css">
         <link rel="stylesheet" href="../../script/ao.css">
         <script type="text/javascript" src="../../script/jquery.min.js"></script>
@@ -20,6 +19,24 @@
                 background-repeat: no-repeat !important;
                 background-attachment: fixed !important;
             }
+            .ui.padded.slate{
+                width: 100%;
+                display: flex;
+                flex-direction: column;
+                padding: 4em;
+            }
+
+            .ui.heading.slate{
+                align-items: flex-start;
+            }
+
+            .ui.slate .header:not(.ui):not(.sub):not(.item){
+                font-size: 1.6em;
+                line-height: 1.42857em;
+                font-weight: 500;
+                display: block;
+            }
+
             .required{
                 color:red;
             }
@@ -29,7 +46,7 @@
         </style>
     </head>
     <body>
-        <div class="ts heading fluid padded slate themebackground" >
+        <div class="ui heading fluid padded slate themebackground" >
             <span class="header">
             <i class="users icon"></i> Edit Users Group</span>
             <span class="description">Fill in the following group information to proceed.</span>
@@ -87,7 +104,7 @@
                     </div>
                 </div>
                 <div class="ui divider"></div>
-                <table class="ts celled striped table">
+                <table class="ui celled striped unstackable table">
                     <thead>
                         <tr>
                             <th >#</th>

+ 20 - 3
src/web/SystemAO/users/newgroup.html

@@ -3,7 +3,6 @@
         <title>Create Group</title>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
-        <link rel="stylesheet" href="../../script/tocas/tocas.css">
         <link rel="stylesheet" href="../../script/semantic/semantic.css">
         <link rel="stylesheet" href="../../script/ao.css">
         <script type="text/javascript" src="../../script/jquery.min.js"></script>
@@ -20,6 +19,24 @@
                 background-repeat: no-repeat !important;
                 background-attachment: fixed !important;
             }
+            .ui.padded.slate{
+                width: 100%;
+                display: flex;
+                flex-direction: column;
+                padding: 4em;
+            }
+
+            .ui.heading.slate{
+                align-items: flex-start;
+            }
+
+            .ui.slate .header:not(.ui):not(.sub):not(.item){
+                font-size: 1.6em;
+                line-height: 1.42857em;
+                font-weight: 500;
+                display: block;
+            }
+
             .required{
                 color:red;
             }
@@ -29,7 +46,7 @@
         </style>
     </head>
     <body>
-        <div class="ts heading fluid padded slate themebackground" >
+        <div class="ui heading fluid padded slate themebackground" >
             <span class="header">
             <i class="users icon"></i> Create Users Group</span>
             <span class="description">Fill in the following group information to proceed.</span>
@@ -84,7 +101,7 @@
                     </div>
                 </div>
                 <div class="ui divider"></div>
-                <table class="ts celled striped table">
+                <table class="ui celled striped compact unstackable table">
                     <thead>
                         <tr>
                             <th >#</th>

+ 35 - 25
src/web/SystemAO/utilities/audio.html

@@ -7,8 +7,8 @@
 <script type='text/javascript' charset='utf-8'>
 </script>
 <title>ArOZ Onlineβ</title>
-<link rel="stylesheet" href="../../script/tocas/tocas.css">
-<script src="../../script/tocas/tocas.js"></script>
+<link rel="stylesheet" href="../../script/semantic/semantic.min.css">
+<script src="../../script/semantic/semantic.min.js"></script>
 <script src="../../script/jquery.min.js"></script>
 <script src="../../script/ao_module.js"></script>
 <style>
@@ -22,16 +22,16 @@
 	border:1px solid transparent;
 }
 body {
-	background: rgba(255,255,255,0.7);
+	background: rgba(255,255,255,1);
 }
 
 </style>
 </head>
 <body>
 
-    <div class="ts small attached segmented single line selection items" style="top:0;">
+    <div >
 		<!-- Audio Control System with no HTML5 Audio Attribute-->
-		<div class="ts fluid container transparent" style="cursor: pointer;">
+		<div>
 			<div id="audio_attr"style="display:none;">
 			<audio id="player" controls autoplay>
 			  <source src="" type="audio/mpeg">
@@ -39,27 +39,37 @@ body {
 			</audio>
 			</div>
 			<div id="YamiPlayer" class="content transparent" style="top:0;">
-			<div id="songname" class="ts top attached segment transparent">
-			NOW PLAYING ||
+			<div id="songname" class="ui top attached segment" style="border: 1px solid transparent;">
+				<i class="music icon"></i> 
 			</div>
-			<div id="progressbardiv" class="ts attached progress">
-				<div id="audioprogress" class="bar" style="width: 0%"></div>
+			<div id="progressbardiv" class="ui attached progress" style="height: 1em; background-color: #eeeeee;">
+				<div id="audioprogress" class="bar" style="min-width: 0px; width: 0%; height: 1em;"></div>
 			</div>
-			<div class="ts bottom attached segment seventytransparent">
-				<div class="ts icon buttons">
-					<button class="ts disabled button" onclick="PreviousSong()"><i class="step backward icon"></i></button>
-					<button class="ts button" onclick="playbtn()"><i id='playbtn' class="pause icon"></i></button>
-					<button class="ts disabled button" onclick="NextSong()"><i class="step forward icon"></i></button>
-					<button class="ts button" onclick="stopbtn()"><i class="stop icon"></i></button>
-					<button class="ts button" onclick="volDown()"><i class="volume down icon"></i></button>
-					<button class="ts button" onclick="volUp()"><i class="volume up icon"></i></button>
-					<button class="ts button" onclick="repeatmode()"><i class="repeat icon"></i></button>
+			<div style="background-color: white; margin-top: 5px;">
+				<div style="display: flex; flex-wrap: nowrap;">
+					<div class="ui icon buttons" style="margin-left: 12px;">
+						<!-- <button class="ui disabled button" onclick="PreviousSong()"><i class="step backward icon"></i></button> -->
+						<button class="ui basic black button" onclick="playbtn()"><i id='playbtn' class="pause icon"></i></button>
+						<!-- <button class="ui disabled button" onclick="NextSong()"><i class="step forward icon"></i></button> -->
+						<button class="ui basic black button" onclick="stopbtn()"><i class="stop icon"></i></button>
+						<button class="ui basic black button" onclick="volDown()"><i class="volume down icon"></i></button>
+						<button class="ui basic black button" onclick="volUp()"><i class="volume up icon"></i></button>
+						<button class="ui basic black button" onclick="repeatmode()"><i class="repeat icon"></i></button>
+					</div>
+					<div style="display: flex; flex-wrap: nowrap; margin-left: 1em; padding-top: 8px;">
+						<div>
+							<i class="volume off icon"></i><span id="voldis">100%</span> 
+						</div>
+						<div style="margin-left: 0.5em;">
+							<i class="time icon"></i><span id="timecode">0:00/0:00</span>
+						</div>
+						<div style="margin-left: 0.5em;">
+							<i class="repeat icon"></i><span id="repmode">Single</span>
+						</div>
+					</div>
 				</div>
-				<span>
-				<i id="voldis" class="volume off icon"> 100%</i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-				<i id="timecode" class="time icon"> 0:00/0:00</i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-				<i id="repmode" class="repeat icon"> Single</i>
-				</span>
+				
+
 			</div>
 			
 			<!-- <button class="ts button" onclick="Show_Audio_Attrubute()">Show HTML5 Attrubute</button> -->
@@ -70,7 +80,7 @@ body {
 	/*
 		ArOZ Online Beta Audio Module Embedded Player
 		Migrated to arozos 1.105 by tobychui
-
+		Updated UI design in arozos v1.121 by tobychui
 	*/
 //ArOZ Online BETA control system
 var pwa = false;
@@ -256,7 +266,7 @@ $( document ).ready(function() {
   function PlaySong(name,displayname,id){
 	  if (downloadmode == false){
 		  //This operation is for choosing song
-		  $('#songname').html('NOW PLAYING || '+ decodeURIComponent(displayname));
+		  $('#songname').html(`<i class="music icon"></i> `+ decodeURIComponent(displayname));
 		  change(name);
 		  playingSong = [name,displayname,id];
 		  //console.log(playingSong);

+ 24 - 22
src/web/Timer/timer.html

@@ -1,61 +1,63 @@
 <html>
     <head>
         <title>Timer</title>
-        <link rel="stylesheet" href="../script/tocas/tocas.css">
+        <link rel="stylesheet" href="../script/semantic/semantic.min.css">
         <script src="../script/jquery.min.js"></script>
         <script src="../script/ao_module.js"></script>
         <style>
             body{
                 padding-top:40px;
                 padding-bottom:50px;
-                background-color:rgba(242, 242, 242, 0.8);
+                background-color:rgba(242, 242, 242, 0.85);
+                backdrop-filter: blur(4px) !important;
+                overflow: hidden;
             }
         </style>
     </head>
     <body>
-        <div class="ts container" style="position:absolute;left:0;right:0;width:100%;" align="center">
-            <div class="ts grid">
+        <div class="ui container" style="position:absolute;left:0;right:0;width:100%;" align="center">
+            <div class="ui grid" style="margin-top: -30px;">
                 <div class="five wide column">
-                    <div class="ts statistic">
+                    <div class="ui statistic">
                         <div id="hour" class="value">00</div>
                         <div class="label">Hours</div>
                     </div><br>
-                    <div class="ts icon tiny buttons">
-                        <button class="ts button" onClick="adjustValue('hour',-1);"><i class="minus icon"></i></button>
-                        <button class="ts button" onClick="resetTimer('hour');"><i class="undo icon"></i></button>
-                        <button class="ts button"onClick="adjustValue('hour',1);"><i class="plus icon"></i></button>
+                    <div class="ui icon tiny buttons">
+                        <button class="ui button" onClick="adjustValue('hour',-1);"><i class="minus icon"></i></button>
+                        <button class="ui button" onClick="resetTimer('hour');"><i class="undo icon"></i></button>
+                        <button class="ui button"onClick="adjustValue('hour',1);"><i class="plus icon"></i></button>
                     </div>
                 </div>
                 <div class="five wide column">
-                    <div class="ts statistic">
+                    <div class="ui statistic">
                         <div id="min" class="value">00</div>
                         <div class="label">Minutes</div>
                     </div><br>
-                     <div class="ts icon tiny buttons">
-                        <button class="ts button" onClick="adjustValue('min',-1);"><i class="minus icon"></i></button>
-                        <button class="ts button" onClick="resetTimer('min');"><i class="undo icon"></i></button>
-                        <button class="ts button"onClick="adjustValue('min',1);"><i class="plus icon"></i></button>
+                     <div class="ui icon tiny buttons">
+                        <button class="ui button" onClick="adjustValue('min',-1);"><i class="minus icon"></i></button>
+                        <button class="ui button" onClick="resetTimer('min');"><i class="undo icon"></i></button>
+                        <button class="ui button"onClick="adjustValue('min',1);"><i class="plus icon"></i></button>
                     </div>
                 </div>
                 <div class="five wide column">
-                    <div class="ts statistic">
+                    <div class="ui statistic">
                         <div id="sec" class="value">00</div>
                         <div class="label">Seconds</div>
                     </div><br>
-                     <div class="ts icon tiny buttons">
-                        <button class="ts button" onClick="adjustValue('sec',-1);"><i class="minus icon"></i></button>
-                        <button class="ts button" onClick="resetTimer('sec');"><i class="undo icon"></i></button>
-                        <button class="ts button"onClick="adjustValue('sec',1);"><i class="plus icon"></i></button>
+                     <div class="ui icon tiny buttons">
+                        <button class="ui button" onClick="adjustValue('sec',-1);"><i class="minus icon"></i></button>
+                        <button class="ui button" onClick="resetTimer('sec');"><i class="undo icon"></i></button>
+                        <button class="ui button"onClick="adjustValue('sec',1);"><i class="plus icon"></i></button>
                     </div>
                 </div>
                 <div class="one wide column">
-                    <button id="startbtn" class="ts positive tiny icon button" onClick="startCountDown();" style="position:fixed;top:3px;right:3px;">
+                    <button id="startbtn" class="ui positive tiny icon button" onClick="startCountDown();" style="position:fixed;top:3px;right:3px;">
                         <i class="play icon"></i>
                     </button>
-                    <button id="pausebtn" class="ts basic tiny icon button disabled" onClick="pauseCountDown();" style="position:fixed;top:40px;right:3px;">
+                    <button id="pausebtn" class="ui basic tiny icon button disabled" onClick="pauseCountDown();" style="position:fixed;top:40px;right:3px;">
                         <i class="pause icon"></i>
                     </button>
-                    <button id="stopalarm" class="ts negative tiny icon button disabled" onClick="stopAlarm();" style="position:fixed;top:80px;right:3px;">
+                    <button id="stopalarm" class="ui negative tiny icon button disabled" onClick="stopAlarm();" style="position:fixed;top:80px;right:3px;">
                         <i class="alarm mute icon"></i>
                     </button>
                 </div>

+ 7 - 1
src/web/desktop.system

@@ -4496,7 +4496,7 @@
             let keycode = event.which || event.keyCode;
             if (keycode == "17") {
                 ctrlHold = false;
-            } else if (keycode == "16") {
+            }else if (keycode == "16") {
                 shiftHold = false;
             }else if (keycode == "27"){
                 //ESC key, stop all floatWindow operations
@@ -4504,6 +4504,12 @@
                 movingWindow = false;
                 resizingWindow = false;
                 event.preventDefault();
+            }else if (keycode == "13"){
+                //Enter key. open the selected object if exists
+                $(".launchIconWrapper.selected").each(function(){
+                    console.log($(this).parent());
+                    iconDoubleClicked($(this).parent(), event);
+                })
             }
         });
 

+ 28 - 10
src/web/user.system

@@ -4,9 +4,9 @@
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
         <title>ArozOS - Users</title>
-        <link rel="stylesheet" href="script/tocas/tocas.css">
+        <link rel="stylesheet" href="script/semantic/semantic.min.css">
         <link rel="stylesheet" href="script/ao.css">
-        <script type="application/javascript" src="script/tocas/tocas.js"></script>
+        <script type="application/javascript" src="script/semantic/semantic.min.js"></script>
         <script type="application/javascript" src="script/jquery.min.js"></script>
         <script type="application/javascript" src="script/ao_module.js"></script>
         <style>
@@ -20,6 +20,24 @@
                 background-repeat: no-repeat !important;
                 background-attachment: fixed !important;
             }
+
+            .ui.padded.slate{
+                width: 100%;
+                display: flex;
+                flex-direction: column;
+                padding: 4em;
+            }
+
+            .ui.heading.slate{
+                align-items: flex-start;
+            }
+
+            .ts.slate .header:not(.ts):not(.sub):not(.item){
+                line-height: 1.42857em;
+                font-weight: 500;
+                display: block;
+            }
+
             .required{
                 color:red;
             }
@@ -29,13 +47,13 @@
         </style>
     </head>
     <body>
-        <div class="ts heading fluid padded slate themebackground" >
-            <span class="header"><i class="add icon"></i> New User</span>
+        <div class="ui heading fluid padded slate themebackground" >
+            <span class="header" style="font-size: 1.6em;"><i class="add icon"></i> New User</span>
             <span class="description">Fill in the following user information to proceed.</span>
         </div>
         <br><br>
-        <div class="ts container">
-            <div class="ts horizontal form">
+        <div class="ui container">
+            <div class="ui horizontal form">
                 <div class="field">
                     <label>Username <span class="required">*</span></label>
                     <input id="username" type="text">
@@ -63,13 +81,13 @@
             </div>
             <br>
             <p><span class="required">*</span> This field is required.</p>
-            <div class="ts negative segment" id="err" style="display:none;">
+            <div class="ui negative segment" id="err" style="display:none;">
                 <p><i class="remove icon"></i> <span id="errmsg"></span></p>
             </div>
-            <div class="ts section divider"></div>
+            <div class="ui section divider"></div>
             <div id="actionbtns" align="right">
-                <button class="ts primary button" onclick="createUser();">Create</button>
-                <button id="cancelbtn" class="ts button" onclick="cancel();">Cancel</button>
+                <button class="ui primary button" onclick="createUser();">Create</button>
+                <button id="cancelbtn" class="ui button" onclick="cancel();">Cancel</button>
             </div>
            
         </div>