liquid_feedback_frontend

annotate app/main/draft/_action/add.lua @ 1498:50f5b8a97f91

Added support for embedding initiatives
author bsw
date Tue Mar 24 16:48:20 2020 +0100 (2020-03-24)
parents ed3c40911ae1
children 770ab0a7f79b
rev   line source
bsw@1496 1 local initiative
bsw@1496 2 local new_initiative
bsw@1496 3 local draft_id
bsw@1496 4 local status
bsw@1496 5
bsw@1496 6 if param.get("initiative_id", atom.integer) then
bsw@1496 7
bsw@1496 8 local function donew()
bsw@1496 9 local draft_text = param.get("content")
bsw@1496 10
bsw@1496 11 if not draft_text then
bsw@1496 12 return false
bsw@1496 13 end
bsw@1496 14
bsw@1496 15 local draft_text = util.wysihtml_preproc(draft_text)
bsw@1496 16
bsw@1496 17 local valid_html, error_message = util.html_is_safe(draft_text)
bsw@1496 18 if not valid_html then
bsw@1496 19 slot.put_into("error", _("Draft contains invalid formatting or character sequence: #{error_message}", { error_message = error_message }) )
bsw@1496 20 return false
bsw@1496 21 end
bsw@1496 22
bsw@1496 23 if config.initiative_abstract then
bsw@1496 24 local abstract = param.get("abstract")
bsw@1496 25 if not abstract then
bsw@1496 26 return false
bsw@1496 27 end
bsw@1496 28 abstract = encode.html(abstract)
bsw@1496 29 draft_text = abstract .. "<!--END_OF_ABSTRACT-->" .. draft_text
bsw@1496 30 end
bsw@1496 31
bsw@1496 32 draft_id = Draft:update_content(
bsw@1496 33 app.session.member.id,
bsw@1496 34 param.get("initiative_id", atom.integer),
bsw@1496 35 param.get("formatting_engine"),
bsw@1496 36 draft_text,
bsw@1496 37 nil,
bsw@1496 38 param.get("preview") or param.get("edit")
bsw@1496 39 )
bsw@1496 40 return draft_id and true or false
bsw@1496 41 end
bsw@1496 42
bsw@1496 43 status = donew()
bsw@1496 44
bsw@1496 45 else
bsw@1496 46
bsw@1496 47 local function donew()
bsw@1496 48 local issue
bsw@1496 49 local area
bsw/jbe@1309 50
bsw@1496 51 local issue_id = param.get("issue_id", atom.integer)
bsw@1496 52 if issue_id then
bsw@1496 53 issue = Issue:new_selector():add_where{"id=?",issue_id}:for_share():single_object_mode():exec()
bsw@1496 54 if issue.closed then
bsw@1496 55 slot.put_into("error", _"This issue is already closed.")
bsw@1496 56 return false
bsw@1496 57 elseif issue.fully_frozen then
bsw@1496 58 slot.put_into("error", _"Voting for this issue has already begun.")
bsw@1496 59 return false
bsw@1496 60 elseif issue.phase_finished then
bsw@1496 61 slot.put_into("error", _"Current phase is already closed.")
bsw@1496 62 return false
bsw@1496 63 end
bsw@1496 64 area = issue.area
bsw@1496 65 else
bsw@1496 66 local area_id = param.get("area_id", atom.integer)
bsw@1496 67 area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec()
bsw@1496 68 if not area.active then
bsw@1496 69 slot.put_into("error", "Invalid area.")
bsw@1496 70 return false
bsw@1496 71 end
bsw@1496 72 end
bsw@1496 73
bsw@1496 74 if not app.session.member:has_voting_right_for_unit_id(area.unit_id) then
bsw@1496 75 return execute.view { module = "index", view = "403" }
bsw@1496 76 end
bsw@1496 77
bsw@1496 78 local policy_id = param.get("policy_id", atom.integer)
bsw@1496 79 local policy
bsw@1496 80 if policy_id then
bsw@1496 81 policy = Policy:by_id(policy_id)
bsw@1496 82 end
bsw/jbe@1309 83
bsw@1496 84 if not issue then
bsw@1496 85 if policy_id == -1 then
bsw@1496 86 slot.put_into("error", _"Please choose a policy")
bsw@1496 87 return false
bsw@1496 88 end
bsw@1496 89 if not policy.active then
bsw@1496 90 slot.put_into("error", "Invalid policy.")
bsw@1496 91 return false
bsw@1496 92 end
bsw@1496 93 if policy.polling and not app.session.member:has_polling_right_for_unit_id(area.unit_id) then
bsw@1496 94 return execute.view { module = "index", view = "403" }
bsw@1496 95 end
bsw@1496 96 if not area:get_reference_selector("allowed_policies")
bsw@1496 97 :add_where{ "policy.id = ?", policy_id }
bsw@1496 98 :optional_object_mode()
bsw@1496 99 :exec()
bsw@1496 100 then
bsw@1496 101 slot.put_into("error", "policy not allowed")
bsw@1496 102 return false
bsw@1496 103 end
bsw@1496 104 end
bsw@1496 105
bsw@1496 106 local is_polling = (issue and param.get("polling", atom.boolean)) or (policy and policy.polling) or false
bsw@1496 107
bsw@1496 108 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 109 if not tmp or tmp.initiatives_left < 1 then
bsw@1496 110 slot.put_into("error", _"Sorry, your contingent for creating initiatives has been used up. Please try again later.")
bsw@1496 111 return false
bsw@1496 112 end
bsw@1496 113 if tmp and tmp.text_entries_left < 1 then
bsw@1496 114 slot.put_into("error", _"Sorry, you have reached your personal flood limit. Please be slower...")
bsw@1496 115 return false
bsw@1496 116 end
bsw@1496 117
bsw@1496 118 local name = param.get("name")
bsw@1496 119
bsw@1496 120 local name = util.trim(name)
bsw@1496 121
bsw@1496 122 if #name < 3 then
bsw@1496 123 slot.put_into("error", _"Please enter a meaningful title for your initiative!")
bsw@1496 124 return false
bsw@1496 125 end
bsw@1496 126
bsw@1496 127 if #name > 140 then
bsw@1496 128 slot.put_into("error", _"This title is too long!")
bsw@1496 129 return false
bsw@1496 130 end
bsw/jbe@1309 131
bsw@1496 132 local timing
bsw@1496 133 if not issue and policy.free_timeable then
bsw@1496 134 local free_timing_string = util.trim(param.get("free_timing"))
bsw@1496 135 if not free_timing_string or #free_timing_string < 1 then
bsw@1496 136 slot.put_into("error", _"Choose timing")
bsw@1496 137 return false
bsw@1496 138 end
bsw@1496 139 local available_timings
bsw@1496 140 if config.free_timing and config.free_timing.available_func then
bsw@1496 141 available_timings = config.free_timing.available_func(policy)
bsw@1496 142 if available_timings == false then
bsw@1496 143 slot.put_into("error", "error in free timing config")
bsw@1496 144 return false
bsw@1496 145 end
bsw@1496 146 end
bsw@1496 147 if available_timings then
bsw@1496 148 local timing_available = false
bsw@1496 149 for i, available_timing in ipairs(available_timings) do
bsw@1496 150 if available_timing.id == free_timing_string then
bsw@1496 151 timing_available = true
bsw@1496 152 end
bsw@1496 153 end
bsw@1496 154 if not timing_available then
bsw@1496 155 slot.put_into("error", _"Invalid timing")
bsw@1496 156 return false
bsw@1496 157 end
bsw@1496 158 end
bsw@1496 159 timing = config.free_timing.calculate_func(policy, free_timing_string)
bsw@1496 160 if not timing then
bsw@1496 161 slot.put_into("error", "error in free timing config")
bsw@1496 162 return false
bsw@1496 163 end
bsw@1496 164 end
bsw@1496 165
bsw@1496 166 local draft_text = param.get("content")
bsw@1496 167
bsw@1496 168 if not draft_text then
bsw@1496 169 slot.put_into("error", "no draft text")
bsw@1496 170 return false
bsw@1496 171 end
bsw@1496 172
bsw@1496 173 local draft_text = util.wysihtml_preproc(draft_text)
bsw@1496 174
bsw@1496 175 local valid_html, error_message = util.html_is_safe(draft_text)
bsw@1496 176 if not valid_html then
bsw@1496 177 slot.put_into("error", _("Draft contains invalid formatting or character sequence: #{error_message}", { error_message = error_message }) )
bsw@1496 178 return false
bsw@1496 179 end
bsw@1496 180
bsw@1496 181 if config.initiative_abstract then
bsw@1496 182 local abstract = param.get("abstract")
bsw@1496 183 if not abstract then
bsw@1496 184 slot.put_into("error", "no abstract")
bsw@1496 185 return false
bsw@1496 186 end
bsw@1496 187 abstract = encode.html(abstract)
bsw@1496 188 draft_text = abstract .. "<!--END_OF_ABSTRACT-->" .. draft_text
bsw@1496 189 end
bsw@1496 190
bsw@1496 191 local location = param.get("location")
bsw@1496 192 if location == "" then
bsw@1496 193 location = nil
bsw@1496 194 end
bsw@1496 195
bsw@1496 196 if param.get("preview") or param.get("edit") then
bsw@1496 197 return false
bsw@1496 198 end
bsw/jbe@1309 199
bsw@1496 200 initiative = Initiative:new()
bsw@1496 201
bsw@1496 202 if not issue then
bsw@1496 203 issue = Issue:new()
bsw@1496 204 issue.area_id = area.id
bsw@1496 205 issue.policy_id = policy_id
bsw@1496 206
bsw@1496 207 if policy.polling then
bsw@1496 208 issue.accepted = 'now'
bsw@1496 209 issue.state = 'discussion'
bsw@1496 210 initiative.polling = true
bsw@1496 211
bsw@1496 212 if policy.free_timeable then
bsw@1496 213 issue.discussion_time = timing.discussion
bsw@1496 214 issue.verification_time = timing.verification
bsw@1496 215 issue.voting_time = timing.voting
bsw@1496 216 end
bsw@1496 217
bsw@1496 218 end
bsw@1496 219
bsw@1496 220 issue:save()
bsw@1496 221
bsw@1496 222 if config.etherpad then
bsw@1496 223 local result = net.curl(
bsw@1496 224 config.etherpad.api_base
bsw@1496 225 .. "api/1/createGroupPad?apikey=" .. config.etherpad.api_key
bsw@1496 226 .. "&groupID=" .. config.etherpad.group_id
bsw@1496 227 .. "&padName=Issue" .. tostring(issue.id)
bsw@1496 228 .. "&text=" .. request.get_absolute_baseurl() .. "issue/show/" .. tostring(issue.id) .. ".html"
bsw@1496 229 )
bsw@1496 230 end
bsw@1496 231 end
bsw@1496 232
bsw@1496 233 if param.get("polling", atom.boolean) and app.session.member:has_polling_right_for_unit_id(area.unit_id) then
bsw@1496 234 initiative.polling = true
bsw@1496 235 end
bsw@1496 236 initiative.issue_id = issue.id
bsw@1496 237 initiative.name = name
bsw@1496 238 initiative:save()
bsw@1496 239
bsw@1496 240 new_initiative = initiative
bsw@1496 241
bsw@1496 242 local draft = Draft:new()
bsw@1496 243 draft.initiative_id = initiative.id
bsw@1496 244 draft.formatting_engine = formatting_engine
bsw@1496 245 draft.content = draft_text
bsw@1496 246 draft.location = location
bsw@1496 247 draft.author_id = app.session.member.id
bsw@1496 248 draft:save()
bsw@1496 249
bsw@1496 250 draft_id = draft.id
bsw@1496 251
bsw@1496 252 local initiator = Initiator:new()
bsw@1496 253 initiator.initiative_id = initiative.id
bsw@1496 254 initiator.member_id = app.session.member.id
bsw@1496 255 initiator.accepted = true
bsw@1496 256 initiator:save()
bsw@1496 257
bsw@1496 258 if not is_polling then
bsw@1496 259 local supporter = Supporter:new()
bsw@1496 260 supporter.initiative_id = initiative.id
bsw@1496 261 supporter.member_id = app.session.member.id
bsw@1496 262 supporter.draft_id = draft.id
bsw@1496 263 supporter:save()
bsw@1496 264 end
bsw@1496 265
bsw/jbe@1309 266 end
bsw@1496 267 status = donew()
bsw/jbe@1309 268 end
bsw/jbe@1309 269
bsw@1495 270 if config.attachments then
bsw@1495 271 local file_upload_session = param.get("file_upload_session")
bsw@1495 272 file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
bsw@1495 273 local file_uploads = json.array()
bsw@1495 274 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
bsw@1495 275 local fh = io.open(filename, "r")
bsw@1495 276 if fh then
bsw@1495 277 file_uploads = json.import(fh:read("*a"))
bsw@1495 278 end
bsw@1495 279 for i, file_upload in ipairs(file_uploads) do
bsw@1495 280 if param.get("file_upload_delete_" .. file_upload.id, atom.boolean) then
bsw@1495 281 for j = i, #file_uploads - 1 do
bsw@1495 282 file_uploads[j] = file_uploads[j+1]
bsw@1495 283 end
bsw@1495 284 file_uploads[#file_uploads] = nil
bsw@1495 285 end
bsw@1495 286 end
bsw@1495 287 local convert_func = config.attachments.convert_func
bsw@1495 288 local last_id = param.get("file_upload_last_id", atom.number)
bsw@1495 289 if last_id and last_id > 0 then
bsw@1495 290 if last_id > 1024 then
bsw@1495 291 last_id = 1024
bsw@1495 292 end
bsw@1495 293 for i = 1, last_id do
bsw@1495 294 local file = param.get("file_" .. i)
bsw@1495 295 if file and #file > 0 then
bsw@1495 296 local id = multirand.string(
bsw@1495 297 32,
bsw@1495 298 '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
bsw@1495 299 )
bsw@1495 300 local data, err, status = convert_func(file)
bsw@1495 301 if status ~= 0 or data == nil then
bsw@1495 302 slot.put_into("error", _"Error while converting image. Please note, that only JPG files are supported!")
bsw@1495 303 return false
bsw@1495 304 end
bsw@1495 305 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".jpg")
bsw@1495 306 local fh = assert(io.open(filename, "w"))
bsw@1495 307 fh:write(file)
bsw@1495 308 fh:write("\n")
bsw@1495 309 fh:close()
bsw@1495 310 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".preview.jpg")
bsw@1495 311 local fh = assert(io.open(filename, "w"))
bsw@1495 312 fh:write(data)
bsw@1495 313 fh:write("\n")
bsw@1495 314 fh:close()
bsw@1495 315 table.insert(file_uploads, json.object{
bsw@1495 316 id = id,
bsw@1495 317 filename = filename,
bsw@1495 318 title = param.get("title_" .. i),
bsw@1495 319 description = param.get("description_" .. i)
bsw@1495 320 })
bsw@1495 321 end
bsw@1495 322 end
bsw@1495 323 end
bsw@1495 324 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
bsw@1495 325 local fh = assert(io.open(filename, "w"))
bsw@1495 326 fh:write(json.export(file_uploads))
bsw@1495 327 fh:write("\n")
bsw@1495 328 fh:close()
bsw@1496 329
bsw@1496 330 if draft_id then
bsw@1496 331 local file_upload_session = param.get("file_upload_session")
bsw@1496 332 file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
bsw@1495 333
bsw@1496 334 local draft_attachments = DraftAttachment:new_selector()
bsw@1496 335 :add_where{ "draft_attachment.draft_id = ?", draft_id }
bsw@1496 336 :exec()
bsw@1496 337
bsw@1496 338 for i, draft_attachment in ipairs(draft_attachments) do
bsw@1496 339 if param.get("file_delete_" .. draft_attachment.file_id, atom.boolean) then
bsw@1496 340 draft_attachment:destroy()
bsw@1496 341 end
bsw@1496 342 end
bsw@1495 343
bsw@1496 344 local file_uploads = json.array()
bsw@1496 345 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
bsw@1496 346 local fh = io.open(filename, "r")
bsw@1496 347 if fh then
bsw@1496 348 file_uploads = json.import(fh:read("*a"))
bsw@1496 349 end
bsw@1496 350 for i, file_upload in ipairs(file_uploads) do
bsw@1496 351 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".jpg")
bsw@1496 352 local data
bsw@1496 353 local fh = io.open(filename, "r")
bsw@1496 354 if fh then
bsw@1496 355 data = fh:read("*a")
bsw@1496 356 end
bsw@1496 357 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".preview.jpg")
bsw@1496 358 local data_preview
bsw@1496 359 local fh = io.open(filename, "r")
bsw@1496 360 if fh then
bsw@1496 361 data_preview = fh:read("*a")
bsw@1496 362 end
bsw@1496 363
bsw@1496 364 local hash = moonhash.sha3_512(data)
bsw@1495 365
bsw@1496 366 local file = File:new_selector()
bsw@1496 367 :add_where{ "hash = ?", hash }
bsw@1496 368 :add_where{ "content_type = ?", "image/jpeg" }
bsw@1496 369 :optional_object_mode()
bsw@1496 370 :exec()
bsw@1495 371
bsw@1496 372 if not file then
bsw@1496 373 file = File:new()
bsw@1496 374 file.content_type = "image/jpeg"
bsw@1496 375 file.hash = hash
bsw@1496 376 file.data = data
bsw@1496 377 file.preview_content_type = "image/jpeg"
bsw@1496 378 file.preview_data = data_preview
bsw@1496 379 file:save()
bsw@1496 380 end
bsw@1496 381
bsw@1496 382 local draft_attachment = DraftAttachment:new()
bsw@1496 383 draft_attachment.draft_id = draft_id
bsw@1496 384 draft_attachment.file_id = file.id
bsw@1496 385 draft_attachment.title = file_upload.title
bsw@1496 386 draft_attachment.description = file_upload.description
bsw@1496 387 draft_attachment:save()
bsw@1495 388 end
bsw@1495 389 end
bsw@1495 390
bsw@1495 391 end
bsw@1495 392
bsw@1496 393 print(new_initiative, status)
bsw@1496 394 if new_initiative and status ~= false then
bsw@1496 395 request.redirect{
bsw@1496 396 module = "initiative",
bsw@1496 397 view = "show",
bsw@1496 398 id = new_initiative.id
bsw@1496 399 }
bsw@1496 400 end
bsw@1496 401
bsw@1496 402 return status
bsw@1496 403

Impressum / About Us