# HG changeset patch
# User bsw
# Date 1581365449 -3600
# Node ID 17e7082c377a6db8798087b037eec588c1cd42c0
# Parent 3e9b0f1adec3ea3e542535a1be88e841e4ee55b2
Added image attachments for initiatives
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/_filter/21_auth.lua
--- a/app/main/_filter/21_auth.lua Mon Dec 09 15:54:57 2019 +0100
+++ b/app/main/_filter/21_auth.lua Mon Feb 10 21:10:49 2020 +0100
@@ -71,6 +71,7 @@
or module == "suggestion" and view == "show"
or module == "draft" and view == "diff"
or module == "draft" and view == "show"
+ or module == "file" and view == "show.jpg"
or module == "index" and view == "search"
or module == "index" and view == "usage_terms"
or module == "help" and view == "introduction"
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/draft/_action/add.lua
--- a/app/main/draft/_action/add.lua Mon Dec 09 15:54:57 2019 +0100
+++ b/app/main/draft/_action/add.lua Mon Feb 10 21:10:49 2020 +0100
@@ -21,7 +21,68 @@
draft_text = abstract .. "" .. draft_text
end
-return Draft:update_content(
+if config.attachments then
+ local file_upload_session = param.get("file_upload_session")
+ file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
+ local file_uploads = json.array()
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
+ local fh = io.open(filename, "r")
+ if fh then
+ file_uploads = json.import(fh:read("*a"))
+ end
+ for i, file_upload in ipairs(file_uploads) do
+ if param.get("file_upload_delete_" .. file_upload.id, atom.boolean) then
+ for j = i, #file_uploads - 1 do
+ file_uploads[j] = file_uploads[j+1]
+ end
+ file_uploads[#file_uploads] = nil
+ end
+ end
+ local convert_func = config.attachments.convert_func
+ local last_id = param.get("file_upload_last_id", atom.number)
+ if last_id and last_id > 0 then
+ if last_id > 1024 then
+ last_id = 1024
+ end
+ for i = 1, last_id do
+ local file = param.get("file_" .. i)
+ if file and #file > 0 then
+ local id = multirand.string(
+ 32,
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+ )
+ local data, err, status = convert_func(file)
+ if status ~= 0 or data == nil then
+ slot.put_into("error", _"Error while converting image. Please note, that only JPG files are supported!")
+ return false
+ end
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".jpg")
+ local fh = assert(io.open(filename, "w"))
+ fh:write(file)
+ fh:write("\n")
+ fh:close()
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".preview.jpg")
+ local fh = assert(io.open(filename, "w"))
+ fh:write(data)
+ fh:write("\n")
+ fh:close()
+ table.insert(file_uploads, json.object{
+ id = id,
+ filename = filename,
+ title = param.get("title_" .. i),
+ description = param.get("description_" .. i)
+ })
+ end
+ end
+ end
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
+ local fh = assert(io.open(filename, "w"))
+ fh:write(json.export(file_uploads))
+ fh:write("\n")
+ fh:close()
+end
+
+local draft_id = Draft:update_content(
app.session.member.id,
param.get("initiative_id", atom.integer),
param.get("formatting_engine"),
@@ -29,3 +90,66 @@
nil,
param.get("preview") or param.get("edit")
)
+
+if draft_id and config.attachments then
+ local file_upload_session = param.get("file_upload_session")
+ file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
+
+ local draft_attachments = DraftAttachment:new_selector()
+ :add_where{ "draft_attachment.draft_id = ?", draft_id }
+ :exec()
+
+ for i, draft_attachment in ipairs(draft_attachments) do
+ if param.get("file_delete_" .. draft_attachment.file_id, atom.boolean) then
+ draft_attachment:destroy()
+ end
+ end
+
+ local file_uploads = json.array()
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
+ local fh = io.open(filename, "r")
+ if fh then
+ file_uploads = json.import(fh:read("*a"))
+ end
+ for i, file_upload in ipairs(file_uploads) do
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".jpg")
+ local data
+ local fh = io.open(filename, "r")
+ if fh then
+ data = fh:read("*a")
+ end
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".preview.jpg")
+ local data_preview
+ local fh = io.open(filename, "r")
+ if fh then
+ data_preview = fh:read("*a")
+ end
+
+ local hash = moonhash.sha3_512(data)
+
+ local file = File:new_selector()
+ :add_where{ "hash = ?", hash }
+ :add_where{ "content_type = ?", "image/jpeg" }
+ :optional_object_mode()
+ :exec()
+
+ if not file then
+ file = File:new()
+ file.content_type = "image/jpeg"
+ file.hash = hash
+ file.data = data
+ file.preview_content_type = "image/jpeg"
+ file.preview_data = data_preview
+ file:save()
+ end
+
+ local draft_attachment = DraftAttachment:new()
+ draft_attachment.draft_id = draft_id
+ draft_attachment.file_id = file.id
+ draft_attachment.title = file_upload.title
+ draft_attachment.description = file_upload.description
+ draft_attachment:save()
+ end
+end
+
+return draft_id and true or false
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/draft/new.lua
--- a/app/main/draft/new.lua Mon Dec 09 15:54:57 2019 +0100
+++ b/app/main/draft/new.lua Mon Feb 10 21:10:49 2020 +0100
@@ -23,7 +23,7 @@
ui.form{
record = draft,
- attr = { class = "vertical section" },
+ attr = { class = "vertical section", enctype = 'multipart/form-data' },
module = "draft",
action = "add",
params = { initiative_id = initiative.id },
@@ -65,6 +65,54 @@
}
slot.put("
")
+ if config.attachments then
+ local file_upload_session = param.get("file_upload_session") or multirand.string(
+ 32,
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+ )
+ file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
+ ui.field.hidden{ name = "file_upload_session", value = file_upload_session }
+ local files = File:new_selector()
+ :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
+ :add_where{ "draft_attachment.draft_id = ?", initiative.current_draft.id }
+ :reset_fields()
+ :add_field("file.id")
+ :add_field("draft_attachment.title")
+ :add_field("draft_attachment.description")
+ :add_order_by("draft_attachment.id")
+ :exec()
+
+ if #files > 0 then
+ ui.container {
+ content = function()
+ for i, file in ipairs(files) do
+ if param.get("file_delete_" .. file.id, atom.boolean) then
+ ui.field.hidden{ name = "file_delete_" .. file.id, value = "1" }
+ else
+ ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
+ ui.container{ content = file.title or "" }
+ ui.container{ content = file.description or "" }
+ slot.put("
")
+ end
+ end
+ end
+ }
+ end
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
+ local fh = io.open(filename, "r")
+ if fh then
+ local file_uploads = json.import(fh:read("*a"))
+ for i, file_upload in ipairs(file_uploads) do
+ ui.image{ module = "draft", view = "show_file_upload", params = {
+ file_upload_session = file_upload_session, file_id = file_upload.id, preview = true
+ } }
+ ui.container{ content = file_upload.title or "" }
+ ui.container{ content = file_upload.description or "" }
+ slot.put("
")
+ end
+ end
+ end
+
ui.tag{
tag = "input",
attr = {
@@ -123,7 +171,71 @@
else
ui.container { content = _"You cannot change your text again later, because this issue is already in verfication phase!" }
end
+
slot.put("
")
+ if config.attachments then
+ local file_upload_session = param.get("file_upload_session") or multirand.string(
+ 32,
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
+ )
+ file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
+ ui.field.hidden{ name = "file_upload_session", value = file_upload_session }
+ local files = File:new_selector()
+ :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
+ :add_where{ "draft_attachment.draft_id = ?", initiative.current_draft.id }
+ :reset_fields()
+ :add_field("file.id")
+ :add_field("draft_attachment.title")
+ :add_field("draft_attachment.description")
+ :add_order_by("draft_attachment.id")
+ :exec()
+
+ if #files > 0 then
+ ui.container {
+ content = function()
+ for i, file in ipairs(files) do
+ ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
+ ui.container{ content = file.title or "" }
+ ui.container{ content = file.description or "" }
+ ui.field.boolean{ label = _"delete", name = "file_delete_" .. file.id, value = param.get("file_delete_" .. file.id) and true or false }
+ slot.put("
")
+ end
+ end
+ }
+ end
+
+ local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
+ local fh = io.open(filename, "r")
+ if fh then
+ local file_uploads = json.import(fh:read("*a"))
+ for i, file_upload in ipairs(file_uploads) do
+ ui.image{ module = "draft", view = "show_file_upload", params = {
+ file_upload_session = file_upload_session, file_id = file_upload.id, preview = true
+ } }
+ ui.container{ content = file_upload.title or "" }
+ ui.container{ content = file_upload.description or "" }
+ ui.field.boolean{ label = _"delete", name = "file_upload_delete_" .. file_upload.id }
+ slot.put("
")
+ end
+ end
+ ui.container{ attr = { id = "file_upload_template", style = "display: none;" }, content = function()
+ ui.field.text{ label = _"Title", name = "__ID_title__" }
+ ui.field.text{ label = _"Description", name = "__ID_description__" }
+ ui.field.image{ field_name = "__ID_file__" }
+ end }
+ ui.container{ attr = { id = "file_upload" }, content = function()
+ end }
+ ui.field.hidden{ attr = { id = "file_upload_last_id" }, name = "file_upload_last_id" }
+ ui.script{ script = [[ var file_upload_id = 1; ]] }
+ ui.tag{ tag = "a", content = _"Attach image", attr = {
+ href = "#",
+ onclick = "var html = document.getElementById('file_upload_template').innerHTML; html = html.replace('__ID_file__', 'file_' + file_upload_id); html = html.replace('__ID_title__', 'title_' + file_upload_id); html = html.replace('__ID_description__', 'description_' + file_upload_id); var el = document.createElement('div'); el.innerHTML = html; document.getElementById('file_upload').appendChild(el); document.getElementById('file_upload_last_id').value = file_upload_id; file_upload_id++; return false;"
+ } }
+ slot.put("
")
+
+ slot.put("
")
+
+ end
ui.tag{
tag = "input",
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/draft/show.lua
--- a/app/main/draft/show.lua Mon Dec 09 15:54:57 2019 +0100
+++ b/app/main/draft/show.lua Mon Feb 10 21:10:49 2020 +0100
@@ -6,61 +6,122 @@
return
end
+local member = app.session.member
+
+if member then
+ draft.initiative:load_everything_for_member_id(member.id)
+ draft.initiative.issue:load_everything_for_member_id(member.id)
+end
local source = param.get("source", atom.boolean)
-execute.view{
- module = "issue",
- view = "_head",
- params = { issue = draft.initiative.issue }
-}
+execute.view{ module = "issue", view = "_head", params = { issue = draft.initiative.issue, link_issue = true } }
+
+ui.grid{ content = function()
-ui.section( function()
-
- ui.sectionHead( function()
- ui.link{
- module = "initiative", view = "show", id = draft.initiative.id,
- content = function ()
+ ui.cell_main{ content = function()
+
+ ui.container{ attr = { class = "mdl-card mdl-card__fullwidth mdl-shadow--2dp" }, content = function()
+
+ ui.container{ attr = { class = "mdl-card__title mdl-card--has-fab mdl-card--border" }, content = function ()
+
ui.heading {
- level = 1,
- content = draft.initiative.display_name
+ attr = { class = "mdl-card__title-text" },
+ level = 2,
+ content = function()
+ ui.link{
+ module = "initiative", view = "show", id = draft.initiative.id,
+ content = draft.initiative.display_name
+ }
+ ui.container{ content = _("Draft revision #{id}", { id = draft.id } ) }
+ end
}
- end
- }
- ui.container { attr = { class = "right" }, content = function()
- if source then
- ui.link{
- content = _"Rendered",
+ end }
+
+ ui.container{ attr = { class = "draft mdl-card__title mdl-card--border" }, content = function()
+ if config.render_external_reference and config.render_external_reference.draft then
+ config.render_external_reference.draft(draft, function (callback)
+ ui.sectionRow(callback)
+ end)
+ end
+
+ execute.view{
module = "draft",
- view = "show",
- id = param.get_id(),
- params = { source = false }
+ view = "_show",
+ params = { draft = draft, source = source }
}
- else
- ui.link{
- content = _"Source",
- module = "draft",
- view = "show",
- id = param.get_id(),
- params = { source = true }
- }
+
+
+
+ end }
+
+ if config.attachments then
+
+ local files = File:new_selector()
+ :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
+ :add_where{ "draft_attachment.draft_id = ?", draft.id }
+ :reset_fields()
+ :add_field("file.id")
+ :add_field("draft_attachment.title")
+ :add_field("draft_attachment.description")
+ :add_order_by("draft_attachment.id")
+ :exec()
+
+ if #files > 0 then
+ ui.container {
+ attr = { class = "mdl-card__content mdl-card--border" },
+ content = function()
+ for i, file in ipairs(files) do
+ ui.link{ module = "file", view = "show.jpg", id = file.id, content = function()
+ ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
+ end }
+ ui.container{ content = file.title or "" }
+ ui.container{ content = file.description or "" }
+ slot.put("
")
+ end
+ end
+ }
+ end
end
+
+ ui.container{ attr = { class = "mdl-card__actions" }, content = function()
+ if source then
+ ui.link{
+ attr = { class = "mdl-button mdl-js-button" },
+ content = _"Rendered",
+ module = "draft",
+ view = "show",
+ id = param.get_id(),
+ params = { source = false }
+ }
+ else
+ ui.link{
+ attr = { class = "mdl-button mdl-js-button" },
+ content = _"Source",
+ module = "draft",
+ view = "show",
+ id = param.get_id(),
+ params = { source = true }
+ }
+ end
+
+ end }
end }
- ui.heading { level = 2, content = _("Draft revision #{id}", { id = draft.id } ) }
- end)
-
- if config.render_external_reference and config.render_external_reference.draft then
- config.render_external_reference.draft(draft, function (callback)
- ui.sectionRow(callback)
- end)
- end
-
- ui.sectionRow( function()
-
- execute.view{
- module = "draft",
- view = "_show",
- params = { draft = draft, source = source }
+
+ end }
+
+ ui.cell_sidebar{ content = function()
+ if config.logo then
+ config.logo()
+ end
+ execute.view {
+ module = "issue", view = "_sidebar",
+ params = {
+ issue = draft.initiative.issue,
+ initiative = draft.initiative,
+ member = app.session.member
+ }
}
- end)
-end)
+ end }
+
+end }
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/draft/show_file_upload.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/draft/show_file_upload.lua Mon Feb 10 21:10:49 2020 +0100
@@ -0,0 +1,22 @@
+local file_upload_session = param.get("file_upload_session")
+file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
+
+local file_id = param.get("file_id")
+file_id = string.gsub(file_id, "[^A-Za-z0-9]", "")
+
+local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_id .. ".jpg")
+
+if param.get("preview", atom.boolean) then
+ filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_id .. ".preview.jpg")
+end
+
+local data
+
+local fh = io.open(filename, "r")
+if fh then
+ data = fh:read("*a")
+end
+
+
+slot.set_layout(nil, content_type)
+slot.put_into("data", data)
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/file/show.jpg.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/file/show.jpg.lua Mon Feb 10 21:10:49 2020 +0100
@@ -0,0 +1,12 @@
+local id = param.get_id()
+
+local file = File:by_id(id)
+
+local output = file.data
+
+if param.get("preview", atom.boolean) then
+ output = file.preview_data
+end
+
+slot.set_layout(nil, file.content_type)
+slot.put_into("data", output)
diff -r 3e9b0f1adec3 -r 17e7082c377a app/main/initiative/show.lua
--- a/app/main/initiative/show.lua Mon Dec 09 15:54:57 2019 +0100
+++ b/app/main/initiative/show.lua Mon Feb 10 21:10:49 2020 +0100
@@ -135,6 +135,35 @@
end
}
+ if config.attachments then
+
+ local files = File:new_selector()
+ :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
+ :add_where{ "draft_attachment.draft_id = ?", initiative.current_draft.id }
+ :reset_fields()
+ :add_field("file.id")
+ :add_field("draft_attachment.title")
+ :add_field("draft_attachment.description")
+ :add_order_by("draft_attachment.id")
+ :exec()
+
+ if #files > 0 then
+ ui.container {
+ attr = { class = "mdl-card__content mdl-card--border" },
+ content = function()
+ for i, file in ipairs(files) do
+ ui.link{ module = "file", view = "show.jpg", id = file.id, content = function()
+ ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
+ end }
+ ui.container{ content = file.title or "" }
+ ui.container{ content = file.description or "" }
+ slot.put("
")
+ end
+ end
+ }
+ end
+ end
+
local drafts_count = initiative:get_reference_selector("drafts"):count()
if not config.voting_only then
diff -r 3e9b0f1adec3 -r 17e7082c377a model/draft.lua
--- a/model/draft.lua Mon Dec 09 15:54:57 2019 +0100
+++ b/model/draft.lua Mon Feb 10 21:10:49 2020 +0100
@@ -97,5 +97,19 @@
draft:render_content()
+ local draft_attachments = DraftAttachment:new_selector()
+ :add_where{ "draft_id = ?", old_draft.id }
+ :exec()
+
+ for i, draft_attachment in ipairs(draft_attachments) do
+ local new_draft_attachment = DraftAttachment:new()
+ new_draft_attachment.draft_id = draft.id
+ new_draft_attachment.file_id = draft_attachment.file_id
+ new_draft_attachment.title = draft_attachment.title
+ new_draft_attachment.description = draft_attachment.description
+ new_draft_attachment:save()
+ end
+
slot.put_into("notice", _"The initiative text has been updated")
+ return draft.id
end
diff -r 3e9b0f1adec3 -r 17e7082c377a model/draft_attachment.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/model/draft_attachment.lua Mon Feb 10 21:10:49 2020 +0100
@@ -0,0 +1,5 @@
+DraftAttachment = mondelefant.new_class()
+DraftAttachment.table = "draft_attachment"
+DraftAttachment.primary_key = "id"
+
+
diff -r 3e9b0f1adec3 -r 17e7082c377a model/file.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/model/file.lua Mon Feb 10 21:10:49 2020 +0100
@@ -0,0 +1,8 @@
+File = mondelefant.new_class()
+File.table = "file"
+File.primary_key = "id"
+
+File.binary_columns = {
+ data = true,
+ preview_data = true
+}