From 87df74aaf1fc55047bac7ac234546db7547cf166 Mon Sep 17 00:00:00 2001 From: pho Date: Thu, 12 Nov 2009 19:26:32 +0900 Subject: [PATCH] misc changes Ignore-this: 4d480ac253d37bf3e9f0a508e5843100 darcs-hash:20091112102632-62b54-bd888319e64c39535edd9d2684d1cc910679ccb5.gz --- Rakka/Authorization.hs | 9 +- Rakka/Resource/Users.hs | 50 +-- defaultPages/StyleSheet/CieloNegro.xml | 8 + defaultPages/StyleSheet/Default.xml | 8 + js/Makefile | 1 + js/hashedParam.js | 55 +++ js/redirection.js | 14 +- js/systemConfig.js | 466 ++++++++++++------------- 8 files changed, 330 insertions(+), 281 deletions(-) create mode 100644 js/hashedParam.js diff --git a/Rakka/Authorization.hs b/Rakka/Authorization.hs index ccc4b36..adf2da5 100644 --- a/Rakka/Authorization.hs +++ b/Rakka/Authorization.hs @@ -28,7 +28,6 @@ data AuthDB = AuthDB { adbFilePath :: !FilePath , adbUserMap :: !(TVar UserMap) - , adbSyncRequest :: !(TVar Bool) } @@ -38,12 +37,10 @@ type UserMap = Map String String mkAuthDB :: FilePath -> IO AuthDB mkAuthDB lsdir = do let path = lsdir "authDB" - m <- newTVarIO =<< loadUserMap path - req <- newTVarIO False + m <- newTVarIO =<< loadUserMap path return AuthDB { - adbFilePath = path - , adbUserMap = m - , adbSyncRequest = req + adbFilePath = path + , adbUserMap = m } diff --git a/Rakka/Resource/Users.hs b/Rakka/Resource/Users.hs index a4bf602..fa61ad8 100644 --- a/Rakka/Resource/Users.hs +++ b/Rakka/Resource/Users.hs @@ -16,14 +16,14 @@ import Text.XML.HXT.Arrow hiding (when) resUsers :: Environment -> ResourceDef resUsers env = ResourceDef { - resUsesNativeThread = False - , resIsGreedy = True - , resGet = Just $ handleGet env - , resHead = Nothing - , resPost = Nothing - , resPut = Just $ handlePut env - , resDelete = Just $ handleDelete env - } + resUsesNativeThread = False + , resIsGreedy = True + , resGet = Just $ handleGet env + , resHead = Nothing + , resPost = Nothing + , resPut = Just $ handlePut env + , resDelete = Just $ handleDelete env + } {- @@ -44,13 +44,13 @@ handleGet :: Environment -> Resource () handleGet env = do userID <- getUserID env when (isNothing userID) - $ abort Forbidden [] Nothing + $ abort Forbidden [] Nothing path <- getPathInfo case path of - [] -> returnUserList - [name] -> returnUser name - _ -> foundNoEntity Nothing + [] -> returnUserList + [name] -> returnUser name + _ -> foundNoEntity Nothing where returnUserList :: Resource () returnUserList @@ -92,17 +92,17 @@ handlePut env path <- getPathInfo case path of - [name] -> do mimeType <- getContentType - case mimeType of - Nothing - -> abort BadRequest [] (Just "Missing Content-Type") - Just (MIMEType "text" "plain" _) - -> do pass <- input defaultLimit - addUser (envAuthDB env) name pass - Just t - -> abort UnsupportedMediaType [] (Just $ "Unsupported media type: " ++ show t) - setStatus Created - _ -> abort BadRequest [] (Just "Invalid URI") + [name] -> do mimeType <- getContentType + case mimeType of + Nothing + -> abort BadRequest [] (Just "Missing Content-Type") + Just (MIMEType "text" "plain" _) + -> do pass <- input defaultLimit + addUser (envAuthDB env) name pass + Just t + -> abort UnsupportedMediaType [] (Just $ "Unsupported media type: " ++ show t) + setStatus Created + _ -> abort BadRequest [] (Just "Invalid URI") handleDelete :: Environment -> Resource () @@ -113,6 +113,6 @@ handleDelete env path <- getPathInfo case path of - [name] -> delUser (envAuthDB env) name - _ -> abort BadRequest [] (Just "Invalid URI") + [name] -> delUser (envAuthDB env) name + _ -> abort BadRequest [] (Just "Invalid URI") setStatus NoContent diff --git a/defaultPages/StyleSheet/CieloNegro.xml b/defaultPages/StyleSheet/CieloNegro.xml index e89e3ab..9e808ea 100644 --- a/defaultPages/StyleSheet/CieloNegro.xml +++ b/defaultPages/StyleSheet/CieloNegro.xml @@ -93,6 +93,10 @@ option { margin: 5px 0px; } +.body * + h2 { + margin-top: 20px; +} + .body ul, .body ol { list-style-position: inside; margin: 0 0 0.8em 0; @@ -171,6 +175,9 @@ option { table.pageEditor { width: 100%; } +table.pageEditor td { + text-align: center; +} .pageEditor th { width: 6em; } @@ -280,6 +287,7 @@ th, td { th { background-color: #aaaaaa; font-weight: bold; + text-align: center; } td { background-color: #cccccc; diff --git a/defaultPages/StyleSheet/Default.xml b/defaultPages/StyleSheet/Default.xml index 06b84e8..5f2126a 100644 --- a/defaultPages/StyleSheet/Default.xml +++ b/defaultPages/StyleSheet/Default.xml @@ -93,6 +93,10 @@ option { margin: 5px 0px; } +.body * + h2 { + margin-top: 20px; +} + .body ul, .body ol { list-style-position: inside; margin: 0 0 0.8em 0; @@ -175,6 +179,9 @@ table.configPanel { table.pageEditor { width: 100%; } +table.pageEditor td { + text-align: center; +} .pageEditor th { width: 6em; } @@ -276,6 +283,7 @@ th, td { th { background-color: #eeeeee; font-weight: bold; + text-align: center; } td { background-color: #fafafa; diff --git a/js/Makefile b/js/Makefile index 1fc9cee..e3f1623 100644 --- a/js/Makefile +++ b/js/Makefile @@ -7,6 +7,7 @@ SOURCES = \ base.js \ base64.js \ editPage.js \ + hashedParam.js \ hexDump.js \ localFile.js \ login.js \ diff --git a/js/hashedParam.js b/js/hashedParam.js new file mode 100644 index 0000000..8942626 --- /dev/null +++ b/js/hashedParam.js @@ -0,0 +1,55 @@ +$(document).ready(function () { + + /* Example of hashed params: + * + * /Foo/Bar.html#Redirect:Baz + * /Foo/Bar.html#EditPage + * /Foo/Bar.html#Redirect:Baz|EditPage + */ + + var cached_rawHash; + var cached_map; + + Rakka.getRawHash = function () { + // Don't use window.location.hash as it isn't portable. + var r = window.location.hash; + var i = r.indexOf("#"); + return (i >= 0 + ? r.substr(i + 1) + : ""); + }; + + Rakka.getHashedParamMap = function () { + var raw = Rakka.getRawHash(); + + if (cached_rawHash != raw) { + var src = decodeURIComponent(raw); + + // Split the source by '|' letters. + cached_map = {}; + $.each(src.split("|"), function () { + // tuple ::= key ':' value + // | key (value is empty) + var i = this.indexOf(":"); + if (i >= 0) { + cached_map[this.substr(0, i)] = this.substr(i + 1); + } + else { + cached_map[this.substr(0, i)] = ""; + } + }); + + cached_rawHash = raw; + } + + return cached_map; + }; + + Rakka.getHashedParam = function (key) { + return Rakka.getHashedParamMap()[key]; + }; + + Rakka.setHashedParamMap = function (map) { + ////////// + }; +}); \ No newline at end of file diff --git a/js/redirection.js b/js/redirection.js index 1a0c99b..5ceff26 100644 --- a/js/redirection.js +++ b/js/redirection.js @@ -1,17 +1,8 @@ $(document).ready(function () { - var fragment; - - if ($.browser.mozilla) { - fragment = window.location.hash; // 何故か勝手に URI デコードされる - } - else { - fragment = decodeURIComponent(window.location.hash); - } - var m = fragment.match(/^#Redirect:(.*)$/); - if (m) { - var from = m[1]; + var from = Rakka.getHashedParam("Redirect"); + if (from != null) { var editButton = $.INPUT({className: "editButton", type: "button", @@ -26,4 +17,5 @@ $(document).ready(function () { $("div.title").after(box); } + }); \ No newline at end of file diff --git a/js/systemConfig.js b/js/systemConfig.js index fee6d36..82abaee 100644 --- a/js/systemConfig.js +++ b/js/systemConfig.js @@ -13,7 +13,7 @@ var lines = src.split(/\n/); $.each(lines, function () { - var m = this.match(/^(\S+)\s+(\S+)$/); + var m = this.match(/^(\S+)\s+(\S+)$/); map[ m[1] ] = m[2]; }); @@ -63,39 +63,41 @@ parseUri.options.strictMode = true; var uri = parseUri(str); - return (uri.protocol != "" && + return (uri.protocol != "" && uri.authority != "" && - uri.path != "" && + uri.path != "" && uri.path.match(/\/$/) && - uri.query == "" && - uri.anchor == ""); + uri.query == "" && + uri.anchor == ""); }; + // FIXME: Don't let user to manipulate directly this structure. + // FIXME: Values may include spaces. var isValidMap = function (src) { - return src.match(/^\S+\s+\S+(?:\n\S+\s+\S+)*\n?$/) != null; + return src.match(/^(?:\S+\s+\S+(?:\n\S+\s+\S+)*\n?)?$/) != null; }; Rakka.getUserList = function () { - var users = []; - - $.ajax({ - type: "GET", - url: Rakka.baseURI + "users", - async: false, - beforeSend: function (req) { - Rakka.setAuthorization(req); - }, - success: function (xml) { - $(xml).find("user").each(function () { - users.push(this.getAttribute("id")); - }); - }, - error: function (req) { - throw new Error(req.status + " " + req.statusText); - } - }); - - return users; + var users = []; + + $.ajax({ + type: "GET", + url: Rakka.baseURI + "users", + async: false, + beforeSend: function (req) { + Rakka.setAuthorization(req); + }, + success: function (xml) { + $(xml).find("user").each(function () { + users.push(this.getAttribute("id")); + }); + }, + error: function (req) { + throw new Error(req.status + " " + req.statusText); + } + }); + + return users; }; Rakka.getSystemConfig = function () { @@ -132,152 +134,142 @@ }; var appendConfigPanel = function ($area) { - var conf = Rakka.getSystemConfig(); - $area.append($.H1({}, "Configuration")); - - var fldSiteName - = $.INPUT({type: "text", value: conf.siteName}); - - var fldBaseURI - = $.INPUT({type: "text", value: conf.baseURI}); - - var fldDefaultPage - = $.INPUT({type: "text", value: conf.defaultPage}); - - var fldStyleSheet - = $.INPUT({type: "text", value: conf.styleSheet}); - - var fldLanguages - = $.TEXTAREA({value: encoder_of.languages(conf.languages)}); - - var chkGlobalLock - = $.INPUT({type: "checkbox", checked: conf.globalLock}); - - var btnSave - = $.INPUT({type: "button", value: "Save"}); - - $(btnSave).click(function () { - var NS = "http://cielonegro.org/schema/Rakka/Config/1.0"; - var doc = document.implementation.createDocument(NS, "systemConfig", null); - var sc = doc.documentElement; - - var mkValue = function (path, value) { - var elem = doc.createElementNS(NS, "value"); - elem.setAttribute("path", path); - elem.appendChild(doc.createTextNode(value)); - return elem; - }; - - sc.appendChild(mkValue("siteName" , fldSiteName.value)); - sc.appendChild(mkValue("baseURI" , fldBaseURI.value)); - sc.appendChild(mkValue("defaultPage", fldDefaultPage.value)); - sc.appendChild(mkValue("styleSheet" , fldStyleSheet.value)); - sc.appendChild(mkValue("languages" , fldLanguages.value)); - sc.appendChild(mkValue("globalLock" , encoder_of["globalLock"](chkGlobalLock.checked))); - - Rakka.displayWaitingMessage("Submitting... please wait."); - - var url = Rakka.baseURI + "systemConfig"; - $.ajax({ type : "PUT", - url : url, - contentType: "text/xml", - data : doc, - processData: false, - beforeSend : function (req) { - Rakka.setAuthorization(req); - }, - success : function () { - cachedConf = null; - Rakka.hideWaitingMessage(); - Rakka.restoreScreen(); - }, - error : function (req) { - Rakka.hideWaitingMessage(); - - var $area = Rakka.switchScreen(); - $area.text("Error: " + req.status + " " + req.statusText); - } - }); - }); - - var btnCancel - = $.INPUT({type: "button", value: "Cancel"}); - - $(btnCancel).click(function () { - if (isDirty) { - if (window.confirm("Do you really want to discard changes?")) { - Rakka.restoreScreen(); - } - } - else { - Rakka.restoreScreen(); - } - }); - - var configPanel - = $.TABLE({className: "pageEditor"}, - $.TBODY({}, - $.TR({}, - $.TH({}, "Site name"), - $.TD({}, fldSiteName) - ), - $.TR({}, - $.TH({}, "Base URI"), - $.TD({}, fldBaseURI) - ), - $.TR({}, - $.TH({}, "Default page"), - $.TD({}, fldDefaultPage) - ), - $.TR({}, - $.TH({}, "Style sheet"), - $.TD({}, fldStyleSheet) - ), - $.TR({}, - $.TH({}, "Languages"), - $.TD({}, fldLanguages) - ), - $.TR({}, - $.TH({}, "Global lock"), - $.TD({}, - $.LABEL({}, - chkGlobalLock, - "Disallow guest users to edit pages.")) - ), - $.TR({}, - $.TH({}), - $.TD({}, btnSave, btnCancel) - ) - ) - ); - - var validate = function () { - var isValid = (function () { - if (!isValidBaseURI(fldBaseURI.value)) { - return false; - } - - if (fldDefaultPage.value.match(Rakka.rePageName) == null) { - return false; - } + var conf = Rakka.getSystemConfig(); + $area.append($.H1({}, "Configuration")); + + var fldSiteName + = $.INPUT({type: "text", value: conf.siteName}); + + var fldBaseURI + = $.INPUT({type: "text", value: conf.baseURI}); + + var fldDefaultPage + = $.INPUT({type: "text", value: conf.defaultPage}); + + var fldStyleSheet + = $.INPUT({type: "text", value: conf.styleSheet}); + + var fldLanguages + = $.TEXTAREA({value: encoder_of.languages(conf.languages)}); + + var chkGlobalLock + = $.INPUT({type: "checkbox", checked: conf.globalLock}); + + var btnSave + = $.INPUT({type: "button", value: "Save changes", disabled: "disabled"}); + + $(btnSave).click(function () { + var NS = "http://cielonegro.org/schema/Rakka/Config/1.0"; + var doc = document.implementation.createDocument(NS, "systemConfig", null); + var sc = doc.documentElement; + + var mkValue = function (path, value) { + var elem = doc.createElementNS(NS, "value"); + elem.setAttribute("path", path); + elem.appendChild(doc.createTextNode(value)); + return elem; + }; + + sc.appendChild(mkValue("siteName" , fldSiteName.value)); + sc.appendChild(mkValue("baseURI" , fldBaseURI.value)); + sc.appendChild(mkValue("defaultPage", fldDefaultPage.value)); + sc.appendChild(mkValue("styleSheet" , fldStyleSheet.value)); + sc.appendChild(mkValue("languages" , fldLanguages.value)); + sc.appendChild(mkValue("globalLock" , encoder_of["globalLock"](chkGlobalLock.checked))); + + Rakka.displayWaitingMessage("Submitting... please wait."); + + var url = Rakka.baseURI + "systemConfig"; + $.ajax({ type : "PUT", + url : url, + contentType: "text/xml", + data : doc, + processData: false, + beforeSend : function (req) { + Rakka.setAuthorization(req); + }, + success : function () { + cachedConf = null; + Rakka.hideWaitingMessage(); + Rakka.restoreScreen(); + }, + error : function (req) { + Rakka.hideWaitingMessage(); + + var $area = Rakka.switchScreen(); + $area.text("Error: " + req.status + " " + req.statusText); + } + }); + }); - if (fldStyleSheet.value.match(Rakka.rePageName) == null) { - return false; - } + var btnRevert + = $.INPUT({type: "button", value: "Revert changes", disabled: "disabled"}); - if (!isValidMap(fldLanguages.value)) { - return false; - } + $(btnRevert).click(function () { + if (window.confirm("Do you really want to discard changes?")) { + // FIXME: implement this + } + }); - return true; - })(); + var configPanel + = $.TABLE({className: "pageEditor"}, + $.TBODY({}, + $.TR({}, + $.TH({}, "Site name"), + $.TD({}, fldSiteName)), + $.TR({}, + $.TH({}, "Base URI"), + $.TD({}, fldBaseURI)), + $.TR({}, + $.TH({}, "Default page"), + $.TD({}, fldDefaultPage)), + $.TR({}, + $.TH({}, "Style sheet"), + $.TD({}, fldStyleSheet)), + $.TR({}, + $.TH({}, "Languages"), + $.TD({}, fldLanguages)), + $.TR({}, + $.TH({}, "Global lock"), + $.TD({}, + $.LABEL({}, + chkGlobalLock, + "Disallow guest users to edit pages."))), + $.TR({}, + $.TH({}), + $.TD({}, btnSave, btnRevert)))); + + var validate = function () { + var isValid = (function () { + if (!isValidBaseURI(fldBaseURI.value)) { + return false; + } + + if (fldDefaultPage.value.match(Rakka.rePageName) == null) { + return false; + } + + if (fldStyleSheet.value.match(Rakka.rePageName) == null) { + return false; + } + + if (!isValidMap(fldLanguages.value)) { + return false; + } + + return true; + })(); $(btnSave).attr({disabled: (isValid ? "" : "disabled")}); }; var isDirty = null; var makeDirty = function () { - isDirty = true; + if (!isDirty) { + $(btnRevert).attr({disabled: ""}); + isDirty = true; + } + validate(); }; $(fldSiteName) @@ -285,8 +277,7 @@ .add(fldDefaultPage) .add(fldStyleSheet) .add(fldLanguages) - .change(validate) - .keyup(validate) + .keyup(makeDirty) .change(makeDirty); $area.append(configPanel); @@ -294,52 +285,49 @@ var appendUsersPanel = function ($area) { $area.append($.H2({}, "Users")); - $area.append($.H3({}, "Existing Users")); + $area.append($.H3({}, "Existing Users")); - var tbody = $.TBODY(); + var tbody = $.TBODY(); - var usersPanel + var usersPanel = $.TABLE({className: "pageEditor"}, - $.THEAD({}, - $.TR({}, - $.TH({}, "User ID"), - $.TH({}, "Change Password"), - $.TH({}, "Delete User") - ) - ), - tbody - ); - - var updateUserList = function () { + $.THEAD({}, + $.TR({}, + $.TH({}, "User ID"), + $.TH({}, "Change Password"), + $.TH({}, "Delete User"))), + tbody); + + var updateUserList = function () { var users = Rakka.getUserList(); - $(tbody).empty(); + $(tbody).empty(); - for (var i = 0; i < users.length; i++) { - var pass1 = $.INPUT({type: "password"}); - var pass2 = $.INPUT({type: "password"}); - var change = $.INPUT({type: "button", value: "Change"}); + for (var i = 0; i < users.length; i++) { + var pass1 = $.INPUT({type: "password"}); + var pass2 = $.INPUT({type: "password"}); + var change = $.INPUT({type: "button", value: "Change"}); - var chpass = $.TABLE({className: "pageEditor"}, + var chpass = $.TABLE({className: "pageEditor"}, $.TR({}, - $.TD({}, pass1), - $.TD({rowSpan: 2}, change)), - $.TR({}, - $.TD({}, pass2))); + $.TD({}, pass1), + $.TD({rowSpan: 2}, change)), + $.TR({}, + $.TD({}, pass2))); - var delUser = $.INPUT({type: "button", value: "Delete"}); + var delUser = $.INPUT({type: "button", value: "Delete"}); var validatePassword = function () { var isValid = (function () { - if (pass1.value == "") { - return false; - } + if (pass1.value == "") { + return false; + } - if (pass1.value != pass2.value) { - return false; - } + if (pass1.value != pass2.value) { + return false; + } - return true; + return true; })(); $(change).attr({disabled: (isValid ? "" : "disabled")}); }; @@ -349,55 +337,55 @@ .change(validatePassword) .keyup(validatePassword); - $.each(users, function () { - var id = users[i]; - var tr = $.TR({}, - $.TD({}, id), - $.TD({}, chpass), - $.TD({}, delUser) - ); - tbody.appendChild(tr); - }); - } - }; - updateUserList(); - - $area.append(usersPanel); - - $area.append($.H3({}, "Add new user")); - - var userID = $.INPUT({type: "text"}); - var pass1 = $.INPUT({type: "password"}); - var pass2 = $.INPUT({type: "password"}); - var addUser = $.INPUT({type: "button", value: "Add"}); - var addUserPanel = $.TABLE({className: "pageEditor"}, + $.each(users, function () { + var id = users[i]; + var tr = $.TR({}, + $.TD({}, id), + $.TD({}, chpass), + $.TD({}, delUser) + ); + tbody.appendChild(tr); + }); + } + }; + updateUserList(); + + $area.append(usersPanel); + + $area.append($.H3({}, "Add new user")); + + var userID = $.INPUT({type: "text"}); + var pass1 = $.INPUT({type: "password"}); + var pass2 = $.INPUT({type: "password"}); + var addUser = $.INPUT({type: "button", value: "Add"}); + var addUserPanel = $.TABLE({className: "pageEditor"}, $.TR({}, - $.TH({}, "User ID"), - $.TD({}, userID)), - $.TR({}, - $.TH({}, "Password"), - $.TD({}, pass1)), - $.TR({}, - $.TH({}, "Password (retype)"), - $.TD({}, pass2)), - $.TR({}, - $.TH({}), - $.TD({}, addUser))); - - $area.append(addUserPanel); + $.TH({}, "User ID"), + $.TD({}, userID)), + $.TR({}, + $.TH({}, "Password"), + $.TD({}, pass1)), + $.TR({}, + $.TH({}, "Password (retype)"), + $.TD({}, pass2)), + $.TR({}, + $.TH({}), + $.TD({}, addUser))); + + $area.append(addUserPanel); }; Rakka.showConfigPanel = function () { var $area = Rakka.switchScreen(); - appendConfigPanel($area); - appendUsersPanel($area); + appendConfigPanel($area); + appendUsersPanel($area); }; $(document).ready(function () { - $("input.configButton") - .click(function () { - Rakka.showConfigPanel(); - }); - }); + $("input.configButton") + .click(function () { + Rakka.showConfigPanel(); + }); + }); })(); \ No newline at end of file -- 2.40.0