liquid_feedback_frontend

changeset 286:c587d8762e62

Registration process updated for Core 2.0, lockable member fields, notification settings
author bsw
date Sat Feb 25 11:51:37 2012 +0100 (2012-02-25)
parents 6c88b4bfb56c
children ee477a136fd4
files app/main/_filter/20_session.lua app/main/_layout/default.html app/main/admin/_action/member_update.lua app/main/admin/member_edit.lua app/main/index/_action/login.lua app/main/index/_action/logout.lua app/main/index/_action/register.lua app/main/index/_action/set_lang.lua app/main/index/register.lua app/main/initiative/_action/create.lua app/main/initiative/_show.lua app/main/initiative/_show_voting.lua app/main/issue/_filters.lua app/main/issue/_list.lua app/main/issue/_show_head.lua app/main/member/_action/update.lua app/main/member/_action/update_email.lua app/main/member/_action/update_login.lua app/main/member/_action/update_name.lua app/main/member/_list.lua app/main/member/_profile.lua app/main/member/edit.lua app/main/member/settings.lua app/main/member/settings_notification.lua app/main/member/show.lua app/main/policy/show.lua app/main/unit/_list.lua app/main/vote/list.lua config/default.lua config/development.lua env/net/curl.lua env/ui/tabs.lua model/event.lua model/event_seen_by_member.lua model/member.lua model/unit.lua static/style.css
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("&nbsp;")
   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("&nbsp;")
   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(" &middot; ")
   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(" &middot; ")
   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(" &middot; ")
  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(" &middot; ")
  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(" &middot; ")
  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("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;")
    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;

Impressum / About Us