| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082 |
- <!DOCTYPE html>
- <html>
- <head>
- <title locale="title/title">File Properties</title>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
- <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/ao_module.js"></script>
- <script type="text/javascript" src="../../script/applocale.js"></script>
- <link rel="stylesheet" href="dialog.css">
- <script type="text/javascript" src="js/dialog.js"></script>
- </head>
- <body class="whiteTheme">
- <div class="dialog">
- <div class="dlgTabs">
- <div class="dlgTab active" tab="basic" onclick="switchTab('basic');" locale="tab/basic">Basic</div>
- <div class="dlgTab" tab="permission" onclick="switchTab('permission');" locale="tab/permission">Permissions</div>
- <div class="dlgTab" tab="versions" onclick="switchTab('versions');" locale="tab/versions">Versions</div>
- <div class="dlgTab" tab="storage" id="storageTab" style="display:none;" onclick="switchTab('storage');" locale="tab/storage">Storage</div>
- </div>
- <div class="dialogBody">
- <!-- Basic -->
- <div class="dlgPage active" id="page_basic">
- <div class="dlgFileHead">
- <div id="basicIcon"></div>
- <div class="dlgFileHeadInfo">
- <input id="basicName" class="dlgField" type="text" value="" readonly>
- <div class="dlgMime" id="basicMime"></div>
- </div>
- </div>
- <div id="basicProperties"></div>
- </div>
- <!-- Permissions -->
- <div class="dlgPage" id="page_permission">
- <div id="permissionNotice" class="dlgNotice" style="display:none;">
- <i class="info circle icon"></i>
- <span id="permissionNoticeText"></span>
- </div>
- <div id="permissionForm">
- <div class="dlgFormRow">
- <label locale="perm/owner">Owner</label>
- <div class="control">
- <div class="staticValue"><i class="user icon"></i> <span id="permOwnerName">-</span></div>
- </div>
- </div>
- <div class="dlgFormRow">
- <label locale="perm/group">Group</label>
- <div class="control">
- <div class="dlgSelectWrap">
- <select id="permGroup" class="dlgField" disabled></select>
- </div>
- </div>
- </div>
- <div class="dlgSectionLabel" locale="perm/access">Access Permissions</div>
- <div class="dlgFormRow">
- <label locale="perm/owner">Owner</label>
- <div class="control">
- <div class="dlgSelectWrap">
- <select id="permUser" class="dlgField" onchange="syncFromSelectors();"></select>
- </div>
- </div>
- <div class="suffix" id="permUserBits">(---)</div>
- </div>
- <div class="dlgFormRow">
- <label locale="perm/group">Group</label>
- <div class="control">
- <div class="dlgSelectWrap">
- <select id="permGrp" class="dlgField" onchange="syncFromSelectors();"></select>
- </div>
- </div>
- <div class="suffix" id="permGrpBits">(---)</div>
- </div>
- <div class="dlgFormRow">
- <label locale="perm/others">Others</label>
- <div class="control">
- <div class="dlgSelectWrap">
- <select id="permOther" class="dlgField" onchange="syncFromSelectors();"></select>
- </div>
- </div>
- <div class="suffix" id="permOtherBits">(---)</div>
- </div>
- <div class="dlgDivider"></div>
- <div class="dlgFormRow">
- <label locale="perm/octal">Octal</label>
- <div class="control" style="max-width: 100px;">
- <input id="permOctal" class="dlgField" type="text" maxlength="4" oninput="syncFromOctal();">
- </div>
- </div>
- <div class="dlgFormRow" id="recursiveRow" title="Recursive permission change is not supported by this server">
- <label style="flex: 1 1 auto;" locale="perm/recursive">Apply to enclosed files and folders</label>
- <div class="dlgToggle">
- <input id="permRecursive" type="checkbox" disabled>
- <label class="slider" for="permRecursive"></label>
- </div>
- </div>
- </div>
- </div>
- <!-- Versions -->
- <div class="dlgPage" id="page_versions">
- <div id="versionsUnavailable" class="dlgEmpty" style="display:none;">
- <i class="history icon"></i>
- <div locale="message/noVersions">Version history is only kept for files</div>
- </div>
- <div id="versionsManager" style="display:none;">
- <div class="dlgNotice">
- <i class="info circle icon"></i>
- <span locale="versions/desc">Any file version that is more than 30 days old is removed from the system automatically.</span>
- </div>
- <button class="dlgBtn" onclick="createSnapshot();">
- <i class="plus icon"></i> <span locale="versions/newver">Create New Version</span>
- </button>
- <div class="dlgSectionLabel" locale="versions/history">Version History</div>
- <div class="dlgList" id="versionList"></div>
- </div>
- </div>
- <!-- Storage: where the file physically is, for drives that know -->
- <div class="dlgPage" id="page_storage">
- <div id="storageFields"></div>
- <div id="storageCopiesSection" style="display:none;">
- <div class="dlgSectionLabel" id="storageCopiesLabel" locale="storage/copies">File Copies</div>
- <div id="storageCopies"></div>
- </div>
- </div>
- </div>
- <div class="dialogFooter">
- <button class="dlgBtn footerBasic" onclick="copyPath();"><i class="copy outline icon"></i> <span locale="button/copyPath">Copy Path</span></button>
- <button id="deleteAllVersionsBtn" class="dlgBtn danger footerVersions" style="display:none;" onclick="deleteAllVersions();">
- <i class="trash icon"></i> <span locale="versions/removeAll">Delete All Versions</span>
- </button>
- <div class="spacer"></div>
- <button class="dlgBtn footerBasic footerVersions footerStorage" onclick="ao_module_close();" locale="button/close">Close</button>
- <button class="dlgBtn footerPermission" style="display:none;" onclick="ao_module_close();" locale="button/cancel">Cancel</button>
- <button id="permApplyBtn" class="dlgBtn primary footerPermission" style="display:none;" onclick="applyPermission();" locale="button/confirm">OK</button>
- </div>
- </div>
- <div class="dlgMenu" id="rowMenu"></div>
- <div id="dlgLoader" class="dlgLoader">
- <div class="dlgSpinner"></div>
- <div locale="loader/loadingFileSize">Calculating File Size</div>
- </div>
- <script>
- /*
- file_properties.html
- Three tabs over one selection: everything the object is -
- type, size, opener, paths, dates and ownership (Basic), who may
- touch it (Permissions) and its snapshot history (Versions). The
- tab strip replaced a single scrolling table that mixed all
- three, plus two buttons that opened further windows.
- Basic groups its rows by kind - what / opens with / where /
- when / who - because a flat list of ten rows reads as one
- undifferentiated block however the rows are ordered.
- Versions is the whole snapshot manager, which used to be a
- separate window reached through a button in here. Nothing about
- it wanted its own window: it acts on the one file this dialog
- is already about.
- Permissions are a POSIX concept: on a Windows host the tab is
- still readable but visibly out of reach, because the server
- refuses chmod there.
- */
- var files = ao_module_loadInputFiles();
- var fileInfo = {}; //Properties of the single selected object
- var multiProperties = []; //Properties of every object, multi selection only
- var hostOS = "";
- var permissionSupported = true;
- var permissionLoaded = false;
- var currentTab = "basic";
- //Storage tab state, filled in only by a drive that reports one
- var storageInfo = null;
- //Version manager state
- var versionsLoaded = false;
- var versionList = [];
- var versionPollTimer = null;
- //Octal digit to a human readable access level. Index is the digit.
- var permissionLevels = [
- {bits: "---", key: "perm/level/none", text: "None"},
- {bits: "--x", key: "perm/level/execute", text: "Execute only"},
- {bits: "-w-", key: "perm/level/write", text: "Write only"},
- {bits: "-wx", key: "perm/level/writeExecute", text: "Write and execute"},
- {bits: "r--", key: "perm/level/read", text: "Read only"},
- {bits: "r-x", key: "perm/level/readExecute", text: "Read and execute"},
- {bits: "rw-", key: "perm/level/readWrite", text: "Read and write"},
- {bits: "rwx", key: "perm/level/readWriteExecute", text: "Read, write and execute"}
- ];
- $(document).on("click", function(event){
- if ($(event.target).closest("#rowMenu, .dlgIconBtn").length == 0){
- $("#rowMenu").hide();
- }
- });
- applocale.init("../locale/file_properties.json", function(){
- applocale.translate();
- buildPermissionSelectors();
- dlgInitTheme();
- loadHostInfo();
- initFileProperties();
- loadStorageInfo();
- });
- /* ---------------------------------------------------------------
- Tabs
- --------------------------------------------------------------- */
- function switchTab(tabName){
- currentTab = tabName;
- $(".dlgTab").removeClass("active");
- $('.dlgTab[tab="' + tabName + '"]').addClass("active");
- $(".dlgPage").removeClass("active");
- $("#page_" + tabName).addClass("active");
- $(".footerBasic, .footerVersions, .footerPermission, .footerStorage").hide();
- if (tabName == "permission"){
- $(".footerPermission").show();
- if (!permissionLoaded){
- loadPermission();
- }
- }else if (tabName == "storage"){
- $(".footerStorage").show();
- }else if (tabName == "versions"){
- $(".footerVersions").show();
- if (!versionsSupported()){
- //Nothing to delete where no history is kept
- $("#deleteAllVersionsBtn").hide();
- }else if (!versionsLoaded){
- versionsLoaded = true;
- loadVersionHistory();
- }
- }else{
- $(".footerBasic").show();
- }
- //Another window may snapshot the same file, but only watch for
- //that while the list is actually on screen
- $("#rowMenu").hide();
- if (versionPollTimer != null){
- clearInterval(versionPollTimer);
- versionPollTimer = null;
- }
- if (tabName == "versions" && versionsSupported()){
- versionPollTimer = setInterval(checkHistoryListUpdate, 3000);
- }
- }
- /* ---------------------------------------------------------------
- Host capabilities
- --------------------------------------------------------------- */
- function loadHostInfo(){
- $.get("../../system/info/getArOZInfo", function(data){
- hostOS = data.HostOS;
- if (hostOS == "windows"){
- //The server rejects chmod on a Windows host, so the
- //whole tab is shown read only rather than pretending
- permissionSupported = false;
- $('.dlgTab[tab="permission"]').addClass("unsupported");
- $("#permissionNoticeText").text(applocale.getString("perm/unsupported/windows",
- "This server runs on Windows, which does not use POSIX file permissions. The values below are shown for reference only."));
- $("#permissionNotice").show();
- $("#permissionForm").addClass("dlgUnsupported");
- $("#permApplyBtn").addClass("disabled");
- }
- });
- }
- /* ---------------------------------------------------------------
- Basic and Versions tabs
- --------------------------------------------------------------- */
- function initFileProperties(){
- if (files.length == 1){
- getFileProp(files[0], renderSingleObject);
- }else if (files.length > 1){
- multiProperties = [];
- for (var i = 0; i < files.length; i++){
- getFileProp(files[i], function(data){
- multiProperties.push(data);
- if (multiProperties.length == files.length){
- renderMultipleObjects();
- }
- });
- }
- }else{
- hideLoader();
- $("#basicProperties").html('<div class="dlgEmpty"><i class="file outline icon"></i><div>' +
- applocale.getString("message/nofile", "No file selected") + "</div></div>");
- }
- }
- function getFileProp(vpath, callback){
- $.ajax({
- url: "../../system/file_system/getProperties",
- data: {path: vpath},
- method: "POST",
- success: function(data){
- callback(data);
- },
- error: function(){
- callback({error: "Unable to reach the file system"});
- }
- });
- }
- function propRow(key, value, dim){
- return '<div class="dlgPropRow"><span class="k">' + dlgEscape(key) + '</span>' +
- '<span class="v' + (dim ? " dim" : "") + '">' + value + "</span></div>";
- }
- function localizedKey(key){
- return applocale.getString("properties/key/" + key, key);
- }
- //Rows of the same kind travel together; the gap between groups is
- //what keeps a ten row list scannable
- function propGroup(rows){
- var kept = rows.filter(function(row){ return row != ""; });
- if (kept.length == 0){
- return "";
- }
- return '<div class="dlgPropGroup">' + kept.join("") + "</div>";
- }
- function renderSingleObject(data){
- hideLoader();
- fileInfo = data;
- if (data.error !== undefined){
- $("#basicProperties").html('<div class="dlgEmpty"><i class="question circle outline icon"></i><div>' +
- dlgEscape(data.error) + "</div></div>");
- $('.dlgTab[tab="permission"]').addClass("unsupported");
- return;
- }
- var isShortcut = (!data.IsDirectory && data.Basename.split(".").pop() == "shortcut");
- //Heading
- $("#basicIcon").html(dlgFileIconHTML(data.Basename, data.IsDirectory));
- $("#basicName").val(data.Basename);
- $("#basicMime").text(data.MimeType);
- loadThumbnail(data);
- //Basic property rows
- var fileTypeText = applocale.getString("type/file", "File");
- if (data.IsDirectory){
- fileTypeText = applocale.getString("type/folder", "Folder");
- }else if (isShortcut){
- fileTypeText = applocale.getString("type/shortcut", "System Shortcut");
- }
- //A real file is the only thing that has an opener to show
- var hasOpener = (!data.IsDirectory && !isShortcut);
- var basicHTML = "";
- //What it is
- basicHTML += propGroup([
- propRow(localizedKey("File Type"), dlgEscape(fileTypeText)),
- propRow(data.IsDirectory ? localizedKey("Folder Size") : localizedKey("File Size"), sizeText(data.Filesize))
- ]);
- //What opens it
- basicHTML += propGroup([
- hasOpener ? '<div id="defaultOpenerRow">' + propRow(localizedKey("Default WebApp"),
- '<span class="dim">' + applocale.getString("message/loading", "Loading...") + "</span>") + "</div>" : ""
- ]);
- //Where it lives
- basicHTML += propGroup([
- propRow(localizedKey("Location"), dlgEscape(data.VirtualDirname)),
- propRow(localizedKey("Virtual Path"), dlgEscape(data.VirtualPath)),
- propRow(localizedKey("Storage Path"), dlgEscape(data.StoragePath))
- ]);
- //When it changed
- basicHTML += propGroup([
- propRow(localizedKey("Last Modified"), modTimeText(data)),
- propRow(localizedKey("Created"), creationTimeText(data))
- ]);
- //Who it belongs to
- basicHTML += propGroup([
- propRow(localizedKey("Owner"), dlgEscape(data.Owner)),
- propRow(localizedKey("Permission"), '<span style="font-family: Consolas, Menlo, monospace;">' + dlgEscape(data.Permission) + "</span>")
- ]);
- $("#basicProperties").html(basicHTML);
- if (hasOpener){
- loadDefaultOpener(data);
- }
- //Versions tab
- renderVersionsTab(data, isShortcut);
- //Permission tab defaults
- $("#permOwnerName").text(data.Owner);
- loadUserGroups(data.Owner);
- }
- function renderMultipleObjects(){
- hideLoader();
- var totalSize = 0;
- var fileCount = 0;
- var folderCount = 0;
- var readable = [];
- for (var i = 0; i < multiProperties.length; i++){
- if (multiProperties[i].error !== undefined){
- continue;
- }
- readable.push(multiProperties[i]);
- totalSize += multiProperties[i].Filesize;
- if (multiProperties[i].IsDirectory){
- folderCount++;
- }else{
- fileCount++;
- }
- }
- if (readable.length == 0){
- $("#basicProperties").html('<div class="dlgEmpty"><i class="question circle outline icon"></i><div>' +
- applocale.getString("message/unreadable", "The selected items could not be read") + "</div></div>");
- return;
- }
- fileInfo = readable[0];
- $("#basicIcon").html(dlgMultiIconHTML());
- $("#basicName").val(readable.length + applocale.getString("counter/items", " items selected"));
- $("#basicMime").text(applocale.getString("selection/multi", "Multiple selections"));
- var basicHTML = "";
- basicHTML += propGroup([
- propRow(localizedKey("File Type"), dlgEscape(
- fileCount + applocale.getString("counter/files", " Files") + ", " +
- folderCount + applocale.getString("counter/folders", " Folders"))),
- propRow(localizedKey("Total Size"), sizeText(totalSize))
- ]);
- basicHTML += propGroup([
- propRow(localizedKey("Location"), dlgEscape(readable[0].VirtualDirname)),
- propRow(localizedKey("Virtual Directory"), dlgEscape(readable[0].VirtualDirname + "/")),
- propRow(localizedKey("Storage Directory"), dlgEscape(readable[0].StorageDirname + "/"))
- ]);
- basicHTML += propGroup([
- propRow(localizedKey("Owner"), dlgEscape(readable[0].Owner))
- ]);
- $("#basicProperties").html(basicHTML);
- //Version history is per file, so a selection has none to show
- renderVersionsTab(null, false);
- $("#permOwnerName").text(readable[0].Owner);
- loadUserGroups(readable[0].Owner);
- }
- /* ---------------------------------------------------------------
- Versions tab
- Snapshots are kept per file under .metadata/.localver, so a
- folder, a shortcut or a multiple selection has nothing to show
- and the tab greys out instead of offering actions that would
- fail.
- --------------------------------------------------------------- */
- function versionsSupported(){
- return (files.length == 1 && fileInfo.error === undefined &&
- fileInfo.IsDirectory === false && !isShortcutFile(fileInfo));
- }
- function isShortcutFile(data){
- return (data != undefined && data.Basename != undefined &&
- data.Basename.split(".").pop() == "shortcut");
- }
- function renderVersionsTab(data, isShortcut){
- if (data == null || data.IsDirectory || isShortcut){
- $("#versionsUnavailable").show();
- $("#versionsManager").hide();
- $("#deleteAllVersionsBtn").hide();
- $('.dlgTab[tab="versions"]').addClass("unsupported");
- return;
- }
- $("#versionsUnavailable").hide();
- $("#versionsManager").show();
- $("#versionList").html('<div class="dlgEmpty"><div class="dlgSpinner" style="margin: 0px auto 10px auto;"></div><div>' +
- applocale.getString("message/loading", "Loading...") + "</div></div>");
- //This tab may already be the one on screen when the properties
- //finally arrive, in which case nothing else starts the load
- if (currentTab == "versions" && !versionsLoaded){
- versionsLoaded = true;
- loadVersionHistory();
- }
- }
- function loadVersionHistory(){
- $.ajax({
- url: "../../system/file_system/versionHistory",
- data: {path: files[0]},
- method: "POST",
- success: function(data){
- if (data == undefined || data.error !== undefined){
- $("#versionList").html('<div class="dlgEmpty"><i class="question circle outline icon"></i><div>' +
- dlgEscape(data == undefined ? "No response" : data.error) + "</div></div>");
- return;
- }
- versionList = data.Versions;
- renderVersionList();
- },
- error: function(){
- $("#versionList").html('<div class="dlgEmpty"><i class="question circle outline icon"></i><div>' +
- applocale.getString("versions/failed", "Unable to read the version history") + "</div></div>");
- }
- });
- }
- function checkHistoryListUpdate(){
- $.ajax({
- url: "../../system/file_system/versionHistory",
- data: {path: files[0]},
- method: "POST",
- success: function(data){
- if (data != undefined && data.error == undefined && versionList.length != data.Versions.length){
- versionList = data.Versions;
- renderVersionList();
- }
- }
- });
- }
- function renderVersionList(){
- var html = "";
- //The file as it stands now is the newest version. Numbers are
- //assigned here, not by the server: a snapshot is named by its
- //timestamp, which says nothing about the order on screen.
- html += versionRowHTML({
- number: versionList.length + 1,
- current: true,
- time: fileInfo.LastModTime,
- size: fileInfo.Filesize,
- historyID: "",
- relpath: ""
- });
- //Server returns newest first, so the number counts down
- for (var i = 0; i < versionList.length; i++){
- html += versionRowHTML({
- number: versionList.length - i,
- current: false,
- time: versionList[i].OverwriteTime,
- size: versionList[i].Filesize,
- historyID: versionList[i].HistoryID,
- relpath: versionList[i].Relpath
- });
- }
- $("#versionList").html(html);
- $("#deleteAllVersionsBtn").toggleClass("disabled", versionList.length == 0);
- if (versionList.length == 0){
- $("#versionList").append('<div class="dlgEmpty"><div>' +
- applocale.getString("versions/empty/desc", "This file has no previous version histories.") + "</div></div>");
- }
- }
- function versionRowHTML(entry){
- var title = applocale.getString("versions/label", "Version") + " " + entry.number;
- if (entry.current){
- title += " " + applocale.getString("versions/current", "(current)");
- }
- var owner = (fileInfo.Owner == undefined || fileInfo.Owner == "") ? "" : " (" + fileInfo.Owner + ")";
- return '<div class="dlgListRow static' + (entry.current ? " selected" : "") + '">' +
- '<div class="rowIcon"><i class="file outline icon"></i></div>' +
- '<div class="rowText">' +
- '<div class="rowTitle">' + dlgEscape(title) + "</div>" +
- '<div class="rowSub">' + dlgEscape(entry.time + owner) + "</div>" +
- "</div>" +
- '<div class="rowMeta">' + dlgEscape(dlgBytesToSize(entry.size)) + "</div>" +
- '<button class="dlgIconBtn" verid="' + dlgEscape(entry.historyID) + '" relpath="' + dlgEscape(entry.relpath) + '" ' +
- 'iscurrent="' + (entry.current ? "true" : "false") + '" onclick="openRowMenu(this);">' +
- '<i class="ellipsis horizontal icon"></i></button>' +
- "</div>";
- }
- function openRowMenu(button){
- var isCurrent = $(button).attr("iscurrent") == "true";
- var versionID = $(button).attr("verid");
- var relpath = $(button).attr("relpath");
- var html = '<div class="dlgMenuItem" onclick="downloadVersion(\'' + dlgEscape(relpath) + '\');">' +
- '<i class="download icon"></i>' + applocale.getString("versions/action/download", "Download Version") + "</div>";
- if (!isCurrent){
- html += '<div class="dlgMenuItem" onclick="restoreVersion(\'' + dlgEscape(versionID) + '\');">' +
- '<i class="history icon"></i>' + applocale.getString("versions/action/restore", "Restore This Version") + "</div>";
- html += '<div class="dlgMenuItem danger" onclick="deleteVersion(\'' + dlgEscape(versionID) + '\');">' +
- '<i class="trash icon"></i>' + applocale.getString("versions/action/delete", "Delete") + "</div>";
- }
- var menu = $("#rowMenu");
- menu.html(html).show();
- //Anchor under the button, kept inside the window
- var offset = $(button).offset();
- var left = offset.left + $(button).outerWidth() - menu.outerWidth();
- var top = offset.top + $(button).outerHeight() + 4;
- if (left < 8){
- left = 8;
- }
- if (top + menu.outerHeight() > $(window).height() - 8){
- top = offset.top - menu.outerHeight() - 4;
- }
- menu.css({left: left + "px", top: top + "px"});
- }
- function downloadVersion(relpath){
- $("#rowMenu").hide();
- var dirname = files[0].split("/");
- var basename = dirname.pop();
- var accessPath = dirname.join("/") + "/" + (relpath == "" ? basename : relpath);
- window.open("../../media/download?file=" + accessPath);
- }
- function createSnapshot(){
- $.ajax({
- url: "../../system/file_system/versionHistory",
- data: {path: files[0], opr: "new"},
- method: "POST",
- success: function(data){
- if (data != undefined && data.error != undefined){
- dlgToast(data.error);
- }else{
- dlgToast(applocale.getString("versions/msg/created", "New Version Created"));
- loadVersionHistory();
- }
- }
- });
- }
- function restoreVersion(versionID){
- $("#rowMenu").hide();
- if (!confirm(applocale.getString("versions/warn/restore", "Restoring this file will remove all newer version histories of this file. Confirm?"))){
- return;
- }
- $.ajax({
- url: "../../system/file_system/versionHistory",
- data: {path: files[0], opr: "restore", histid: versionID},
- method: "POST",
- success: function(data){
- if (data != undefined && data.error != undefined){
- dlgToast(data.error);
- return;
- }
- dlgToast(applocale.getString("versions/msg/restored", "Restore Succeed"));
- //The file itself changed, so the Basic tab is stale too
- refreshAfterRestore();
- }
- });
- }
- //A restore rewrites the file, so the size and modification time on
- //the Basic tab are stale the moment it succeeds
- function refreshAfterRestore(){
- getFileProp(files[0], function(data){
- if (data.error === undefined){
- renderSingleObject(data);
- }
- loadVersionHistory();
- });
- }
- function deleteVersion(versionID){
- $("#rowMenu").hide();
- $.ajax({
- url: "../../system/file_system/versionHistory",
- data: {path: files[0], opr: "delete", histid: versionID},
- method: "POST",
- success: function(data){
- if (data != undefined && data.error != undefined){
- dlgToast(data.error);
- }else{
- loadVersionHistory();
- }
- }
- });
- }
- function deleteAllVersions(){
- if (!confirm(applocale.getString("versions/warn/deleteall", "This operation will PERMANENTLY DELETE ALL version history of this file. Confirm?"))){
- return;
- }
- $.ajax({
- url: "../../system/file_system/versionHistory",
- data: {path: files[0], opr: "deleteAll"},
- method: "POST",
- success: function(data){
- if (data != undefined && data.error != undefined){
- dlgToast(data.error);
- }else{
- dlgToast(applocale.getString("versions/msg/deletedall", "All version history deleted"));
- loadVersionHistory();
- }
- }
- });
- }
- function loadDefaultOpener(data){
- $.ajax({
- url: "../../system/modules/getDefault",
- method: "GET",
- data: {
- opr: "launch",
- ext: "." + data.Basename.split(".").pop(),
- mode: "launch"
- },
- success: function(openerinfo){
- var value = "";
- if (openerinfo == undefined || openerinfo.Name == undefined){
- value = '<a href="#" onclick="changeDefaultWebApp(); return false;">' +
- applocale.getString("message/setDefault", "Set Default WebApp") + "</a>";
- }else{
- var iconPath = (openerinfo.IconPath == "") ? "img/system/favicon.png" : openerinfo.IconPath;
- value = '<span style="display: inline-flex; align-items: center; gap: 8px;">' +
- '<img src="../../' + dlgEscape(iconPath) + '" style="height: 20px; width: 20px; object-fit: contain;">' +
- dlgEscape(openerinfo.Name) +
- '<a href="#" style="margin-left: 4px;" onclick="changeDefaultWebApp(); return false;">' +
- applocale.getString("button/change", "Change") + "</a></span>";
- }
- $("#defaultOpenerRow").html(propRow(localizedKey("Default WebApp"), value));
- }
- });
- }
- //A thumbnail says more about an image than any glyph can, but most
- //file types have none: the drawn icon stays until one arrives
- function loadThumbnail(data){
- if (data.IsDirectory){
- return;
- }
- fetch("../../system/file_system/loadThumbnail?vpath=" + encodeURIComponent(data.VirtualPath))
- .then(function(response){ return response.json(); })
- .then(function(imageData){
- if (imageData == undefined || imageData.error !== undefined || String(imageData).trim() == ""){
- return;
- }
- $("#basicIcon").find(".dlgFileIcon").html('<img class="thumbnail" src="data:image/jpg;base64,' + imageData + '">');
- })
- .catch(function(){
- //Keep the drawn icon
- });
- }
- function sizeText(bytes){
- if (bytes < 0){
- //Network drives do not report a size
- return '<span class="dim">' + applocale.getString("properties/error/Not available for network folders", "Not available for network folders") + "</span>";
- }
- return dlgEscape(dlgBytesToSize(bytes) + " (" + bytes + " bytes)");
- }
- function modTimeText(data){
- if (data.LastModUnix == 0){
- return '<span class="dim">' + applocale.getString("properties/error/No record", "No record") + "</span>";
- }
- return dlgEscape(data.LastModTime);
- }
- //Creation time is not recorded by every platform: Linux only
- //exposes it through statx, so the server may report it as unknown
- function creationTimeText(data){
- if (data.CreationTime == undefined || data.CreationTime == ""){
- return '<span class="dim">' + applocale.getString("properties/error/Not recorded by host", "Not recorded by this host") + "</span>";
- }
- return dlgEscape(data.CreationTime);
- }
- function hideLoader(){
- $("#dlgLoader").hide();
- }
- /* ---------------------------------------------------------------
- Storage tab
- Most drives have nothing to add: the storage path on the Basic
- tab is the whole answer. A drive whose files live somewhere
- else - cluster:/ keeps every file on one or more other nodes -
- reports where through its file system abstraction, and this
- tab appears to show it. Nothing here is cluster specific: the
- server sends rows and locations, whatever kind of drive
- resolved them.
- --------------------------------------------------------------- */
- function loadStorageInfo(){
- if (files.length != 1){
- //A selection has no single place to point at
- return;
- }
- $.ajax({
- url: "../../system/file_system/getStorageInfo",
- data: {path: files[0]},
- method: "POST",
- success: function(data){
- if (data == undefined || data.error !== undefined){
- //This drive does not report one. Leave the tab out
- return;
- }
- storageInfo = data;
- renderStorageTab(data);
- $("#storageTab").show();
- }
- });
- }
- //Keys and the wordy half of values are translated where the locale
- //file has them and shown as they came otherwise, so a drive can
- //report a node name or a hash that no locale can know about
- function storageKey(key){
- return applocale.getString("storage/key/" + key, key);
- }
- function storageValue(value, args){
- var text = applocale.getString("storage/value/" + value, value);
- if (args != undefined){
- for (var i = 0; i < args.length; i++){
- text = text.split("{" + i + "}").join(
- applocale.getString("storage/value/" + args[i], args[i]));
- }
- }
- return text;
- }
- function storageFieldRows(fields){
- var rows = [];
- for (var i = 0; i < fields.length; i++){
- if (fields[i].value == ""){
- continue;
- }
- rows.push(propRow(storageKey(fields[i].key),
- dlgEscape(storageValue(fields[i].value, fields[i].args))));
- }
- return rows;
- }
- function renderStorageTab(data){
- //The tab is named after whatever answered, e.g. Cluster
- $("#storageTab").text(applocale.getString("storage/title/" + data.type, data.title));
- $("#storageFields").html(propGroup(storageFieldRows(data.fields || [])));
- var items = data.items || [];
- if (items.length == 0){
- //A folder has no copies of its own, and a file that has
- //just been written may not have one recorded yet
- $("#storageCopiesSection").hide();
- return;
- }
- var html = "";
- for (var i = 0; i < items.length; i++){
- html += storageCopyHTML(items[i]);
- }
- $("#storageCopies").html(html);
- $("#storageCopiesSection").show();
- }
- function storageCopyHTML(item){
- var state = item.state;
- if (state != "ok" && state != "pending" && state != "stale" && state != "error"){
- state = "unknown";
- }
- var head = '<div class="copyHead">' +
- '<span class="copyDot ' + state + '"></span>' +
- '<span class="copyName">' + dlgEscape(item.title) + "</span>";
- if (item.local){
- //Whether the file is on the host being used right now is
- //the first thing anyone opening this tab wants to know
- head += '<span class="copyBadge">' +
- dlgEscape(applocale.getString("storage/thisNode", "This node")) + "</span>";
- }
- if (item.status != undefined && item.status != ""){
- head += '<span class="copyStatus ' + state + '">' +
- dlgEscape(storageValue(item.status)) + "</span>";
- }
- head += "</div>";
- var sub = "";
- if (item.subtitle != undefined && item.subtitle != ""){
- sub = '<div class="copySub">' + dlgEscape(item.subtitle) + "</div>";
- }
- return '<div class="dlgCopy">' + head + sub +
- storageFieldRows(item.fields || []).join("") + "</div>";
- }
- /* ---------------------------------------------------------------
- Permission tab
- --------------------------------------------------------------- */
- function buildPermissionSelectors(){
- var options = "";
- for (var i = 7; i >= 0; i--){
- options += '<option value="' + i + '">' +
- dlgEscape(applocale.getString(permissionLevels[i].key, permissionLevels[i].text)) + "</option>";
- }
- $("#permUser").html(options);
- $("#permGrp").html(options);
- $("#permOther").html(options);
- }
- /*
- The file's POSIX group is not something ArozOS can change - the
- server only accepts a mode string - so the field lists the
- owner's permission groups for reference and stays disabled.
- */
- function loadUserGroups(owner){
- $.get("../../system/users/userinfo", function(data){
- var groups = [];
- if (data != undefined && data.Usergroup != undefined && data.Username == owner){
- groups = data.Usergroup;
- }
- if (groups.length == 0){
- groups = [applocale.getString("perm/group/unknown", "Not available")];
- }
- var options = "";
- for (var i = 0; i < groups.length; i++){
- options += '<option value="' + dlgEscape(groups[i]) + '">' + dlgEscape(groups[i]) + "</option>";
- }
- $("#permGroup").html(options);
- }).fail(function(){
- $("#permGroup").html('<option>' + applocale.getString("perm/group/unknown", "Not available") + "</option>");
- });
- }
- function loadPermission(){
- if (files.length == 0){
- return;
- }
- permissionLoaded = true;
- $.ajax({
- url: "../../system/file_system/handleFilePermission",
- data: {file: files[0]},
- success: function(data){
- if (data == undefined || data.error !== undefined){
- setOctal("0644");
- return;
- }
- setOctal(String(data));
- },
- error: function(){
- setOctal("0644");
- }
- });
- }
- function setOctal(octal){
- //The server reports a four digit mode where the first digit is
- //always zero; only the last three are editable here
- var mode = String(octal).replace(/[^0-7]/g, "");
- while (mode.length < 4){
- mode = "0" + mode;
- }
- mode = mode.substring(mode.length - 4);
- $("#permOctal").val(mode);
- $("#permUser").val(mode.charAt(1));
- $("#permGrp").val(mode.charAt(2));
- $("#permOther").val(mode.charAt(3));
- updateBitsHint();
- }
- function syncFromSelectors(){
- var mode = "0" + $("#permUser").val() + $("#permGrp").val() + $("#permOther").val();
- $("#permOctal").val(mode);
- updateBitsHint();
- }
- function syncFromOctal(){
- var typed = $("#permOctal").val().replace(/[^0-7]/g, "");
- $("#permOctal").val(typed);
- if (typed.length != 4){
- return;
- }
- $("#permUser").val(typed.charAt(1));
- $("#permGrp").val(typed.charAt(2));
- $("#permOther").val(typed.charAt(3));
- updateBitsHint();
- }
- function updateBitsHint(){
- $("#permUserBits").text("(" + permissionLevels[parseInt($("#permUser").val())].bits + ")");
- $("#permGrpBits").text("(" + permissionLevels[parseInt($("#permGrp").val())].bits + ")");
- $("#permOtherBits").text("(" + permissionLevels[parseInt($("#permOther").val())].bits + ")");
- }
- function applyPermission(){
- if (!permissionSupported){
- return;
- }
- var mode = $("#permOctal").val();
- if (mode.length != 4 || mode.charAt(0) != "0"){
- dlgToast(applocale.getString("perm/invalid", "Invalid permission value"));
- return;
- }
- var pending = files.length;
- var failure = "";
- files.forEach(function(filepath){
- $.ajax({
- url: "../../system/file_system/handleFilePermission",
- data: {file: filepath, mode: mode},
- success: function(data){
- if (data != undefined && data.error !== undefined){
- failure = data.error;
- }
- },
- error: function(){
- failure = applocale.getString("perm/failed", "Unable to update the file permission");
- },
- complete: function(){
- pending--;
- if (pending == 0){
- if (failure != ""){
- dlgToast(failure);
- }else{
- dlgToast(applocale.getString("perm/updated", "Permission updated"));
- permissionLoaded = false;
- }
- }
- }
- });
- });
- }
- /* ---------------------------------------------------------------
- Footer actions
- --------------------------------------------------------------- */
- function copyPath(){
- var path = fileInfo.VirtualPath;
- if (path == undefined && files.length > 0){
- path = files[0];
- }
- if (path == undefined){
- return;
- }
- if (dlgCopyToClipboard(path)){
- dlgToast(applocale.getString("message/copied", "Path copied"));
- }else{
- dlgToast(applocale.getString("message/copyFailed", "Unable to copy the path"));
- }
- }
- //Open the opener picker for this file type
- function changeDefaultWebApp(){
- var openFileObject = {
- filepath: fileInfo.VirtualPath,
- filename: fileInfo.Basename
- };
- ao_module_newfw({
- url: "SystemAO/file_system/defaultOpener.html#" + encodeURIComponent(JSON.stringify(openFileObject)),
- width: 380,
- height: 560,
- appicon: "SystemAO/file_system/img/opener.png",
- title: "Default WebApp for " + fileInfo.Ext,
- parent: ao_module_windowID,
- callback: "handleRefresh"
- });
- }
- //Called back by the opener picker once a new default is stored
- function handleRefresh(){
- if (fileInfo.VirtualPath != undefined){
- loadDefaultOpener(fileInfo);
- }
- }
- </script>
- </body>
- </html>
|