bsw@1496: local initiative bsw@1496: local new_initiative bsw@1496: local draft_id bsw@1496: local status bsw@1496: bsw@1496: if param.get("initiative_id", atom.integer) then bsw@1496: bsw@1496: local function donew() bsw@1496: local draft_text = param.get("content") bsw@1496: bsw@1496: if not draft_text then bsw@1496: return false bsw@1496: end bsw@1496: bsw@1496: local draft_text = util.wysihtml_preproc(draft_text) bsw@1496: bsw@1496: local valid_html, error_message = util.html_is_safe(draft_text) bsw@1496: if not valid_html then bsw@1496: slot.put_into("error", _("Draft contains invalid formatting or character sequence: #{error_message}", { error_message = error_message }) ) bsw@1496: return false bsw@1496: end bsw@1496: bsw@1496: if config.initiative_abstract then bsw@1496: local abstract = param.get("abstract") bsw@1496: if not abstract then bsw@1496: return false bsw@1496: end bsw@1496: abstract = encode.html(abstract) bsw@1496: draft_text = abstract .. "" .. draft_text bsw@1496: end bsw@1496: bsw@1496: draft_id = Draft:update_content( bsw@1496: app.session.member.id, bsw@1496: param.get("initiative_id", atom.integer), bsw@1496: param.get("formatting_engine"), bsw@1496: draft_text, bsw@1496: nil, bsw@1496: param.get("preview") or param.get("edit") bsw@1496: ) bsw@1496: return draft_id and true or false bsw@1496: end bsw@1496: bsw@1496: status = donew() bsw@1496: bsw@1496: else bsw@1496: bsw@1496: local function donew() bsw@1496: local issue bsw@1496: local area bsw/jbe@1309: bsw@1496: local issue_id = param.get("issue_id", atom.integer) bsw@1496: if issue_id then bsw@1496: issue = Issue:new_selector():add_where{"id=?",issue_id}:for_share():single_object_mode():exec() bsw@1496: if issue.closed then bsw@1496: slot.put_into("error", _"This issue is already closed.") bsw@1496: return false bsw@1496: elseif issue.fully_frozen then bsw@1496: slot.put_into("error", _"Voting for this issue has already begun.") bsw@1496: return false bsw@1496: elseif issue.phase_finished then bsw@1496: slot.put_into("error", _"Current phase is already closed.") bsw@1496: return false bsw@1496: end bsw@1496: area = issue.area bsw@1496: else bsw@1496: local area_id = param.get("area_id", atom.integer) bsw@1496: area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec() bsw@1496: if not area.active then bsw@1496: slot.put_into("error", "Invalid area.") bsw@1496: return false bsw@1496: end bsw@1496: end bsw@1496: bsw@1496: if not app.session.member:has_voting_right_for_unit_id(area.unit_id) then bsw@1496: return execute.view { module = "index", view = "403" } bsw@1496: end bsw@1496: bsw@1496: local policy_id = param.get("policy_id", atom.integer) bsw@1496: local policy bsw@1496: if policy_id then bsw@1496: policy = Policy:by_id(policy_id) bsw@1496: end bsw/jbe@1309: bsw@1496: if not issue then bsw@1496: if policy_id == -1 then bsw@1496: slot.put_into("error", _"Please choose a policy") bsw@1496: return false bsw@1496: end bsw@1496: if not policy.active then bsw@1496: slot.put_into("error", "Invalid policy.") bsw@1496: return false bsw@1496: end bsw@1496: if policy.polling and not app.session.member:has_polling_right_for_unit_id(area.unit_id) then bsw@1496: return execute.view { module = "index", view = "403" } bsw@1496: end bsw@1496: if not area:get_reference_selector("allowed_policies") bsw@1496: :add_where{ "policy.id = ?", policy_id } bsw@1496: :optional_object_mode() bsw@1496: :exec() bsw@1496: then bsw@1496: slot.put_into("error", "policy not allowed") bsw@1496: return false bsw@1496: end bsw@1496: end bsw@1496: bsw@1496: local is_polling = (issue and param.get("polling", atom.boolean)) or (policy and policy.polling) or false bsw@1496: bsw@1496: local tmp = db:query({ "SELECT text_entries_left, initiatives_left FROM member_contingent_left WHERE member_id = ? AND polling = ?", app.session.member.id, is_polling }, "opt_object") bsw@1496: if not tmp or tmp.initiatives_left < 1 then bsw@1496: slot.put_into("error", _"Sorry, your contingent for creating initiatives has been used up. Please try again later.") bsw@1496: return false bsw@1496: end bsw@1496: if tmp and tmp.text_entries_left < 1 then bsw@1496: slot.put_into("error", _"Sorry, you have reached your personal flood limit. Please be slower...") bsw@1496: return false bsw@1496: end bsw@1496: bsw@1496: local name = param.get("name") bsw@1496: bsw@1496: local name = util.trim(name) bsw@1496: bsw@1496: if #name < 3 then bsw@1496: slot.put_into("error", _"Please enter a meaningful title for your initiative!") bsw@1496: return false bsw@1496: end bsw@1496: bsw@1496: if #name > 140 then bsw@1496: slot.put_into("error", _"This title is too long!") bsw@1496: return false bsw@1496: end bsw/jbe@1309: bsw@1496: local timing bsw@1496: if not issue and policy.free_timeable then bsw@1496: local free_timing_string = util.trim(param.get("free_timing")) bsw@1496: if not free_timing_string or #free_timing_string < 1 then bsw@1496: slot.put_into("error", _"Choose timing") bsw@1496: return false bsw@1496: end bsw@1496: local available_timings bsw@1496: if config.free_timing and config.free_timing.available_func then bsw@1496: available_timings = config.free_timing.available_func(policy) bsw@1496: if available_timings == false then bsw@1496: slot.put_into("error", "error in free timing config") bsw@1496: return false bsw@1496: end bsw@1496: end bsw@1496: if available_timings then bsw@1496: local timing_available = false bsw@1496: for i, available_timing in ipairs(available_timings) do bsw@1496: if available_timing.id == free_timing_string then bsw@1496: timing_available = true bsw@1496: end bsw@1496: end bsw@1496: if not timing_available then bsw@1496: slot.put_into("error", _"Invalid timing") bsw@1496: return false bsw@1496: end bsw@1496: end bsw@1496: timing = config.free_timing.calculate_func(policy, free_timing_string) bsw@1496: if not timing then bsw@1496: slot.put_into("error", "error in free timing config") bsw@1496: return false bsw@1496: end bsw@1496: end bsw@1496: bsw@1496: local draft_text = param.get("content") bsw@1496: bsw@1496: if not draft_text then bsw@1496: slot.put_into("error", "no draft text") bsw@1496: return false bsw@1496: end bsw@1496: bsw@1496: local draft_text = util.wysihtml_preproc(draft_text) bsw@1496: bsw@1496: local valid_html, error_message = util.html_is_safe(draft_text) bsw@1496: if not valid_html then bsw@1496: slot.put_into("error", _("Draft contains invalid formatting or character sequence: #{error_message}", { error_message = error_message }) ) bsw@1496: return false bsw@1496: end bsw@1496: bsw@1496: if config.initiative_abstract then bsw@1496: local abstract = param.get("abstract") bsw@1496: if not abstract then bsw@1496: slot.put_into("error", "no abstract") bsw@1496: return false bsw@1496: end bsw@1496: abstract = encode.html(abstract) bsw@1496: draft_text = abstract .. "" .. draft_text bsw@1496: end bsw@1496: bsw@1496: local location = param.get("location") bsw@1496: if location == "" then bsw@1496: location = nil bsw@1496: end bsw@1550: bsw@1550: local external_reference bsw@1550: if config.firstlife then bsw@1550: external_reference = param.get("external_reference") bsw@1550: end bsw@1496: bsw@1496: if param.get("preview") or param.get("edit") then bsw@1496: return false bsw@1496: end bsw/jbe@1309: bsw@1496: initiative = Initiative:new() bsw@1496: bsw@1496: if not issue then bsw@1496: issue = Issue:new() bsw@1496: issue.area_id = area.id bsw@1496: issue.policy_id = policy_id bsw@1496: bsw@1496: if policy.polling then bsw@1496: issue.accepted = 'now' bsw@1496: issue.state = 'discussion' bsw@1496: initiative.polling = true bsw@1496: bsw@1496: if policy.free_timeable then bsw@1496: issue.discussion_time = timing.discussion bsw@1496: issue.verification_time = timing.verification bsw@1496: issue.voting_time = timing.voting bsw@1496: end bsw@1496: bsw@1496: end bsw@1496: bsw@1496: issue:save() bsw@1496: bsw@1496: if config.etherpad then bsw@1496: local result = net.curl( bsw@1496: config.etherpad.api_base bsw@1496: .. "api/1/createGroupPad?apikey=" .. config.etherpad.api_key bsw@1496: .. "&groupID=" .. config.etherpad.group_id bsw@1496: .. "&padName=Issue" .. tostring(issue.id) bsw@1496: .. "&text=" .. request.get_absolute_baseurl() .. "issue/show/" .. tostring(issue.id) .. ".html" bsw@1496: ) bsw@1496: end bsw@1496: end bsw@1496: bsw@1496: if param.get("polling", atom.boolean) and app.session.member:has_polling_right_for_unit_id(area.unit_id) then bsw@1496: initiative.polling = true bsw@1496: end bsw@1496: initiative.issue_id = issue.id bsw@1496: initiative.name = name bsw@1550: initiative.external_reference = external_reference bsw@1496: initiative:save() bsw@1496: bsw@1496: new_initiative = initiative bsw@1496: bsw@1496: local draft = Draft:new() bsw@1496: draft.initiative_id = initiative.id bsw@1496: draft.formatting_engine = formatting_engine bsw@1496: draft.content = draft_text bsw@1496: draft.location = location bsw@1496: draft.author_id = app.session.member.id bsw@1496: draft:save() bsw@1496: bsw@1496: draft_id = draft.id bsw@1496: bsw@1496: local initiator = Initiator:new() bsw@1496: initiator.initiative_id = initiative.id bsw@1496: initiator.member_id = app.session.member.id bsw@1496: initiator.accepted = true bsw@1496: initiator:save() bsw@1496: bsw@1496: if not is_polling then bsw@1496: local supporter = Supporter:new() bsw@1496: supporter.initiative_id = initiative.id bsw@1496: supporter.member_id = app.session.member.id bsw@1496: supporter.draft_id = draft.id bsw@1496: supporter:save() bsw@1496: end bsw@1496: bsw/jbe@1309: end bsw@1496: status = donew() bsw/jbe@1309: end bsw/jbe@1309: bsw@1495: if config.attachments then bsw@1495: local file_upload_session = param.get("file_upload_session") bsw@1495: file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "") bsw@1495: local file_uploads = json.array() bsw@1495: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json") bsw@1495: local fh = io.open(filename, "r") bsw@1495: if fh then bsw@1495: file_uploads = json.import(fh:read("*a")) bsw@1495: end bsw@1495: for i, file_upload in ipairs(file_uploads) do bsw@1495: if param.get("file_upload_delete_" .. file_upload.id, atom.boolean) then bsw@1495: for j = i, #file_uploads - 1 do bsw@1495: file_uploads[j] = file_uploads[j+1] bsw@1495: end bsw@1495: file_uploads[#file_uploads] = nil bsw@1495: end bsw@1495: end bsw@1495: local convert_func = config.attachments.convert_func bsw@1495: local last_id = param.get("file_upload_last_id", atom.number) bsw@1495: if last_id and last_id > 0 then bsw@1495: if last_id > 1024 then bsw@1495: last_id = 1024 bsw@1495: end bsw@1495: for i = 1, last_id do bsw@1495: local file = param.get("file_" .. i) bsw@1495: if file and #file > 0 then bsw@1495: local id = multirand.string( bsw@1495: 32, bsw@1495: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' bsw@1495: ) bsw@1495: local data, err, status = convert_func(file) bsw@1495: if status ~= 0 or data == nil then bsw@1495: slot.put_into("error", _"Error while converting image. Please note, that only JPG files are supported!") bsw@1495: return false bsw@1495: end bsw@1495: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".jpg") bsw@1495: local fh = assert(io.open(filename, "w")) bsw@1495: fh:write(file) bsw@1495: fh:write("\n") bsw@1495: fh:close() bsw@1495: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".preview.jpg") bsw@1495: local fh = assert(io.open(filename, "w")) bsw@1495: fh:write(data) bsw@1495: fh:write("\n") bsw@1495: fh:close() bsw@1495: table.insert(file_uploads, json.object{ bsw@1495: id = id, bsw@1495: filename = filename, bsw@1495: title = param.get("title_" .. i), bsw@1495: description = param.get("description_" .. i) bsw@1495: }) bsw@1495: end bsw@1495: end bsw@1495: end bsw@1495: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json") bsw@1495: local fh = assert(io.open(filename, "w")) bsw@1495: fh:write(json.export(file_uploads)) bsw@1495: fh:write("\n") bsw@1495: fh:close() bsw@1496: bsw@1496: if draft_id then bsw@1496: local file_upload_session = param.get("file_upload_session") bsw@1496: file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "") bsw@1495: bsw@1496: local draft_attachments = DraftAttachment:new_selector() bsw@1496: :add_where{ "draft_attachment.draft_id = ?", draft_id } bsw@1496: :exec() bsw@1496: bsw@1496: for i, draft_attachment in ipairs(draft_attachments) do bsw@1496: if param.get("file_delete_" .. draft_attachment.file_id, atom.boolean) then bsw@1496: draft_attachment:destroy() bsw@1496: end bsw@1496: end bsw@1495: bsw@1496: local file_uploads = json.array() bsw@1496: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json") bsw@1496: local fh = io.open(filename, "r") bsw@1496: if fh then bsw@1496: file_uploads = json.import(fh:read("*a")) bsw@1496: end bsw@1496: for i, file_upload in ipairs(file_uploads) do bsw@1496: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".jpg") bsw@1496: local data bsw@1496: local fh = io.open(filename, "r") bsw@1496: if fh then bsw@1496: data = fh:read("*a") bsw@1496: end bsw@1496: local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".preview.jpg") bsw@1496: local data_preview bsw@1496: local fh = io.open(filename, "r") bsw@1496: if fh then bsw@1496: data_preview = fh:read("*a") bsw@1496: end bsw@1496: bsw@1496: local hash = moonhash.sha3_512(data) bsw@1495: bsw@1496: local file = File:new_selector() bsw@1496: :add_where{ "hash = ?", hash } bsw@1496: :add_where{ "content_type = ?", "image/jpeg" } bsw@1496: :optional_object_mode() bsw@1496: :exec() bsw@1495: bsw@1496: if not file then bsw@1496: file = File:new() bsw@1496: file.content_type = "image/jpeg" bsw@1496: file.hash = hash bsw@1496: file.data = data bsw@1496: file.preview_content_type = "image/jpeg" bsw@1496: file.preview_data = data_preview bsw@1496: file:save() bsw@1496: end bsw@1496: bsw@1496: local draft_attachment = DraftAttachment:new() bsw@1496: draft_attachment.draft_id = draft_id bsw@1496: draft_attachment.file_id = file.id bsw@1496: draft_attachment.title = file_upload.title bsw@1496: draft_attachment.description = file_upload.description bsw@1496: draft_attachment:save() bsw@1495: end bsw@1495: end bsw@1495: bsw@1495: end bsw@1495: bsw@1496: if new_initiative and status ~= false then bsw@1535: local callback = param.get("callback") bsw@1535: if config.allow_new_draft_callback and callback then bsw@1535: request.redirect{ external = callback } bsw@1535: else bsw@1535: request.redirect{ bsw@1535: module = "initiative", bsw@1535: view = "show", bsw@1535: id = new_initiative.id bsw@1535: } bsw@1535: end bsw@1496: end bsw@1496: bsw@1496: return status bsw@1496: