liquid_feedback_frontend
changeset 286:c587d8762e62
Registration process updated for Core 2.0, lockable member fields, notification settings
line diff
1.1 --- a/app/main/_filter/20_session.lua Fri Feb 17 15:16:02 2012 +0100 1.2 +++ b/app/main/_filter/20_session.lua Sat Feb 25 11:51:37 2012 +0100 1.3 @@ -1,5 +1,3 @@ 1.4 -request.set_cookie{ path = "/", name = "sessionID", value = "s.lmb0lkEkMu16dO0y" } 1.5 - 1.6 if cgi.cookies.liquid_feedback_session then 1.7 app.session = Session:by_ident(cgi.cookies.liquid_feedback_session) 1.8 end 1.9 @@ -13,6 +11,11 @@ 1.10 1.11 request.set_csrf_secret(app.session.additional_secret) 1.12 1.13 -locale.set{lang = app.session.lang or config.default_lang or "en"} 1.14 +if not app.session.member.lang then 1.15 + app.session.member.lang = app.session.lang or config.default_lang or "en" 1.16 + app.session.member:save() 1.17 +end 1.18 + 1.19 +locale.set{lang = app.session.member.lang } 1.20 1.21 execute.inner()
2.1 --- a/app/main/_layout/default.html Fri Feb 17 15:16:02 2012 +0100 2.2 +++ b/app/main/_layout/default.html Sat Feb 25 11:51:37 2012 +0100 2.3 @@ -60,9 +60,8 @@ 2.4 <div class="support vote_info" id="support"> 2.5 <!-- WEBMCP SLOT support --> 2.6 </div> 2.7 - 2.8 - <div class="content_navigation" id="content_navigation"> 2.9 - <!-- WEBMCP SLOT content_navigation --> 2.10 + <div class="initiative_main" id="initiative_main"> 2.11 + <!-- WEBMCP SLOT initiative_main --> 2.12 </div> 2.13 <div class="main" id="default"> 2.14 <!-- WEBMCP SLOT default -->
3.1 --- a/app/main/admin/_action/member_update.lua Fri Feb 17 15:16:02 2012 +0100 3.2 +++ b/app/main/admin/_action/member_update.lua Sat Feb 25 11:51:37 2012 +0100 3.3 @@ -1,18 +1,9 @@ 3.4 local member = Member:by_id(param.get_id()) or Member:new() 3.5 3.6 -param.update(member, "login", "admin", "name") 3.7 - 3.8 -if param.get("activated", atom.boolean) then 3.9 - member.activated = "now" 3.10 -end 3.11 +param.update(member, "identification", "notify_email", "admin") 3.12 3.13 -local password = param.get("password") 3.14 -if password == "********" or #password == 0 then 3.15 - password = nil 3.16 -end 3.17 - 3.18 -if password then 3.19 - member:set_password(password) 3.20 +if param.get("invite_member", atom.boolean) then 3.21 + member:send_invitation() 3.22 end 3.23 3.24 local err = member:try_save()
4.1 --- a/app/main/admin/member_edit.lua Fri Feb 17 15:16:02 2012 +0100 4.2 +++ b/app/main/admin/member_edit.lua Sat Feb 25 11:51:37 2012 +0100 4.3 @@ -23,11 +23,10 @@ 4.4 } 4.5 }, 4.6 content = function() 4.7 - ui.field.text{ label = _"Login", name = "login" } 4.8 - ui.field.text{ label = _"Name", name = "name" } 4.9 - ui.field.password{ label = _"Password", name = "password", value = (member and member.password) and "********" or "" } 4.10 + ui.field.text{ label = _"Identification", name = "identification" } 4.11 + ui.field.text{ label = _"Notification email", name = "notify_email" } 4.12 ui.field.boolean{ label = _"Admin?", name = "admin" } 4.13 - ui.field.boolean{ label = _"Activated?", name = "activated" } 4.14 + ui.field.boolean{ label = _"Send invite?", name = "invite_member" } 4.15 ui.submit{ text = _"Save" } 4.16 end 4.17 }
5.1 --- a/app/main/index/_action/login.lua Fri Feb 17 15:16:02 2012 +0100 5.2 +++ b/app/main/index/_action/login.lua Sat Feb 25 11:51:37 2012 +0100 5.3 @@ -1,5 +1,55 @@ 5.4 local member = Member:by_login_and_password(param.get('login'), param.get('password')) 5.5 5.6 +function do_etherpad_auth(member) 5.7 + local result = net.curl( 5.8 + config.etherpad.api_base 5.9 + .. "api/1/createAuthorIfNotExistsFor?apikey=" .. config.etherpad.api_key 5.10 + .. "&name=" .. encode.url_part(member.name) .. "&authorMapper=" .. tostring(member.id) 5.11 + ) 5.12 + 5.13 + if not result then 5.14 + slot.put_into("error", _"Etherpad authentication failed" .. " 1") 5.15 + return false 5.16 + end 5.17 + 5.18 + local etherpad_author_id = string.match(result, '"authorID"%s*:%s*"([^"]+)"') 5.19 + 5.20 + if not etherpad_author_id then 5.21 + slot.put_into("error", _"Etherpad authentication failed" .. " 2") 5.22 + return false 5.23 + end 5.24 + 5.25 + local time_in_24h = os.time() + 24 * 60 * 60 5.26 + 5.27 + local result = net.curl( 5.28 + config.etherpad.api_base 5.29 + .. "api/1/createSession?apikey=" .. config.etherpad.api_key 5.30 + .. "&groupID=" .. config.etherpad.group_id 5.31 + .. "&authorID=" .. etherpad_author_id 5.32 + .. "&validUntil=" .. time_in_24h 5.33 + ) 5.34 + 5.35 + if not result then 5.36 + slot.put_into("error", _"Etherpad authentication failed" .. " 3") 5.37 + return false 5.38 + end 5.39 + 5.40 + local etherpad_sesion_id = string.match(result, '"sessionID"%s*:%s*"([^"]+)"') 5.41 + 5.42 + if not etherpad_sesion_id then 5.43 + slot.put_into("error", _"Etherpad authentication failed" .. " 4") 5.44 + return false 5.45 + end 5.46 + 5.47 + request.set_cookie{ 5.48 + path = config.etherpad.cookie_path, 5.49 + name = "sessionID", 5.50 + value = etherpad_sesion_id 5.51 + } 5.52 + 5.53 +end 5.54 + 5.55 + 5.56 if member then 5.57 member.last_login = "now" 5.58 member.last_activity = "now" 5.59 @@ -11,6 +61,9 @@ 5.60 ui.tag{ content = _'Login successful!' } 5.61 end) 5.62 trace.debug('User authenticated') 5.63 + if config.etherpad then 5.64 + do_etherpad_auth(member) 5.65 + end 5.66 else 5.67 slot.select("error", function() 5.68 ui.tag{ content = _'Invalid username or password!' }
6.1 --- a/app/main/index/_action/logout.lua Fri Feb 17 15:16:02 2012 +0100 6.2 +++ b/app/main/index/_action/logout.lua Sat Feb 25 11:51:37 2012 +0100 6.3 @@ -1,4 +1,11 @@ 6.4 if app.session then 6.5 app.session:destroy() 6.6 slot.put_into("notice", _"Logout successful") 6.7 + if config.etherpad then 6.8 + request.set_cookie{ 6.9 + path = config.etherpad.cookie_path, 6.10 + name = "sessionID", 6.11 + value = "invalid" 6.12 + } 6.13 + end 6.14 end
7.1 --- a/app/main/index/_action/register.lua Fri Feb 17 15:16:02 2012 +0100 7.2 +++ b/app/main/index/_action/register.lua Sat Feb 25 11:51:37 2012 +0100 7.3 @@ -1,13 +1,14 @@ 7.4 local code = util.trim(param.get("code")) 7.5 7.6 -local invite_code = InviteCode:new_selector() 7.7 - :add_where{ "code = ?", code } 7.8 +local member = Member:new_selector() 7.9 + :add_where{ "invite_code = ?", code } 7.10 + :add_where{ "activated ISNULL" } 7.11 :optional_object_mode() 7.12 :for_update() 7.13 :exec() 7.14 7.15 -if not invite_code or invite_code.used then 7.16 - slot.put_into("error", _"The code you've entered is invalid" .. ": '" .. code .. "'") 7.17 +if not member then 7.18 + slot.put_into("error", _"The code you've entered is invalid") 7.19 request.redirect{ 7.20 mode = "forward", 7.21 module = "index", 7.22 @@ -18,201 +19,206 @@ 7.23 7.24 local notify_email = param.get("notify_email") 7.25 7.26 -if invite_code and not notify_email then 7.27 - request.redirect{ 7.28 - mode = "redirect", 7.29 - module = "index", 7.30 - view = "register", 7.31 - params = { code = invite_code.code } 7.32 - } 7.33 - return false 7.34 +if not config.locked_profile_fields.notify_email and notify_email then 7.35 + if #notify_email < 5 then 7.36 + slot.put_into("error", _"Email address too short!") 7.37 + request.redirect{ 7.38 + mode = "redirect", 7.39 + module = "index", 7.40 + view = "register", 7.41 + params = { code = member.invite_code } 7.42 + } 7.43 + return false 7.44 + end 7.45 + member.notify_email = notify_email 7.46 end 7.47 7.48 -if #notify_email < 5 then 7.49 - slot.put_into("error", _"Email address too short!") 7.50 +if member and not member.notify_email then 7.51 request.redirect{ 7.52 mode = "redirect", 7.53 module = "index", 7.54 view = "register", 7.55 - params = { code = invite_code.code } 7.56 + params = { code = member.invite_code, step = 1 } 7.57 } 7.58 return false 7.59 end 7.60 7.61 -local name = param.get("name") 7.62 + 7.63 +local name = util.trim(param.get("name")) 7.64 + 7.65 +if not config.locked_profile_fields.name and name then 7.66 7.67 -if notify_email and not name then 7.68 - request.redirect{ 7.69 - mode = "redirect", 7.70 - module = "index", 7.71 - view = "register", 7.72 - params = { 7.73 - code = invite_code.code, 7.74 - notify_email = notify_email 7.75 + if #name < 3 then 7.76 + slot.put_into("error", _"This username is too short!") 7.77 + request.redirect{ 7.78 + mode = "redirect", 7.79 + module = "index", 7.80 + view = "register", 7.81 + params = { 7.82 + code = member.invite_code, 7.83 + notify_email = member.notify_email, 7.84 + step = 1 7.85 + } 7.86 } 7.87 - } 7.88 - return false 7.89 + return false 7.90 + end 7.91 + 7.92 + if Member:by_name(name) then 7.93 + slot.put_into("error", _"This name is already taken, please choose another one!") 7.94 + request.redirect{ 7.95 + mode = "redirect", 7.96 + module = "index", 7.97 + view = "register", 7.98 + params = { 7.99 + code = member.invite_code, 7.100 + notify_email = member.notify_email, 7.101 + step = 1 7.102 + } 7.103 + } 7.104 + return false 7.105 + end 7.106 + 7.107 + member.name = name 7.108 + 7.109 end 7.110 7.111 -name = util.trim(name) 7.112 - 7.113 -if #name < 3 then 7.114 - slot.put_into("error", _"This username is too short!") 7.115 +if member.notify_email and not member.name then 7.116 request.redirect{ 7.117 mode = "redirect", 7.118 module = "index", 7.119 view = "register", 7.120 params = { 7.121 - code = invite_code.code, 7.122 - notify_email = notify_email 7.123 - } 7.124 - } 7.125 - return false 7.126 -end 7.127 - 7.128 -if Member:by_name(name) then 7.129 - slot.put_into("error", _"This name is already taken, please choose another one!") 7.130 - request.redirect{ 7.131 - mode = "redirect", 7.132 - module = "index", 7.133 - view = "register", 7.134 - params = { 7.135 - code = invite_code.code, 7.136 - notify_email = notify_email 7.137 - } 7.138 - } 7.139 - return false 7.140 -end 7.141 - 7.142 -local login = param.get("login") 7.143 - 7.144 -if name and not login then 7.145 - request.redirect{ 7.146 - mode = "redirect", 7.147 - module = "index", 7.148 - view = "register", 7.149 - params = { 7.150 - code = invite_code.code, 7.151 - notify_email = notify_email, 7.152 - name = name 7.153 - } 7.154 - } 7.155 - return false 7.156 -end 7.157 - 7.158 -login = util.trim(login) 7.159 - 7.160 -if #login < 3 then 7.161 - slot.put_into("error", _"This login is too short!") 7.162 - request.redirect{ 7.163 - mode = "redirect", 7.164 - module = "index", 7.165 - view = "register", 7.166 - params = { 7.167 - code = invite_code.code, 7.168 - notify_email = notify_email, 7.169 - name = name 7.170 + code = member.invite_code, 7.171 + notify_email = member.notify_email, 7.172 + step = 1 7.173 } 7.174 } 7.175 return false 7.176 end 7.177 7.178 -if Member:by_login(login) then 7.179 - slot.put_into("error", _"This login is already taken, please choose another one!") 7.180 - request.redirect{ 7.181 - mode = "redirect", 7.182 - module = "index", 7.183 - view = "register", 7.184 - params = { 7.185 - code = invite_code.code, 7.186 - notify_email = notify_email, 7.187 - name = name 7.188 + 7.189 +local login = util.trim(param.get("login")) 7.190 + 7.191 +if not config.locked_profile_fields.login and login then 7.192 + if #login < 3 then 7.193 + slot.put_into("error", _"This login is too short!") 7.194 + request.redirect{ 7.195 + mode = "redirect", 7.196 + module = "index", 7.197 + view = "register", 7.198 + params = { 7.199 + code = member.invite_code, 7.200 + notify_email = member.notify_email, 7.201 + name = member.name, 7.202 + step = 1 7.203 + } 7.204 } 7.205 - } 7.206 - return false 7.207 + return false 7.208 + end 7.209 + 7.210 + if Member:by_login(login) then 7.211 + slot.put_into("error", _"This login is already taken, please choose another one!") 7.212 + request.redirect{ 7.213 + mode = "redirect", 7.214 + module = "index", 7.215 + view = "register", 7.216 + params = { 7.217 + code = member.invite_code, 7.218 + notify_email = member.notify_email, 7.219 + name = member.name, 7.220 + step = 1 7.221 + } 7.222 + } 7.223 + return false 7.224 + end 7.225 + member.login = login 7.226 end 7.227 7.228 -if login and param.get("step") ~= "5" then 7.229 - request.redirect{ 7.230 - mode = "redirect", 7.231 - module = "index", 7.232 - view = "register", 7.233 - params = { 7.234 - code = invite_code.code, 7.235 - notify_email = notify_email, 7.236 - name = name, 7.237 - login = login 7.238 - } 7.239 - } 7.240 - return false 7.241 -end 7.242 - 7.243 -for i, checkbox in ipairs(config.use_terms_checkboxes) do 7.244 - local accepted = param.get("use_terms_checkbox_" .. checkbox.name, atom.boolean) 7.245 - if not accepted then 7.246 - slot.put_into("error", checkbox.not_accepted_error) 7.247 - return false 7.248 - end 7.249 -end 7.250 - 7.251 -local password1 = param.get("password1") 7.252 -local password2 = param.get("password2") 7.253 - 7.254 -if login and not password1 then 7.255 +if member.name and not member.login then 7.256 request.redirect{ 7.257 mode = "redirect", 7.258 module = "index", 7.259 view = "register", 7.260 params = { 7.261 - code = invite_code.code, 7.262 - notify_email = notify_email, 7.263 - name = name, 7.264 - login = login 7.265 + code = member.invite_code, 7.266 + notify_email = member.notify_email, 7.267 + name = member.name, 7.268 + step = 1 7.269 } 7.270 } 7.271 ---]] 7.272 - return false 7.273 -end 7.274 - 7.275 -if password1 ~= password2 then 7.276 - slot.put_into("error", _"Passwords don't match!") 7.277 - return false 7.278 -end 7.279 - 7.280 -if #password1 < 8 then 7.281 - slot.put_into("error", _"Passwords must consist of at least 8 characters!") 7.282 return false 7.283 end 7.284 7.285 -local member = Member:new() 7.286 +local step = param.get("step", atom.integer) 7.287 + 7.288 +if step > 2 then 7.289 7.290 -member.login = login 7.291 -member.name = name 7.292 + for i, checkbox in ipairs(config.use_terms_checkboxes) do 7.293 + local accepted = param.get("use_terms_checkbox_" .. checkbox.name, atom.boolean) 7.294 + if not accepted then 7.295 + slot.put_into("error", checkbox.not_accepted_error) 7.296 + return false 7.297 + end 7.298 + end 7.299 + 7.300 + local password1 = param.get("password1") 7.301 + local password2 = param.get("password2") 7.302 7.303 -local success = member:set_notify_email(notify_email) 7.304 -if not success then 7.305 - slot.put_into("error", _"Can't send confirmation email") 7.306 - return 7.307 -end 7.308 + if login and not password1 then 7.309 + request.redirect{ 7.310 + mode = "redirect", 7.311 + module = "index", 7.312 + view = "register", 7.313 + params = { 7.314 + code = member.invite_code, 7.315 + notify_email = member.notify_email, 7.316 + name = member.name, 7.317 + login = member.login 7.318 + } 7.319 + } 7.320 + --]] 7.321 + return false 7.322 + end 7.323 7.324 -member:set_password(password1) 7.325 -member:save() 7.326 - 7.327 -local now = db:query("SELECT now() AS now", "object").now 7.328 + if password1 ~= password2 then 7.329 + slot.put_into("error", _"Passwords don't match!") 7.330 + return false 7.331 + end 7.332 7.333 -for i, checkbox in ipairs(config.use_terms_checkboxes) do 7.334 - local accepted = param.get("use_terms_checkbox_" .. checkbox.name, atom.boolean) 7.335 - member:set_setting("use_terms_checkbox_" .. checkbox.name, "accepted at " .. tostring(now)) 7.336 -end 7.337 + if #password1 < 8 then 7.338 + slot.put_into("error", _"Passwords must consist of at least 8 characters!") 7.339 + return false 7.340 + end 7.341 + 7.342 + member.login = login 7.343 + member.name = name 7.344 + 7.345 + local success = member:set_notify_email(notify_email) 7.346 + if not success then 7.347 + slot.put_into("error", _"Can't send confirmation email") 7.348 + return 7.349 + end 7.350 + 7.351 + member:set_password(password1) 7.352 + 7.353 + local now = db:query("SELECT now() AS now", "object").now 7.354 7.355 -invite_code.member_id = member.id 7.356 -invite_code.used = "now" 7.357 -invite_code:save() 7.358 + for i, checkbox in ipairs(config.use_terms_checkboxes) do 7.359 + local accepted = param.get("use_terms_checkbox_" .. checkbox.name, atom.boolean) 7.360 + member:set_setting("use_terms_checkbox_" .. checkbox.name, "accepted at " .. tostring(now)) 7.361 + end 7.362 7.363 -slot.put_into("notice", _"You've successfully registered and you can login now with your login and password!") 7.364 + member.activated = 'now' 7.365 + member.active = true 7.366 + member.last_activity = 'now' 7.367 + member:save() 7.368 7.369 -request.redirect{ 7.370 - mode = "redirect", 7.371 - module = "index", 7.372 - view = "login", 7.373 -} 7.374 + slot.put_into("notice", _"You've successfully registered and you can login now with your login and password!") 7.375 + 7.376 + request.redirect{ 7.377 + mode = "redirect", 7.378 + module = "index", 7.379 + view = "login", 7.380 + } 7.381 +end 7.382 + 7.383 \ No newline at end of file
8.1 --- a/app/main/index/_action/set_lang.lua Fri Feb 17 15:16:02 2012 +0100 8.2 +++ b/app/main/index/_action/set_lang.lua Sat Feb 25 11:51:37 2012 +0100 8.3 @@ -2,4 +2,8 @@ 8.4 if lang == "de" or lang == "en" or lang == "eo" then 8.5 app.session.lang = param.get("lang") 8.6 app.session:save() 8.7 + if app.session.member then 8.8 + app.session.member.lang = app.session.lang 8.9 + app.session.member:save() 8.10 + end 8.11 end 8.12 \ No newline at end of file
9.1 --- a/app/main/index/register.lua Fri Feb 17 15:16:02 2012 +0100 9.2 +++ b/app/main/index/register.lua Sat Feb 25 11:51:37 2012 +0100 9.3 @@ -1,6 +1,6 @@ 9.4 slot.put_into("title", _"Registration") 9.5 9.6 - 9.7 +local step = param.get("step", atom.integer) 9.8 local code = param.get("code") 9.9 local notify_email = param.get("notify_email") 9.10 local name = param.get("name") 9.11 @@ -20,191 +20,176 @@ 9.12 content = function() 9.13 9.14 if not code then 9.15 - slot.put_into("title", _"Step 1/5: Invite code") 9.16 + slot.put_into("title", _"Step 1/3: Invite code") 9.17 + ui.field.hidden{ name = "step", value = 1 } 9.18 ui.tag{ 9.19 tag = "p", 9.20 content = _"Please enter the invite code you've received." 9.21 } 9.22 ui.field.text{ 9.23 - label = _'Invite code', 9.24 - name = 'code', 9.25 + label = _'Invite code', 9.26 + name = 'code', 9.27 value = param.get("invite") 9.28 } 9.29 9.30 - elseif not notify_email then 9.31 - slot.put_into("title", _"Step 2/5: Email address") 9.32 - slot.select("actions", function() 9.33 - ui.link{ 9.34 - content = function() 9.35 - ui.image{ static = "icons/16/resultset_previous.png" } 9.36 - slot.put(_"One step back") 9.37 - end, 9.38 - module = "index", 9.39 - view = "register", 9.40 - params = { 9.41 - } 9.42 - } 9.43 - end) 9.44 - ui.tag{ 9.45 - tag = "p", 9.46 - content = _"Please enter your email address. This address will be used for automatic notifications (if you request them) and in case you've lost your password. This address will not be published. After registration you will receive an email with a confirmation link." 9.47 - } 9.48 - ui.field.text{ 9.49 - label = _'Email address', 9.50 - name = 'notify_email', 9.51 - value = param.get("notify_email") 9.52 - } 9.53 - 9.54 - elseif not name then 9.55 - slot.put_into("title", _"Step 3/5: Username") 9.56 - slot.select("actions", function() 9.57 - ui.link{ 9.58 - content = function() 9.59 - ui.image{ static = "icons/16/resultset_previous.png" } 9.60 - slot.put(_"One step back") 9.61 - end, 9.62 - module = "index", 9.63 - view = "register", 9.64 - params = { 9.65 - code = code 9.66 - } 9.67 - } 9.68 - end) 9.69 - ui.tag{ 9.70 - tag = "p", 9.71 - content = _"Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you." 9.72 - } 9.73 - ui.field.text{ 9.74 - label = _'Name', 9.75 - name = 'name', 9.76 - value = param.get("name") 9.77 - } 9.78 - 9.79 - elseif not login then 9.80 - slot.put_into("title", _"Step 4/5: Login name") 9.81 - slot.select("actions", function() 9.82 - ui.link{ 9.83 - content = function() 9.84 - ui.image{ static = "icons/16/resultset_previous.png" } 9.85 - slot.put(_"One step back") 9.86 - end, 9.87 - module = "index", 9.88 - view = "register", 9.89 - params = { 9.90 - code = code, 9.91 - notify_email = notify_email 9.92 - } 9.93 - } 9.94 - end) 9.95 - ui.tag{ 9.96 - tag = "p", 9.97 - content = _"Please choose a login name. This name will not be shown to others and is used only by you to login into the system. The login name is case sensitive." 9.98 - } 9.99 - ui.field.text{ 9.100 - label = _'Login name', 9.101 - name = 'login', 9.102 - value = param.get("login") 9.103 - } 9.104 - 9.105 else 9.106 - ui.field.hidden{ name = "step", value = "5" } 9.107 - slot.put_into("title", _"Step 5/5: Terms of use and password") 9.108 - slot.select("actions", function() 9.109 - ui.link{ 9.110 - content = function() 9.111 - ui.image{ static = "icons/16/resultset_previous.png" } 9.112 - slot.put(_"One step back") 9.113 - end, 9.114 - module = "index", 9.115 - view = "register", 9.116 - params = { 9.117 - code = code, 9.118 - notify_email = notify_email, 9.119 - name = name, 9.120 + local member = Member:new_selector() 9.121 + :add_where{ "invite_code = ?", code } 9.122 + :add_where{ "activated ISNULL" } 9.123 + :optional_object_mode() 9.124 + :for_update() 9.125 + :exec() 9.126 + 9.127 + if not member.notify_email and not notify_email or not member.name and not name or not member.login and not login or step == 1 then 9.128 + slot.put_into("title", _"Step 2/3: Personal information") 9.129 + ui.field.hidden{ name = "step", value = 2 } 9.130 + slot.select("actions", function() 9.131 + ui.link{ 9.132 + content = function() 9.133 + ui.image{ static = "icons/16/resultset_previous.png" } 9.134 + slot.put(_"One step back") 9.135 + end, 9.136 + module = "index", 9.137 + view = "register", 9.138 + params = { 9.139 + } 9.140 } 9.141 + end) 9.142 + 9.143 + ui.tag{ 9.144 + tag = "p", 9.145 + content = _"This invite key is connected with the following information:" 9.146 } 9.147 - end) 9.148 - ui.container{ 9.149 - attr = { class = "wiki use_terms" }, 9.150 - content = function() 9.151 - if config.use_terms_html then 9.152 - slot.put(config.use_terms_html) 9.153 - else 9.154 - slot.put(format.wiki_text(config.use_terms)) 9.155 - end 9.156 - end 9.157 - } 9.158 + 9.159 + execute.view{ module = "member", view = "_profile", params = { member = member, include_private_data = true } } 9.160 9.161 - for i, checkbox in ipairs(config.use_terms_checkboxes) do 9.162 - slot.put("<br />") 9.163 - ui.tag{ 9.164 - tag = "div", 9.165 + if not config.locked_profile_fields.notify_email then 9.166 + ui.tag{ 9.167 + tag = "p", 9.168 + content = _"Please enter your email address. This address will be used for automatic notifications (if you request them) and in case you've lost your password. This address will not be published. After registration you will receive an email with a confirmation link." 9.169 + } 9.170 + ui.field.text{ 9.171 + label = _'Email address', 9.172 + name = 'notify_email', 9.173 + value = param.get("notify_email") or member.notify_email 9.174 + } 9.175 + end 9.176 + if not config.locked_profile_fields.name then 9.177 + ui.tag{ 9.178 + tag = "p", 9.179 + content = _"Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you." 9.180 + } 9.181 + ui.field.text{ 9.182 + label = _'Screen name', 9.183 + name = 'name', 9.184 + value = param.get("name") or member.name 9.185 + } 9.186 + end 9.187 + if not config.locked_profile_fields.login then 9.188 + ui.tag{ 9.189 + tag = "p", 9.190 + content = _"Please choose a login name. This name will not be shown to others and is used only by you to login into the system. The login name is case sensitive." 9.191 + } 9.192 + ui.field.text{ 9.193 + label = _'Login name', 9.194 + name = 'login', 9.195 + value = param.get("login") or member.login 9.196 + } 9.197 + end 9.198 + else 9.199 + 9.200 + ui.field.hidden{ name = "step", value = "3" } 9.201 + slot.put_into("title", _"Step 3/3: Terms of use and password") 9.202 + slot.select("actions", function() 9.203 + ui.link{ 9.204 + content = function() 9.205 + ui.image{ static = "icons/16/resultset_previous.png" } 9.206 + slot.put(_"One step back") 9.207 + end, 9.208 + module = "index", 9.209 + view = "register", 9.210 + params = { 9.211 + code = code, 9.212 + notify_email = notify_email, 9.213 + name = name, 9.214 + login = login, 9.215 + step = 1 9.216 + } 9.217 + } 9.218 + end) 9.219 + ui.container{ 9.220 + attr = { class = "wiki use_terms" }, 9.221 content = function() 9.222 - ui.tag{ 9.223 - tag = "input", 9.224 - attr = { 9.225 - type = "checkbox", 9.226 - name = "use_terms_checkbox_" .. checkbox.name, 9.227 - value = "1", 9.228 - style = "float: left;", 9.229 - checked = param.get("use_terms_checkbox_" .. checkbox.name, atom.boolean) and "checked" or nil 9.230 - } 9.231 - } 9.232 - slot.put(" ") 9.233 - slot.put(checkbox.html) 9.234 + if config.use_terms_html then 9.235 + slot.put(config.use_terms_html) 9.236 + else 9.237 + slot.put(format.wiki_text(config.use_terms)) 9.238 + end 9.239 end 9.240 } 9.241 - end 9.242 9.243 - slot.put("<br />") 9.244 + for i, checkbox in ipairs(config.use_terms_checkboxes) do 9.245 + slot.put("<br />") 9.246 + ui.tag{ 9.247 + tag = "div", 9.248 + content = function() 9.249 + ui.tag{ 9.250 + tag = "input", 9.251 + attr = { 9.252 + type = "checkbox", 9.253 + name = "use_terms_checkbox_" .. checkbox.name, 9.254 + value = "1", 9.255 + style = "float: left;", 9.256 + checked = param.get("use_terms_checkbox_" .. checkbox.name, atom.boolean) and "checked" or nil 9.257 + } 9.258 + } 9.259 + slot.put(" ") 9.260 + slot.put(checkbox.html) 9.261 + end 9.262 + } 9.263 + end 9.264 9.265 - ui.field.text{ 9.266 - label = _'Email address', 9.267 - value = param.get("notify_email"), 9.268 - readonly = true 9.269 - } 9.270 - ui.field.text{ 9.271 - label = _'Name', 9.272 - value = param.get("name"), 9.273 - readonly = true 9.274 - } 9.275 - ui.field.text{ 9.276 - label = _'Login name', 9.277 - value = param.get("login"), 9.278 - readonly = true 9.279 - } 9.280 + slot.put("<br />") 9.281 9.282 - ui.tag{ 9.283 - tag = "p", 9.284 - content = _"Please choose a password and enter it twice. The password is case sensitive." 9.285 - } 9.286 - ui.field.password{ 9.287 - label = _'Password', 9.288 - name = 'password1', 9.289 - } 9.290 - ui.field.password{ 9.291 - label = _'Password (repeat)', 9.292 - name = 'password2', 9.293 - } 9.294 + member.notify_email = notify_email or member.notify_email 9.295 + member.name = name or member.name 9.296 + member.login = login or member.login 9.297 + 9.298 + execute.view{ module = "member", view = "_profile", params = { 9.299 + member = member, include_private_data = true 9.300 + } } 9.301 + 9.302 + ui.tag{ 9.303 + tag = "p", 9.304 + content = _"Please choose a password and enter it twice. The password is case sensitive." 9.305 + } 9.306 + ui.field.password{ 9.307 + label = _'Password', 9.308 + name = 'password1', 9.309 + } 9.310 + ui.field.password{ 9.311 + label = _'Password (repeat)', 9.312 + name = 'password2', 9.313 + } 9.314 9.315 + end 9.316 end 9.317 9.318 - ui.submit{ 9.319 - text = _'Register' 9.320 - } 9.321 + ui.submit{ 9.322 + text = _'Register' 9.323 + } 9.324 9.325 - slot.put_into("title", ")") 9.326 - slot.select("actions", function() 9.327 - ui.link{ 9.328 - content = function() 9.329 - ui.image{ static = "icons/16/cancel.png" } 9.330 - slot.put(_"Cancel registration") 9.331 - end, 9.332 - module = "index", 9.333 - view = "index" 9.334 - } 9.335 - end) 9.336 - 9.337 + slot.put_into("title", ")") 9.338 + slot.select("actions", function() 9.339 + ui.link{ 9.340 + content = function() 9.341 + ui.image{ static = "icons/16/cancel.png" } 9.342 + slot.put(_"Cancel registration") 9.343 + end, 9.344 + module = "index", 9.345 + view = "index" 9.346 + } 9.347 + end) 9.348 end 9.349 } 9.350
10.1 --- a/app/main/initiative/_action/create.lua Fri Feb 17 15:16:02 2012 +0100 10.2 +++ b/app/main/initiative/_action/create.lua Sat Feb 25 11:51:37 2012 +0100 10.3 @@ -87,6 +87,16 @@ 10.4 issue.area_id = area.id 10.5 issue.policy_id = policy_id 10.6 issue:save() 10.7 + 10.8 + if config.etherpad then 10.9 + local result = net.curl( 10.10 + config.etherpad.api_base 10.11 + .. "api/1/createGroupPad?apikey=" .. config.etherpad.api_key 10.12 + .. "&groupID=" .. config.etherpad.group_id 10.13 + .. "&padName=Issue" .. tostring(issue.id) 10.14 + .. "&text=" .. config.absolute_base_url .. "issue/show/" .. tostring(issue.id) .. ".html" 10.15 + ) 10.16 + end 10.17 end 10.18 10.19 initiative.issue_id = issue.id
11.1 --- a/app/main/initiative/_show.lua Fri Feb 17 15:16:02 2012 +0100 11.2 +++ b/app/main/initiative/_show.lua Sat Feb 25 11:51:37 2012 +0100 11.3 @@ -32,87 +32,86 @@ 11.4 attr = { class = "initiative_name" }, 11.5 content = _("Initiative i#{id}: #{name}", { id = initiative.id, name = initiative.name }) 11.6 } 11.7 - if app.session.member_id or config.public_access == "pseudonym" or config.public_access == "full" then 11.8 - ui.tag{ 11.9 - attr = { class = "initiator_names" }, 11.10 - content = function() 11.11 - for i, initiator in ipairs(initiators) do 11.12 - slot.put(" ") 11.13 - ui.link{ 11.14 - content = function () 11.15 - execute.view{ 11.16 - module = "member_image", 11.17 - view = "_show", 11.18 - params = { 11.19 - member = initiator, 11.20 - image_type = "avatar", 11.21 - show_dummy = true, 11.22 - class = "micro_avatar", 11.23 - popup_text = text 11.24 - } 11.25 +end ) 11.26 + 11.27 +if app.session.member_id or config.public_access == "pseudonym" or config.public_access == "full" then 11.28 + ui.tag{ 11.29 + attr = { class = "initiator_names" }, 11.30 + content = function() 11.31 + for i, initiator in ipairs(initiators) do 11.32 + slot.put(" ") 11.33 + ui.link{ 11.34 + content = function () 11.35 + execute.view{ 11.36 + module = "member_image", 11.37 + view = "_show", 11.38 + params = { 11.39 + member = initiator, 11.40 + image_type = "avatar", 11.41 + show_dummy = true, 11.42 + class = "micro_avatar", 11.43 + popup_text = text 11.44 } 11.45 - end, 11.46 - module = "member", view = "show", id = initiator.id 11.47 - } 11.48 - slot.put(" ") 11.49 - ui.link{ 11.50 - text = initiator.name, 11.51 - module = "member", view = "show", id = initiator.id 11.52 - } 11.53 - if not initiator.accepted then 11.54 - ui.tag{ attr = { title = _"Not accepted yet" }, content = "?" } 11.55 - end 11.56 + } 11.57 + end, 11.58 + module = "member", view = "show", id = initiator.id 11.59 + } 11.60 + slot.put(" ") 11.61 + ui.link{ 11.62 + text = initiator.name, 11.63 + module = "member", view = "show", id = initiator.id 11.64 + } 11.65 + if not initiator.accepted then 11.66 + ui.tag{ attr = { title = _"Not accepted yet" }, content = "?" } 11.67 end 11.68 end 11.69 - } 11.70 - end 11.71 + end 11.72 + } 11.73 +end 11.74 11.75 - if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then 11.76 +if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then 11.77 + slot.put(" · ") 11.78 + ui.link{ 11.79 + attr = { class = "action" }, 11.80 + content = function() 11.81 + slot.put(_"Invite initiator") 11.82 + end, 11.83 + module = "initiative", 11.84 + view = "add_initiator", 11.85 + params = { initiative_id = initiative.id } 11.86 + } 11.87 + if #initiators > 1 then 11.88 slot.put(" · ") 11.89 ui.link{ 11.90 - attr = { class = "action" }, 11.91 content = function() 11.92 - slot.put(_"Invite initiator") 11.93 + slot.put(_"Remove initiator") 11.94 end, 11.95 module = "initiative", 11.96 - view = "add_initiator", 11.97 + view = "remove_initiator", 11.98 params = { initiative_id = initiative.id } 11.99 } 11.100 - if #initiators > 1 then 11.101 - slot.put(" · ") 11.102 - ui.link{ 11.103 - content = function() 11.104 - slot.put(_"Remove initiator") 11.105 - end, 11.106 - module = "initiative", 11.107 - view = "remove_initiator", 11.108 - params = { initiative_id = initiative.id } 11.109 - } 11.110 - end 11.111 end 11.112 - if initiator and initiator.accepted == false then 11.113 - slot.put(" · ") 11.114 - ui.link{ 11.115 - text = _"Cancel refuse of invitation", 11.116 - module = "initiative", 11.117 - action = "remove_initiator", 11.118 - params = { 11.119 - initiative_id = initiative.id, 11.120 - member_id = app.session.member.id 11.121 - }, 11.122 - routing = { 11.123 - ok = { 11.124 - mode = "redirect", 11.125 - module = "initiative", 11.126 - view = "show", 11.127 - id = initiative.id 11.128 - } 11.129 +end 11.130 +if initiator and initiator.accepted == false then 11.131 + slot.put(" · ") 11.132 + ui.link{ 11.133 + text = _"Cancel refuse of invitation", 11.134 + module = "initiative", 11.135 + action = "remove_initiator", 11.136 + params = { 11.137 + initiative_id = initiative.id, 11.138 + member_id = app.session.member.id 11.139 + }, 11.140 + routing = { 11.141 + ok = { 11.142 + mode = "redirect", 11.143 + module = "initiative", 11.144 + view = "show", 11.145 + id = initiative.id 11.146 } 11.147 } 11.148 - end 11.149 - 11.150 - 11.151 -end ) 11.152 + } 11.153 +end 11.154 11.155 util.help("initiative.show") 11.156 11.157 @@ -308,3 +307,4 @@ 11.158 end 11.159 11.160 11.161 +
12.1 --- a/app/main/initiative/_show_voting.lua Fri Feb 17 15:16:02 2012 +0100 12.2 +++ b/app/main/initiative/_show_voting.lua Sat Feb 25 11:51:37 2012 +0100 12.3 @@ -12,6 +12,8 @@ 12.4 params = { initiative = initiative } 12.5 } 12.6 12.7 + slot.put("<br />") 12.8 + 12.9 ui.container{ 12.10 attr = { class = "heading" }, 12.11 content = _"Member voting" 12.12 @@ -32,4 +34,37 @@ 12.13 } 12.14 } 12.15 12.16 + slot.put("<br />") 12.17 + 12.18 + ui.container{ 12.19 + attr = { class = "heading" }, 12.20 + content = _"Voting details" 12.21 + } 12.22 + 12.23 + ui.form{ 12.24 + attr = { class = "vertical" }, 12.25 + content = function() 12.26 + 12.27 + ui.field.boolean{ label = _"Direct majority", value = initiative.direct_majority } 12.28 + ui.field.boolean{ label = _"Indirect majority", value = initiative.indirect_majority } 12.29 + ui.field.text{ label = _"Schulze rank", value = tostring(initiative.schulze_rank) .. " (" .. _("Status quo: #{rank}", { rank = initiative.issue.status_quo_schulze_rank }) .. ")" } 12.30 + local texts = {} 12.31 + if initiative.reverse_beat_path then 12.32 + texts[#texts+1] = _"reverse beat path to status quo (including ties)" 12.33 + end 12.34 + if initiative.multistage_majority then 12.35 + texts[#texts+1] = _"possibly instable result caused by multistage majority" 12.36 + end 12.37 + if #texts == 0 then 12.38 + texts[#texts+1] = _"none" 12.39 + end 12.40 + ui.field.text{ 12.41 + label = _"Other failures", 12.42 + value = table.concat(texts, ", ") 12.43 + } 12.44 + ui.field.boolean{ label = _"Eligible as winner", value = initiative.eligible } 12.45 + end 12.46 +} 12.47 + 12.48 + 12.49 end
13.1 --- a/app/main/issue/_filters.lua Fri Feb 17 15:16:02 2012 +0100 13.2 +++ b/app/main/issue/_filters.lua Sat Feb 25 11:51:37 2012 +0100 13.3 @@ -12,6 +12,13 @@ 13.4 selector_modifier = function(selector) end 13.5 }, 13.6 { 13.7 + name = "open", 13.8 + label = _"Open", 13.9 + selector_modifier = function(selector) 13.10 + selector:add_where("issue.closed ISNULL") 13.11 + end 13.12 + }, 13.13 + { 13.14 name = "new", 13.15 label = _"New", 13.16 selector_modifier = function(selector) 13.17 @@ -94,89 +101,90 @@ 13.18 13.19 end 13.20 13.21 - 13.22 -filters[#filters+1] = { 13.23 - name = "filter_interest", 13.24 - { 13.25 - name = "any", 13.26 - label = _"Any", 13.27 - selector_modifier = function() end 13.28 - }, 13.29 - { 13.30 - name = "my", 13.31 - label = _"Interested", 13.32 - selector_modifier = function() end 13.33 - }, 13.34 - { 13.35 - name = "supported", 13.36 - label = _"Supported", 13.37 - selector_modifier = function() end 13.38 - }, 13.39 - { 13.40 - name = "potentially_supported", 13.41 - label = _"Potentially supported", 13.42 - selector_modifier = function() end 13.43 - }, 13.44 - { 13.45 - name = "initiated", 13.46 - label = _"Initiated", 13.47 - selector_modifier = function(selector) 13.48 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN initiator ON initiator.initiative_id = initiative.id AND initiator.member_id = ? AND initiator.accepted WHERE initiative.issue_id = issue.id)", member.id }) 13.49 - end 13.50 - }, 13.51 -} 13.52 - 13.53 -local filter_interest = param.get_all_cgi()["filter_interest"] 13.54 - 13.55 -if filter_interest ~= "any" and filter_interest ~= nil and filter_interest ~= "initiated" then 13.56 +if app.session.member then 13.57 filters[#filters+1] = { 13.58 - name = "filter_delegation", 13.59 + name = "filter_interest", 13.60 { 13.61 name = "any", 13.62 - label = _"Direct and by delegation", 13.63 - selector_modifier = function(selector) 13.64 - if filter_interest == "my" then 13.65 - selector:left_join("delegating_interest_snapshot", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? AND filter_interest.event = issue.latest_snapshot_event", member.id }) 13.66 - selector:left_join("interest", "filter_delegating_interest", { "filter_delegating_interest.issue_id = issue.id AND filter_delegating_interest.member_id = ? ", member.id }) 13.67 - selector:add_where{ "filter_interest.member_id NOTNULL OR filter_delegating_interest.member_id NOTNULL" } 13.68 - elseif filter_interest == "supported" then 13.69 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? LEFT JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id AND critical_opinion.member_id ISNULL LIMIT 1) OR EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND direct_supporter_snapshot.satisfied LIMIT 1)", member.id, member.id, member.id }) 13.70 - elseif filter_interest == "potentially_supported" then 13.71 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id LIMIT 1) OR EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND NOT direct_supporter_snapshot.satisfied LIMIT 1)", member.id, member.id, member.id, member.id }) 13.72 - end 13.73 - end 13.74 + label = _"Any", 13.75 + selector_modifier = function() end 13.76 + }, 13.77 + { 13.78 + name = "my", 13.79 + label = _"Interested", 13.80 + selector_modifier = function() end 13.81 + }, 13.82 + { 13.83 + name = "supported", 13.84 + label = _"Supported", 13.85 + selector_modifier = function() end 13.86 + }, 13.87 + { 13.88 + name = "potentially_supported", 13.89 + label = _"Potentially supported", 13.90 + selector_modifier = function() end 13.91 }, 13.92 { 13.93 - name = "direct", 13.94 - label = _"Direct", 13.95 + name = "initiated", 13.96 + label = _"Initiated", 13.97 selector_modifier = function(selector) 13.98 - if filter_interest == "my" then 13.99 - selector:join("interest", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? ", member.id }) 13.100 - elseif filter_interest == "supported" then 13.101 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? LEFT JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id AND critical_opinion.member_id ISNULL LIMIT 1)", member.id, member.id }) 13.102 - elseif filter_interest == "potentially_supported" then 13.103 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id LIMIT 1)", member.id, member.id }) 13.104 - end 13.105 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN initiator ON initiator.initiative_id = initiative.id AND initiator.member_id = ? AND initiator.accepted WHERE initiative.issue_id = issue.id)", member.id }) 13.106 end 13.107 }, 13.108 - { 13.109 - name = "delegated", 13.110 - label = _"By delegation", 13.111 - selector_modifier = function(selector) 13.112 - if filter_interest == "my" then 13.113 - selector:join("delegating_interest_snapshot", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? AND filter_interest.event = issue.latest_snapshot_event", member.id }) 13.114 - elseif filter_interest == "supported" then 13.115 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND direct_supporter_snapshot.satisfied LIMIT 1)", member.id }) 13.116 - elseif filter_interest == "potentially_supported" then 13.117 - selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND NOT direct_supporter_snapshot.satisfied LIMIT 1)", member.id, member.id }) 13.118 + } 13.119 + 13.120 + local filter_interest = param.get_all_cgi()["filter_interest"] 13.121 + 13.122 + if filter_interest ~= "any" and filter_interest ~= nil and filter_interest ~= "initiated" then 13.123 + filters[#filters+1] = { 13.124 + name = "filter_delegation", 13.125 + { 13.126 + name = "any", 13.127 + label = _"Direct and by delegation", 13.128 + selector_modifier = function(selector) 13.129 + if filter_interest == "my" then 13.130 + selector:left_join("delegating_interest_snapshot", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? AND filter_interest.event = issue.latest_snapshot_event", member.id }) 13.131 + selector:left_join("interest", "filter_delegating_interest", { "filter_delegating_interest.issue_id = issue.id AND filter_delegating_interest.member_id = ? ", member.id }) 13.132 + selector:add_where{ "filter_interest.member_id NOTNULL OR filter_delegating_interest.member_id NOTNULL" } 13.133 + elseif filter_interest == "supported" then 13.134 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? LEFT JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id AND critical_opinion.member_id ISNULL LIMIT 1) OR EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND direct_supporter_snapshot.satisfied LIMIT 1)", member.id, member.id, member.id }) 13.135 + elseif filter_interest == "potentially_supported" then 13.136 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id LIMIT 1) OR EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND NOT direct_supporter_snapshot.satisfied LIMIT 1)", member.id, member.id, member.id, member.id }) 13.137 + end 13.138 end 13.139 - end 13.140 + }, 13.141 + { 13.142 + name = "direct", 13.143 + label = _"Direct", 13.144 + selector_modifier = function(selector) 13.145 + if filter_interest == "my" then 13.146 + selector:join("interest", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? ", member.id }) 13.147 + elseif filter_interest == "supported" then 13.148 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? LEFT JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id AND critical_opinion.member_id ISNULL LIMIT 1)", member.id, member.id }) 13.149 + elseif filter_interest == "potentially_supported" then 13.150 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? JOIN critical_opinion ON critical_opinion.initiative_id = initiative.id AND critical_opinion.member_id = ? WHERE initiative.issue_id = issue.id LIMIT 1)", member.id, member.id }) 13.151 + end 13.152 + end 13.153 + }, 13.154 + { 13.155 + name = "delegated", 13.156 + label = _"By delegation", 13.157 + selector_modifier = function(selector) 13.158 + if filter_interest == "my" then 13.159 + selector:join("delegating_interest_snapshot", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? AND filter_interest.event = issue.latest_snapshot_event", member.id }) 13.160 + elseif filter_interest == "supported" then 13.161 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND direct_supporter_snapshot.satisfied LIMIT 1)", member.id }) 13.162 + elseif filter_interest == "potentially_supported" then 13.163 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN direct_supporter_snapshot ON direct_supporter_snapshot.initiative_id = initiative.id AND direct_supporter_snapshot.event = issue.latest_snapshot_event JOIN delegating_interest_snapshot ON delegating_interest_snapshot.delegate_member_ids[array_upper(delegating_interest_snapshot.delegate_member_ids, 1)] = direct_supporter_snapshot.member_id AND delegating_interest_snapshot.issue_id = issue.id AND delegating_interest_snapshot.member_id = ? AND delegating_interest_snapshot.event = issue.latest_snapshot_event WHERE initiative.issue_id = issue.id AND NOT direct_supporter_snapshot.satisfied LIMIT 1)", member.id, member.id }) 13.164 + end 13.165 + end 13.166 + } 13.167 } 13.168 - } 13.169 + end 13.170 + 13.171 end 13.172 13.173 - 13.174 -if member.id == app.session.member_id and param.get_all_cgi()["filter"] == "frozen" then 13.175 +if app.session.member and member.id == app.session.member_id and (param.get_all_cgi()["filter"] == "frozen" or param.get_all_cgi()["filter"] == "finished") then 13.176 filters[#filters+1] = { 13.177 name = "filter_voting", 13.178 {
14.1 --- a/app/main/issue/_list.lua Fri Feb 17 15:16:02 2012 +0100 14.2 +++ b/app/main/issue/_list.lua Sat Feb 25 11:51:37 2012 +0100 14.3 @@ -100,6 +100,7 @@ 14.4 end 14.5 } 14.6 ui.tag{ 14.7 + attr = { class = "issue_policy_info" }, 14.8 tag = "div", 14.9 content = function() 14.10
15.1 --- a/app/main/issue/_show_head.lua Fri Feb 17 15:16:02 2012 +0100 15.2 +++ b/app/main/issue/_show_head.lua Sat Feb 25 11:51:37 2012 +0100 15.3 @@ -114,6 +114,19 @@ 15.4 end, 15.5 } 15.6 end 15.7 + 15.8 + if config.etherpad and app.session.member then 15.9 + local url = config.etherpad.base_url .. "p/" .. config.etherpad.group_id .. "$Issue" .. issue.id 15.10 + ui.link{ 15.11 + attr = { target = "_blank" }, 15.12 + external = url, 15.13 + content = function() 15.14 + ui.image{ static = "icons/16/comments.png" } 15.15 + slot.put(_"Issue pad") 15.16 + end, 15.17 + } 15.18 + end 15.19 + 15.20 end) 15.21 15.22 if app.session.member_id and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id) then
16.1 --- a/app/main/member/_action/update.lua Fri Feb 17 15:16:02 2012 +0100 16.2 +++ b/app/main/member/_action/update.lua Sat Feb 25 11:51:37 2012 +0100 16.3 @@ -1,4 +1,4 @@ 16.4 -param.update(app.session.member, 16.5 +local fields = { 16.6 "organizational_unit", 16.7 "internal_posts", 16.8 "realname", 16.9 @@ -13,12 +13,22 @@ 16.10 "external_memberships", 16.11 "external_posts", 16.12 "statement" 16.13 -) 16.14 +} 16.15 + 16.16 +local update_args = { app.session.member } 16.17 16.18 -if tostring(app.session.member.birthday) == "invalid_date" then 16.19 - app.session.member.birthday = nil 16.20 - slot.put_into("error", _"Date format is not valid. Please use following format: YYYY-MM-DD") 16.21 - return false 16.22 +for i, field in ipairs(fields) do 16.23 + if not config.locked_profile_fields[field] then 16.24 + param.update(app.session.member, field) 16.25 + end 16.26 +end 16.27 + 16.28 +if not config.locked_profile_fields.birthday then 16.29 + if tostring(app.session.member.birthday) == "invalid_date" then 16.30 + app.session.member.birthday = nil 16.31 + slot.put_into("error", _"Date format is not valid. Please use following format: YYYY-MM-DD") 16.32 + return false 16.33 + end 16.34 end 16.35 16.36 app.session.member:save()
17.1 --- a/app/main/member/_action/update_email.lua Fri Feb 17 15:16:02 2012 +0100 17.2 +++ b/app/main/member/_action/update_email.lua Sat Feb 25 11:51:37 2012 +0100 17.3 @@ -1,5 +1,9 @@ 17.4 local resend = param.get("resend", atom.boolean) 17.5 17.6 +if not resend and config.locked_profile_fields.notify_email then 17.7 + error("access denied") 17.8 +end 17.9 + 17.10 if app.session.member.notify_email_locked then 17.11 if resend then 17.12 slot.put_into("error", _"We have sent an email with activation link already in the last hour. Please try again later.")
18.1 --- a/app/main/member/_action/update_login.lua Fri Feb 17 15:16:02 2012 +0100 18.2 +++ b/app/main/member/_action/update_login.lua Sat Feb 25 11:51:37 2012 +0100 18.3 @@ -1,3 +1,7 @@ 18.4 +if config.locked_profile_fields.login then 18.5 + error("access denied") 18.6 +end 18.7 + 18.8 local login = param.get("login") 18.9 18.10 login = util.trim(login)
19.1 --- a/app/main/member/_action/update_name.lua Fri Feb 17 15:16:02 2012 +0100 19.2 +++ b/app/main/member/_action/update_name.lua Sat Feb 25 11:51:37 2012 +0100 19.3 @@ -1,3 +1,7 @@ 19.4 +if config.locked_profile_fields.name then 19.5 + error("access denied") 19.6 +end 19.7 + 19.8 local name = param.get("name") 19.9 19.10 name = util.trim(name)
20.1 --- a/app/main/member/_list.lua Fri Feb 17 15:16:02 2012 +0100 20.2 +++ b/app/main/member/_list.lua Sat Feb 25 11:51:37 2012 +0100 20.3 @@ -1,4 +1,6 @@ 20.4 local members_selector = param.get("members_selector", "table") 20.5 +members_selector:add_where("member.activated NOTNULL") 20.6 + 20.7 local initiative = param.get("initiative", "table") 20.8 local issue = param.get("issue", "table") 20.9 local trustee = param.get("trustee", "table")
21.1 --- a/app/main/member/_profile.lua Fri Feb 17 15:16:02 2012 +0100 21.2 +++ b/app/main/member/_profile.lua Sat Feb 25 11:51:37 2012 +0100 21.3 @@ -1,5 +1,7 @@ 21.4 local member = param.get("member", "table") 21.5 21.6 +local include_private_data = param.get("include_private_data", atom.boolean) 21.7 + 21.8 if not member then 21.9 local member_id = param.get("member_id", atom.integer) 21.10 if member_id then 21.11 @@ -70,12 +72,18 @@ 21.12 21.13 end 21.14 } 21.15 - 21.16 - if member.ident_number then 21.17 - ui.field.text{ label = _"Ident number", name = "ident_number" } 21.18 + 21.19 + if member.identification then 21.20 + ui.field.text{ label = _"Identification", name = "identification" } 21.21 end 21.22 - ui.field.text{ label = _"Name", name = "name" } 21.23 - 21.24 + if member.name then 21.25 + ui.field.text{ label = _"Screen name", name = "name" } 21.26 + end 21.27 + if include_private_data and member.login then 21.28 + ui.field.text{ label = _"Login name", name = "login" } 21.29 + ui.field.text{ label = _"Notification email", name = "notify_email" } 21.30 + end 21.31 + 21.32 if member.realname and #member.realname > 0 then 21.33 ui.field.text{ label = _"Real name", name = "realname" } 21.34 end 21.35 @@ -128,8 +136,16 @@ 21.36 end 21.37 if member.external_posts and #member.external_posts > 0 then 21.38 ui.field.text{ label = _"Posts", name = "external_posts", multiline = true } 21.39 + end 21.40 + if member.admin then 21.41 + ui.field.boolean{ label = _"Admin?", name = "admin" } 21.42 end 21.43 - slot.put('<br style="clear: right;" />') 21.44 + if member.locked then 21.45 + ui.field.boolean{ label = _"Locked?", name = "locked" } 21.46 + end 21.47 + if member.last_activity then 21.48 + ui.field.text{ label = _"Last activity (updated daily)", value = format.date(member.last_activity) or _"not yet" } 21.49 + end 21.50 21.51 if member.statement and #member.statement > 0 then 21.52 ui.container{ 21.53 @@ -139,14 +155,5 @@ 21.54 end 21.55 } 21.56 end 21.57 - 21.58 - if member.admin then 21.59 - ui.field.boolean{ label = _"Admin?", name = "admin" } 21.60 - end 21.61 - if member.locked then 21.62 - ui.field.boolean{ label = _"Locked?", name = "locked" } 21.63 - end 21.64 - ui.field.text{ label = _"Last activity (updated daily)", value = format.date(member.last_activity) or _"not yet" } 21.65 - 21.66 end 21.67 }
22.1 --- a/app/main/member/edit.lua Fri Feb 17 15:16:02 2012 +0100 22.2 +++ b/app/main/member/edit.lua Sat Feb 25 11:51:37 2012 +0100 22.3 @@ -26,21 +26,22 @@ 22.4 } 22.5 }, 22.6 content = function() 22.7 - ui.field.text{ label = _"Organizational unit", name = "organizational_unit" } 22.8 - ui.field.text{ label = _"Internal posts", name = "internal_posts" } 22.9 - ui.field.text{ label = _"Real name", name = "realname" } 22.10 - ui.field.text{ label = _"Birthday" .. " YYYY-MM-DD ", name = "birthday", attr = { id = "profile_birthday" } } 22.11 + ui.field.text{ label = _"Identification", name = "identification", readonly = true } 22.12 + ui.field.text{ label = _"Organizational unit", name = "organizational_unit", readonly = config.locked_profile_fields.organizational_unit } 22.13 + ui.field.text{ label = _"Internal posts", name = "internal_posts", readonly = config.locked_profile_fields.internal_posts } 22.14 + ui.field.text{ label = _"Real name", name = "realname", readonly = config.locked_profile_fields.realname } 22.15 + ui.field.text{ label = _"Birthday" .. " YYYY-MM-DD ", name = "birthday", attr = { id = "profile_birthday" }, readonly = config.locked_profile_fields.birthday } 22.16 ui.script{ static = "gregor.js/gregor.js" } 22.17 util.gregor("profile_birthday", "document.getElementById('timeline_search_date').form.submit();") 22.18 - ui.field.text{ label = _"Address", name = "address", multiline = true } 22.19 - ui.field.text{ label = _"email", name = "email" } 22.20 - ui.field.text{ label = _"xmpp", name = "xmpp_address" } 22.21 - ui.field.text{ label = _"Website", name = "website" } 22.22 - ui.field.text{ label = _"Phone", name = "phone" } 22.23 - ui.field.text{ label = _"Mobile phone", name = "mobile_phone" } 22.24 - ui.field.text{ label = _"Profession", name = "profession" } 22.25 - ui.field.text{ label = _"External memberships", name = "external_memberships", multiline = true } 22.26 - ui.field.text{ label = _"External posts", name = "external_posts", multiline = true } 22.27 + ui.field.text{ label = _"Address", name = "address", multiline = true, readonly = config.locked_profile_fields.address } 22.28 + ui.field.text{ label = _"email", name = "email", readonly = config.locked_profile_fields.email } 22.29 + ui.field.text{ label = _"xmpp", name = "xmpp_address", readonly = config.locked_profile_fields.xmpp_address } 22.30 + ui.field.text{ label = _"Website", name = "website", readonly = config.locked_profile_fields.website } 22.31 + ui.field.text{ label = _"Phone", name = "phone", readonly = config.locked_profile_fields.phone } 22.32 + ui.field.text{ label = _"Mobile phone", name = "mobile_phone", readonly = config.locked_profile_fields.mobile_phone } 22.33 + ui.field.text{ label = _"Profession", name = "profession", readonly = config.locked_profile_fields.profession } 22.34 + ui.field.text{ label = _"External memberships", name = "external_memberships", multiline = true, readonly = config.locked_profile_fields.external_memberships } 22.35 + ui.field.text{ label = _"External posts", name = "external_posts", multiline = true, readonly = config.locked_profile_fields.external_posts } 22.36 22.37 ui.field.select{ 22.38 label = _"Wiki engine for statement",
23.1 --- a/app/main/member/settings.lua Fri Feb 17 15:16:02 2012 +0100 23.2 +++ b/app/main/member/settings.lua Sat Feb 25 11:51:37 2012 +0100 23.3 @@ -16,16 +16,23 @@ 23.4 content = _"You can change the following settings:" 23.5 } 23.6 23.7 -local pages = { 23.8 - { module = "member", view = "edit", text = _"Edit profile" }, 23.9 - { module = "member", view = "edit_images", text = _"Upload images" }, 23.10 - { view = "settings_display", text = _"Display settings" }, 23.11 - { view = "settings_email", text = _"Change your notification email address" }, 23.12 - { view = "settings_name", text = _"Change your name" }, 23.13 - { view = "settings_login", text = _"Change your login" }, 23.14 - { view = "settings_password", text = _"Change your password" }, 23.15 - { view = "developer_settings", text = _"Developer settings" }, 23.16 -} 23.17 +local pages = {} 23.18 + 23.19 +pages[#pages+1] = { module = "member", view = "edit", text = _"Edit profile" } 23.20 +pages[#pages+1] = { module = "member", view = "edit_images", text = _"Upload images" } 23.21 +pages[#pages+1] = { view = "settings_notification", text = _"Notification settings" } 23.22 +pages[#pages+1] = { view = "settings_display", text = _"Display settings" } 23.23 +if not config.locked_profile_fields.notify_email then 23.24 + pages[#pages+1] = { view = "settings_email", text = _"Change your notification email address" } 23.25 +end 23.26 +if not config.locked_profile_fields.name then 23.27 + pages[#pages+1] = { view = "settings_name", text = _"Change your screen name" } 23.28 +end 23.29 +if not config.locked_profile_fields.login then 23.30 + pages[#pages+1] = { view = "settings_login", text = _"Change your login" } 23.31 +end 23.32 +pages[#pages+1] = { view = "settings_password", text = _"Change your password" } 23.33 +pages[#pages+1] = { view = "developer_settings", text = _"Developer settings" } 23.34 23.35 ui.list{ 23.36 attr = { class = "menu_list" },
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/app/main/member/settings_notification.lua Sat Feb 25 11:51:37 2012 +0100 24.3 @@ -0,0 +1,185 @@ 24.4 +function send_notification(event) 24.5 + 24.6 + local url 24.7 + 24.8 + local body = "" 24.9 + 24.10 + body = body .. _(" Unit: #{name}\n", { name = event.issue.area.unit.name }) 24.11 + body = body .. _(" Area: #{name}\n", { name = event.issue.area.name }) 24.12 + body = body .. _(" Issue: ##{id}\n", { id = event.issue_id }) 24.13 + body = body .. _(" Policy: #{phase}\n", { phase = event.issue.policy.name }) 24.14 + body = body .. _(" Phase: #{phase}\n\n", { phase = event.state }) 24.15 + body = body .. _(" Event: #{event}\n\n", { event = event.event }) 24.16 + 24.17 + if event.initiative_id then 24.18 + url = request.get_absolute_baseurl() .. "initiative/show/" .. event.initiative_id .. ".html" 24.19 + elseif event.suggestion_id then 24.20 + url = request.get_absolute_baseurl() .. "suggestion/show/" .. event.suggestion_id .. ".html" 24.21 + else 24.22 + url = request.get_absolute_baseurl() .. "issue/show/" .. event.issue_id .. ".html" 24.23 + end 24.24 + 24.25 + body = body .. _(" URL: #{url}\n\n", { url = url }) 24.26 + 24.27 + if event.initiative_id then 24.28 + local initiative = Initiative:by_id(event.initiative_id) 24.29 + body = body .. _("i#{id}: #{name}\n\n", { id = initiative.id, name = initiative.name }) 24.30 + else 24.31 + local initiative_count = Initiative:new_selector() 24.32 + :add_where{ "initiative.issue_id = ?", event.issue_id } 24.33 + :count() 24.34 + local initiatives = Initiative:new_selector() 24.35 + :add_where{ "initiative.issue_id = ?", event.issue_id } 24.36 + :add_order_by("initiative.supporter_count DESC") 24.37 + :limit(3) 24.38 + :exec() 24.39 + for i, initiative in ipairs(initiatives) do 24.40 + body = body .. _("i#{id}: #{name}\n", { id = initiative.id, name = initiative.name }) 24.41 + end 24.42 + if initiative_count - 3 > 0 then 24.43 + body = body .. _("and #{count} more initiatives\n", { count = initiative_count }) 24.44 + end 24.45 + body = body .. "\n" 24.46 + end 24.47 + 24.48 + if event.suggestion_id then 24.49 + local suggestion = Suggestion:by_id(event.suggestion_id) 24.50 + body = body .. _("#{name}\n\n", { name = suggestion.name }) 24.51 + end 24.52 + 24.53 + slot.put("<pre>", encode.html_newlines(body), "</pre>") 24.54 + slot.put("<hr />") 24.55 +end 24.56 + 24.57 + 24.58 + 24.59 +slot.put_into("title", _"Notification settings") 24.60 + 24.61 +slot.select("actions", function() 24.62 + ui.link{ 24.63 + content = function() 24.64 + ui.image{ static = "icons/16/cancel.png" } 24.65 + slot.put(_"Cancel") 24.66 + end, 24.67 + module = "member", 24.68 + view = "settings" 24.69 + } 24.70 +end) 24.71 + 24.72 + 24.73 +util.help("member.settings.notification", _"Notification settings") 24.74 + 24.75 +ui.form{ 24.76 + attr = { class = "vertical" }, 24.77 + module = "member", 24.78 + action = "update_notification", 24.79 + routing = { 24.80 + ok = { 24.81 + mode = "redirect", 24.82 + module = "index", 24.83 + view = "index" 24.84 + } 24.85 + }, 24.86 + content = function() 24.87 + ui.tag{ tag = "p", _"Send me notifications about issues in following phases:" } 24.88 + 24.89 + ui.container{ content = function() 24.90 + ui.tag{ 24.91 + tag = "input", 24.92 + attr = { type = "radio", name = "notification_level", value = "voting" } 24.93 + } 24.94 + ui.tag{ content = _"Voting phase" } 24.95 + ui.tag{ tag = "ul", content = function() 24.96 + ui.tag{ tag = "li", content = _"Voting of an issue in one of my areas or I'm interested in starts" } 24.97 + end } 24.98 + end } 24.99 + 24.100 + ui.container{ content = function() 24.101 + ui.tag{ 24.102 + tag = "input", 24.103 + attr = { type = "radio", name = "notification_level", value = "frozen" } 24.104 + } 24.105 + ui.tag{ content = _"Frozen and voting phase" } 24.106 + ui.tag{ tag = "ul", content = function() 24.107 + ui.tag{ tag = "li", content = _"An issue in one of my areas or I'm interested in enters phase 'frozen'" } 24.108 + ui.tag{ tag = "li", content = _"A new initiative is created in an issue I'm interested in" } 24.109 + ui.tag{ tag = "li", content = _"Voting of an issue in one of my areas or I'm interested in starts" } 24.110 + end } 24.111 + end } 24.112 + 24.113 + ui.container{ content = function() 24.114 + ui.tag{ 24.115 + tag = "input", 24.116 + attr = { type = "radio", name = "notification_level", value = "discussion" } 24.117 + } 24.118 + ui.tag{ content = _"Discussion, frozen and voting phase" } 24.119 + ui.tag{ tag = "ul", content = function() 24.120 + ui.tag{ tag = "li", content = _"An issue in one of my areas or I'm interested in enters phase 'discussion'" } 24.121 + ui.tag{ tag = "li", content = _"A new initiative is created in an issue I'm interested in" } 24.122 + ui.tag{ tag = "li", content = _"The draft of an initiative I'm supporting is updated" } 24.123 + ui.tag{ tag = "li", content = _"An initiative I was supporting is revoked" } 24.124 + ui.tag{ tag = "li", content = _"A new suggestion is created in an initiative I'm supporting" } 24.125 + ui.tag{ tag = "li", content = _"An issue in one of my areas or I'm interested in enters phase 'frozen'" } 24.126 + ui.tag{ tag = "li", content = _"Voting of an issue in one of my areas or I'm interested in starts" } 24.127 + end } 24.128 + end } 24.129 + 24.130 + ui.container{ content = function() 24.131 + ui.tag{ 24.132 + tag = "input", 24.133 + attr = { type = "radio", name = "notification_level", value = "any" } 24.134 + } 24.135 + ui.tag{ content = _"Any phase" } 24.136 + ui.tag{ tag = "ul", content = function() 24.137 + ui.tag{ tag = "li", content = _"A new issue is created in one of my areas" } 24.138 + ui.tag{ tag = "li", content = _"An issue in one of my areas or i'm interested in enters phase 'discussion'" } 24.139 + ui.tag{ tag = "li", content = _"A new initiative is created in an issue I'm interested in" } 24.140 + ui.tag{ tag = "li", content = _"The draft of an initiative I'm supporting is updated" } 24.141 + ui.tag{ tag = "li", content = _"An initiative I was supporting is revoked" } 24.142 + ui.tag{ tag = "li", content = _"A new suggestion is created in an initiative I'm supporting" } 24.143 + ui.tag{ tag = "li", content = _"An issue in one of my areas or I'm interested in enters phase 'frozen'" } 24.144 + ui.tag{ tag = "li", content = _"Voting of an issue in one of my areas or I'm interested in starts" } 24.145 + end } 24.146 + end } 24.147 + 24.148 + ui.container{ content = function() 24.149 + ui.tag{ 24.150 + tag = "input", 24.151 + attr = { type = "radio", name = "notification_level", value = "none" } 24.152 + } 24.153 + ui.tag{ content = _"No notifications at all" } 24.154 + end } 24.155 + 24.156 + 24.157 + 24.158 + ui.submit{ value = _"Change display settings" } 24.159 + end 24.160 +} 24.161 + 24.162 +local last_id = 6000; 24.163 + 24.164 +while last_id < 6050 do 24.165 + 24.166 + local event = Event:new_selector() 24.167 + :add_where{ "event.id > ?", last_id } 24.168 + :add_order_by("event.id") 24.169 + :limit(1) 24.170 + :optional_object_mode() 24.171 + :exec() 24.172 + 24.173 + last_id = nil 24.174 + if event then 24.175 + last_id = event.id 24.176 + local members_to_notify = Member:new_selector() 24.177 + :join("event_seen_by_member", nil, { "event_seen_by_member.seen_by_member_id = member.id AND event_seen_by_member.notify_level <= member.notify_level AND event_seen_by_member.id = ?", event.id } ) 24.178 + :add_where("member.activated NOTNULL AND member.notify_email NOTNULL") 24.179 + :exec() 24.180 + ui.container{ content = _("Event #{id} -> #{num} members", { id = event.id, num = #members_to_notify }) } 24.181 + 24.182 + send_notification(event) 24.183 + 24.184 + end 24.185 +end 24.186 + 24.187 + 24.188 +-- select event.id, event.occurrence, membership.member_id NOTNULL as membership, interest.member_id NOTNULL as interest, supporter.member_id NOTNULL as supporter, event.event, event.state, issue.id, initiative.name FROM event JOIN issue ON issue.id = event.issue_id LEFT JOIN membership ON membership.area_id = issue.area_id AND membership.member_id = 41 LEFT JOIN interest ON interest.issue_id = issue.id AND interest.member_id = 41 LEFT JOIN initiative ON initiative.id = event.initiative_id LEFT JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = 41 WHERE (((event.event = 'issue_state_changed' OR event.event = 'initiative_created_in_new_issue') AND membership.member_id NOTNULL OR interest.member_id NOTNULL) OR (event.event = 'initiative_created_in_existing_issue' AND interest.member_id NOTNULL) OR ((event.event = 'initiative_revoked' OR event.event = 'new_draft_created' OR event.event = 'suggestion_created') AND supporter.member_id NOTNULL)) AND event.id > 7000 ORDER by event.id ASC LIMIT 1; 24.189 \ No newline at end of file
25.1 --- a/app/main/member/show.lua Fri Feb 17 15:16:02 2012 +0100 25.2 +++ b/app/main/member/show.lua Sat Feb 25 11:51:37 2012 +0100 25.3 @@ -1,5 +1,9 @@ 25.4 local member = Member:by_id(param.get_id()) 25.5 25.6 +if not member or not member.activated then 25.7 + error("access denied") 25.8 +end 25.9 + 25.10 app.html_title.title = member.name 25.11 app.html_title.subtitle = _("Member") 25.12
26.1 --- a/app/main/policy/show.lua Fri Feb 17 15:16:02 2012 +0100 26.2 +++ b/app/main/policy/show.lua Sat Feb 25 11:51:37 2012 +0100 26.3 @@ -22,10 +22,36 @@ 26.4 value = "≥ " .. tostring(policy.initiative_quorum_num) .. "/" .. tostring(policy.initiative_quorum_den) 26.5 } 26.6 ui.field.text{ 26.7 - label = _"Majority", 26.8 - value = (policy.majority_strict and ">" or "≥" ) .. " " .. tostring(policy.majority_num) .. "/" .. tostring(policy.majority_den) 26.9 + label = _"Direct majority", 26.10 + value = 26.11 + (policy.direct_majority_strict and ">" or "≥" ) .. " " 26.12 + .. tostring(policy.direct_majority_num) .. "/" 26.13 + .. tostring(policy.direct_majority_den) 26.14 + .. (policy.direct_majority_positive > 1 and ", " .. _("at least #{count} approvals", { count = policy.direct_majority_positive }) or "") 26.15 + .. (policy.direct_majority_non_negative > 1 and ", " .. _("at least #{count} approvals or abstentions", { count = policy.direct_majority_non_negative }) or "") 26.16 } 26.17 26.18 + ui.field.text{ 26.19 + label = _"Indirect majority", 26.20 + value = 26.21 + (policy.indirect_majority_strict and ">" or "≥" ) .. " " 26.22 + .. tostring(policy.indirect_majority_num) .. "/" 26.23 + .. tostring(policy.indirect_majority_den) 26.24 + .. (policy.indirect_majority_positive > 1 and ", " .. _("at least #{count} approvals", { count = policy.indirect_majority_positive }) or "") 26.25 + .. (policy.indirect_majority_non_negative > 1 and ", " .. _("at least #{count} approvals or abstentions", { count = policy.indirect_majority_non_negative }) or "") 26.26 + } 26.27 + 26.28 + local texts = {} 26.29 + if policy.no_reverse_beat_path then 26.30 + texts[#texts+1] = _"no reverse beat path to status quo (including ties)" 26.31 + end 26.32 + if policy.no_multistage_majority then 26.33 + texts[#texts+1] = _"prohibit potentially instable results caused by multistage majorities" 26.34 + end 26.35 + ui.field.text{ 26.36 + label = _"Options", 26.37 + value = table.concat(texts, ", ") 26.38 + } 26.39 ui.container{ 26.40 attr = { class = "suggestion_content wiki" }, 26.41 content = function()
27.1 --- a/app/main/unit/_list.lua Fri Feb 17 15:16:02 2012 +0100 27.2 +++ b/app/main/unit/_list.lua Sat Feb 25 11:51:37 2012 +0100 27.3 @@ -5,6 +5,9 @@ 27.4 columns = { 27.5 { 27.6 content = function(unit) 27.7 + for i = 1, unit.depth - 1 do 27.8 + slot.put(" ") 27.9 + end 27.10 ui.link{ text = unit.name, module = "area", view = "list", params = { unit_id = unit.id } } 27.11 end 27.12 }
28.1 --- a/app/main/vote/list.lua Fri Feb 17 15:16:02 2012 +0100 28.2 +++ b/app/main/vote/list.lua Sat Feb 25 11:51:37 2012 +0100 28.3 @@ -401,16 +401,32 @@ 28.4 content = function() 28.5 ui.tag{ content = "i" .. initiative.id .. ": " } 28.6 ui.tag{ content = initiative.shortened_name } 28.7 - if #initiators > 1 then 28.8 - ui.container{ 28.9 - attr = { style = "font-size: 80%;" }, 28.10 - content = _"Initiators" .. ": " .. initiator_names_string 28.11 + slot.put("<br />") 28.12 + for i, initiator in ipairs(initiators) do 28.13 + ui.link{ 28.14 + attr = { class = "clickable" }, 28.15 + content = function () 28.16 + execute.view{ 28.17 + module = "member_image", 28.18 + view = "_show", 28.19 + params = { 28.20 + member = initiator, 28.21 + image_type = "avatar", 28.22 + show_dummy = true, 28.23 + class = "micro_avatar", 28.24 + popup_text = text 28.25 + } 28.26 + } 28.27 + end, 28.28 + module = "member", view = "show", id = initiator.id 28.29 } 28.30 - else 28.31 - ui.container{ 28.32 - attr = { style = "font-size: 80%;" }, 28.33 - content = _"Initiator" .. ": " .. initiator_names_string 28.34 + slot.put(" ") 28.35 + ui.link{ 28.36 + attr = { class = "clickable" }, 28.37 + text = initiator.name, 28.38 + module = "member", view = "show", id = initiator.id 28.39 } 28.40 + slot.put(" ") 28.41 end 28.42 end 28.43 }
29.1 --- a/config/default.lua Fri Feb 17 15:16:02 2012 +0100 29.2 +++ b/config/default.lua Sat Feb 25 11:51:37 2012 +0100 29.3 @@ -20,6 +20,10 @@ 29.4 } 29.5 } 29.6 29.7 +config.locked_profile_fields = { 29.8 + field_name = true, 29.9 + notify_email = true 29.10 +} 29.11 29.12 config.member_image_content_type = "image/jpeg" 29.13 config.member_image_convert_func = {
30.1 --- a/config/development.lua Fri Feb 17 15:16:02 2012 +0100 30.2 +++ b/config/development.lua Sat Feb 25 11:51:37 2012 +0100 30.3 @@ -1,4 +1,4 @@ 30.4 -config.absolute_base_url = "http://10.8.33.34/lf/" 30.5 +config.absolute_base_url = "http://10.1.11.5/lf/" 30.6 30.7 execute.config("default") 30.8 30.9 @@ -42,3 +42,11 @@ 30.10 -- "custom_script", 30.11 -- "document.getElementById('trace_show').onclick();" 30.12 --) 30.13 + 30.14 +config.etherpad = { 30.15 + base_url = "http://10.1.11.5:9001/", 30.16 + api_base = "http://127.0.0.1:9001/", 30.17 + api_key = "g5XAVrRb5EgPuEqIdVrRNt2Juipx3PoH", 30.18 + group_id = "g.7WDKN3StkEyuWkyN", 30.19 + cookie_path = "/" 30.20 +}
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 31.2 +++ b/env/net/curl.lua Sat Feb 25 11:51:37 2012 +0100 31.3 @@ -0,0 +1,10 @@ 31.4 +function net.curl(url) 31.5 + local stdout, errmsg, status = os.pfilter(nil, "curl", url) 31.6 + if not stdout then 31.7 + error("Error while executing curl: " .. errmsg) 31.8 + end 31.9 + if status ~= 0 then 31.10 + return nil 31.11 + end 31.12 + return stdout 31.13 +end 31.14 \ No newline at end of file
32.1 --- a/env/ui/tabs.lua Fri Feb 17 15:16:02 2012 +0100 32.2 +++ b/env/ui/tabs.lua Sat Feb 25 11:51:37 2012 +0100 32.3 @@ -1,6 +1,8 @@ 32.4 function ui.tabs(tabs) 32.5 + local attr = tabs.attr or {} 32.6 + attr.class = (attr.class and attr.class .. " " or "") .. "ui_tabs" 32.7 ui.container{ 32.8 - attr = { class = "ui_tabs" }, 32.9 + attr = attr, 32.10 content = function() 32.11 local params = param.get_all_cgi() 32.12 local current_tab = params["tab"]
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 33.2 +++ b/model/event.lua Sat Feb 25 11:51:37 2012 +0100 33.3 @@ -0,0 +1,10 @@ 33.4 +Event = mondelefant.new_class() 33.5 +Event.table = 'event' 33.6 + 33.7 +Event:add_reference{ 33.8 + mode = 'm1', 33.9 + to = "Issue", 33.10 + this_key = 'issue_id', 33.11 + that_key = 'id', 33.12 + ref = 'issue', 33.13 +} 33.14 \ No newline at end of file
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 34.2 +++ b/model/event_seen_by_member.lua Sat Feb 25 11:51:37 2012 +0100 34.3 @@ -0,0 +1,4 @@ 34.4 +EventSeenByMember = mondelefant.new_class() 34.5 +EventSeenByMember.table = 'event_seen_by_member' 34.6 + 34.7 +
35.1 --- a/model/member.lua Fri Feb 17 15:16:02 2012 +0100 35.2 +++ b/model/member.lua Sat Feb 25 11:51:37 2012 +0100 35.3 @@ -325,6 +325,30 @@ 35.4 :add_where("active") 35.5 end 35.6 35.7 +function Member.object:send_invitation() 35.8 + trace.disable() 35.9 + self.invite_code = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" ) 35.10 + local content = slot.use_temporary(function() 35.11 + slot.put(_"Hello\n\n") 35.12 + slot.put(_"You are invited to LiquidFeedback. To register please click the following link:\n\n") 35.13 + slot.put(config.absolute_base_url .. "index/register.html?invite_key=" .. self.invite_code .. "\n\n") 35.14 + slot.put(_"If this link is not working, please open following url in your web browser:\n\n") 35.15 + slot.put(config.absolute_base_url .. "index/register.html\n\n") 35.16 + slot.put(_"On that page please enter the invite key:\n\n") 35.17 + slot.put(self.invite_code .. "\n\n") 35.18 + end) 35.19 + local success = net.send_mail{ 35.20 + envelope_from = config.mail_envelope_from, 35.21 + from = config.mail_from, 35.22 + reply_to = config.mail_reply_to, 35.23 + to = self.notify_email_unconfirmed or self.notify_email, 35.24 + subject = config.mail_subject_prefix .. _"Invitation to LiquidFeedback", 35.25 + content_type = "text/plain; charset=UTF-8", 35.26 + content = content 35.27 + } 35.28 + return success 35.29 +end 35.30 + 35.31 function Member.object:set_notify_email(notify_email) 35.32 trace.disable() 35.33 local expiry = db:query("SELECT now() + '7 days'::interval as expiry", "object").expiry
36.1 --- a/model/unit.lua Fri Feb 17 15:16:02 2012 +0100 36.2 +++ b/model/unit.lua Sat Feb 25 11:51:37 2012 +0100 36.3 @@ -21,8 +21,39 @@ 36.4 ref = 'members' 36.5 } 36.6 36.7 -function Unit:get_flattened_tree() 36.8 - -- TODO implement 36.9 +function recursive_add_child_units(units, parent_unit) 36.10 + parent_unit.childs = {} 36.11 + for i, unit in ipairs(units) do 36.12 + if unit.parent_id == parent_unit.id then 36.13 + parent_unit.childs[#(parent_unit.childs)+1] = unit 36.14 + recursive_add_child_units(units, unit) 36.15 + end 36.16 + end 36.17 +end 36.18 + 36.19 +function recursive_get_child_units(units, parent_unit, depth) 36.20 + for i, unit in ipairs(parent_unit.childs) do 36.21 + unit.depth = depth 36.22 + units[#units+1] = unit 36.23 + recursive_get_child_units(units, unit, depth + 1) 36.24 + end 36.25 +end 36.26 36.27 - return Unit:new_selector():exec() 36.28 +function Unit:get_flattened_tree() 36.29 + local units = Unit:new_selector():add_order_by("name"):exec() 36.30 + local unit_tree = {} 36.31 + for i, unit in ipairs(units) do 36.32 + if not unit.parent_id then 36.33 + unit_tree[#unit_tree+1] = unit 36.34 + recursive_add_child_units(units, unit) 36.35 + end 36.36 + end 36.37 + local depth = 1 36.38 + local units = {} 36.39 + for i, unit in ipairs(unit_tree) do 36.40 + unit.depth = depth 36.41 + units[#units+1] = unit 36.42 + recursive_get_child_units(units, unit, depth + 1) 36.43 + end 36.44 + return units 36.45 end
37.1 --- a/static/style.css Fri Feb 17 15:16:02 2012 +0100 37.2 +++ b/static/style.css Sat Feb 25 11:51:37 2012 +0100 37.3 @@ -269,9 +269,7 @@ 37.4 37.5 .slot_initiative_head { 37.6 background: -webkit-gradient(linear, left top, left bottom, 37.7 - /*color-stop(0%,#AFEFB9), color-stop(100%,#ffffff) */ 37.8 - color-stop(0%, #fff), color-stop(15%,#e7e7e7), color-stop(100%,#fff) 37.9 - ); 37.10 + color-stop(0%,#e7e7e7), color-stop(66%,#fff)); 37.11 margin-top: 2ex; 37.12 padding-left: 1em; 37.13 padding-top: 2ex; 37.14 @@ -328,7 +326,9 @@ 37.15 } 37.16 37.17 37.18 - 37.19 +.member_image_photo { 37.20 + border-radius: 8px; 37.21 +} 37.22 37.23 /************************************************************************* 37.24 * vote info / delegation 37.25 @@ -469,7 +469,6 @@ 37.26 margin-right: 1em; 37.27 } 37.28 37.29 - 37.30 /************************************************************************* 37.31 * ui.tab 37.32 */ 37.33 @@ -823,6 +822,10 @@ 37.34 font-size: 125%; 37.35 } 37.36 37.37 +.issues .issue .issue_policy_info { 37.38 + font-style: italic; 37.39 +} 37.40 + 37.41 .issues .issue .interest_by_delegation { 37.42 float: right; 37.43 } 37.44 @@ -851,7 +854,7 @@ 37.45 37.46 .initiative_link.supported, 37.47 .initiative_link.potentially_supported { 37.48 - background-color: #C9FFD1; 37.49 + background-color: #cdf; 37.50 border-radius: 5px; 37.51 background: -webkit-radial-gradient(center, ellipse cover, #cdf 50%,#fff 100%); /* Chrome10+,Safari5.1+ */ 37.52 } 37.53 @@ -975,6 +978,9 @@ 37.54 border-radius: 8px; 37.55 } 37.56 37.57 +.member_statement { 37.58 + margin-right: 250px; 37.59 +} 37.60 37.61 #suggestion_description { 37.62 height: 15ex; 37.63 @@ -1021,6 +1027,7 @@ 37.64 background-color: #dfd; 37.65 padding: 1ex; 37.66 margin-bottom: 2ex; 37.67 + border-radius: 8px; 37.68 } 37.69 37.70 .not_admitted_info, 37.71 @@ -1153,15 +1160,21 @@ 37.72 * Voting 37.73 */ 37.74 37.75 +#voting_form { 37.76 + margin-top: 20px; 37.77 +} 37.78 + 37.79 #voting { 37.80 position: relative; 37.81 } 37.82 + 37.83 #voting .approval, .abstention, .disapproval { 37.84 border: 2px black solid; 37.85 - margin-top: 5ex; 37.86 + margin-top: 2ex; 37.87 margin-bottom: 5ex; 37.88 padding: 1ex; 37.89 padding-bottom: 2ex; 37.90 + border-radius: 8px; 37.91 } 37.92 #voting .approval { 37.93 background-color: #9f9; 37.94 @@ -1187,8 +1200,9 @@ 37.95 #voting .movable { 37.96 position: relative; 37.97 border: 1px black solid; 37.98 - margin: 1ex; 37.99 + margin-top: 1ex; 37.100 padding: 0.5ex; 37.101 + border-radius: 8px; 37.102 } 37.103 #voting .voting_form_active .movable { 37.104 cursor: pointer;