Selaa lähdekoodia

Fixed network info icon not correct issue

TC pushbot 5 4 vuotta sitten
vanhempi
commit
8abb914033
51 muutettua tiedostoa jossa 924 lisäystä ja 48 poistoa
  1. 3 0
      file_system.go
  2. 115 0
      mod/storage/bridge/bridge.go
  3. 6 5
      startup.go
  4. 79 0
      storage.go
  5. 116 21
      storage.pool.go
  6. 6 0
      system/bridge.json
  7. 18 6
      web/SystemAO/network/hardware.html
  8. BIN
      web/SystemAO/network/img/Ethernet.png
  9. BIN
      web/SystemAO/network/img/WiFi.png
  10. 27 0
      web/SystemAO/network/img/ethernet.ai
  11. 14 0
      web/SystemAO/network/img/ethernet.svg
  12. 27 0
      web/SystemAO/network/img/loopback.ai
  13. 11 0
      web/SystemAO/network/img/loopback.svg
  14. BIN
      web/SystemAO/network/img/portforward.png
  15. 27 0
      web/SystemAO/network/img/wifi.ai
  16. 13 0
      web/SystemAO/network/img/wifi.svg
  17. 1 1
      web/SystemAO/storage/fshedit.html
  18. BIN
      web/SystemAO/storage/img/slate.png
  19. BIN
      web/SystemAO/storage/img/slate.psd
  20. 174 12
      web/SystemAO/storage/poolEditor.html
  21. 2 2
      web/SystemAO/users/account.html
  22. BIN
      web/SystemAO/users/img/People icon.png
  23. BIN
      web/SystemAO/users/img/group_white.png
  24. BIN
      web/SystemAO/users/img/group_white.psd
  25. BIN
      web/SystemAO/users/img/noprofileicon.png
  26. 10 0
      web/SystemAO/users/img/noprofileicon.svg
  27. 27 0
      web/SystemAO/users/img/user - 複製.ai
  28. 27 0
      web/SystemAO/users/img/user-white.ai
  29. 10 0
      web/SystemAO/users/img/user-white.svg
  30. BIN
      web/SystemAO/users/img/user.png
  31. BIN
      web/SystemAO/users/img/user.psd
  32. 10 0
      web/SystemAO/users/img/user.svg
  33. BIN
      web/SystemAO/users/img/user_white.png
  34. BIN
      web/SystemAO/users/img/user_white.psd
  35. 27 0
      web/SystemAO/users/img/users-white.ai
  36. 22 0
      web/SystemAO/users/img/users-white.svg
  37. 27 0
      web/SystemAO/users/img/users.ai
  38. BIN
      web/SystemAO/users/img/users.png
  39. BIN
      web/SystemAO/users/img/users.psd
  40. 22 0
      web/SystemAO/users/img/users.svg
  41. 1 1
      web/desktop.system
  42. BIN
      web/img/desktop/system_icon/user.png
  43. 10 0
      web/img/desktop/system_icon/user.svg
  44. 27 0
      web/img/system/cluster.ai
  45. BIN
      web/img/system/cluster.png
  46. 21 0
      web/img/system/cluster.svg
  47. BIN
      web/img/system/drive-virtual.png
  48. 27 0
      web/img/system/nas.ai
  49. 17 0
      web/img/system/nas.svg
  50. BIN
      web/img/system/slates/pool-slate.png
  51. BIN
      web/img/system/slates/pool-slate.psd

+ 3 - 0
file_system.go

@@ -2103,6 +2103,9 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 		if filepath.Clean(realpath) == filepath.Clean(userRoot) {
 			//Initiate user folder (Initiaed in user object)
 			userinfo.GetHomeDirectory()
+		} else if !strings.Contains(filepath.ToSlash(filepath.Clean(currentDir)), "/") {
+			//User root not created. Create the root folder
+			os.MkdirAll(filepath.Clean(realpath), 0775)
 		} else {
 			//Folder not exists
 			log.Println("[File Explorer] Requested path: ", realpath, " does not exists!")

+ 115 - 0
mod/storage/bridge/bridge.go

@@ -0,0 +1,115 @@
+package bridge
+
+import (
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+	"os"
+)
+
+/*
+	Bridge.go
+
+	This module handle File System Handler bridging cross different storage pool
+	Tricky to use, use with your own risk and make sure admin permission is
+	nessary for all request to this module.
+*/
+
+type Record struct {
+	Filename string
+}
+
+type BridgeConfig struct {
+	FSHUUID string
+	SPOwner string
+}
+
+func NewBridgeRecord(filename string) *Record {
+	return &Record{
+		Filename: filename,
+	}
+}
+
+//Read bridge record
+func (r *Record) ReadConfig() ([]*BridgeConfig, error) {
+	result := []*BridgeConfig{}
+
+	if _, err := os.Stat(r.Filename); os.IsNotExist(err) {
+		//File not exists. Create it
+		js, _ := json.Marshal([]*BridgeConfig{})
+		ioutil.WriteFile(r.Filename, js, 0775)
+	}
+
+	content, err := ioutil.ReadFile(r.Filename)
+	if err != nil {
+		return result, err
+	}
+
+	err = json.Unmarshal(content, &result)
+	if err != nil {
+		return result, err
+	}
+	return result, nil
+}
+
+//Append a new config into the Bridge Record
+func (r *Record) AppendToConfig(config *BridgeConfig) error {
+	currentConfigs, err := r.ReadConfig()
+	if err != nil {
+		return err
+	}
+
+	//Check if this config already exists
+	for _, previousConfig := range currentConfigs {
+		if previousConfig.FSHUUID == config.FSHUUID && previousConfig.SPOwner == config.SPOwner {
+			//Already exists
+			return errors.New("Idential config already registered")
+		}
+	}
+
+	currentConfigs = append(currentConfigs, config)
+
+	err = r.WriteConfig(currentConfigs)
+	return err
+}
+
+//Remove a given config from file
+func (r *Record) RemoveFromConfig(FSHUUID string, groupOwner string) error {
+	currentConfigs, err := r.ReadConfig()
+	if err != nil {
+		return err
+	}
+
+	newConfigs := []*BridgeConfig{}
+	for _, config := range currentConfigs {
+		if !(config.SPOwner == groupOwner && config.FSHUUID == FSHUUID) {
+			newConfigs = append(newConfigs, config)
+		}
+	}
+
+	err = r.WriteConfig(newConfigs)
+	return err
+
+}
+
+//Check if the given UUID in this pool is a bridge object
+func (r *Record) IsBridgedFSH(FSHUUID string, groupOwner string) (bool, error) {
+	currentConfigs, err := r.ReadConfig()
+	if err != nil {
+		return false, err
+	}
+
+	for _, config := range currentConfigs {
+		if config.SPOwner == groupOwner && config.FSHUUID == FSHUUID {
+			return true, nil
+		}
+	}
+	return false, nil
+}
+
+//Write FSHConfig to disk
+func (r *Record) WriteConfig(config []*BridgeConfig) error {
+	js, _ := json.MarshalIndent(config, "", " ")
+	err := ioutil.WriteFile(r.Filename, js, 0775)
+	return err
+}

+ 6 - 5
startup.go

@@ -39,11 +39,12 @@ func RunStartup() {
 	StorageInit() //See storage.go
 
 	//5. Startup user and permission sytem
-	UserSystemInit()       //See user.go
-	permissionInit()       //Register permission interface after user
-	RegisterSystemInit()   //See register.go
-	OAuthInit()            //Oauth system init
-	GroupStoragePoolInit() //Register permission groups's storage pool, require permissionInit()
+	UserSystemInit()        //See user.go
+	permissionInit()        //Register permission interface after user
+	RegisterSystemInit()    //See register.go
+	OAuthInit()             //Oauth system init
+	GroupStoragePoolInit()  //Register permission groups's storage pool, require permissionInit()
+	BridgeStoragePoolInit() //Register the bridged storage pool based on mounted storage pools
 
 	//6. Start Modules and Package Manager
 	ModuleServiceInit() //Module Handler

+ 79 - 0
storage.go

@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"imuslab.com/arozos/mod/permission"
+	"imuslab.com/arozos/mod/storage/bridge"
 
 	fs "imuslab.com/arozos/mod/filesystem"
 	storage "imuslab.com/arozos/mod/storage"
@@ -19,6 +20,7 @@ var (
 	baseStoragePool *storage.StoragePool    //base storage pool, all user can access these virtual roots
 	fsHandlers      []*fs.FileSystemHandler //All File system handlers. All opened handles must be registered in here
 	storagePools    []*storage.StoragePool  //All Storage pool opened
+	bridgeManager   *bridge.Record          //Manager to handle bridged FSH
 )
 
 func StorageInit() {
@@ -32,6 +34,11 @@ func StorageInit() {
 	if err != nil {
 		panic(err)
 	}
+
+	//Create a brdige record manager
+	bm := bridge.NewBridgeRecord("system/bridge.json")
+	bridgeManager = bm
+
 }
 
 func LoadBaseStoragePool() error {
@@ -125,6 +132,63 @@ func GroupStoragePoolInit() {
 	StoragePoolEditorInit()
 }
 
+//Initiate bridged storage pool configs
+func BridgeStoragePoolInit() {
+	bridgeRecords, err := bridgeManager.ReadConfig()
+	if err != nil {
+		log.Println("[ERROR] Fail to read File System Handler bridge config")
+		panic(err.Error())
+	}
+
+	for _, bridgeConf := range bridgeRecords {
+		fsh, err := GetFsHandlerByUUID(bridgeConf.FSHUUID)
+		if err != nil {
+			//This fsh is not found. Skip this
+			continue
+		}
+
+		basePool, err := GetStoragePoolByOwner(bridgeConf.SPOwner)
+		if err != nil {
+			//This fsh is not found. Skip this
+			continue
+		}
+
+		BridgeFSHandlerToGroup(fsh, basePool)
+		log.Println(fsh.UUID + ":/ bridged to " + basePool.Owner + " Storage Pool")
+	}
+}
+
+//Bridge a FSH to a given Storage Pool
+func BridgeFSHandlerToGroup(fsh *fs.FileSystemHandler, sp *storage.StoragePool) error {
+	//Check if the fsh already exists in the basepool
+	for _, thisFSH := range sp.Storages {
+		if thisFSH.UUID == fsh.UUID {
+			return errors.New("Target File System Handler already bridged to this pool")
+		}
+	}
+	sp.Storages = append(sp.Storages, fsh)
+	return nil
+}
+
+func DebridgeFSHandlerFromGroup(fshUUID string, sp *storage.StoragePool) error {
+	newStorageList := []*fs.FileSystemHandler{}
+	fshExists := false
+	for _, fsh := range sp.Storages {
+		if fsh.UUID != fshUUID {
+			fshExists = true
+			newStorageList = append(newStorageList, fsh)
+		}
+	}
+
+	if fshExists {
+		sp.Storages = newStorageList
+		return nil
+	} else {
+		return errors.New("Target File System Handler not found")
+	}
+
+}
+
 func LoadStoragePoolForGroup(pg *permission.PermissionGroup) error {
 	expectedConfigPath := "./system/storage/" + pg.Name + ".json"
 	if fileExists(expectedConfigPath) {
@@ -171,6 +235,21 @@ func LoadStoragePoolForGroup(pg *permission.PermissionGroup) error {
 	return nil
 }
 
+//Check if a storage pool exists by its group owner name
+func StoragePoolExists(poolOwner string) bool {
+	_, err := GetStoragePoolByOwner(poolOwner)
+	return err == nil
+}
+
+func GetStoragePoolByOwner(owner string) (*storage.StoragePool, error) {
+	for _, pool := range storagePools {
+		if pool.Owner == owner {
+			return pool, nil
+		}
+	}
+	return nil, errors.New("Storage pool owned by " + owner + " not found")
+}
+
 func GetFsHandlerByUUID(uuid string) (*fs.FileSystemHandler, error) {
 	//Filter out the :/ fropm uuid if exists
 	if strings.Contains(uuid, ":") {

+ 116 - 21
storage.pool.go

@@ -13,6 +13,7 @@ import (
 
 	"imuslab.com/arozos/mod/database"
 	"imuslab.com/arozos/mod/permission"
+	"imuslab.com/arozos/mod/storage/bridge"
 
 	"github.com/tidwall/pretty"
 	fs "imuslab.com/arozos/mod/filesystem"
@@ -29,6 +30,7 @@ import (
 */
 
 func StoragePoolEditorInit() {
+
 	adminRouter := prout.NewModuleRouter(prout.RouterOption{
 		ModuleName:  "System Settings",
 		AdminOnly:   true,
@@ -45,6 +47,8 @@ func StoragePoolEditorInit() {
 	adminRouter.HandleFunc("/system/storage/pool/reload", HandleStoragePoolReload)
 	adminRouter.HandleFunc("/system/storage/pool/toggle", HandleFSHToggle)
 	adminRouter.HandleFunc("/system/storage/pool/edit", HandleFSHEdit)
+	adminRouter.HandleFunc("/system/storage/pool/bridge", HandleFSHBridging)
+	adminRouter.HandleFunc("/system/storage/pool/checkBridge", HandleFSHBridgeCheck)
 }
 
 //Handle editing of a given File System Handler
@@ -383,31 +387,51 @@ func HandleStoragePoolRemove(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	//Remove it from the json file
-	//Read and parse from old config
-	oldConfigs := []fs.FileSystemOption{}
-	originalConfigFile, _ := ioutil.ReadFile(targetConfigFile)
-	err = json.Unmarshal(originalConfigFile, &oldConfigs)
-	if err != nil {
-		sendErrorResponse(w, "Failed to parse original config file")
+	//Check if this handler is bridged handler
+	bridged, _ := bridgeManager.IsBridgedFSH(uuid, groupname)
+	if bridged {
+		//Bridged FSH. Remove it from bridge config
+		basePool, err := GetStoragePoolByOwner(groupname)
+		if err != nil {
+			sendErrorResponse(w, err.Error())
+			return
+		}
+		err = DebridgeFSHandlerFromGroup(uuid, basePool)
+		if err != nil {
+			sendErrorResponse(w, err.Error())
+		}
+
+		//Remove it from the config
+		bridgeManager.RemoveFromConfig(uuid, groupname)
+		sendOK(w)
 		return
-	}
+	} else {
+		//Remove it from the json file
+		//Read and parse from old config
+		oldConfigs := []fs.FileSystemOption{}
+		originalConfigFile, _ := ioutil.ReadFile(targetConfigFile)
+		err = json.Unmarshal(originalConfigFile, &oldConfigs)
+		if err != nil {
+			sendErrorResponse(w, "Failed to parse original config file")
+			return
+		}
 
-	//Generate new confic by filtering
-	newConfigs := []fs.FileSystemOption{}
-	for _, config := range oldConfigs {
-		if config.Uuid != uuid {
-			newConfigs = append(newConfigs, config)
+		//Generate new confic by filtering
+		newConfigs := []fs.FileSystemOption{}
+		for _, config := range oldConfigs {
+			if config.Uuid != uuid {
+				newConfigs = append(newConfigs, config)
+			}
 		}
-	}
 
-	//Parse and put it into file
-	if len(newConfigs) > 0 {
-		js, _ := json.Marshal(newConfigs)
-		resultingJson := pretty.Pretty(js)
-		ioutil.WriteFile(targetConfigFile, resultingJson, 777)
-	} else {
-		os.Remove(targetConfigFile)
+		//Parse and put it into file
+		if len(newConfigs) > 0 {
+			js, _ := json.Marshal(newConfigs)
+			resultingJson := pretty.Pretty(js)
+			ioutil.WriteFile(targetConfigFile, resultingJson, 0777)
+		} else {
+			os.Remove(targetConfigFile)
+		}
 	}
 
 	sendOK(w)
@@ -559,3 +583,74 @@ func HandleListStoragePools(w http.ResponseWriter, r *http.Request) {
 	js, _ := json.Marshal(storagePools)
 	sendJSONResponse(w, string(js))
 }
+
+//Handler for bridging two FSH, require admin permission
+func HandleFSHBridging(w http.ResponseWriter, r *http.Request) {
+	//Get the target pool and fsh to bridge
+	basePool, err := mv(r, "base", true)
+	if err != nil {
+		sendErrorResponse(w, "Invalid base pool")
+		return
+	}
+
+	//Add the target FSH into the base pool
+	basePoolObject, err := GetStoragePoolByOwner(basePool)
+	if err != nil {
+		sendErrorResponse(w, "Storage pool not found")
+		return
+	}
+
+	targetFSH, err := mv(r, "fsh", true)
+	if err != nil {
+		sendErrorResponse(w, "Invalid fsh given")
+		return
+	}
+
+	fsh, err := GetFsHandlerByUUID(targetFSH)
+	if err != nil {
+		sendErrorResponse(w, "Given FSH UUID does not exists")
+		return
+	}
+
+	err = BridgeFSHandlerToGroup(fsh, basePoolObject)
+	if err != nil {
+		sendErrorResponse(w, err.Error())
+		return
+	}
+
+	bridgeConfig := bridge.BridgeConfig{
+		FSHUUID: fsh.UUID,
+		SPOwner: basePoolObject.Owner,
+	}
+
+	//Write changes to file
+	err = bridgeManager.AppendToConfig(&bridgeConfig)
+	if err != nil {
+		sendErrorResponse(w, err.Error())
+		return
+	}
+	sendOK(w)
+}
+
+func HandleFSHBridgeCheck(w http.ResponseWriter, r *http.Request) {
+	basePool, err := mv(r, "base", true)
+	if err != nil {
+		sendErrorResponse(w, "Invalid base pool")
+		return
+	}
+
+	fsh, err := mv(r, "fsh", true)
+	if err != nil {
+		sendErrorResponse(w, "Invalid fsh UUID")
+		return
+	}
+
+	isBridged, err := bridgeManager.IsBridgedFSH(fsh, basePool)
+	if err != nil {
+		sendErrorResponse(w, err.Error())
+		return
+	}
+
+	js, _ := json.Marshal(isBridged)
+	sendJSONResponse(w, string(js))
+}

+ 6 - 0
system/bridge.json

@@ -0,0 +1,6 @@
+[
+ {
+  "FSHUUID": "admin",
+  "SPOwner": "system"
+ }
+]

+ 18 - 6
web/SystemAO/network/hardware.html

@@ -6,8 +6,7 @@
     <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> -->
-    <title>WIFI</title>
+    <title>Network Interfaces</title>
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <style>
         .ts.items>.item>.image:not(.ts):not(.flexible) {
@@ -20,18 +19,31 @@
 <body>
     <br>
     <div class="ui container">
-        <div class="ui divided items" id="wifi">
+        <div class="ui divided items" id="nics">
         </div>
     </div>
 
     <script>
-        var template = '<div class="item"><div class="image"><img style="height:64px;width:auto" src="/SystemAO/network/img/%image%.png"></div><div class="content"><p class="header">%interfacename%</p><div class="description">%data%</div></div></div>';
+        var template = '<div class="item"><div class="image"><img style="height:64px;width:auto" src="/SystemAO/network/img/%image%"></div><div class="content"><p class="header">%interfacename%</p><div class="description">%data%</div></div></div>';
 
-        $.getJSON("/system/network/getNICinfo", function(result) {
+        $.getJSON("../../system/network/getNICinfo", function(result) {
             $.each(result, function(i, field) {
-                $("#wifi").append(template.replace("%image%", "Unknown").replace("%interfacename%", field["Name"]).replace("%data%", "MAC Address : " + field["HardwareAddr"] + "<br>IPv4 Address : " + field["IPv4Addr"] + "<br>IPv6 Address : " + field["IPv6Addr"]));
+                var image = getImageFromName(field.Name);
+                $("#nics").append(template.replace("%image%", image).replace("%interfacename%", field["Name"]).replace("%data%", "MAC Address : " + field["HardwareAddr"] + "<br>IPv4 Address : " + field["IPv4Addr"] + "<br>IPv6 Address : " + field["IPv6Addr"]));
             });
         });
+
+        function getImageFromName(nicname){
+            nicname = nicname.toLowerCase().trim();
+            if (nicname.includes("loopback")){
+                return "loopback.svg"
+            }else if (nicname.substr(0,3) == "enp"){
+                return "ethernet.svg";
+            }else if (nicname.substr(0,3) == "wlp"){
+                return "wifi.svg";
+            }
+            return "Unknown.png"
+        }
     </script>
 </body>
 

BIN
web/SystemAO/network/img/Ethernet.png


BIN
web/SystemAO/network/img/WiFi.png


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/network/img/ethernet.ai


+ 14 - 0
web/SystemAO/network/img/ethernet.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<path fill="#FFFFFF" stroke="#231815" stroke-width="7" stroke-miterlimit="10" d="M115.668,108.334c0,3.313-2.688,6-6,6H21.333
+	c-3.313,0-6-2.687-6-6V20c0-3.313,2.687-6,6-6h88.335c3.313,0,6,2.687,6,6V108.334z"/>
+<polyline fill="#231815" stroke="#231815" stroke-width="7" stroke-miterlimit="10" points="83.834,92 102.5,92 102.5,30 29.167,30 
+	29.167,92 47.771,92 47.646,92 47.646,101.834 84.021,101.834 84.021,92 "/>
+<line fill="none" stroke="#FFFFFF" stroke-width="7" stroke-miterlimit="10" x1="45.25" y1="32.342" x2="45.25" y2="46.926"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="7" stroke-miterlimit="10" x1="59.583" y1="32.342" x2="59.583" y2="46.926"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="7" stroke-miterlimit="10" x1="73.667" y1="46.926" x2="73.667" y2="32.342"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="7" stroke-miterlimit="10" x1="87.167" y1="32.342" x2="87.167" y2="46.926"/>
+</svg>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/network/img/loopback.ai


+ 11 - 0
web/SystemAO/network/img/loopback.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<line fill="none" stroke="#231815" stroke-width="8" stroke-miterlimit="10" x1="25" y1="16" x2="25" y2="106.834"/>
+<polyline fill="none" stroke="#231815" stroke-width="8" stroke-miterlimit="10" points="41.667,94.5 101.334,94.5 101.334,33.167 
+	100.5,33.167 41.667,33.167 "/>
+<polyline fill="none" stroke="#231815" stroke-width="8" stroke-miterlimit="10" points="55.208,46.708 41.667,33.167 
+	55.417,19.417 "/>
+</svg>

BIN
web/SystemAO/network/img/portforward.png


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/network/img/wifi.ai


+ 13 - 0
web/SystemAO/network/img/wifi.svg

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<circle fill="#231815" stroke="#231815" stroke-miterlimit="10" cx="64" cy="98.551" r="8.801"/>
+<path fill="none" stroke="#231815" stroke-width="14" stroke-miterlimit="10" d="M10.296,49.66
+	c29.139-29.141,76.297-29.141,105.436,0"/>
+<path fill="none" stroke="#231815" stroke-width="14" stroke-miterlimit="10" d="M28.269,65.352
+	c19.917-19.917,52.152-19.917,72.068,0"/>
+<path fill="none" stroke="#231815" stroke-width="14" stroke-miterlimit="10" d="M45.548,81.838
+	c10.367-10.367,27.145-10.367,37.509,0"/>
+</svg>

+ 1 - 1
web/SystemAO/storage/fshedit.html

@@ -13,7 +13,7 @@
             background-color:white;
         }
         .themebackground{
-            background-color:#23916d !important;
+            background-color:#242424 !important;
             color:white !important;
             background-image: url("img/slate.png") !important;
             background-repeat: no-repeat !important;

BIN
web/SystemAO/storage/img/slate.png


BIN
web/SystemAO/storage/img/slate.psd


+ 174 - 12
web/SystemAO/storage/poolEditor.html

@@ -13,9 +13,9 @@
             background-color:white;
         }
         .themebackground{
-            background-color:#29cc96 !important;
+            background-color:#242424 !important;
             color:white !important;
-            background-image: url("/img/public/slate.png") !important;
+            background-image: url("../../img/system/slates/pool-slate.png") !important;
             background-repeat: no-repeat !important;
             background-attachment: fixed !important;
             height:100px;
@@ -192,15 +192,55 @@
               </form>
               <div class="ui divider"></div>
         </div>
-        <br>
-        <button class="ui green button" onclick="toggleCreateNewFileHandler();"><i class="add icon"></i>New File System Handler</button>
-        <button class="ui right floated button" onclick="done();"></i>Done</button>
-        <br><br>
+
+        <!-- Bridge File System Handler options -->
+        <div id="bridgeFsh" style="display:none;">
+            <h3><i class="pallet icon"></i> Bridge File System Handler</h3>
+            <p>Bridge a File System Handler (FSH) from other Storage Pool to this pool. The bridged FSH will implements the same permission and hierarchy as its source pool.</p>
+            <div class="ui form">
+                <p>Source File System Handler</p>
+                <div class="field">
+                    <label>Storage Pool from Permission Group</label>
+                    <div class="ui selection dropdown">
+                        <input id="bridgeTargetPool" type="hidden" name="sourcePool" value="" onchange="loadFSHForPool(this.value);">
+                        <i class="dropdown icon"></i>
+                        <div class="default text"></div>
+                        <div id="otherPools" class="menu">
+                        
+                        </div>
+                    </div>
+                </div>
+
+                <div class="field">
+                    <label>Target File System Handler</label>
+                    <div class="ui selection dropdown">
+                    <input id="souceFSH" type="hidden" name="souceFSH" value="">
+                    <i class="dropdown icon"></i>
+                    <div class="default text"></div>
+                    <div id="sourceFSH" class="menu">
+
+                    </div>
+                    </div>
+                </div>
+                <div class="ui blue message">
+                    Notes: All users that has access to this storage pool will be able to access the newly bridged File System Handler
+                </div>
+                <button class="ui right floated button" onclick='event.preventDefault(); $("#bridgeFsh").slideUp("fast"); '>Close</button>
+                <button class="ui green right floated button" onclick="bridgeFSH();">Bridge Handler to Pool</button>
+                <br><br>
+                <div class="ui divider"></div>
+            </div>
+        </div>
+        <button class="ui blue button" onclick="toggleCreateNewFileHandler();"><i class="add icon"></i>New File System Handler</button><br>
+        <button style="margin-top: 8px;" class="ui basic button" onclick="toggleBridgeFileHandler();"><i class="pallet icon"></i>Bridge FSH from other Pool</button>
+        <button style="margin-top: 8px;" class="ui right floated button" onclick="done();"></i>Done</button>
+        <br><br><br>
     </div>
 
     <script>
         //Load the editing storage pool from window hash
         var editingStoragePool = "system"
+        var otherPoolConfigMap = {};
         if (window.location.hash.length > 1){
             editingStoragePool = window.location.hash.substr(1)
         }
@@ -214,12 +254,22 @@
         $(".ui.dropdown").dropdown();
         $(".ui.checkbox").checkbox();
 
+        initBridgingOptions();
+
         function toggleCreateNewFileHandler(){
+            $("#bridgeFsh").slideUp("fast");
             $("#newfsh").slideToggle("fast", function(){
                 $(window).scrollTop($("#newfsh").offset().top - 10);
             });
         }
 
+        function toggleBridgeFileHandler(){
+            $("#newfsh").slideUp("fast");
+            $("#bridgeFsh").slideDown("fast", function(){
+                $(window).scrollTop($("#bridgeFsh").offset().top - 10);
+            });
+        }
+
         function PoolAlreadyListed(uuid){
             var alreadyExisted = false;
             $(".fsh").each(function(){
@@ -235,6 +285,73 @@
             ao_module_close();
         }
 
+        function initBridgingOptions(){
+            $.get("../../system/storage/pool/list", function(data){
+                $("#otherPools").html('');
+                var firstPool = "";
+                for (var i = 0; i < data.length; i++){
+                    let thisPool = data[i];
+                    if (thisPool.Owner == "system"){
+                        //Do not allow bridging
+                        continue;
+                    }
+                    if (thisPool.Owner != editingStoragePool){
+                        if (firstPool == ""){
+                            firstPool = JSON.parse(JSON.stringify(thisPool.Owner));
+                        }
+                        if (thisPool.Storages == null || thisPool.Storages.length == 0){
+                            otherPoolConfigMap[thisPool.Owner] = [];
+                        }else{
+                            otherPoolConfigMap[thisPool.Owner] = thisPool.Storages
+                        }
+                        $("#otherPools").append(`<div class="item" data-value="${thisPool.Owner}">${thisPool.Owner}</div>`);
+                    }
+                }
+                $("#otherPools").parent().dropdown();
+                $("#otherPools").parent().dropdown("set selected", firstPool);
+                loadFSHForPool(firstPool);
+            });
+        }
+
+        function getDiskIcon(Hierarchy){
+            let diskIcon = "disk";
+            if (Hierarchy == "user"){
+                diskIcon = "blue disk";
+            }else if (Hierarchy == "public"){
+                diskIcon = "grey disk";
+            }else if (Hierarchy == "backup"){
+                diskIcon = "green refresh";
+            }
+
+            return diskIcon;
+        }
+
+        function loadFSHForPool(poolName){
+            if (otherPoolConfigMap[poolName]){
+                $("#sourceFSH").html("");
+                $("#sourceFSH").parent().removeClass('disabled');
+                var storages = otherPoolConfigMap[poolName];
+                if (storages.length > 0){
+                    storages.forEach(fsh => {
+                        var diskIcon = getDiskIcon(fsh.Hierarchy);
+                        $("#sourceFSH").append(`<div class="item" data-value="${fsh.UUID}"><i class="${diskIcon} icon"></i> ${fsh.Name} (${fsh.UUID}:/)</div>`);
+                    });
+                    $("#sourceFSH").parent().dropdown();
+                    $("#sourceFSH").parent().dropdown("set selected", storages[0].UUID);
+                }else{
+                    //No storage inside
+                    $("#sourceFSH").parent().addClass('disabled');
+                    $("#sourceFSH").append(`<div class="item" data-value="nodisk"><i class="remove icon"></i> No File System Handler in this Storage Pool</div>`);
+                    $("#sourceFSH").parent().dropdown();
+                    $("#sourceFSH").parent().dropdown("set selected", "nodisk");
+                }
+               
+            }else{
+                //Pool not exists
+                $("#sourceFSH").parent().addClass('disabled');
+            }
+        }
+
         function loadStoragePoolForGroup(pgname){
             $("#fshList").html("");
             $.get("../../system/storage/pool/list?filter=" + pgname, function(data){
@@ -252,8 +369,10 @@
                             usableIcon = "remove";
                         }
 
+                        var diskIcon = getDiskIcon(storage.Hierarchy);
+
                         $("#fshList").append(`<div class="ui ${color} segment fsh" uuid="${storage.UUID}">
-                            <h3><i class="disk icon"></i> ${storage.Name} (${storage.UUID}:/)</h3>
+                            <h3><i class="${diskIcon} icon typeicon"></i> ${storage.Name} (${storage.UUID}:/)</h3>
                             <ol class="ui list">
                                 <li value="-"><i class="folder icon"></i> Mount Point: ${storage.Path}</li>
                                 <li value="-"><i class="user icon"></i> Hierarchy: ${storage.Hierarchy}</li>
@@ -264,7 +383,7 @@
                                 <div class="ui text loader">Waiting Response</div>
                             </div>
                             <div class="controls">
-                                <a title="Edit Config" uuid="${storage.UUID}" group="${pgname}" onclick="editFSH(this);"><i class="edit icon"></i></a>
+                                <a class="edit" title="Edit Config" uuid="${storage.UUID}" group="${pgname}" onclick="editFSH(this);"><i class="edit icon"></i></a>
                                 <a title="Close Handler" uuid="${storage.UUID}" group="${pgname}" onclick="toggleFSH(this);"><i class="power off icon"></i></a>
                                 <a title="Remove Handler" uuid="${storage.UUID}" group="${pgname}" onclick="removeThisFSH(this);"><i class="remove icon"></i></a>
                             </div>
@@ -320,10 +439,29 @@
                         //List the handlers that will be removed during next boot
                         onlineFSH.forEach(ofsh => {
                             if (allFSH.includes(ofsh) == false){
-                                //Currently online but not in original config. aka Delete Pending
+                                //Currently online but not in original config. aka Delete Pending or it is bridged
                                 $(".fsh").each(function(){
                                     if ($(this).attr("uuid") == ofsh && !($(this).attr("uuid") == "user" || $(this).attr("uuid") == "tmp")){
-                                        $(this).append("<p style='color: red;'>**Will be removed after next reboot</p>")
+                                        let targetDOMElement = $(this);
+                                        $.ajax({
+                                            url: "../../system/storage/pool/checkBridge",
+                                            data: {base: editingStoragePool, fsh: ofsh},
+                                            success: function(data){
+                                                if (data.error != undefined){
+
+                                                }else{
+                                                    if (data == true){
+                                                        targetDOMElement.find(".controls").find(".edit").remove();
+                                                        targetDOMElement.find(".typeicon").attr("class","blue pallet icon typeicon");
+                                                        targetDOMElement.find(".ui.list").prepend(`<li value="-"><i class="exchange icon"></i> Bridged File System Handler</i>`);
+
+                                                    }else{
+                                                        targetDOMElement.append("<p style='color: red;'>**Will be removed after next reboot</p>")
+                                                        targetDOMElement.find(".controls").find(".edit").remove();
+                                                    }
+                                                }
+                                            }
+                                        });
                                     }
                                 });
                             }
@@ -372,8 +510,12 @@
                     url: "../../system/storage/pool/removeHandler",
                     data: {uuid: uuid, group: group},
                     success: function(data){
-                        console.log(data);
-                        window.location.href = "removeSuccess.html#" + JSON.stringify([group, uuid]);
+                        if (data.error !== undefined){
+                            alert(data.error);
+                        }else{
+                            window.location.href = "removeSuccess.html#" + JSON.stringify([group, uuid]);
+                        }
+                        
                     }
                 });
             }
@@ -414,6 +556,26 @@
             });
         }
 
+        function bridgeFSH(){
+            var currentStoragePool = editingStoragePool;
+            var selectedSourceFSH = $("#souceFSH").val();
+
+            $.ajax({
+                url: "../../system/storage/pool/bridge",
+                data: {base: currentStoragePool, fsh: selectedSourceFSH},
+                success: function(data){
+                    if (data.error !== undefined){
+                        alert(data.error);
+                    }else{
+                        //Success
+                        $("#bridgeFsh").slideUp("fast");
+                        loadStoragePoolForGroup(editingStoragePool);
+                        $(window).scrollTop(0);
+                    }
+                }
+            })
+        }
+
         function finishFshEdit(){
             //Reload storage pool
             loadStoragePoolForGroup(editingStoragePool);

+ 2 - 2
web/SystemAO/users/account.html

@@ -22,7 +22,7 @@
                 <div class="six wide column">
                     <div class="ui card">
                         <div class="image">
-                            <img id="profileIcon" src="../users/img/noprofileicon.png">
+                            <img id="profileIcon" src="../users/img/user.svg">
                         </div>
                         <div class="content">
                             <a id="username" class="header"></a>
@@ -74,7 +74,7 @@
 
                 }else{
                     if (data.Icondata == ""){
-                        $("#profileIcon").attr('src', "../users/img/noprofileicon.png");
+                        $("#profileIcon").attr('src', "../users/img/noprofileicon.svg");
                     }else{
                         $("#profileIcon").attr('src', data.Icondata);
                     }

BIN
web/SystemAO/users/img/People icon.png


BIN
web/SystemAO/users/img/group_white.png


BIN
web/SystemAO/users/img/group_white.psd


BIN
web/SystemAO/users/img/noprofileicon.png


+ 10 - 0
web/SystemAO/users/img/noprofileicon.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<circle fill="#00A0E9" cx="64" cy="42.483" r="20.033"/>
+<path fill="#00A0E9" d="M62.895,106.882c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231
+	c0,3.252-3.99,5.883-8.922,5.883c-4.693,0-8.492-2.631-8.492-5.883c-10.728,0-19.41,17.099-19.41,38.231
+	c0,2.742,12.235,4.959,27.355,4.959"/>
+</svg>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/users/img/user - 複製.ai


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/users/img/user-white.ai


+ 10 - 0
web/SystemAO/users/img/user-white.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<circle fill="#FFFFFF" cx="64" cy="42.483" r="20.033"/>
+<path fill="#FFFFFF" d="M62.895,106.882c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231
+	c0,3.252-3.99,5.883-8.922,5.883c-4.693,0-8.492-2.631-8.492-5.883c-10.728,0-19.41,17.099-19.41,38.231
+	c0,2.742,12.235,4.959,27.355,4.959"/>
+</svg>

BIN
web/SystemAO/users/img/user.png


BIN
web/SystemAO/users/img/user.psd


+ 10 - 0
web/SystemAO/users/img/user.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<circle fill="#00A0E9" cx="64" cy="42.483" r="20.033"/>
+<path fill="#00A0E9" d="M62.895,106.882c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231
+	c0,3.252-3.99,5.883-8.922,5.883c-4.693,0-8.492-2.631-8.492-5.883c-10.728,0-19.41,17.099-19.41,38.231
+	c0,2.742,12.235,4.959,27.355,4.959"/>
+</svg>

BIN
web/SystemAO/users/img/user_white.png


BIN
web/SystemAO/users/img/user_white.psd


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/users/img/users-white.ai


+ 22 - 0
web/SystemAO/users/img/users-white.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
+	 height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<g id="圖層_2">
+	<circle fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" cx="97.083" cy="47.867" r="15.248"/>
+	<path fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" d="M96.241,96.882c12.353,0,22.348-1.688,22.348-3.774
+		c0-16.084-6.702-29.099-14.986-29.099c0,2.476-3.037,4.478-6.79,4.478c-3.572,0-6.464-2.002-6.464-4.478
+		c-8.165,0-14.772,13.015-14.772,29.099c0,2.087,9.313,3.774,20.82,3.774"/>
+	<circle fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" cx="31.583" cy="45.632" r="15.248"/>
+	<path fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" d="M30.742,94.646c12.353,0,22.348-1.688,22.348-3.773
+		c0-16.084-6.702-29.1-14.986-29.1c0,2.476-3.037,4.479-6.79,4.479c-3.572,0-6.464-2.003-6.464-4.479
+		c-8.165,0-14.772,13.016-14.772,29.1c0,2.086,9.313,3.773,20.82,3.773"/>
+</g>
+<g id="圖層_1">
+	<circle fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" cx="64" cy="42.483" r="20.033"/>
+	<path fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" d="M62.895,106.882
+		c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231c0,3.252-3.99,5.883-8.922,5.883
+		c-4.693,0-8.492-2.631-8.492-5.883c-10.728,0-19.41,17.099-19.41,38.231c0,2.742,12.235,4.959,27.355,4.959"/>
+</g>
+</svg>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/SystemAO/users/img/users.ai


BIN
web/SystemAO/users/img/users.png


BIN
web/SystemAO/users/img/users.psd


+ 22 - 0
web/SystemAO/users/img/users.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
+	 height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<g id="圖層_2">
+	<circle fill="#00A0E9" stroke="#FFFFFF" stroke-miterlimit="10" cx="97.083" cy="47.867" r="15.248"/>
+	<path fill="#00A0E9" stroke="#FFFFFF" stroke-miterlimit="10" d="M96.241,96.882c12.353,0,22.348-1.688,22.348-3.774
+		c0-16.084-6.702-29.099-14.986-29.099c0,2.476-3.037,4.478-6.79,4.478c-3.572,0-6.464-2.002-6.464-4.478
+		c-8.165,0-14.772,13.015-14.772,29.099c0,2.087,9.313,3.774,20.82,3.774"/>
+	<circle fill="#00A0E9" stroke="#FFFFFF" stroke-miterlimit="10" cx="31.583" cy="45.632" r="15.248"/>
+	<path fill="#00A0E9" stroke="#FFFFFF" stroke-miterlimit="10" d="M30.742,94.646c12.353,0,22.348-1.688,22.348-3.773
+		c0-16.084-6.702-29.1-14.986-29.1c0,2.476-3.037,4.479-6.79,4.479c-3.572,0-6.464-2.003-6.464-4.479
+		c-8.165,0-14.772,13.016-14.772,29.1c0,2.086,9.313,3.773,20.82,3.773"/>
+</g>
+<g id="圖層_1">
+	<circle fill="#00A0E9" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" cx="64" cy="42.483" r="20.033"/>
+	<path fill="#00A0E9" stroke="#FFFFFF" stroke-width="3" stroke-miterlimit="10" d="M62.895,106.882
+		c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231c0,3.252-3.99,5.883-8.922,5.883
+		c-4.693,0-8.492-2.631-8.492-5.883c-10.728,0-19.41,17.099-19.41,38.231c0,2.742,12.235,4.959,27.355,4.959"/>
+</g>
+</svg>

+ 1 - 1
web/desktop.system

@@ -923,7 +923,7 @@
         <div class="ts small single line items">
             <div class="item">
                 <div class="ts mini image">
-                    <img class="usericon" src="img/desktop/system_icon/user.png">
+                    <img class="usericon" src="img/desktop/system_icon/user.svg">
                 </div>
                 <div class="content">
                     <div id="username" class="header">User</div>

BIN
web/img/desktop/system_icon/user.png


+ 10 - 0
web/img/desktop/system_icon/user.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<circle fill="#00A0E9" cx="64" cy="42.483" r="20.033"/>
+<path fill="#00A0E9" d="M62.895,106.882c16.229,0,29.361-2.217,29.361-4.959c0-21.133-8.806-38.231-19.689-38.231
+	c0,3.252-3.99,5.883-8.922,5.883c-4.693,0-8.492-2.631-8.492-5.883c-10.728,0-19.41,17.099-19.41,38.231
+	c0,2.742,12.235,4.959,27.355,4.959"/>
+</svg>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/img/system/cluster.ai


BIN
web/img/system/cluster.png


+ 21 - 0
web/img/system/cluster.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
+	 height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<g id="圖層_1">
+	<rect x="12.563" y="32.823" fill="#727171" width="102.834" height="86.829"/>
+	<path fill="#3E3A39" d="M113.23,114.962c0,1.729-1.357,3.127-3.035,3.127H17.64c-1.676,0-3.035-1.398-3.035-3.127V95.153
+		c0-1.729,1.359-3.127,3.035-3.127h92.556c1.678,0,3.035,1.398,3.035,3.127V114.962z"/>
+	<circle fill="#00A0E9" cx="100.48" cy="105.066" r="3.708"/>
+</g>
+<g id="圖層_2">
+	<polygon fill="#DCDDDD" points="96.438,14.002 30.271,14.002 12.604,32.823 115.438,32.823 	"/>
+	<path fill="#3E3A39" d="M113.023,85.822c0,1.729-1.357,3.127-3.035,3.127H17.433c-1.676,0-3.035-1.398-3.035-3.127V66.014
+		c0-1.729,1.359-3.127,3.035-3.127h92.556c1.678,0,3.035,1.398,3.035,3.127V85.822z"/>
+	<circle fill="#00A0E9" cx="100.273" cy="75.929" r="3.708"/>
+	<path fill="#3E3A39" d="M112.92,57.3c0,1.729-1.357,3.127-3.035,3.127H17.329c-1.676,0-3.035-1.398-3.035-3.127V37.492
+		c0-1.729,1.359-3.127,3.035-3.127h92.556c1.678,0,3.035,1.398,3.035,3.127V57.3z"/>
+	<circle fill="#00A0E9" cx="100.17" cy="47.406" r="3.708"/>
+</g>
+</svg>

BIN
web/img/system/drive-virtual.png


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27 - 0
web/img/system/nas.ai


+ 17 - 0
web/img/system/nas.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="128px" height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<rect x="15.49" y="12.625" fill="#727171" width="79.344" height="102.833"/>
+<path fill="#FFFFFF" d="M41.144,105.691c0,3.358,2.174,6.082,4.856,6.082h40.651c2.68,0,4.854-2.724,4.854-6.082V33.053
+	c0-3.359-2.175-6.083-4.854-6.083H46c-2.682,0-4.856,2.724-4.856,6.083V105.691z"/>
+<polygon fill="#DCDDDD" points="112.51,96.458 112.51,30.292 94.834,12.626 94.834,115.458 "/>
+<path fill="#3E3A39" d="M70.303,109.527c-1.399,0-2.532-1.1-2.532-2.457V32.105c0-1.357,1.133-2.458,2.532-2.458h16.044
+	c1.4,0,2.533,1.101,2.533,2.458v74.965c0,1.359-1.133,2.459-2.533,2.459L70.303,109.527z"/>
+<circle fill="#00A0E9" cx="78.326" cy="39.904" r="3.708"/>
+<path fill="#3E3A39" d="M46.237,109.527c-1.399,0-2.532-1.1-2.532-2.457V32.105c0-1.357,1.133-2.458,2.532-2.458h16.044
+	c1.4,0,2.533,1.101,2.533,2.458v74.965c0,1.359-1.133,2.459-2.533,2.459L46.237,109.527z"/>
+<circle fill="#00A0E9" cx="54.261" cy="39.904" r="3.708"/>
+<circle fill="#00A0E9" cx="28.677" cy="27.549" r="6.385"/>
+</svg>

BIN
web/img/system/slates/pool-slate.png


BIN
web/img/system/slates/pool-slate.psd


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä