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@1550
|
195
|
bsw@1550
|
196 local external_reference
|
bsw@1550
|
197 if config.firstlife then
|
bsw@1550
|
198 external_reference = param.get("external_reference")
|
bsw@1550
|
199 end
|
bsw@1496
|
200
|
bsw@1496
|
201 if param.get("preview") or param.get("edit") then
|
bsw@1496
|
202 return false
|
bsw@1496
|
203 end
|
bsw/jbe@1309
|
204
|
bsw@1496
|
205 initiative = Initiative:new()
|
bsw@1496
|
206
|
bsw@1496
|
207 if not issue then
|
bsw@1496
|
208 issue = Issue:new()
|
bsw@1496
|
209 issue.area_id = area.id
|
bsw@1496
|
210 issue.policy_id = policy_id
|
bsw@1496
|
211
|
bsw@1496
|
212 if policy.polling then
|
bsw@1496
|
213 issue.accepted = 'now'
|
bsw@1496
|
214 issue.state = 'discussion'
|
bsw@1496
|
215 initiative.polling = true
|
bsw@1496
|
216
|
bsw@1496
|
217 if policy.free_timeable then
|
bsw@1496
|
218 issue.discussion_time = timing.discussion
|
bsw@1496
|
219 issue.verification_time = timing.verification
|
bsw@1496
|
220 issue.voting_time = timing.voting
|
bsw@1496
|
221 end
|
bsw@1496
|
222
|
bsw@1496
|
223 end
|
bsw@1496
|
224
|
bsw@1496
|
225 issue:save()
|
bsw@1496
|
226
|
bsw@1496
|
227 if config.etherpad then
|
bsw@1496
|
228 local result = net.curl(
|
bsw@1496
|
229 config.etherpad.api_base
|
bsw@1496
|
230 .. "api/1/createGroupPad?apikey=" .. config.etherpad.api_key
|
bsw@1496
|
231 .. "&groupID=" .. config.etherpad.group_id
|
bsw@1496
|
232 .. "&padName=Issue" .. tostring(issue.id)
|
bsw@1496
|
233 .. "&text=" .. request.get_absolute_baseurl() .. "issue/show/" .. tostring(issue.id) .. ".html"
|
bsw@1496
|
234 )
|
bsw@1496
|
235 end
|
bsw@1496
|
236 end
|
bsw@1496
|
237
|
bsw@1496
|
238 if param.get("polling", atom.boolean) and app.session.member:has_polling_right_for_unit_id(area.unit_id) then
|
bsw@1496
|
239 initiative.polling = true
|
bsw@1496
|
240 end
|
bsw@1496
|
241 initiative.issue_id = issue.id
|
bsw@1496
|
242 initiative.name = name
|
bsw@1550
|
243 initiative.external_reference = external_reference
|
bsw@1496
|
244 initiative:save()
|
bsw@1496
|
245
|
bsw@1496
|
246 new_initiative = initiative
|
bsw@1496
|
247
|
bsw@1496
|
248 local draft = Draft:new()
|
bsw@1496
|
249 draft.initiative_id = initiative.id
|
bsw@1496
|
250 draft.formatting_engine = formatting_engine
|
bsw@1496
|
251 draft.content = draft_text
|
bsw@1496
|
252 draft.location = location
|
bsw@1496
|
253 draft.author_id = app.session.member.id
|
bsw@1496
|
254 draft:save()
|
bsw@1496
|
255
|
bsw@1496
|
256 draft_id = draft.id
|
bsw@1496
|
257
|
bsw@1496
|
258 local initiator = Initiator:new()
|
bsw@1496
|
259 initiator.initiative_id = initiative.id
|
bsw@1496
|
260 initiator.member_id = app.session.member.id
|
bsw@1496
|
261 initiator.accepted = true
|
bsw@1496
|
262 initiator:save()
|
bsw@1496
|
263
|
bsw@1496
|
264 if not is_polling then
|
bsw@1496
|
265 local supporter = Supporter:new()
|
bsw@1496
|
266 supporter.initiative_id = initiative.id
|
bsw@1496
|
267 supporter.member_id = app.session.member.id
|
bsw@1496
|
268 supporter.draft_id = draft.id
|
bsw@1496
|
269 supporter:save()
|
bsw@1496
|
270 end
|
bsw@1496
|
271
|
bsw/jbe@1309
|
272 end
|
bsw@1496
|
273 status = donew()
|
bsw/jbe@1309
|
274 end
|
bsw/jbe@1309
|
275
|
bsw@1495
|
276 if config.attachments then
|
bsw@1495
|
277 local file_upload_session = param.get("file_upload_session")
|
bsw@1495
|
278 file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
|
bsw@1495
|
279 local file_uploads = json.array()
|
bsw@1495
|
280 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
|
bsw@1495
|
281 local fh = io.open(filename, "r")
|
bsw@1495
|
282 if fh then
|
bsw@1495
|
283 file_uploads = json.import(fh:read("*a"))
|
bsw@1495
|
284 end
|
bsw@1495
|
285 for i, file_upload in ipairs(file_uploads) do
|
bsw@1495
|
286 if param.get("file_upload_delete_" .. file_upload.id, atom.boolean) then
|
bsw@1495
|
287 for j = i, #file_uploads - 1 do
|
bsw@1495
|
288 file_uploads[j] = file_uploads[j+1]
|
bsw@1495
|
289 end
|
bsw@1495
|
290 file_uploads[#file_uploads] = nil
|
bsw@1495
|
291 end
|
bsw@1495
|
292 end
|
bsw@1495
|
293 local convert_func = config.attachments.convert_func
|
bsw@1495
|
294 local last_id = param.get("file_upload_last_id", atom.number)
|
bsw@1495
|
295 if last_id and last_id > 0 then
|
bsw@1495
|
296 if last_id > 1024 then
|
bsw@1495
|
297 last_id = 1024
|
bsw@1495
|
298 end
|
bsw@1495
|
299 for i = 1, last_id do
|
bsw@1495
|
300 local file = param.get("file_" .. i)
|
bsw@1495
|
301 if file and #file > 0 then
|
bsw@1495
|
302 local id = multirand.string(
|
bsw@1495
|
303 32,
|
bsw@1495
|
304 '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
bsw@1495
|
305 )
|
bsw@1495
|
306 local data, err, status = convert_func(file)
|
bsw@1495
|
307 if status ~= 0 or data == nil then
|
bsw@1495
|
308 slot.put_into("error", _"Error while converting image. Please note, that only JPG files are supported!")
|
bsw@1495
|
309 return false
|
bsw@1495
|
310 end
|
bsw@1495
|
311 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".jpg")
|
bsw@1495
|
312 local fh = assert(io.open(filename, "w"))
|
bsw@1495
|
313 fh:write(file)
|
bsw@1495
|
314 fh:write("\n")
|
bsw@1495
|
315 fh:close()
|
bsw@1495
|
316 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. id .. ".preview.jpg")
|
bsw@1495
|
317 local fh = assert(io.open(filename, "w"))
|
bsw@1495
|
318 fh:write(data)
|
bsw@1495
|
319 fh:write("\n")
|
bsw@1495
|
320 fh:close()
|
bsw@1495
|
321 table.insert(file_uploads, json.object{
|
bsw@1495
|
322 id = id,
|
bsw@1495
|
323 filename = filename,
|
bsw@1495
|
324 title = param.get("title_" .. i),
|
bsw@1495
|
325 description = param.get("description_" .. i)
|
bsw@1495
|
326 })
|
bsw@1495
|
327 end
|
bsw@1495
|
328 end
|
bsw@1495
|
329 end
|
bsw@1495
|
330 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
|
bsw@1495
|
331 local fh = assert(io.open(filename, "w"))
|
bsw@1495
|
332 fh:write(json.export(file_uploads))
|
bsw@1495
|
333 fh:write("\n")
|
bsw@1495
|
334 fh:close()
|
bsw@1496
|
335
|
bsw@1496
|
336 if draft_id then
|
bsw@1496
|
337 local file_upload_session = param.get("file_upload_session")
|
bsw@1496
|
338 file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
|
bsw@1495
|
339
|
bsw@1496
|
340 local draft_attachments = DraftAttachment:new_selector()
|
bsw@1496
|
341 :add_where{ "draft_attachment.draft_id = ?", draft_id }
|
bsw@1496
|
342 :exec()
|
bsw@1496
|
343
|
bsw@1496
|
344 for i, draft_attachment in ipairs(draft_attachments) do
|
bsw@1496
|
345 if param.get("file_delete_" .. draft_attachment.file_id, atom.boolean) then
|
bsw@1496
|
346 draft_attachment:destroy()
|
bsw@1496
|
347 end
|
bsw@1496
|
348 end
|
bsw@1495
|
349
|
bsw@1496
|
350 local file_uploads = json.array()
|
bsw@1496
|
351 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
|
bsw@1496
|
352 local fh = io.open(filename, "r")
|
bsw@1496
|
353 if fh then
|
bsw@1496
|
354 file_uploads = json.import(fh:read("*a"))
|
bsw@1496
|
355 end
|
bsw@1496
|
356 for i, file_upload in ipairs(file_uploads) do
|
bsw@1496
|
357 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".jpg")
|
bsw@1496
|
358 local data
|
bsw@1496
|
359 local fh = io.open(filename, "r")
|
bsw@1496
|
360 if fh then
|
bsw@1496
|
361 data = fh:read("*a")
|
bsw@1496
|
362 end
|
bsw@1496
|
363 local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. "-" .. file_upload.id .. ".preview.jpg")
|
bsw@1496
|
364 local data_preview
|
bsw@1496
|
365 local fh = io.open(filename, "r")
|
bsw@1496
|
366 if fh then
|
bsw@1496
|
367 data_preview = fh:read("*a")
|
bsw@1496
|
368 end
|
bsw@1496
|
369
|
bsw@1496
|
370 local hash = moonhash.sha3_512(data)
|
bsw@1495
|
371
|
bsw@1496
|
372 local file = File:new_selector()
|
bsw@1496
|
373 :add_where{ "hash = ?", hash }
|
bsw@1496
|
374 :add_where{ "content_type = ?", "image/jpeg" }
|
bsw@1496
|
375 :optional_object_mode()
|
bsw@1496
|
376 :exec()
|
bsw@1495
|
377
|
bsw@1496
|
378 if not file then
|
bsw@1496
|
379 file = File:new()
|
bsw@1496
|
380 file.content_type = "image/jpeg"
|
bsw@1496
|
381 file.hash = hash
|
bsw@1496
|
382 file.data = data
|
bsw@1496
|
383 file.preview_content_type = "image/jpeg"
|
bsw@1496
|
384 file.preview_data = data_preview
|
bsw@1496
|
385 file:save()
|
bsw@1496
|
386 end
|
bsw@1496
|
387
|
bsw@1496
|
388 local draft_attachment = DraftAttachment:new()
|
bsw@1496
|
389 draft_attachment.draft_id = draft_id
|
bsw@1496
|
390 draft_attachment.file_id = file.id
|
bsw@1496
|
391 draft_attachment.title = file_upload.title
|
bsw@1496
|
392 draft_attachment.description = file_upload.description
|
bsw@1496
|
393 draft_attachment:save()
|
bsw@1495
|
394 end
|
bsw@1495
|
395 end
|
bsw@1495
|
396
|
bsw@1495
|
397 end
|
bsw@1495
|
398
|
bsw@1496
|
399 if new_initiative and status ~= false then
|
bsw@1535
|
400 local callback = param.get("callback")
|
bsw@1535
|
401 if config.allow_new_draft_callback and callback then
|
bsw@1535
|
402 request.redirect{ external = callback }
|
bsw@1535
|
403 else
|
bsw@1535
|
404 request.redirect{
|
bsw@1535
|
405 module = "initiative",
|
bsw@1535
|
406 view = "show",
|
bsw@1535
|
407 id = new_initiative.id
|
bsw@1535
|
408 }
|
bsw@1535
|
409 end
|
bsw@1496
|
410 end
|
bsw@1496
|
411
|
bsw@1496
|
412 return status
|
bsw@1496
|
413
|