liquid_feedback_frontend

changeset 6:8d91bccab0bf beta2

Version beta2

Possibility to browse voters of a closed issue

Registration with invite code

Email confirmation and password recovery

Download function (for database dumps) added

Critical bug solved, which made it impossible to select your opinion on other peoples suggestions

Catching error, when trying to set an opinion on a suggestion which has been meanwhile deleted

Fixed wrong sorting order for "supporters" or "potential supporters"

Added format info for birthday (Error when entering dates in wrong format is NOT fixed in this release)

Strip space characters from certain fields and ensure they contain at least 3 characters

Showing grade in opinion/list as clear text instead of integer value

More information on initiative is displayed while voting

Colored notification box shown on pages of issues or initiatives which are currently in voting state

Changed default filter for issues to "Open"

Back link on suggestion page

Some optical changes

Removed wrong space character in LICENSE file
author bsw/jbe
date Sat Jan 02 12:00:00 2010 +0100 (2010-01-02)
parents afd9f769c7ae
children 3941792e8be6
files LICENSE app/main/_filter/21_auth.lua app/main/_filter_view/30_navigation.lua app/main/index/_action/confirm_notify_email.lua app/main/index/_action/register.lua app/main/index/_action/reset_password.lua app/main/index/confirm_notify_email.lua app/main/index/download.lua app/main/index/download_file.lua app/main/index/index.lua app/main/index/register.lua app/main/index/reset_password.lua app/main/initiative/_action/create.lua app/main/initiative/show.lua app/main/issue/_list.lua app/main/issue/_show_head.lua app/main/member/_show_thumb.lua app/main/member/edit.lua app/main/opinion/_action/update.lua app/main/opinion/_list.lua app/main/suggestion/_action/add.lua app/main/suggestion/show.lua app/main/vote/list.lua app/main/vote/show_incoming.lua config/default.lua config/development.lua config/testing.lua env/util/trim.lua locale/help/index.download.de.txt locale/help/initiative.show.de.txt locale/translations.de.lua model/initiative.lua model/issue.lua model/member.lua static/icons/16/bullet_yellow.png static/icons/16/database_save.png static/icons/16/key_forgot.png static/icons/16/resultset_previous.png static/icons/grabber.png static/js/dragdrop.js static/style.css
line diff
     1.1 --- a/LICENSE	Fri Dec 25 12:00:00 2009 +0100
     1.2 +++ b/LICENSE	Sat Jan 02 12:00:00 2010 +0100
     1.3 @@ -21,7 +21,7 @@
     1.4  
     1.5  3rd party license information:
     1.6  
     1.7 -The icons used in Liquid Feedback (except national flags) are from Silk 
     1.8 +The icons used in LiquidFeedback (except national flags) are from Silk 
     1.9  icon set 1.3 by Mark James. [ http://www.famfamfam.com/lab/icons/silk/ ]
    1.10  His work is licensed under a Creative Commons Attribution 2.5 License.
    1.11 -[ http://creativecommons.org/licenses/by/2.5/ ]
    1.12 \ No newline at end of file
    1.13 +[ http://creativecommons.org/licenses/by/2.5/ ]
     2.1 --- a/app/main/_filter/21_auth.lua	Fri Dec 25 12:00:00 2009 +0100
     2.2 +++ b/app/main/_filter/21_auth.lua	Sat Jan 02 12:00:00 2010 +0100
     2.3 @@ -6,6 +6,10 @@
     2.4      or request.get_view()   == "register"
     2.5      or request.get_action() == "register"
     2.6      or request.get_view()   == "about"
     2.7 +    or request.get_view()   == "reset_password"
     2.8 +    or request.get_action() == "reset_password"
     2.9 +    or request.get_view()   == "confirm_notify_email"
    2.10 +    or request.get_action() == "confirm_notify_email"
    2.11    )
    2.12  )
    2.13  
     3.1 --- a/app/main/_filter_view/30_navigation.lua	Fri Dec 25 12:00:00 2009 +0100
     3.2 +++ b/app/main/_filter_view/30_navigation.lua	Sat Jan 02 12:00:00 2010 +0100
     3.3 @@ -21,6 +21,14 @@
     3.4      }
     3.5      ui.link{
     3.6        content = function()
     3.7 +        ui.image{ static = "icons/16/key_forgot.png" }
     3.8 +        slot.put(_"Reset password")
     3.9 +      end,
    3.10 +      module = 'index',
    3.11 +      view = 'reset_password'
    3.12 +    }
    3.13 +    ui.link{
    3.14 +      content = function()
    3.15          ui.image{ static = "icons/16/information.png" }
    3.16          slot.put('About / Impressum')
    3.17        end,
    3.18 @@ -79,15 +87,6 @@
    3.19        view = 'about'
    3.20      }
    3.21  
    3.22 -    ui.link{
    3.23 -      content = function()
    3.24 -        ui.image{ static = "icons/16/bug.png" }
    3.25 -        slot.put(_"Bug report")
    3.26 -      end,
    3.27 -      external = "http://trac.public-software-group.org/projects/lf" --/newticket?description=" .. encode.url_part("\n\n\n\nReport for: " .. os.getenv("REQUEST_URI") )
    3.28 -    }
    3.29 -
    3.30 -
    3.31    if app.session.member.admin then
    3.32  
    3.33      slot.put(" ")
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/app/main/index/_action/confirm_notify_email.lua	Sat Jan 02 12:00:00 2010 +0100
     4.3 @@ -0,0 +1,18 @@
     4.4 +local secret = param.get("secret")
     4.5 +
     4.6 +local member = Member:new_selector()
     4.7 +  :add_where{ "notify_email_secret = ?", secret }
     4.8 +  :add_where("notify_email_secret_expiry > now()")
     4.9 +  :optional_object_mode()
    4.10 +  :exec()
    4.11 +
    4.12 +if member then
    4.13 +  member.notify_email = member.notify_email_unconfirmed
    4.14 +  member.notify_email_unconfirmed = nil
    4.15 +  member.notify_email_secret = nil
    4.16 +  member:save()
    4.17 +  slot.put_into("notice", _"Email address is confirmed now")
    4.18 +else
    4.19 +  slot.put_into("error", _"Confirmation code invalid!")
    4.20 +  return false
    4.21 +end
     5.1 --- a/app/main/index/_action/register.lua	Fri Dec 25 12:00:00 2009 +0100
     5.2 +++ b/app/main/index/_action/register.lua	Sat Jan 02 12:00:00 2010 +0100
     5.3 @@ -10,10 +10,20 @@
     5.4    return false
     5.5  end
     5.6  
     5.7 -local name = param.get("name")
     5.8 +local notify_email = param.get("notify_email")
     5.9  
    5.10 -if invite_code and not name then
    5.11 -  slot.put_into("notice", _"Invite code valid!")
    5.12 +if invite_code and not notify_email then
    5.13 +  request.redirect{
    5.14 +    mode   = "redirect",
    5.15 +    module = "index",
    5.16 +    view   = "register",
    5.17 +    params = { code = invite_code.code }
    5.18 +  }
    5.19 +  return false
    5.20 +end
    5.21 +
    5.22 +if #notify_email < 5 then
    5.23 +  slot.put_into("error", _"Email address too short!")
    5.24    request.redirect{
    5.25      mode   = "redirect",
    5.26      module = "index",
    5.27 @@ -23,13 +33,47 @@
    5.28    return false
    5.29  end
    5.30  
    5.31 +local name = param.get("name")
    5.32 +
    5.33 +if notify_email and not name then
    5.34 +  request.redirect{
    5.35 +    mode   = "redirect",
    5.36 +    module = "index",
    5.37 +    view   = "register",
    5.38 +    params = {
    5.39 +      code = invite_code.code,
    5.40 +      notify_email = notify_email
    5.41 +    }
    5.42 +  }
    5.43 +  return false
    5.44 +end
    5.45 +
    5.46 +name = util.trim(name)
    5.47 +
    5.48 +if #name < 3 then
    5.49 +  slot.put_into("error", _"This username is too short!")
    5.50 +  request.redirect{
    5.51 +    mode   = "redirect",
    5.52 +    module = "index",
    5.53 +    view   = "register",
    5.54 +    params = {
    5.55 +      code = invite_code.code,
    5.56 +      notify_email = notify_email
    5.57 +    }
    5.58 +  }
    5.59 +  return false
    5.60 +end
    5.61 +
    5.62  if Member:by_name(name) then
    5.63    slot.put_into("error", _"This name is already taken, please choose another one!")
    5.64    request.redirect{
    5.65      mode   = "redirect",
    5.66      module = "index",
    5.67      view   = "register",
    5.68 -    params = { code = invite_code.code }
    5.69 +    params = {
    5.70 +      code = invite_code.code,
    5.71 +      notify_email = notify_email
    5.72 +    }
    5.73    }
    5.74    return false
    5.75  end
    5.76 @@ -37,13 +81,30 @@
    5.77  local login = param.get("login")
    5.78  
    5.79  if name and not login then
    5.80 -  slot.put_into("notice", _"Name is available")
    5.81    request.redirect{
    5.82      mode   = "redirect",
    5.83      module = "index",
    5.84      view   = "register",
    5.85      params = { 
    5.86        code = invite_code.code,
    5.87 +      notify_email = notify_email,
    5.88 +      name = name
    5.89 +    }
    5.90 +  }
    5.91 +  return false
    5.92 +end
    5.93 +
    5.94 +login = util.trim(login)
    5.95 +
    5.96 +if #login < 3 then 
    5.97 +  slot.put_into("error", _"This login is too short!")
    5.98 +  request.redirect{
    5.99 +    mode   = "redirect",
   5.100 +    module = "index",
   5.101 +    view   = "register",
   5.102 +    params = { 
   5.103 +      code = invite_code.code,
   5.104 +      notify_email = notify_email,
   5.105        name = name
   5.106      }
   5.107    }
   5.108 @@ -58,23 +119,57 @@
   5.109      view   = "register",
   5.110      params = { 
   5.111        code = invite_code.code,
   5.112 +      notify_email = notify_email,
   5.113        name = name
   5.114      }
   5.115    }
   5.116    return false
   5.117  end
   5.118  
   5.119 +local use_terms_accepted = param.get("use_terms_accepted", atom.boolean)
   5.120 +
   5.121 +if login and use_terms_accepted == nil then
   5.122 +  request.redirect{
   5.123 +    mode   = "redirect",
   5.124 +    module = "index",
   5.125 +    view   = "register",
   5.126 +    params = { 
   5.127 +      code = invite_code.code,
   5.128 +      notify_email = notify_email,
   5.129 +      name = name,
   5.130 +      login = login
   5.131 +    }
   5.132 +  }
   5.133 +  return false
   5.134 +end
   5.135 +
   5.136 +if use_terms_accepted ~= true then
   5.137 +  slot.put_into("error", _"You have to accept the terms of use to complete registration.")
   5.138 +  request.redirect{
   5.139 +    mode   = "redirect",
   5.140 +    module = "index",
   5.141 +    view   = "register",
   5.142 +    params = { 
   5.143 +      code = invite_code.code,
   5.144 +      notify_email = notify_email,
   5.145 +      name = name,
   5.146 +      login = login
   5.147 +    }
   5.148 +  }
   5.149 +  return false
   5.150 +end
   5.151 +
   5.152  local password1 = param.get("password1")
   5.153  local password2 = param.get("password2")
   5.154  
   5.155  if login and not password1 then
   5.156 -  slot.put_into("notice", _"Login is available")
   5.157    request.redirect{
   5.158      mode   = "redirect",
   5.159      module = "index",
   5.160      view   = "register",
   5.161      params = { 
   5.162        code = invite_code.code,
   5.163 +      notify_email = notify_email,
   5.164        name = name,
   5.165        login = login
   5.166      }
   5.167 @@ -90,6 +185,7 @@
   5.168      view   = "register",
   5.169      params = { 
   5.170        code = invite_code.code,
   5.171 +      notify_email = notify_email,
   5.172        name = name,
   5.173        login = login
   5.174      }
   5.175 @@ -105,6 +201,7 @@
   5.176      view   = "register",
   5.177      params = { 
   5.178        code = invite_code.code,
   5.179 +      notify_email = notify_email,
   5.180        name = name,
   5.181        login = login
   5.182      }
   5.183 @@ -116,6 +213,24 @@
   5.184  
   5.185  member.login = login
   5.186  member.name = name
   5.187 +
   5.188 +local success = member:set_notify_email(notify_email)
   5.189 +if not success then
   5.190 +  slot.put_into("error", _"Can't send confirmation email")
   5.191 +  request.redirect{
   5.192 +    mode   = "redirect",
   5.193 +    module = "index",
   5.194 +    view   = "register",
   5.195 +    params = { 
   5.196 +      code = invite_code.code,
   5.197 +      notify_email = notify_email,
   5.198 +      name = name,
   5.199 +      login = login
   5.200 +    }
   5.201 +  }
   5.202 +  return
   5.203 +end
   5.204 +
   5.205  member:set_password(password1)
   5.206  member:save()
   5.207  
   5.208 @@ -125,8 +240,8 @@
   5.209  
   5.210  slot.put_into("notice", _"You've successfully registered and you can login now with your login and password!")
   5.211  
   5.212 -  request.redirect{
   5.213 -    mode   = "redirect",
   5.214 -    module = "index",
   5.215 -    view   = "login",
   5.216 -  }
   5.217 +request.redirect{
   5.218 +  mode   = "redirect",
   5.219 +  module = "index",
   5.220 +  view   = "login",
   5.221 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/app/main/index/_action/reset_password.lua	Sat Jan 02 12:00:00 2010 +0100
     6.3 @@ -0,0 +1,74 @@
     6.4 +local secret = param.get("secret")
     6.5 +
     6.6 +if not secret then
     6.7 +
     6.8 +  local member = Member:new_selector()
     6.9 +    :add_where{ "login = ?", param.get("login") }
    6.10 +    :add_where("password_reset_secret ISNULL OR password_reset_secret_expiry < now()")
    6.11 +    :optional_object_mode()
    6.12 +    :exec()
    6.13 +
    6.14 +  if member then
    6.15 +    if not member.notify_email then
    6.16 +      slot.put_into("error", _"Sorry, but there is not confirmed email address for your account. Please contact the administrator or support.")
    6.17 +      return false
    6.18 +    end
    6.19 +    member.password_reset_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
    6.20 +    local expiry = db:query("SELECT now() + '1 days'::interval as expiry", "object").expiry
    6.21 +    member.password_reset_secret_expiry = expiry
    6.22 +    member:save()
    6.23 +    local content = slot.use_temporary(function()
    6.24 +      slot.put(_"Hello " .. member.name .. ",\n\n")
    6.25 +      slot.put(_"to reset your password please click on the following link:\n\n")
    6.26 +      slot.put(config.absolute_base_url .. "index/reset_password.html?secret=" .. member.password_reset_secret .. "\n\n")
    6.27 +      slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
    6.28 +      slot.put(config.absolute_base_url .. "index/reset_password.html\n\n")
    6.29 +      slot.put(_"On that page please enter the reset code:\n\n")
    6.30 +      slot.put(member.password_reset_secret .. "\n\n")
    6.31 +    end)
    6.32 +    local success = net.send_mail{
    6.33 +      envelope_from = config.mail_envelope_from,
    6.34 +      from          = config.mail_from,
    6.35 +      reply_to      = config.mail_reply_to,
    6.36 +      to            = member.notify_email,
    6.37 +      subject       = config.mail_subject_prefix .. _"Password reset request",
    6.38 +      content_type  = "text/plain; charset=UTF-8",
    6.39 +      content       = content
    6.40 +    }
    6.41 +  end
    6.42 +
    6.43 +  slot.put_into("notice", _"Reset link has been send for this member")
    6.44 +
    6.45 +else
    6.46 +  local member = Member:new_selector()
    6.47 +    :add_where{ "password_reset_secret = ?", secret }
    6.48 +    :add_where{ "password_reset_secret_expiry > now()" }
    6.49 +    :optional_object_mode()
    6.50 +    :exec()
    6.51 +
    6.52 +  if not member then
    6.53 +    slot.put_into("error", _"Reset code is invalid!")
    6.54 +    return false
    6.55 +  end
    6.56 +
    6.57 +  local password1 = param.get("password1")
    6.58 +  local password2 = param.get("password2")
    6.59 +
    6.60 +  if password1 ~= password2 then
    6.61 +    slot.put_into("error", _"Passwords don't match!")
    6.62 +    return false
    6.63 +  end
    6.64 +
    6.65 +  if #password1 < 8 then
    6.66 +    slot.put_into("error", _"Passwords must consist of at least 8 characters!")
    6.67 +    return false
    6.68 +  end
    6.69 +
    6.70 +  member:set_password(password1)
    6.71 +  member.password_reset_secret = nil
    6.72 +  member.password_reset_secret_expiry = nil
    6.73 +  member:save()
    6.74 +
    6.75 +  slot.put_into("notice", _"Password has been reset successfully")
    6.76 +
    6.77 +end
    6.78 \ No newline at end of file
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/app/main/index/confirm_notify_email.lua	Sat Jan 02 12:00:00 2010 +0100
     7.3 @@ -0,0 +1,22 @@
     7.4 +slot.put_into("title", _"Email address confirmation")
     7.5 +
     7.6 +ui.form{
     7.7 +  attr = { class = "vertical" },
     7.8 +  module = "index",
     7.9 +  action = "confirm_notify_email",
    7.10 +  routing = {
    7.11 +    ok = {
    7.12 +      mode = "redirect",
    7.13 +      module = "index",
    7.14 +      view = "index"
    7.15 +    }
    7.16 +  },
    7.17 +  content = function()
    7.18 +    ui.field.text{
    7.19 +      label = _"Confirmation code",
    7.20 +      name = "secret",
    7.21 +      value = param.get("secret")
    7.22 +    }
    7.23 +    ui.submit{ text = _"Confirm" }
    7.24 +  end
    7.25 +}
    7.26 \ No newline at end of file
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/app/main/index/download.lua	Sat Jan 02 12:00:00 2010 +0100
     8.3 @@ -0,0 +1,60 @@
     8.4 +if not config.download_dir then
     8.5 +  error("feature not enabled")
     8.6 +end
     8.7 +
     8.8 +slot.put_into("title", _"Download database export")
     8.9 +
    8.10 +slot.select("actions", function()
    8.11 +  ui.link{
    8.12 +    content = function()
    8.13 +        ui.image{ static = "icons/16/cancel.png" }
    8.14 +        slot.put(_"Cancel")
    8.15 +    end,
    8.16 +    module = "index",
    8.17 +    view = "index"
    8.18 +  }
    8.19 +end)
    8.20 +
    8.21 +util.help("index.download", _"Download")
    8.22 +
    8.23 +ui.container{
    8.24 +  attr = { class = "wiki use_terms" },
    8.25 +  content = function()
    8.26 +    slot.put(format.wiki_text(config.download_use_terms))
    8.27 +  end
    8.28 +}
    8.29 +
    8.30 +
    8.31 +local file_list = os.listdir(config.download_dir)
    8.32 +
    8.33 +local tmp = {}
    8.34 +for i, filename in ipairs(file_list) do
    8.35 +  if not filename:find("^%.") then
    8.36 +    tmp[#tmp+1] = filename
    8.37 +  end
    8.38 +end
    8.39 +
    8.40 +local file_list = tmp
    8.41 +
    8.42 +table.sort(file_list, function(a, b) return a > b end)
    8.43 +
    8.44 +ui.list{
    8.45 +  records = file_list,
    8.46 +  columns = {
    8.47 +    {
    8.48 +      content = function(filename)
    8.49 +        slot.put(encode.html(filename))
    8.50 +      end
    8.51 +    },
    8.52 +    {
    8.53 +      content = function(filename)
    8.54 +        ui.link{
    8.55 +          content = _"Download",
    8.56 +          module = "index",
    8.57 +          view = "download_file",
    8.58 +          params = { filename = filename }
    8.59 +        }
    8.60 +      end
    8.61 +    }
    8.62 +  }
    8.63 +}
    8.64 \ No newline at end of file
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/app/main/index/download_file.lua	Sat Jan 02 12:00:00 2010 +0100
     9.3 @@ -0,0 +1,15 @@
     9.4 +if not config.download_dir then
     9.5 +  error("feature not enabled")
     9.6 +end
     9.7 +
     9.8 +local filename = param.get("filename")
     9.9 +
    9.10 +local file = assert(io.open(encode.file_path(config.download_dir, filename)), "file not found")
    9.11 +
    9.12 +print('Content-type: application/octet-stream')
    9.13 +print('Content-disposition: attachment; filename=' .. filename)
    9.14 +print('')
    9.15 +
    9.16 +io.stdout:write(file:read("*a"))
    9.17 +
    9.18 +exit()
    10.1 --- a/app/main/index/index.lua	Fri Dec 25 12:00:00 2009 +0100
    10.2 +++ b/app/main/index/index.lua	Sat Jan 02 12:00:00 2010 +0100
    10.3 @@ -75,6 +75,16 @@
    10.4      view = "change_password"
    10.5    }
    10.6  
    10.7 +  if config.download_dir then
    10.8 +    ui.link{
    10.9 +      content = function()
   10.10 +          ui.image{ static = "icons/16/database_save.png" }
   10.11 +          slot.put(_"Download")
   10.12 +      end,
   10.13 +      module = "index",
   10.14 +      view = "download"
   10.15 +    }
   10.16 +  end 
   10.17  end)
   10.18  
   10.19  local lang = locale.get("lang")
    11.1 --- a/app/main/index/register.lua	Fri Dec 25 12:00:00 2009 +0100
    11.2 +++ b/app/main/index/register.lua	Sat Jan 02 12:00:00 2010 +0100
    11.3 @@ -1,32 +1,26 @@
    11.4  slot.put_into("title", _"Registration")
    11.5  
    11.6 -slot.select("actions", function()
    11.7 -  ui.link{
    11.8 -    content = function()
    11.9 -        ui.image{ static = "icons/16/cancel.png" }
   11.10 -        slot.put(_"Cancel")
   11.11 -    end,
   11.12 -    module = "index",
   11.13 -    view = "index"
   11.14 -  }
   11.15 -end)
   11.16  
   11.17  local code = param.get("code")
   11.18 +local notify_email = param.get("notify_email")
   11.19  local name = param.get("name")
   11.20  local login = param.get("login")
   11.21  
   11.22 +slot.put_into("title", " (")
   11.23  ui.form{
   11.24 -  attr = { class = "login" },
   11.25 +  attr = { class = "vertical" },
   11.26    module = 'index',
   11.27    action = 'register',
   11.28    params = {
   11.29      code = code,
   11.30 +    notify_email = notify_email,
   11.31      name = name,
   11.32      login = login
   11.33    },
   11.34    content = function()
   11.35  
   11.36      if not code then
   11.37 +      slot.put_into("title", _"Step 1/5: Invite code")
   11.38        ui.tag{
   11.39          tag = "p",
   11.40          content = _"Please enter the invite code you've received."
   11.41 @@ -34,9 +28,48 @@
   11.42        ui.field.text{
   11.43          label     = _'Invite code',
   11.44          name = 'code',
   11.45 +        value = param.get("invite")
   11.46 +      }
   11.47 +
   11.48 +    elseif not notify_email then
   11.49 +      slot.put_into("title", _"Step 2/5: Email address")
   11.50 +      slot.select("actions", function()
   11.51 +        ui.link{
   11.52 +          content = function()
   11.53 +              ui.image{ static = "icons/16/resultset_previous.png" }
   11.54 +              slot.put(_"One step back")
   11.55 +          end,
   11.56 +          module = "index",
   11.57 +          view = "register",
   11.58 +          params = {
   11.59 +          }
   11.60 +        }
   11.61 +      end)
   11.62 +      ui.tag{
   11.63 +        tag = "p",
   11.64 +        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."
   11.65 +      }
   11.66 +      ui.field.text{
   11.67 +        label     = _'Email address',
   11.68 +        name      = 'notify_email',
   11.69 +        value     = param.get("notify_email")
   11.70        }
   11.71  
   11.72      elseif not name then
   11.73 +      slot.put_into("title", _"Step 3/5: Username")
   11.74 +      slot.select("actions", function()
   11.75 +        ui.link{
   11.76 +          content = function()
   11.77 +              ui.image{ static = "icons/16/resultset_previous.png" }
   11.78 +              slot.put(_"One step back")
   11.79 +          end,
   11.80 +          module = "index",
   11.81 +          view = "register",
   11.82 +          params = {
   11.83 +            code = code
   11.84 +          }
   11.85 +        }
   11.86 +      end)
   11.87        ui.tag{
   11.88          tag = "p",
   11.89          content = _"Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you. You CAN'T change this name later, so please choose it wisely!"
   11.90 @@ -48,6 +81,21 @@
   11.91        }
   11.92  
   11.93      elseif not login then
   11.94 +      slot.put_into("title", _"Step 4/5: Login name")
   11.95 +      slot.select("actions", function()
   11.96 +        ui.link{
   11.97 +          content = function()
   11.98 +              ui.image{ static = "icons/16/resultset_previous.png" }
   11.99 +              slot.put(_"One step back")
  11.100 +          end,
  11.101 +          module = "index",
  11.102 +          view = "register",
  11.103 +          params = {
  11.104 +            code = code,
  11.105 +            notify_email = notify_email
  11.106 +          }
  11.107 +        }
  11.108 +      end)
  11.109        ui.tag{
  11.110          tag = "p",
  11.111          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."
  11.112 @@ -59,18 +107,54 @@
  11.113        }
  11.114  
  11.115      else
  11.116 +      slot.put_into("title", _"Step 5/5: Terms of use and password")
  11.117 +      slot.select("actions", function()
  11.118 +        ui.link{
  11.119 +          content = function()
  11.120 +              ui.image{ static = "icons/16/resultset_previous.png" }
  11.121 +              slot.put(_"One step back")
  11.122 +          end,
  11.123 +          module = "index",
  11.124 +          view = "register",
  11.125 +          params = {
  11.126 +            code = code,
  11.127 +            notify_email = notify_email,
  11.128 +            name = name,
  11.129 +          }
  11.130 +        }
  11.131 +      end)
  11.132 +      ui.container{
  11.133 +        attr = { class = "wiki use_terms" },
  11.134 +        content = function()
  11.135 +          slot.put(format.wiki_text(config.use_terms))
  11.136 +        end
  11.137 +      }
  11.138 +      slot.put("<br />")
  11.139 +      ui.field.text{
  11.140 +        label     = _'Email address',
  11.141 +        value     = param.get("notify_email"),
  11.142 +        readonly = true
  11.143 +      }
  11.144        ui.field.text{
  11.145          label     = _'Name',
  11.146 -        name      = 'name',
  11.147          value     = param.get("name"),
  11.148          readonly = true
  11.149        }
  11.150        ui.field.text{
  11.151          label     = _'Login name',
  11.152 -        name      = 'login',
  11.153          value     = param.get("login"),
  11.154          readonly = true
  11.155        }
  11.156 +
  11.157 +      ui.tag{
  11.158 +        tag = "p",
  11.159 +        content = _"I accept the terms of use by checking the following checkbox:"
  11.160 +      }
  11.161 +      ui.field.boolean{
  11.162 +        label     = _"Terms accepted",
  11.163 +        name      = "use_terms_accepted",
  11.164 +      }
  11.165 +
  11.166        ui.tag{
  11.167          tag = "p",
  11.168          content = _"Please choose a password and enter it twice. The password is case sensitive."
  11.169 @@ -90,6 +174,18 @@
  11.170        text = _'Register'
  11.171      }
  11.172  
  11.173 +    slot.put_into("title", ")")
  11.174 +    slot.select("actions", function()
  11.175 +      ui.link{
  11.176 +        content = function()
  11.177 +            ui.image{ static = "icons/16/cancel.png" }
  11.178 +            slot.put(_"Cancel registration")
  11.179 +        end,
  11.180 +        module = "index",
  11.181 +        view = "index"
  11.182 +      }
  11.183 +    end)
  11.184 +
  11.185    end
  11.186  }
  11.187  
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/app/main/index/reset_password.lua	Sat Jan 02 12:00:00 2010 +0100
    12.3 @@ -0,0 +1,81 @@
    12.4 +slot.put_into("title", _"Reset password")
    12.5 +
    12.6 +slot.select("actions", function()
    12.7 +  ui.link{
    12.8 +    content = function()
    12.9 +        ui.image{ static = "icons/16/cancel.png" }
   12.10 +        slot.put(_"Cancel password reset")
   12.11 +    end,
   12.12 +    module = "index",
   12.13 +    view = "index"
   12.14 +  }
   12.15 +end)
   12.16 +
   12.17 +
   12.18 +local secret = param.get("secret")
   12.19 +
   12.20 +if not secret then
   12.21 +  ui.tag{
   12.22 +    tag = 'p',
   12.23 +    content = _'Please enter your login name. You will receive an email with a link to reset your password.'
   12.24 +  }
   12.25 +  ui.form{
   12.26 +    attr = { class = "vertical" },
   12.27 +    module = "index",
   12.28 +    action = "reset_password",
   12.29 +    routing = {
   12.30 +      ok = {
   12.31 +        mode = "redirect",
   12.32 +        module = "index",
   12.33 +        view = "index"
   12.34 +      }
   12.35 +    },
   12.36 +    content = function()
   12.37 +      ui.field.text{ 
   12.38 +        label = "Login",
   12.39 +        name = "login"
   12.40 +      }
   12.41 +      ui.submit{ text = _"Request password reset link" }
   12.42 +    end
   12.43 +  }
   12.44 +
   12.45 +else
   12.46 +
   12.47 +  ui.form{
   12.48 +    attr = { class = "vertical" },
   12.49 +    module = "index",
   12.50 +    action = "reset_password",
   12.51 +    routing = {
   12.52 +      ok = {
   12.53 +        mode = "redirect",
   12.54 +        module = "index",
   12.55 +        view = "index"
   12.56 +      }
   12.57 +    },
   12.58 +    content = function()
   12.59 +      ui.tag{
   12.60 +        tag = 'p',
   12.61 +        content = _'Please enter the email reset code you have received:'
   12.62 +      }
   12.63 +      ui.field.text{
   12.64 +        label = _"Reset code",
   12.65 +        name = "secret",
   12.66 +        value = secret
   12.67 +      }
   12.68 +      ui.tag{
   12.69 +        tag = 'p',
   12.70 +        content = _'Please enter your new password twice.'
   12.71 +      }
   12.72 +      ui.field.password{
   12.73 +        label = "New password",
   12.74 +        name = "password1"
   12.75 +      }
   12.76 +      ui.field.password{
   12.77 +        label = "New password (repeat)",
   12.78 +        name = "password2"
   12.79 +      }
   12.80 +      ui.submit{ text = _"Set new password" }
   12.81 +    end
   12.82 +  }
   12.83 +
   12.84 +end
   12.85 \ No newline at end of file
    13.1 --- a/app/main/initiative/_action/create.lua	Fri Dec 25 12:00:00 2009 +0100
    13.2 +++ b/app/main/initiative/_action/create.lua	Sat Jan 02 12:00:00 2010 +0100
    13.3 @@ -29,6 +29,15 @@
    13.4    area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec()
    13.5  end
    13.6  
    13.7 +local name = param.get("name")
    13.8 +
    13.9 +local name = util.trim(name)
   13.10 +
   13.11 +if #name < 3 then
   13.12 +  slot.put_into("error", _"This name is really too short!")
   13.13 +  return false
   13.14 +end
   13.15 +
   13.16  local initiative = Initiative:new()
   13.17  
   13.18  if not issue then
   13.19 @@ -38,11 +47,9 @@
   13.20    issue:save()
   13.21  end
   13.22  
   13.23 -
   13.24 -
   13.25  initiative.issue_id = issue.id
   13.26 -
   13.27 -param.update(initiative, "name", "discussion_url")
   13.28 +initiative.name = name
   13.29 +param.update(initiative, "discussion_url")
   13.30  initiative:save()
   13.31  
   13.32  local draft = Draft:new()
    14.1 --- a/app/main/initiative/show.lua	Fri Dec 25 12:00:00 2009 +0100
    14.2 +++ b/app/main/initiative/show.lua	Sat Jan 02 12:00:00 2010 +0100
    14.3 @@ -188,10 +188,19 @@
    14.4  end
    14.5  
    14.6  
    14.7 -ui.tabs{
    14.8 +local current_draft_name = _"Current draft"
    14.9 +if initiative.issue.half_frozen then
   14.10 +  current_draft_name = _"Voting proposal"
   14.11 +end
   14.12 +
   14.13 +if initiative.issue.state == "finished" then
   14.14 +  current_draft_name = _"Voted proposal"
   14.15 +end
   14.16 +
   14.17 +local tabs = {
   14.18    {
   14.19      name = "current_draft",
   14.20 -    label = _"Current draft",
   14.21 +    label = current_draft_name,
   14.22      content = function()
   14.23        if initiator then
   14.24          ui.link{
   14.25 @@ -206,108 +215,136 @@
   14.26        end
   14.27        execute.view{ module = "draft", view = "_show", params = { draft = initiative.current_draft } }
   14.28      end
   14.29 -  },
   14.30 -  {
   14.31 -    name = "suggestion",
   14.32 -    label = _"Suggestions",
   14.33 -    content = function()
   14.34 -      execute.view{
   14.35 -        module = "suggestion",
   14.36 -        view = "_list",
   14.37 -        params = {
   14.38 -          initiative = initiative,
   14.39 -          suggestions_selector = initiative:get_reference_selector("suggestions")
   14.40 -        }
   14.41 -      }
   14.42 -      slot.put("<br />")
   14.43 -      if not initiative.issue.fully_frozen and not initiative.issue.closed then
   14.44 -        ui.link{
   14.45 -          content = function()
   14.46 -            ui.image{ static = "icons/16/comment_add.png" }
   14.47 -            slot.put(_"Add new suggestion")
   14.48 -          end,
   14.49 -          attr = { onclick = "document.getElementById('add_suggestion_form').style.display='block';return(false)" },
   14.50 -          static = "#"
   14.51 -        }
   14.52 -      end
   14.53 -    end
   14.54 -  },
   14.55 -  {
   14.56 -    name = "satisfied_supporter",
   14.57 -    label = _"Supporter",
   14.58 +  }
   14.59 +}
   14.60 +
   14.61 +if initiative.issue.ranks_available then
   14.62 +  tabs[#tabs+1] = {
   14.63 +    name = "voter",
   14.64 +    label = _"Voter",
   14.65      content = function()
   14.66        execute.view{
   14.67          module = "member",
   14.68          view = "_list",
   14.69          params = {
   14.70            initiative = initiative,
   14.71 -          members_selector =  initiative:get_reference_selector("supporting_members_snapshot")
   14.72 -            :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
   14.73 -            :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id")
   14.74 -            :add_field("direct_interest_snapshot.weight")
   14.75 -            :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
   14.76 -            :add_where("direct_supporter_snapshot.satisfied")
   14.77 -        }
   14.78 -      }
   14.79 -    end
   14.80 -  },
   14.81 -  {
   14.82 -    name = "supporter",
   14.83 -    label = _"Potential supporter",
   14.84 -    content = function()
   14.85 -      execute.view{
   14.86 -        module = "member",
   14.87 -        view = "_list",
   14.88 -        params = {
   14.89 -          initiative = initiative,
   14.90 -          members_selector =  initiative:get_reference_selector("supporting_members_snapshot")
   14.91 -            :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
   14.92 -            :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id")
   14.93 -            :add_field("direct_interest_snapshot.weight")
   14.94 -            :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
   14.95 -            :add_where("NOT direct_supporter_snapshot.satisfied")
   14.96 +          members_selector =  initiative.issue:get_reference_selector("direct_voters")
   14.97 +            :left_join("vote", nil, { "vote.initiative_id = ? AND vote.member_id = member.id", initiative.id })
   14.98 +            :add_field("direct_voter.weight as voter_weight")
   14.99 +            :add_field("coalesce(vote.grade, 0) as grade")
  14.100          }
  14.101        }
  14.102      end
  14.103 -  },
  14.104 -  {
  14.105 -    name = "initiators",
  14.106 -    label = _"Initiators",
  14.107 -    content = function()
  14.108 -      execute.view{ module = "member", view = "_list", params = { members_selector = initiative:get_reference_selector("initiating_members") } }
  14.109 -    end
  14.110 -  },
  14.111 -  {
  14.112 -    name = "drafts",
  14.113 -    label = _"Old drafts",
  14.114 -    content = function()
  14.115 -      execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } }
  14.116 -    end
  14.117 -  },
  14.118 -  {
  14.119 -    name = "details",
  14.120 -    label = _"Details",
  14.121 -    content = function()
  14.122 -      ui.form{
  14.123 -        attr = { class = "vertical" },
  14.124 -        record = initiative,
  14.125 -        readonly = true,
  14.126 +  }
  14.127 +end
  14.128 +
  14.129 +tabs[#tabs+1] = {
  14.130 +  name = "suggestion",
  14.131 +  label = _"Suggestions",
  14.132 +  content = function()
  14.133 +    execute.view{
  14.134 +      module = "suggestion",
  14.135 +      view = "_list",
  14.136 +      params = {
  14.137 +        initiative = initiative,
  14.138 +        suggestions_selector = initiative:get_reference_selector("suggestions")
  14.139 +      }
  14.140 +    }
  14.141 +    slot.put("<br />")
  14.142 +    if not initiative.issue.fully_frozen and not initiative.issue.closed then
  14.143 +      ui.link{
  14.144          content = function()
  14.145 -          ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
  14.146 -          ui.field.text{
  14.147 -            label = _"Created at",
  14.148 -            value = tostring(initiative.created)
  14.149 -          }
  14.150 -          ui.field.text{
  14.151 -            label = _"Created at",
  14.152 -            value = format.timestamp(initiative.created)
  14.153 -          }
  14.154 -          ui.field.date{ label = _"Revoked at", name = "revoked" }
  14.155 -          ui.field.boolean{ label = _"Admitted", name = "admitted" }
  14.156 -        end
  14.157 +          ui.image{ static = "icons/16/comment_add.png" }
  14.158 +          slot.put(_"Add new suggestion")
  14.159 +        end,
  14.160 +        attr = { onclick = "document.getElementById('add_suggestion_form').style.display='block';return(false)" },
  14.161 +        static = "#"
  14.162        }
  14.163      end
  14.164 -  },
  14.165 +  end
  14.166 +}
  14.167 +
  14.168 +tabs[#tabs+1] = {
  14.169 +  name = "satisfied_supporter",
  14.170 +  label = _"Supporter",
  14.171 +  content = function()
  14.172 +    execute.view{
  14.173 +      module = "member",
  14.174 +      view = "_list",
  14.175 +      params = {
  14.176 +        initiative = initiative,
  14.177 +        members_selector =  initiative:get_reference_selector("supporting_members_snapshot")
  14.178 +          :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
  14.179 +          :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id")
  14.180 +          :add_field("direct_interest_snapshot.weight")
  14.181 +          :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
  14.182 +          :add_where("direct_supporter_snapshot.satisfied")
  14.183 +      }
  14.184 +    }
  14.185 +  end
  14.186 +}
  14.187 +
  14.188 +tabs[#tabs+1] = {
  14.189 +  name = "supporter",
  14.190 +  label = _"Potential supporter",
  14.191 +  content = function()
  14.192 +    execute.view{
  14.193 +      module = "member",
  14.194 +      view = "_list",
  14.195 +      params = {
  14.196 +        initiative = initiative,
  14.197 +        members_selector =  initiative:get_reference_selector("supporting_members_snapshot")
  14.198 +          :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
  14.199 +          :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id")
  14.200 +          :add_field("direct_interest_snapshot.weight")
  14.201 +          :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
  14.202 +          :add_where("NOT direct_supporter_snapshot.satisfied")
  14.203 +      }
  14.204 +    }
  14.205 +  end
  14.206 +}
  14.207 +
  14.208 +tabs[#tabs+1] = {
  14.209 +  name = "initiators",
  14.210 +  label = _"Initiators",
  14.211 +  content = function()
  14.212 +    execute.view{ module = "member", view = "_list", params = { members_selector = initiative:get_reference_selector("initiating_members") } }
  14.213 +  end
  14.214 +}
  14.215 +
  14.216 +tabs[#tabs+1] = {
  14.217 +  name = "drafts",
  14.218 +  label = _"Old drafts",
  14.219 +  content = function()
  14.220 +    execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } }
  14.221 +  end
  14.222 +}
  14.223 +
  14.224 +tabs[#tabs+1] = {
  14.225 +  name = "details",
  14.226 +  label = _"Details",
  14.227 +  content = function()
  14.228 +    ui.form{
  14.229 +      attr = { class = "vertical" },
  14.230 +      record = initiative,
  14.231 +      readonly = true,
  14.232 +      content = function()
  14.233 +        ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
  14.234 +        ui.field.text{
  14.235 +          label = _"Created at",
  14.236 +          value = tostring(initiative.created)
  14.237 +        }
  14.238 +        ui.field.text{
  14.239 +          label = _"Created at",
  14.240 +          value = format.timestamp(initiative.created)
  14.241 +        }
  14.242 +        ui.field.date{ label = _"Revoked at", name = "revoked" }
  14.243 +        ui.field.boolean{ label = _"Admitted", name = "admitted" }
  14.244 +      end
  14.245 +    }
  14.246 +  end
  14.247  }
  14.248  
  14.249  
  14.250 +ui.tabs(tabs)
  14.251 +
    15.1 --- a/app/main/issue/_list.lua	Fri Dec 25 12:00:00 2009 +0100
    15.2 +++ b/app/main/issue/_list.lua	Sat Jan 02 12:00:00 2010 +0100
    15.3 @@ -11,9 +11,13 @@
    15.4    filters = {
    15.5      {
    15.6        type = "boolean",
    15.7 -      name = "any",
    15.8 -      label = _"Any",
    15.9 -      selector_modifier = function()  end
   15.10 +      name = "open",
   15.11 +      label = _"Open",
   15.12 +      selector_modifier = function(selector, value)
   15.13 +        if value then
   15.14 +          selector:add_where("issue.closed ISNULL")
   15.15 +        end
   15.16 +      end
   15.17      },
   15.18      {
   15.19        type = "boolean",
   15.20 @@ -150,14 +154,14 @@
   15.21                    name = "max_potential_support",
   15.22                    label = _"Max potential support",
   15.23                    selector_modifier = function(selector)
   15.24 -                    selector:add_order_by("(SELECT max(supporter_count) FROM initiative WHERE initiative.issue_id = issue.id)")
   15.25 +                    selector:add_order_by("(SELECT max(supporter_count) FROM initiative WHERE initiative.issue_id = issue.id) DESC")
   15.26                    end
   15.27                  },
   15.28                  {
   15.29                    name = "max_support",
   15.30                    label = _"Max support",
   15.31                    selector_modifier = function(selector)
   15.32 -                    selector:add_order_by("(SELECT max(satisfied_supporter_count) FROM initiative WHERE initiative.issue_id = issue.id)")
   15.33 +                    selector:add_order_by("(SELECT max(satisfied_supporter_count) FROM initiative WHERE initiative.issue_id = issue.id) DESC")
   15.34                    end
   15.35                  },
   15.36                  {
   15.37 @@ -214,7 +218,16 @@
   15.38                          {
   15.39                            label = _"State",
   15.40                            content = function(record)
   15.41 -                            ui.field.issue_state{ value = record.state }
   15.42 +                            if record.state == "voting" then
   15.43 +                              ui.link{
   15.44 +                                content = _"Voting",
   15.45 +                                module = "vote",
   15.46 +                                view = "list",
   15.47 +                                params = { issue_id = record.id }
   15.48 +                              }
   15.49 +                            else
   15.50 +                              ui.field.issue_state{ value = record.state }
   15.51 +                            end
   15.52                            end
   15.53                          },
   15.54                          {
    16.1 --- a/app/main/issue/_show_head.lua	Fri Dec 25 12:00:00 2009 +0100
    16.2 +++ b/app/main/issue/_show_head.lua	Sat Jan 02 12:00:00 2010 +0100
    16.3 @@ -85,3 +85,24 @@
    16.4  }
    16.5  
    16.6  --  ui.twitter("http://example.com/t" .. tostring(issue.id))
    16.7 +
    16.8 +
    16.9 +if issue.state == 'voting' then
   16.10 +  ui.container{
   16.11 +    attr = { class = "voting_active_info" },
   16.12 +    content = function()
   16.13 +      slot.put(_"Voting for this issue is currently running!")
   16.14 +      slot.put(" ")
   16.15 +      ui.link{
   16.16 +        content = function()
   16.17 +          slot.put(_"Vote now")
   16.18 +        end,
   16.19 +        module = "vote",
   16.20 +        view = "list",
   16.21 +        params = { issue_id = issue.id }
   16.22 +      }
   16.23 +    end
   16.24 +  }
   16.25 +  slot.put("<br />")
   16.26 +end
   16.27 +
    17.1 --- a/app/main/member/_show_thumb.lua	Fri Dec 25 12:00:00 2009 +0100
    17.2 +++ b/app/main/member/_show_thumb.lua	Sat Jan 02 12:00:00 2010 +0100
    17.3 @@ -17,16 +17,27 @@
    17.4      ui.container{
    17.5        attr = { class = "flags" },
    17.6        content = function()
    17.7 -        if (issue or initiative) and member.weight > 1 then
    17.8 +        local weight = 0
    17.9 +        if member.weight then
   17.10 +          weight = member.weight
   17.11 +        end
   17.12 +        if member.voter_weight then
   17.13 +          weight = member.voter_weight
   17.14 +        end
   17.15 +        if (issue or initiative) and weight > 1 then
   17.16            local module
   17.17            if issue then
   17.18              module = "interest"
   17.19            elseif initiative then
   17.20 -            module = "supporter"
   17.21 +            if member.voter_weight then
   17.22 +               module = "vote"
   17.23 +            else
   17.24 +              module = "supporter"
   17.25 +            end
   17.26            end
   17.27            ui.link{
   17.28              attr = { title = _"Number of incoming delegations, follow link to see more details" },
   17.29 -            content = _("+ #{weight}", { weight = member.weight - 1 }),
   17.30 +            content = _("+ #{weight}", { weight = weight - 1 }),
   17.31              module = module,
   17.32              view = "show_incoming",
   17.33              params = { 
   17.34 @@ -35,6 +46,39 @@
   17.35                issue_id = issue and issue.id or nil
   17.36              }
   17.37            }
   17.38 +        else
   17.39 +          slot.put("&nbsp;")
   17.40 +        end
   17.41 +        if member.grade then
   17.42 +          ui.container{
   17.43 +            content = function()
   17.44 +              if member.grade > 0 then
   17.45 +                ui.image{
   17.46 +                  attr = { 
   17.47 +                    alt   = _"Voted yes",
   17.48 +                    title = _"Voted yes"
   17.49 +                  },
   17.50 +                  static = "icons/16/thumb_up_green.png"
   17.51 +                }
   17.52 +              elseif member.grade < 0 then
   17.53 +                ui.image{
   17.54 +                  attr = { 
   17.55 +                    alt   = _"Voted no",
   17.56 +                    title = _"Voted no"
   17.57 +                  },
   17.58 +                  static = "icons/16/thumb_down_red.png"
   17.59 +                }
   17.60 +              else
   17.61 +                ui.image{
   17.62 +                  attr = { 
   17.63 +                    alt   = _"Abstention",
   17.64 +                    title = _"Abstention"
   17.65 +                  },
   17.66 +                  static = "icons/16/bullet_yellow.png"
   17.67 +                }
   17.68 +              end
   17.69 +            end
   17.70 +          }
   17.71          end
   17.72          if member.admin then
   17.73            ui.image{
    18.1 --- a/app/main/member/edit.lua	Fri Dec 25 12:00:00 2009 +0100
    18.2 +++ b/app/main/member/edit.lua	Sat Jan 02 12:00:00 2010 +0100
    18.3 @@ -29,7 +29,7 @@
    18.4      ui.field.text{ label = _"Organizational unit", name = "organizational_unit" }
    18.5      ui.field.text{ label = _"Internal posts", name = "internal_posts" }
    18.6      ui.field.text{ label = _"Real name", name = "realname" }
    18.7 -    ui.field.text{ label = _"Birthday", name = "birthday" }
    18.8 +    ui.field.text{ label = _"Birthday" .. " YYYY-MM-DD ", name = "birthday" }
    18.9      ui.field.text{ label = _"Address", name = "address", multiline = true }
   18.10      ui.field.text{ label = _"email", name = "email" }
   18.11      ui.field.text{ label = _"xmpp", name = "xmpp_address" }
    19.1 --- a/app/main/opinion/_action/update.lua	Fri Dec 25 12:00:00 2009 +0100
    19.2 +++ b/app/main/opinion/_action/update.lua	Sat Jan 02 12:00:00 2010 +0100
    19.3 @@ -4,8 +4,15 @@
    19.4  
    19.5  local opinion = Opinion:by_pk(member_id, suggestion_id)
    19.6  
    19.7 +local suggestion = Suggestion:by_id(suggestion_id)
    19.8 +
    19.9 +if not suggestion then
   19.10 +  slot.put_into("error", _"This suggestion has been meanwhile deleted")
   19.11 +  return false
   19.12 +end
   19.13 +
   19.14  -- TODO important m1 selectors returning result _SET_!
   19.15 -local issue = opinion.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec()
   19.16 +local issue = suggestion.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec()
   19.17  
   19.18  if issue.closed then
   19.19    slot.put_into("error", _"This issue is already closed.")
    20.1 --- a/app/main/opinion/_list.lua	Fri Dec 25 12:00:00 2009 +0100
    20.2 +++ b/app/main/opinion/_list.lua	Sat Jan 02 12:00:00 2010 +0100
    20.3 @@ -4,20 +4,32 @@
    20.4    records = opinions_selector:exec(),
    20.5    columns = {
    20.6      {
    20.7 -      label = _"Member login",
    20.8 -      name = "member_login"
    20.9 -    },
   20.10 -    {
   20.11        label = _"Member name",
   20.12        name = "member_name"
   20.13      },
   20.14      {
   20.15        label = _"Degree",
   20.16 -      name = "degree"
   20.17 +      content = function(record)
   20.18 +        if record.degree == -2 then
   20.19 +          slot.put(_"must not")
   20.20 +        elseif record.degree == -1 then
   20.21 +          slot.put(_"should not")
   20.22 +        elseif record.degree == 1 then
   20.23 +          slot.put(_"should")
   20.24 +        elseif record.degree == 2 then
   20.25 +          slot.put(_"must")
   20.26 +        end
   20.27 +      end
   20.28      },
   20.29      {
   20.30 -      label = _"Fulfilled",
   20.31 -      name = "fulfilled"
   20.32 +      label = _"Suggestion currently implemented",
   20.33 +      content = function(record)
   20.34 +        if record.fulfilled then
   20.35 +          slot.put(_"Yes")
   20.36 +        else
   20.37 +          slot.put(_"No")
   20.38 +        end
   20.39 +      end
   20.40      },
   20.41    }
   20.42  }
   20.43 \ No newline at end of file
    21.1 --- a/app/main/suggestion/_action/add.lua	Fri Dec 25 12:00:00 2009 +0100
    21.2 +++ b/app/main/suggestion/_action/add.lua	Sat Jan 02 12:00:00 2010 +0100
    21.3 @@ -4,10 +4,19 @@
    21.4    return false
    21.5  end
    21.6  
    21.7 +local name = param.get("name")
    21.8 +local name = util.trim(name)
    21.9 +
   21.10 +if #name < 3 then
   21.11 +  slot.put_into("error", _"This title is really too short!")
   21.12 +  return false
   21.13 +end
   21.14 +
   21.15  local suggestion = Suggestion:new()
   21.16  
   21.17  suggestion.author_id = app.session.member.id
   21.18 -param.update(suggestion, "name", "description", "initiative_id")
   21.19 +suggestion.name = name
   21.20 +param.update(suggestion, "description", "initiative_id")
   21.21  suggestion:save()
   21.22  
   21.23  -- TODO important m1 selectors returning result _SET_!
    22.1 --- a/app/main/suggestion/show.lua	Fri Dec 25 12:00:00 2009 +0100
    22.2 +++ b/app/main/suggestion/show.lua	Sat Jan 02 12:00:00 2010 +0100
    22.3 @@ -2,6 +2,19 @@
    22.4  
    22.5  slot.put_into("title", encode.html(_"Suggestion for initiative: '#{name}'":gsub("#{name}", suggestion.initiative.name) ))
    22.6  
    22.7 +slot.select("actions", function()
    22.8 +  ui.link{
    22.9 +    content = function()
   22.10 +        ui.image{ static = "icons/16/resultset_previous.png" }
   22.11 +        slot.put(_"Back")
   22.12 +    end,
   22.13 +    module = "initiative",
   22.14 +    view = "show",
   22.15 +    id = suggestion.initiative.id,
   22.16 +    params = { tab = "suggestion" }
   22.17 +  }
   22.18 +end)
   22.19 +
   22.20  ui.form{
   22.21    attr = { class = "vertical" },
   22.22    record = suggestion,
    23.1 --- a/app/main/vote/list.lua	Fri Dec 25 12:00:00 2009 +0100
    23.2 +++ b/app/main/vote/list.lua	Sat Jan 02 12:00:00 2010 +0100
    23.3 @@ -109,13 +109,46 @@
    23.4                      id = "entry_" .. tostring(initiative.id)
    23.5                    },
    23.6                    content = function()
    23.7 -                    ui.link{
    23.8 -                      attr = { class = "clickable" },
    23.9 -                      content = initiative.name,
   23.10 -                      module = "initiative",
   23.11 -                      view = "show",
   23.12 -                      id = initiative.id
   23.13 +                    local initiators = initiative.initiating_members
   23.14 +                    local initiator_names = {}
   23.15 +                    for i, initiator in ipairs(initiators) do
   23.16 +                      initiator_names[#initiator_names+1] = initiator.name
   23.17 +                    end
   23.18 +                    local initiator_names_string = table.concat(initiator_names, ", ")
   23.19 +                    ui.container{
   23.20 +                      attr = { style = "float: right;" },
   23.21 +                      content = function()
   23.22 +                        ui.link{
   23.23 +                          attr = { class = "clickable" },
   23.24 +                          content = _"Show",
   23.25 +                          module = "initiative",
   23.26 +                          view = "show",
   23.27 +                          id = initiative.id
   23.28 +                        }
   23.29 +                        slot.put(" ")
   23.30 +                        ui.link{
   23.31 +                          attr = { class = "clickable", target = "_blank" },
   23.32 +                          content = _"(new window)",
   23.33 +                          module = "initiative",
   23.34 +                          view = "show",
   23.35 +                          id = initiative.id
   23.36 +                        }
   23.37 +                        slot.put(" ")
   23.38 +                        ui.image{ attr = { class = "grabber" }, static = "icons/grabber.png" }
   23.39 +                      end
   23.40                      }
   23.41 +                    slot.put(encode.html(initiative.shortened_name))
   23.42 +                    if #initiators > 1 then
   23.43 +                      ui.container{
   23.44 +                        attr = { style = "font-size: 80%;" },
   23.45 +                        content = _"Initiators" .. ": " .. initiator_names_string
   23.46 +                      }
   23.47 +                    else
   23.48 +                      ui.container{
   23.49 +                        attr = { style = "font-size: 80%;" },
   23.50 +                        content = _"Initiator" .. ": " .. initiator_names_string
   23.51 +                      }
   23.52 +                    end
   23.53                    end
   23.54                  }
   23.55                end
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/app/main/vote/show_incoming.lua	Sat Jan 02 12:00:00 2010 +0100
    24.3 @@ -0,0 +1,19 @@
    24.4 +local initiative = Initiative:by_id(param.get("initiative_id", atom.integer))
    24.5 +local issue = initiative.issue
    24.6 +local member = Member:by_id(param.get("member_id", atom.integer))
    24.7 +
    24.8 +local members_selector = Member:new_selector()
    24.9 +  :join("delegating_voter", nil, "delegating_voter.member_id = member.id")
   24.10 +  :add_where{ "delegating_voter.issue_id = ?", issue.id }
   24.11 +  :add_where{ "delegating_voter.delegate_member_ids[1] = ?", member.id }
   24.12 +  :add_field{ "delegating_voter.weight" }
   24.13 +
   24.14 +execute.view{
   24.15 +  module = "member",
   24.16 +  view = "_list",
   24.17 +  params = { 
   24.18 +    members_selector = members_selector,
   24.19 +    issue = issue,
   24.20 +    trustee = member
   24.21 +  }
   24.22 +}
   24.23 \ No newline at end of file
    25.1 --- a/config/default.lua	Fri Dec 25 12:00:00 2009 +0100
    25.2 +++ b/config/default.lua	Sat Jan 02 12:00:00 2010 +0100
    25.3 @@ -1,5 +1,5 @@
    25.4  config.app_name = "LiquidFeedback"
    25.5 -config.app_version = "beta1"
    25.6 +config.app_version = "beta2"
    25.7  
    25.8  config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)"
    25.9  
   25.10 @@ -7,6 +7,8 @@
   25.11  
   25.12  config.app_service_provider = "Snake Oil<br/>10000 Berlin<br/>Germany"
   25.13  
   25.14 +config.use_terms = "=== Nutzungsbedingungen ===\nAlles ist verboten"
   25.15 +
   25.16  config.member_image_convert_func = {
   25.17    avatar = function(data) return os.pfilter(data, "convert", "jpeg:-", "-thumbnail",   "48x48", "jpeg:-") end,
   25.18    photo =  function(data) return os.pfilter(data, "convert", "jpeg:-", "-thumbnail", "240x240", "jpeg:-") end
   25.19 @@ -17,8 +19,13 @@
   25.20    photo = nil
   25.21  }
   25.22  
   25.23 +config.mail_subject_prefix = "[LiquidFeedback] "
   25.24 +
   25.25  config.fastpath_url_func = nil
   25.26  
   25.27 +config.download_dir = nil
   25.28 +
   25.29 +config.download_use_terms = "=== Nutzungsbedingungen ===\nAlles ist verboten"
   25.30  
   25.31  -- uncomment the following two lines to use C implementations of chosen
   25.32  -- functions and to disable garbage collection during the request, to
   25.33 @@ -45,10 +52,7 @@
   25.34    end
   25.35  end
   25.36  
   25.37 --- 'request.get_relative_baseurl()' should be replaced by the absolute
   25.38 --- base URL of the application, as otherwise HTTP redirects will not be
   25.39 --- standard compliant
   25.40 -request.set_absolute_baseurl(request.get_relative_baseurl())
   25.41 +request.set_absolute_baseurl(config.absolute_base_url)
   25.42  
   25.43  
   25.44  
    26.1 --- a/config/development.lua	Fri Dec 25 12:00:00 2009 +0100
    26.2 +++ b/config/development.lua	Sat Jan 02 12:00:00 2010 +0100
    26.3 @@ -1,6 +1,11 @@
    26.4 +config.absolute_base_url = "http://10.8.33.34/lf/"
    26.5 +
    26.6  execute.config("default")
    26.7  
    26.8  config.formatting_engine_executeables = {
    26.9    rocketwiki= "/opt/rocketwiki/rocketwiki-lqfb",
   26.10    compat = "/opt/rocketwiki/rocketwiki-lqfb-compat"
   26.11  }
   26.12 +
   26.13 +config.mail_from = "LiquidFeedback"
   26.14 +config.mail_reply_to = "liquid-support@localhost"
    27.1 --- a/config/testing.lua	Fri Dec 25 12:00:00 2009 +0100
    27.2 +++ b/config/testing.lua	Sat Jan 02 12:00:00 2010 +0100
    27.3 @@ -1,3 +1,5 @@
    27.4 +config.absolute_base_url = "http://www.public-software-group.org/liquid_feedback_testing/"
    27.5 +
    27.6  execute.config("default")
    27.7  
    27.8  config.formatting_engine_executeables = {
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/env/util/trim.lua	Sat Jan 02 12:00:00 2010 +0100
    28.3 @@ -0,0 +1,3 @@
    28.4 +function util.trim(string)
    28.5 +  return (string:gsub("^%s*", ""):gsub("%s*$", ""))
    28.6 +end
    28.7 \ No newline at end of file
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/locale/help/index.download.de.txt	Sat Jan 02 12:00:00 2010 +0100
    29.3 @@ -0,0 +1,2 @@
    29.4 +=Download der Datenbank=
    29.5 +Im Interesse der Nachvollziehbarkeit ist es jedem Mitglied möglich, eine Kopie der gesamten Datenbank herunterzuladen. Hierbei werden jedoch aus Sicherheits- und Datenschutzgründen bestimmte Informationen (z. B. Passwörter und E-Mail-Adressen) entfernt. Das Dateiformat ist gzip-komprimiertes SQL für die Datenbank PostgreSQL.
    30.1 --- a/locale/help/initiative.show.de.txt	Fri Dec 25 12:00:00 2009 +0100
    30.2 +++ b/locale/help/initiative.show.de.txt	Sat Jan 02 12:00:00 2010 +0100
    30.3 @@ -5,6 +5,6 @@
    30.4  =überarbeiteter Entwurf, Umsetzungsvermerk=
    30.5  Anhand der (klassifizierten und quantifizierten) Anregungen entscheiden die Initiatoren, was sie in einem neuen Entwurf besser dargestellen, ergänzen oder ändern. Der geänderte Entwurf wird den Unterstützern zur Bestätigung vorgelegt. Unterstützer können Anregungen als umgesetzt markieren, wenn die Anregung aus ihrer Sicht (hinreichend) umgesetzt wurde. Die einzelnen Unterstützer können diese Frage durchaus unterschiedlich beurteilen.
    30.6  =wenn du nicht gehört wirst=
    30.7 -Wenn die Initiatoren deine Anregungen aus für dich nicht nachvollziehbaren Gründen nicht berücksichtigen, kannst du natürlich deine Änderungen in einer eigenen Initiative zur Diskussion stellen.
    30.8 +Wenn die Initiatoren deine Anregungen aus für dich nicht nachvollziehbaren Gründen nicht berücksichtigen, kannst du natürlich jederzeit eine eigene Initiative starten.
    30.9  =wenn du diese Initiative ablehnst=
   30.10  Wenn du diese Initiative grundsätzlich ablehnst, solltest du auf dieser Seite gar nichts machen, sondern die Initiative(n), der/denen du positiv gegenüber stehst, unterstützen und/oder deine eigene Initiative zu diesem Thema starten.
    31.1 --- a/locale/translations.de.lua	Fri Dec 25 12:00:00 2009 +0100
    31.2 +++ b/locale/translations.de.lua	Sat Jan 02 12:00:00 2010 +0100
    31.3 @@ -5,6 +5,7 @@
    31.4  ["#{number} Image(s) has been deleted"] = "Es wurde(n) #{number} Bild(er) gelöscht";
    31.5  ["#{number} Image(s) has been updated"] = "Es wurde(n) #{number} Bild(er) aktualisiert";
    31.6  ["(change URL)"] = "(URL ändern)";
    31.7 +["(new window)"] = "(neues Fenster)";
    31.8  ["+ #{weight}"] = "+ #{weight}";
    31.9  ["A-Z"] = "A-Z";
   31.10  ["About"] = "About";
   31.11 @@ -35,10 +36,14 @@
   31.12  ["Autoreject is off."] = "Auto-Ablehnen ist aus";
   31.13  ["Autoreject is on."] = "Auto-Ablehnen ist an";
   31.14  ["Avatar"] = "Avatar";
   31.15 +["Back"] = "Zurück";
   31.16  ["Become a member"] = "Mitglied werden";
   31.17  ["Birthday"] = "Geburtstag";
   31.18  ["Bug report"] = "Fehlerbericht";
   31.19 +["Can't send confirmation email"] = "Bestätigungs-E-Mail kann nicht versendet werden.";
   31.20  ["Cancel"] = "Abbrechen";
   31.21 +["Cancel password reset"] = "Kennwort-Rücksetzung abbrechen";
   31.22 +["Cancel registration"] = "Registration abbrechen";
   31.23  ["Cancelled"] = "Abgebrochen";
   31.24  ["Change area delegation"] = "Delegation für Themengebiet ändern";
   31.25  ["Change global delegation"] = "Globale Delegation ändern";
   31.26 @@ -50,6 +55,9 @@
   31.27  ["Collective opinion"] = "Meinungsbild";
   31.28  ["Commit suggestion"] = "Anregung speichern";
   31.29  ["Compare"] = "Vergleichen";
   31.30 +["Confirm"] = "Bestätigen";
   31.31 +["Confirmation code"] = "Bestätigungscode";
   31.32 +["Confirmation code invalid!"] = "Bestätigungscode ist ungültig!";
   31.33  ["Contacts"] = "Kontakte";
   31.34  ["Content"] = "Inhalt";
   31.35  ["Counting of votes"] = "Auszählung";
   31.36 @@ -75,6 +83,11 @@
   31.37  ["Edit initiative"] = "Initiative bearbeiten";
   31.38  ["Edit my page"] = "Meine Seite bearbeiten";
   31.39  ["Edit my profile"] = "Mein Profil bearbeiten";
   31.40 +["Email address"] = "E-Mail-Adresse";
   31.41 +["Email address confirmation"] = "Bestätigung der E-Mail-Adresse";
   31.42 +["Email address is confirmed now"] = "E-Mail-Adresse ist jetzt bestätigt";
   31.43 +["Email address too short!"] = "E-Mail-Adresse ist zu kurz!";
   31.44 +["Email confirmation request"] = "Bestätigung Deiner E-Mail-Adresse";
   31.45  ["Empty help text: #{id}.#{lang}.txt"] = "Leerer Hilfe-Text: #{id}.#{lang}.txt";
   31.46  ["Error while updating member, database reported:<br /><br /> (#{errormessage})"] = "Fehler beim aktualisieren des Mitglieds, die Datenbank berichtet folgenden Fehler:<br /><br /> (#{errormessage})";
   31.47  ["External memberships"] = "Externe Mitgliedschaften";
   31.48 @@ -88,12 +101,15 @@
   31.49  ["Global delegation"] = "Globale Delegation";
   31.50  ["Global delegation active"] = "Globale Delegation aktiv";
   31.51  ["Half frozen at"] = "Halb eingefroren am/um";
   31.52 +["Hello "] = "Hallo ";
   31.53  ["Help for: #{text}"] = "Hilfe zu: #{text}";
   31.54  ["Hide"] = "Verstecken";
   31.55  ["Hide this help message"] = "Diesen Hilfetext ausblenden";
   31.56  ["Home"] = "Startseite";
   31.57 +["I accept the terms of use by checking the following checkbox:"] = "Ich akzeptiere die Nutzungsbedingungen durch Auswahl der folgenden Ankreuzbox:";
   31.58  ["Id"] = "Id";
   31.59  ["Ident number"] = "Ident-Nummer";
   31.60 +["If this link is not working, please open following url in your web browser:\n\n"] = "Sollte der Link nicht funktionieren, öffne bitte die folgenden URL in Deinem Web-Browser:\n\n";
   31.61  ["Images"] = "Bilder";
   31.62  ["In discussion"] = "In Diskussion";
   31.63  ["Incoming delegations"] = "Eingehende Delegationen";
   31.64 @@ -103,6 +119,7 @@
   31.65  ["Initiative successfully updated"] = "Initiative erfolgreich aktualisiert";
   31.66  ["Initiative: '#{name}'"] = "Initiative: '#{name}'";
   31.67  ["Initiatives"] = "Initiativen";
   31.68 +["Initiator"] = "Initiator";
   31.69  ["Initiators"] = "Initiatoren";
   31.70  ["Interest not existant"] = "Interesse existiert nicht";
   31.71  ["Interest removed"] = "Interesse entfernt";
   31.72 @@ -112,7 +129,6 @@
   31.73  ["Internal posts"] = "Interne Ämter";
   31.74  ["Invalid username or password!"] = "Ungültiger Benutzername oder Kennwort";
   31.75  ["Invite code"] = "Invite-Code";
   31.76 -["Invite code valid!"] = "Invite-Code gültig!";
   31.77  ["Issue"] = "Thema";
   31.78  ["Issue ##{id}"] = "Issue ##{id}";
   31.79  ["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})";
   31.80 @@ -127,7 +143,6 @@
   31.81  ["License"] = "Lizenz";
   31.82  ["Locked?"] = "Gesperrt?";
   31.83  ["Login"] = "Anmeldung";
   31.84 -["Login is available"] = "Anmeldename ist verfügbar";
   31.85  ["Login name"] = "Anmeldename";
   31.86  ["Login successful!"] = "Anmeldung erfolgreich";
   31.87  ["Logout"] = "Abmelden";
   31.88 @@ -159,7 +174,6 @@
   31.89  ["Mobile phone"] = "Mobiltelefon";
   31.90  ["My opinion"] = "Meine Meinung";
   31.91  ["Name"] = "Name";
   31.92 -["Name is available"] = "Name ist verfügbar";
   31.93  ["New"] = "Neu";
   31.94  ["New draft has been added to initiative"] = "Neuer Entwurf wurde der Initiative hinzugefügt";
   31.95  ["New draft revision"] = "Neue Revision des Entwurfs";
   31.96 @@ -182,13 +196,19 @@
   31.97  ["Old password"] = "Altes Kennwort";
   31.98  ["Old password is wrong"] = "Das alte Kennwort ist falsch";
   31.99  ["Oldest"] = "Älteste";
  31.100 +["On that page please enter the confirmation code:\n\n"] = "Auf dieser Seite gebe bitte folgenden Bestätigungscode ein:\n\n";
  31.101 +["On that page please enter the reset code:\n\n"] = "Auf dieser Seite gebe bitte den folgenden Rücksetzcode ein:\n\n";
  31.102  ["One issue"] = "Ein Thema";
  31.103  ["One issue you are interested in"] = "Ein Thema, das Dich interessiert";
  31.104 +["One step back"] = "Ein Schritt zurück";
  31.105 +["Open"] = "Offen";
  31.106  ["Order by"] = "Sortieren nach";
  31.107  ["Organizational unit"] = "Organisationseinheit";
  31.108  ["Outgoing delegations"] = "Ausgehende Delegationen";
  31.109  ["Password"] = "Kennwort";
  31.110  ["Password (repeat)"] = "Kennwort (wiederholen)";
  31.111 +["Password has been reset successfully"] = "Kennwort wurde erfolgreich zurückgesetzt";
  31.112 +["Password reset request"] = "Kennwort-Rücksetzung anfordern";
  31.113  ["Passwords don't match!"] = "Kennwörter stimmen nicht überein!";
  31.114  ["Passwords must consist of at least 8 characters!"] = "Das Kennwort muß zumindest 8 Zeichen lang sein!";
  31.115  ["Phone"] = "Telefon";
  31.116 @@ -196,7 +216,12 @@
  31.117  ["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."] = "Bitte wähle einen Anmeldenamen. Dieser wird anderen nicht gezeigt und nur von Dir zum Anmelden verwendet. Groß- und Kleinschreibung wird berücksichtigt.";
  31.118  ["Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you. You CAN'T change this name later, so please choose it wisely!"] = "Bitte wähle einen Namen, z. B. Deinen Real- oder Nicknamen. Dieser wird anderen angezeigt um Dich zu identifizieren. Du kannst Deinen Namen später NICHT ändern, wähle ihn also weise!";
  31.119  ["Please choose a password and enter it twice. The password is case sensitive."] = "Bitte wähle ein Kennwort und gebe es zweimal ein. Groß- und Kleinschreibung wird berücksichtigt.";
  31.120 +["Please confirm your email address by clicking the following link:\n\n"] = "Bitte bestätige Deine E-Mail-Adresse, indem Du den folgenden Link anklickst:\n\n";
  31.121 +["Please enter the email reset code you have received:"] = "Bitte gib den Rücksetzcode ein, den Du erhalten hast:";
  31.122  ["Please enter the invite code you've received."] = "Bitte gib den Invite-Code ein, den Du erhalten hast.";
  31.123 +["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."] = "Bitte gib Deine E-Mail-Adresse ein. Diese Adresse wird für automatische Benachrichtigungen (wenn Du diese anforderst) sowie zum Zurücksetzen des Kennworts verwendet. Diese Adresse wird nicht veröffentlicht. Nach Abschluß der Registration wirst Du eine E-Mail mit einem Link zum Bestätigen der Adresse erhalten.";
  31.124 +["Please enter your login name. You will receive an email with a link to reset your password."] = "Bitte gib Deinen Anmeldenamen ein. Du wirst eine E-Mail mit einem Link zum Zurücksetzen des Kennworts erhalten.";
  31.125 +["Please enter your new password twice."] = "Bitte gib Dein neues Kennwort zweimal ein:";
  31.126  ["Policy"] = "Regelwerk";
  31.127  ["Population"] = "Grundgesamtheit";
  31.128  ["Posts"] = "Ämter";
  31.129 @@ -220,6 +245,11 @@
  31.130  ["Remove my membership"] = "Mitgliedschaft aufgeben";
  31.131  ["Remove my support from this initiative"] = "Meine Unterstützung der Initiative entziehen";
  31.132  ["Repeat new password"] = "Neues Kennwort wiederholen";
  31.133 +["Request password reset link"] = "Link zum Rücksetzen des Kennworts anfordern";
  31.134 +["Reset code"] = "Rücksetzcode";
  31.135 +["Reset code is invalid!"] = "Rücksetzcode ist ungültig";
  31.136 +["Reset link has been send for this member"] = "Rücksetz-Link wurde versendet";
  31.137 +["Reset password"] = "Kennwort zurücksetzen";
  31.138  ["Revoke"] = "Widerrufen";
  31.139  ["Revoked at"] = "Zurückgezogen am/um";
  31.140  ["Save"] = "Speichern";
  31.141 @@ -235,6 +265,7 @@
  31.142  ["Set delegation for Issue ##{number} in Area '#{area_name}'"] = "Delegation für Thema ##{number} im Themenbereich '#{area_name}' festlegen";
  31.143  ["Set global delegation"] = "Globale Delegation festlegen";
  31.144  ["Set issue delegation"] = "Delegation für Thema festlegen";
  31.145 +["Set new password"] = "Neues Kennwort setzen";
  31.146  ["Show"] = "Zeige";
  31.147  ["Show active members"] = "Zeige aktive Mitglieder";
  31.148  ["Show all initiatives"] = "Zeige alle Initiativen";
  31.149 @@ -245,10 +276,16 @@
  31.150  ["Show member"] = "Mitglied anzeigen";
  31.151  ["Software"] = "Software";
  31.152  ["Some JavaScript based functions (voting in particular) will not work.\nFor this beta, please use a current version of Firefox, Safari, Opera(?), Konqueror or another (more) standard compliant browser.\nAlternative access without JavaScript will be available soon."] = "Einige auf JavaScript basierende Funktionen (insbesondere der Abstimmung) sind nicht benutzbar.\nFür diese Beta verwende bitte eine aktuelle Version von Firefox, Safari, Opera(?), Konqueror oder einen anderen (mehr) den Standards entsprechenden Browser.\nEin alternativer Zugriff ohne JavaScript wird bald zur Verfügung stehen.";
  31.153 +["Sorry, but there is not confirmed email address for your account. Please contact the administrator or support."] = "Sorry, aber für diesen Account ist keine bestätigte E-Mail-Adresse hinterlegt. Bitte wende Dich an den Administrator oder den Support.";
  31.154  ["Sorry, you have reached your personal flood limit. Please be slower..."] = "Sorry, Du hast Dein persönliches Flood-Limit erreicht. Bitte sei langsamer...";
  31.155  ["Sorry, your contingent for creating initiatives has been used up. Please try again later."] = "Sorry, Dein Antragskontingent ist zur Zeit ausgeschöpft. Bitte versuche es später erneut!";
  31.156  ["State"] = "Zustand";
  31.157  ["Statement"] = "Statement";
  31.158 +["Step 1/5: Invite code"] = "Schritt 1/5: Invite-Code";
  31.159 +["Step 2/5: Email address"] = "Schritt 2/5: E-Mail-Adresse";
  31.160 +["Step 3/5: Username"] = "Schritt 3/5: Benutzername";
  31.161 +["Step 4/5: Login name"] = "Schritt 4/5: Anmeldename";
  31.162 +["Step 5/5: Terms of use and password"] = "Schritt 5/5: Nutzungsbedingungen und Kennwort";
  31.163  ["Suggestion"] = "Anregung";
  31.164  ["Suggestion currently implemented"] = "Anregung zur Zeit umgesetzt";
  31.165  ["Suggestion currently not implemented"] = "Anregung zur Zeit nicht umgesetzt";
  31.166 @@ -258,13 +295,19 @@
  31.167  ["Support this initiative"] = "Diese Initiative unterstützen";
  31.168  ["Supported initiatives"] = "Unterstützte Initiativen";
  31.169  ["Supporter"] = "Unterstützer";
  31.170 +["Terms accepted"] = "Bedingungen akzeptiert";
  31.171  ["That's me!"] = "Das bin ich";
  31.172  ["The code you've entered is invalid"] = "Den Code den Du eingeben hast ist nicht gültig!";
  31.173  ["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht";
  31.174  ["This issue is already closed."] = "Das Thema ist schon geschlossen.";
  31.175  ["This issue is already frozen."] = "Das Thema ist schon eingefroren";
  31.176  ["This login is already taken, please choose another one!"] = "Dieser Anmeldename ist bereits vergeben, bitte wähle einen anderen!";
  31.177 +["This login is too short!"] = "Dieser Anmeldename ist zu kurz!";
  31.178  ["This name is already taken, please choose another one!"] = "Dieser Name ist bereits vergeben, bitte wähle einen anderen!";
  31.179 +["This name is really too short!"] = "Dieser Name ist wirklich zu kurz!";
  31.180 +["This suggestion has been meanwhile deleted"] = "Diese Anregung wurde zwischenzeitlich gelöscht";
  31.181 +["This title is really too short!"] = "Dieser Titel ist wirklich zu kurz!";
  31.182 +["This username is too short!"] = "Dieser Benutzername ist zu kurz!";
  31.183  ["Time left"] = "Restzeit";
  31.184  ["Title (80 chars max)"] = "Title (max. 80 Zeichen)";
  31.185  ["Traditional wiki syntax"] = "Traditionaller Wiki-Syntax";
  31.186 @@ -277,9 +320,15 @@
  31.187  ["Vote now"] = "Jetzt abstimmen";
  31.188  ["Vote now/later"] = "Jetzt/später abstimmen";
  31.189  ["Voted"] = "Abgestimmt";
  31.190 +["Voted no"] = "Mit Nein gestimmt";
  31.191 +["Voted proposal"] = "Abgestimmte Vorlage";
  31.192 +["Voted yes"] = "Mit Ja gestimmt";
  31.193 +["Voter"] = "Abstimmende";
  31.194  ["Voting"] = "Abstimmung";
  31.195  ["Voting for this issue has already begun."] = "Die Abstimmung für dieses Thema hat schon begonnen.";
  31.196 +["Voting for this issue is currently running!"] = "Über dieses Thema wird gerade abgestimmt!";
  31.197  ["Voting has not started yet."] = "Die Abstimmung hat noch nicht begonnen.";
  31.198 +["Voting proposal"] = "Abstimmungsvorlage";
  31.199  ["Voting requests"] = "Abstimmanträge";
  31.200  ["Voting time"] = "Zeit für die Abstimmung";
  31.201  ["Website"] = "Webseite";
  31.202 @@ -292,6 +341,7 @@
  31.203  ["You didn't saved any member as contact yet."] = "Du hast noch kein Mitglied als Kontakt gespeichert!";
  31.204  ["You have saved this member as contact"] = "Du hast das Mitglied als Kontakt gespeichert";
  31.205  ["You have saved this member as contact."] = "Du hast das Mitglied als Kontakt gespeichert.";
  31.206 +["You have to accept the terms of use to complete registration."] = "Du musst die Nutzungsbedingungen akzeptieren um die Registration abzuschliessen.";
  31.207  ["You need to be logged in, to use this system."] = "Du musst eingeloggt sein, um das System zu benutzen";
  31.208  ["You've successfully registered and you can login now with your login and password!"] = "Du hast Dich erfolgreich registriert und kannst Dich jetzt mit Deinen Benutzernamen und Kennwort anmelden!";
  31.209  ["Your are interested"] = "Du bist interessiert";
  31.210 @@ -325,5 +375,6 @@
  31.211  ["not implemented"] = "nicht umgesetzt";
  31.212  ["should"] = "soll";
  31.213  ["should not"] = "soll nicht";
  31.214 +["to reset your password please click on the following link:\n\n"] = "um Dein Kennwort zurückzusetzen klicke bitte den folgenden Link an:\n\n";
  31.215  ["xmpp"] = "Jabber (XMPP)";
  31.216  }
    32.1 --- a/model/initiative.lua	Fri Dec 25 12:00:00 2009 +0100
    32.2 +++ b/model/initiative.lua	Sat Jan 02 12:00:00 2010 +0100
    32.3 @@ -101,10 +101,29 @@
    32.4    ref                   = 'supporting_members_snapshot'
    32.5  }
    32.6  
    32.7 +
    32.8  function Initiative:get_search_selector(search_string)
    32.9    return self:new_selector()
   32.10 +    :join("draft", nil, "draft.initiative_id = initiative.id")
   32.11      :add_field( {'"highlight"("initiative"."name", ?)', search_string }, "name_highlighted")
   32.12 -    :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string }
   32.13 +    :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?) OR "draft"."text_search_data" @@ "text_search_query"(?)', search_string, search_string }
   32.14 +    :add_group_by('"initiative"."id"')
   32.15 +    :add_group_by('"initiative"."issue_id"')
   32.16 +    :add_group_by('"initiative"."name"')
   32.17 +    :add_group_by('"initiative"."discussion_url"')
   32.18 +    :add_group_by('"initiative"."created"')
   32.19 +    :add_group_by('"initiative"."revoked"')
   32.20 +    :add_group_by('"initiative"."admitted"')
   32.21 +    :add_group_by('"initiative"."supporter_count"')
   32.22 +    :add_group_by('"initiative"."informed_supporter_count"')
   32.23 +    :add_group_by('"initiative"."satisfied_supporter_count"')
   32.24 +    :add_group_by('"initiative"."satisfied_informed_supporter_count"')
   32.25 +    :add_group_by('"initiative"."positive_votes"')
   32.26 +    :add_group_by('"initiative"."negative_votes"')
   32.27 +    :add_group_by('"initiative"."agreed"')
   32.28 +    :add_group_by('"initiative"."rank"')
   32.29 +    :add_group_by('"initiative"."text_search_data"')
   32.30 +    :add_group_by('"issue"."population"')
   32.31  end
   32.32  
   32.33  function Member:get_search_selector(search_string)
    33.1 --- a/model/issue.lua	Fri Dec 25 12:00:00 2009 +0100
    33.2 +++ b/model/issue.lua	Sat Jan 02 12:00:00 2010 +0100
    33.3 @@ -97,6 +97,19 @@
    33.4    ref                   = 'interested_members_snapshot'
    33.5  }
    33.6  
    33.7 +Issue:add_reference{
    33.8 +  mode                  = 'mm',
    33.9 +  to                    = "Member",
   33.10 +  this_key              = 'id',
   33.11 +  that_key              = 'id',
   33.12 +  connected_by_table    = 'direct_voter',
   33.13 +  connected_by_this_key = 'issue_id',
   33.14 +  connected_by_that_key = 'member_id',
   33.15 +  ref                   = 'direct_voters'
   33.16 +}
   33.17 +
   33.18 +
   33.19 +
   33.20  function Issue:get_state_name_for_state(value)
   33.21    local state_name_table = {
   33.22      new          = _"New",
   33.23 @@ -112,8 +125,24 @@
   33.24  function Issue:get_search_selector(search_string)
   33.25    return self:new_selector()
   33.26      :join('"initiative"', nil, '"initiative"."issue_id" = "issue"."id"')
   33.27 -    :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string }
   33.28 -    :set_distinct()
   33.29 +    :join('"draft"', nil, '"draft"."initiative_id" = "initiative"."id"')
   33.30 +    :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?) OR "draft"."text_search_data" @@ "text_search_query"(?)', search_string, search_string }
   33.31 +    :add_group_by('"issue"."id"')
   33.32 +    :add_group_by('"issue"."area_id"')
   33.33 +    :add_group_by('"issue"."policy_id"')
   33.34 +    :add_group_by('"issue"."created"')
   33.35 +    :add_group_by('"issue"."accepted"')
   33.36 +    :add_group_by('"issue"."half_frozen"')
   33.37 +    :add_group_by('"issue"."fully_frozen"')
   33.38 +    :add_group_by('"issue"."closed"')
   33.39 +    :add_group_by('"issue"."ranks_available"')
   33.40 +    :add_group_by('"issue"."snapshot"')
   33.41 +    :add_group_by('"issue"."latest_snapshot_event"')
   33.42 +    :add_group_by('"issue"."population"')
   33.43 +    :add_group_by('"issue"."vote_now"')
   33.44 +    :add_group_by('"issue"."vote_later"')
   33.45 +    :add_group_by('"issue"."voter_count"')
   33.46 +    --:set_distinct()
   33.47  end
   33.48  
   33.49  function Issue.object_get:state()
    34.1 --- a/model/member.lua	Fri Dec 25 12:00:00 2009 +0100
    34.2 +++ b/model/member.lua	Sat Jan 02 12:00:00 2010 +0100
    34.3 @@ -277,3 +277,28 @@
    34.4      :add_where("active")
    34.5  end
    34.6  
    34.7 +function Member.object:set_notify_email(notify_email)
    34.8 +  local expiry = db:query("SELECT now() + '7 days'::interval as expiry", "object").expiry
    34.9 +  self.notify_email_unconfirmed = notify_email
   34.10 +  self.notify_email_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
   34.11 +  self.notify_email_secret_expiry = expiry
   34.12 +  local content = slot.use_temporary(function()
   34.13 +    slot.put(_"Hello " .. self.name .. ",\n\n")
   34.14 +    slot.put(_"Please confirm your email address by clicking the following link:\n\n")
   34.15 +    slot.put(config.absolute_base_url .. "index/confirm_notify_email.html?secret=" .. self.notify_email_secret .. "\n\n")
   34.16 +    slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
   34.17 +    slot.put(config.absolute_base_url .. "index/confirm_notify_email.html\n\n")
   34.18 +    slot.put(_"On that page please enter the confirmation code:\n\n")
   34.19 +    slot.put(self.notify_email_secret .. "\n\n")
   34.20 +  end)
   34.21 +  local success = net.send_mail{
   34.22 +    envelope_from = config.mail_envelope_from,
   34.23 +    from          = config.mail_from,
   34.24 +    reply_to      = config.mail_reply_to,
   34.25 +    to            = self.notify_email_unconfirmed,
   34.26 +    subject       = config.mail_subject_prefix .. _"Email confirmation request",
   34.27 +    content_type  = "text/plain; charset=UTF-8",
   34.28 +    content       = content
   34.29 +  }
   34.30 +  return success
   34.31 +end
    35.1 Binary file static/icons/16/bullet_yellow.png has changed
    36.1 Binary file static/icons/16/database_save.png has changed
    37.1 Binary file static/icons/16/key_forgot.png has changed
    38.1 Binary file static/icons/16/resultset_previous.png has changed
    39.1 Binary file static/icons/grabber.png has changed
    40.1 --- a/static/js/dragdrop.js	Fri Dec 25 12:00:00 2009 +0100
    40.2 +++ b/static/js/dragdrop.js	Sat Jan 02 12:00:00 2010 +0100
    40.3 @@ -59,7 +59,7 @@
    40.4        element.addEventListener("mousedown", function(event) {
    40.5          event.target.style.cursor = "move";
    40.6          dragElement(event.currentTarget, function(element, dropX, dropY) {
    40.7 -          event.target.style.cursor = null;
    40.8 +          event.target.style.cursor = '';
    40.9            elementDropped(element, dropX, dropY);
   40.10          });
   40.11          event.preventDefault();
    41.1 --- a/static/style.css	Fri Dec 25 12:00:00 2009 +0100
    41.2 +++ b/static/style.css	Sat Jan 02 12:00:00 2010 +0100
    41.3 @@ -792,9 +792,10 @@
    41.4    margin-right: 0.7em;
    41.5  }
    41.6  
    41.7 -.draft_updated_info {
    41.8 -  border: 2px solid #faa;
    41.9 -  background-color: #fee;
   41.10 +.draft_updated_info,
   41.11 +.voting_active_info {
   41.12 +  background-color: #fec;
   41.13 +  border: 2px solid #b96;
   41.14    padding: 1ex;
   41.15  }
   41.16  
   41.17 @@ -806,7 +807,8 @@
   41.18    line-height: 120%;
   41.19  }
   41.20  
   41.21 -.help {
   41.22 +.help,
   41.23 +.use_terms {
   41.24    border: 1px solid #bcd;
   41.25    background-color: #def;
   41.26    color: #000;
   41.27 @@ -918,3 +920,8 @@
   41.28  #voting a.clickable {
   41.29    cursor: pointer;
   41.30  }
   41.31 +
   41.32 +#voting .grabber {
   41.33 +  vertical-align: middle;
   41.34 +  cursor: move;
   41.35 +}
   41.36 \ No newline at end of file

Impressum / About Us