require "proto" require "lfs" require "umake" require "upath" local upload = Object:clone() -- Metaacls: path meta user call constr -- #metaacl# / type User new recursive -- #metaacl# / requires User new recursive -- #metaacl# / generates User new recursive -- #metaacl# / type User reset recursive -- #metaacl# / requires User reset recursive -- #metaacl# / generates User reset recursive -- #metaacl# / type User delete recursive -- #metaacl# / requires User delete recursive -- #metaacl# / generates User delete recursive function upload:invoke(session, request, response) local appendnewline = true -- TODO initialize from a parameter -- data from textbox local content = request.params.blob if type(request.params.filename) == 'table' then -- file upload takes precedence content = request.params.filename.contents -- disable newline append for uploads appendnewline = false end local scm = session:scm_load(session.path) local thefile = scm:enter(session.path) if not request.params.id then assert(request.params.doctype, "ERANGE:No Document Type selected") -- create new file assert(not upath.exists(thefile), "EEXIST:File exists already") -- allow only someone with create permission assert(session:acl_check(session.path, "create"), "EACCESS:May not create content") -- with a new file a new acl might be established if request.params.aclenable == 'on' then session:acl_new(session.path, acl.parse(request.params.newacl)) end -- recursively mkdir if permitted upath.ensuredir(session, scm.path, thefile) -- write content to file local file = io.open(thefile, "w") file:write(content) if appendnewline and content:sub(-1) ~= '\n' then file:write('\n') end file:close() -- TODO the whole operation might be rolled back, mkdir and file writing above included -- reasons: some abort on error parameter was given (refine errorrevert) -- spam check failed (TODO) -- first chance to commit a file TODO before, after, never if request.params.commitctl == "before" then local comment = request.params.comment if not comment or comment == "" then comment = "Created "..thefile end scm:commit(session, thefile, comment) end local doctypes if type(request.params.doctype) == 'string' then -- got only one doctypes = {request.params.doctype} else -- got many doctypes = request.params.doctype end local types = {} for _,typename in ipairs(doctypes) do local type = session:type_load(typename) types[typename] = type if type:check(session.path) then type:register(session.path) type:discover(session.path) end end umake.recbuild(session, session.path, types) -- second chance to commit the file if request.params.commitctl == "after" then local comment = request.params.comment if not comment or comment == "" then comment = "Created "..thefile end scm:commit(session, thefile, comment) end else -- create new file assert(upath.exists(thefile), "EEXIST:File does not exist") -- edited existing file assert(session:acl_check(session.path, "edit"), "EACCESS:May not edit content") local tmpfile = os.tmpname () local file = io.open(tmpfile, "w") file:write(content) if appendnewline and content:sub(-1) ~= '\n' then file:write('\n') end file:close() -- when the file currently present is not the one we started with then someone else edited the file meanwhile an a merge is required if request.params.id ~= scm:id(session.path) then local merged, conflict = scm:merge(request.params.id, tmpfile, thefile) if conflict then ustring.file_interpolate( uwiki_conf.prefix .. uwiki_conf.docroot .. session.sys .. "form/edit.html", { login = session.user.login, pass = session.user.pass, id = scm:id(thefile), message = "Conflict! Please fix", path= session.path:sanitize('html'), content= io.open(merged, "r"):read("*a"):sanitize('html') }, function (str) response:write(str) end ) os.remove(tmpfile) os.remove(merged) scm:leave() session:done() return response:finish() else -- merged without conflict, replace tmpfile with the merged file and proceed upath.move(merged, tmpfile) end end -- check what kind of edit the user did local types = {} for _,typename in pairs(session:meta_get(session.path, "type", "type")) do local type = session:type_load(typename) types[typename] = type -- ask the types about what kind of edit this was local editkind = type:diffkind(thefile, tmpfile) -- check if this edit type is allowed or rollback throw if editkind and not session:acl_check(session.path, "edit."..editkind) then os.remove(tmpfile) error("EACCESS:May not "..editkind.." content") end end -- ok lets replace the file upath.move(tmpfile, thefile) -- first chance to commit a file TODO before, after, never if request.params.commitctl == "before" then local comment = request.params.comment if not comment or comment == "" then comment = "Edited "..thefile end scm:commit(session, thefile, comment) end for typename,type in pairs(types) do if type:check(session.path) then type:register(session.path) type:discover(session.path) end end umake.recbuild(session, session.path, types) -- second chance to commit the file if request.params.commitctl == "after" then local comment = request.params.comment if not comment or comment == "" then comment = "Edited "..thefile end scm:commit(session, thefile, comment) end end scm:leave() session:done() return response:finish() end return upload -- Local Variables: -- mode: lua -- lua-indent-level: 4 -- indent-tabs-mode: nil -- End: