# HG changeset patch
# User bsw/jbe
# Date 1262430000 -3600
# Node ID 8d91bccab0bf09588155ed63a964e9525efd701b
# Parent afd9f769c7ae803fe9401a212c480be513c01a69
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
diff -r afd9f769c7ae -r 8d91bccab0bf LICENSE
--- a/LICENSE Fri Dec 25 12:00:00 2009 +0100
+++ b/LICENSE Sat Jan 02 12:00:00 2010 +0100
@@ -21,7 +21,7 @@
3rd party license information:
-The icons used in Liquid Feedback (except national flags) are from Silk
+The icons used in LiquidFeedback (except national flags) are from Silk
icon set 1.3 by Mark James. [ http://www.famfamfam.com/lab/icons/silk/ ]
His work is licensed under a Creative Commons Attribution 2.5 License.
-[ http://creativecommons.org/licenses/by/2.5/ ]
\ No newline at end of file
+[ http://creativecommons.org/licenses/by/2.5/ ]
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/_filter/21_auth.lua
--- a/app/main/_filter/21_auth.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/_filter/21_auth.lua Sat Jan 02 12:00:00 2010 +0100
@@ -6,6 +6,10 @@
or request.get_view() == "register"
or request.get_action() == "register"
or request.get_view() == "about"
+ or request.get_view() == "reset_password"
+ or request.get_action() == "reset_password"
+ or request.get_view() == "confirm_notify_email"
+ or request.get_action() == "confirm_notify_email"
)
)
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/_filter_view/30_navigation.lua
--- a/app/main/_filter_view/30_navigation.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/_filter_view/30_navigation.lua Sat Jan 02 12:00:00 2010 +0100
@@ -21,6 +21,14 @@
}
ui.link{
content = function()
+ ui.image{ static = "icons/16/key_forgot.png" }
+ slot.put(_"Reset password")
+ end,
+ module = 'index',
+ view = 'reset_password'
+ }
+ ui.link{
+ content = function()
ui.image{ static = "icons/16/information.png" }
slot.put('About / Impressum')
end,
@@ -79,15 +87,6 @@
view = 'about'
}
- ui.link{
- content = function()
- ui.image{ static = "icons/16/bug.png" }
- slot.put(_"Bug report")
- end,
- external = "http://trac.public-software-group.org/projects/lf" --/newticket?description=" .. encode.url_part("\n\n\n\nReport for: " .. os.getenv("REQUEST_URI") )
- }
-
-
if app.session.member.admin then
slot.put(" ")
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/_action/confirm_notify_email.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/index/_action/confirm_notify_email.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,18 @@
+local secret = param.get("secret")
+
+local member = Member:new_selector()
+ :add_where{ "notify_email_secret = ?", secret }
+ :add_where("notify_email_secret_expiry > now()")
+ :optional_object_mode()
+ :exec()
+
+if member then
+ member.notify_email = member.notify_email_unconfirmed
+ member.notify_email_unconfirmed = nil
+ member.notify_email_secret = nil
+ member:save()
+ slot.put_into("notice", _"Email address is confirmed now")
+else
+ slot.put_into("error", _"Confirmation code invalid!")
+ return false
+end
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/_action/register.lua
--- a/app/main/index/_action/register.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/index/_action/register.lua Sat Jan 02 12:00:00 2010 +0100
@@ -10,10 +10,20 @@
return false
end
-local name = param.get("name")
+local notify_email = param.get("notify_email")
-if invite_code and not name then
- slot.put_into("notice", _"Invite code valid!")
+if invite_code and not notify_email then
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = { code = invite_code.code }
+ }
+ return false
+end
+
+if #notify_email < 5 then
+ slot.put_into("error", _"Email address too short!")
request.redirect{
mode = "redirect",
module = "index",
@@ -23,13 +33,47 @@
return false
end
+local name = param.get("name")
+
+if notify_email and not name then
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email
+ }
+ }
+ return false
+end
+
+name = util.trim(name)
+
+if #name < 3 then
+ slot.put_into("error", _"This username is too short!")
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email
+ }
+ }
+ return false
+end
+
if Member:by_name(name) then
slot.put_into("error", _"This name is already taken, please choose another one!")
request.redirect{
mode = "redirect",
module = "index",
view = "register",
- params = { code = invite_code.code }
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email
+ }
}
return false
end
@@ -37,13 +81,30 @@
local login = param.get("login")
if name and not login then
- slot.put_into("notice", _"Name is available")
request.redirect{
mode = "redirect",
module = "index",
view = "register",
params = {
code = invite_code.code,
+ notify_email = notify_email,
+ name = name
+ }
+ }
+ return false
+end
+
+login = util.trim(login)
+
+if #login < 3 then
+ slot.put_into("error", _"This login is too short!")
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email,
name = name
}
}
@@ -58,23 +119,57 @@
view = "register",
params = {
code = invite_code.code,
+ notify_email = notify_email,
name = name
}
}
return false
end
+local use_terms_accepted = param.get("use_terms_accepted", atom.boolean)
+
+if login and use_terms_accepted == nil then
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email,
+ name = name,
+ login = login
+ }
+ }
+ return false
+end
+
+if use_terms_accepted ~= true then
+ slot.put_into("error", _"You have to accept the terms of use to complete registration.")
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email,
+ name = name,
+ login = login
+ }
+ }
+ return false
+end
+
local password1 = param.get("password1")
local password2 = param.get("password2")
if login and not password1 then
- slot.put_into("notice", _"Login is available")
request.redirect{
mode = "redirect",
module = "index",
view = "register",
params = {
code = invite_code.code,
+ notify_email = notify_email,
name = name,
login = login
}
@@ -90,6 +185,7 @@
view = "register",
params = {
code = invite_code.code,
+ notify_email = notify_email,
name = name,
login = login
}
@@ -105,6 +201,7 @@
view = "register",
params = {
code = invite_code.code,
+ notify_email = notify_email,
name = name,
login = login
}
@@ -116,6 +213,24 @@
member.login = login
member.name = name
+
+local success = member:set_notify_email(notify_email)
+if not success then
+ slot.put_into("error", _"Can't send confirmation email")
+ request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "register",
+ params = {
+ code = invite_code.code,
+ notify_email = notify_email,
+ name = name,
+ login = login
+ }
+ }
+ return
+end
+
member:set_password(password1)
member:save()
@@ -125,8 +240,8 @@
slot.put_into("notice", _"You've successfully registered and you can login now with your login and password!")
- request.redirect{
- mode = "redirect",
- module = "index",
- view = "login",
- }
+request.redirect{
+ mode = "redirect",
+ module = "index",
+ view = "login",
+}
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/_action/reset_password.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/index/_action/reset_password.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,74 @@
+local secret = param.get("secret")
+
+if not secret then
+
+ local member = Member:new_selector()
+ :add_where{ "login = ?", param.get("login") }
+ :add_where("password_reset_secret ISNULL OR password_reset_secret_expiry < now()")
+ :optional_object_mode()
+ :exec()
+
+ if member then
+ if not member.notify_email then
+ slot.put_into("error", _"Sorry, but there is not confirmed email address for your account. Please contact the administrator or support.")
+ return false
+ end
+ member.password_reset_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
+ local expiry = db:query("SELECT now() + '1 days'::interval as expiry", "object").expiry
+ member.password_reset_secret_expiry = expiry
+ member:save()
+ local content = slot.use_temporary(function()
+ slot.put(_"Hello " .. member.name .. ",\n\n")
+ slot.put(_"to reset your password please click on the following link:\n\n")
+ slot.put(config.absolute_base_url .. "index/reset_password.html?secret=" .. member.password_reset_secret .. "\n\n")
+ slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
+ slot.put(config.absolute_base_url .. "index/reset_password.html\n\n")
+ slot.put(_"On that page please enter the reset code:\n\n")
+ slot.put(member.password_reset_secret .. "\n\n")
+ end)
+ local success = net.send_mail{
+ envelope_from = config.mail_envelope_from,
+ from = config.mail_from,
+ reply_to = config.mail_reply_to,
+ to = member.notify_email,
+ subject = config.mail_subject_prefix .. _"Password reset request",
+ content_type = "text/plain; charset=UTF-8",
+ content = content
+ }
+ end
+
+ slot.put_into("notice", _"Reset link has been send for this member")
+
+else
+ local member = Member:new_selector()
+ :add_where{ "password_reset_secret = ?", secret }
+ :add_where{ "password_reset_secret_expiry > now()" }
+ :optional_object_mode()
+ :exec()
+
+ if not member then
+ slot.put_into("error", _"Reset code is invalid!")
+ return false
+ end
+
+ local password1 = param.get("password1")
+ local password2 = param.get("password2")
+
+ if password1 ~= password2 then
+ slot.put_into("error", _"Passwords don't match!")
+ return false
+ end
+
+ if #password1 < 8 then
+ slot.put_into("error", _"Passwords must consist of at least 8 characters!")
+ return false
+ end
+
+ member:set_password(password1)
+ member.password_reset_secret = nil
+ member.password_reset_secret_expiry = nil
+ member:save()
+
+ slot.put_into("notice", _"Password has been reset successfully")
+
+end
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/confirm_notify_email.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/index/confirm_notify_email.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,22 @@
+slot.put_into("title", _"Email address confirmation")
+
+ui.form{
+ attr = { class = "vertical" },
+ module = "index",
+ action = "confirm_notify_email",
+ routing = {
+ ok = {
+ mode = "redirect",
+ module = "index",
+ view = "index"
+ }
+ },
+ content = function()
+ ui.field.text{
+ label = _"Confirmation code",
+ name = "secret",
+ value = param.get("secret")
+ }
+ ui.submit{ text = _"Confirm" }
+ end
+}
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/download.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/index/download.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,60 @@
+if not config.download_dir then
+ error("feature not enabled")
+end
+
+slot.put_into("title", _"Download database export")
+
+slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/cancel.png" }
+ slot.put(_"Cancel")
+ end,
+ module = "index",
+ view = "index"
+ }
+end)
+
+util.help("index.download", _"Download")
+
+ui.container{
+ attr = { class = "wiki use_terms" },
+ content = function()
+ slot.put(format.wiki_text(config.download_use_terms))
+ end
+}
+
+
+local file_list = os.listdir(config.download_dir)
+
+local tmp = {}
+for i, filename in ipairs(file_list) do
+ if not filename:find("^%.") then
+ tmp[#tmp+1] = filename
+ end
+end
+
+local file_list = tmp
+
+table.sort(file_list, function(a, b) return a > b end)
+
+ui.list{
+ records = file_list,
+ columns = {
+ {
+ content = function(filename)
+ slot.put(encode.html(filename))
+ end
+ },
+ {
+ content = function(filename)
+ ui.link{
+ content = _"Download",
+ module = "index",
+ view = "download_file",
+ params = { filename = filename }
+ }
+ end
+ }
+ }
+}
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/download_file.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/index/download_file.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,15 @@
+if not config.download_dir then
+ error("feature not enabled")
+end
+
+local filename = param.get("filename")
+
+local file = assert(io.open(encode.file_path(config.download_dir, filename)), "file not found")
+
+print('Content-type: application/octet-stream')
+print('Content-disposition: attachment; filename=' .. filename)
+print('')
+
+io.stdout:write(file:read("*a"))
+
+exit()
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/index.lua
--- a/app/main/index/index.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/index/index.lua Sat Jan 02 12:00:00 2010 +0100
@@ -75,6 +75,16 @@
view = "change_password"
}
+ if config.download_dir then
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/database_save.png" }
+ slot.put(_"Download")
+ end,
+ module = "index",
+ view = "download"
+ }
+ end
end)
local lang = locale.get("lang")
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/register.lua
--- a/app/main/index/register.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/index/register.lua Sat Jan 02 12:00:00 2010 +0100
@@ -1,32 +1,26 @@
slot.put_into("title", _"Registration")
-slot.select("actions", function()
- ui.link{
- content = function()
- ui.image{ static = "icons/16/cancel.png" }
- slot.put(_"Cancel")
- end,
- module = "index",
- view = "index"
- }
-end)
local code = param.get("code")
+local notify_email = param.get("notify_email")
local name = param.get("name")
local login = param.get("login")
+slot.put_into("title", " (")
ui.form{
- attr = { class = "login" },
+ attr = { class = "vertical" },
module = 'index',
action = 'register',
params = {
code = code,
+ notify_email = notify_email,
name = name,
login = login
},
content = function()
if not code then
+ slot.put_into("title", _"Step 1/5: Invite code")
ui.tag{
tag = "p",
content = _"Please enter the invite code you've received."
@@ -34,9 +28,48 @@
ui.field.text{
label = _'Invite code',
name = 'code',
+ value = param.get("invite")
+ }
+
+ elseif not notify_email then
+ slot.put_into("title", _"Step 2/5: Email address")
+ slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/resultset_previous.png" }
+ slot.put(_"One step back")
+ end,
+ module = "index",
+ view = "register",
+ params = {
+ }
+ }
+ end)
+ ui.tag{
+ tag = "p",
+ 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."
+ }
+ ui.field.text{
+ label = _'Email address',
+ name = 'notify_email',
+ value = param.get("notify_email")
}
elseif not name then
+ slot.put_into("title", _"Step 3/5: Username")
+ slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/resultset_previous.png" }
+ slot.put(_"One step back")
+ end,
+ module = "index",
+ view = "register",
+ params = {
+ code = code
+ }
+ }
+ end)
ui.tag{
tag = "p",
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!"
@@ -48,6 +81,21 @@
}
elseif not login then
+ slot.put_into("title", _"Step 4/5: Login name")
+ slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/resultset_previous.png" }
+ slot.put(_"One step back")
+ end,
+ module = "index",
+ view = "register",
+ params = {
+ code = code,
+ notify_email = notify_email
+ }
+ }
+ end)
ui.tag{
tag = "p",
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."
@@ -59,18 +107,54 @@
}
else
+ slot.put_into("title", _"Step 5/5: Terms of use and password")
+ slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/resultset_previous.png" }
+ slot.put(_"One step back")
+ end,
+ module = "index",
+ view = "register",
+ params = {
+ code = code,
+ notify_email = notify_email,
+ name = name,
+ }
+ }
+ end)
+ ui.container{
+ attr = { class = "wiki use_terms" },
+ content = function()
+ slot.put(format.wiki_text(config.use_terms))
+ end
+ }
+ slot.put("
")
+ ui.field.text{
+ label = _'Email address',
+ value = param.get("notify_email"),
+ readonly = true
+ }
ui.field.text{
label = _'Name',
- name = 'name',
value = param.get("name"),
readonly = true
}
ui.field.text{
label = _'Login name',
- name = 'login',
value = param.get("login"),
readonly = true
}
+
+ ui.tag{
+ tag = "p",
+ content = _"I accept the terms of use by checking the following checkbox:"
+ }
+ ui.field.boolean{
+ label = _"Terms accepted",
+ name = "use_terms_accepted",
+ }
+
ui.tag{
tag = "p",
content = _"Please choose a password and enter it twice. The password is case sensitive."
@@ -90,6 +174,18 @@
text = _'Register'
}
+ slot.put_into("title", ")")
+ slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/cancel.png" }
+ slot.put(_"Cancel registration")
+ end,
+ module = "index",
+ view = "index"
+ }
+ end)
+
end
}
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/index/reset_password.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/index/reset_password.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,81 @@
+slot.put_into("title", _"Reset password")
+
+slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/cancel.png" }
+ slot.put(_"Cancel password reset")
+ end,
+ module = "index",
+ view = "index"
+ }
+end)
+
+
+local secret = param.get("secret")
+
+if not secret then
+ ui.tag{
+ tag = 'p',
+ content = _'Please enter your login name. You will receive an email with a link to reset your password.'
+ }
+ ui.form{
+ attr = { class = "vertical" },
+ module = "index",
+ action = "reset_password",
+ routing = {
+ ok = {
+ mode = "redirect",
+ module = "index",
+ view = "index"
+ }
+ },
+ content = function()
+ ui.field.text{
+ label = "Login",
+ name = "login"
+ }
+ ui.submit{ text = _"Request password reset link" }
+ end
+ }
+
+else
+
+ ui.form{
+ attr = { class = "vertical" },
+ module = "index",
+ action = "reset_password",
+ routing = {
+ ok = {
+ mode = "redirect",
+ module = "index",
+ view = "index"
+ }
+ },
+ content = function()
+ ui.tag{
+ tag = 'p',
+ content = _'Please enter the email reset code you have received:'
+ }
+ ui.field.text{
+ label = _"Reset code",
+ name = "secret",
+ value = secret
+ }
+ ui.tag{
+ tag = 'p',
+ content = _'Please enter your new password twice.'
+ }
+ ui.field.password{
+ label = "New password",
+ name = "password1"
+ }
+ ui.field.password{
+ label = "New password (repeat)",
+ name = "password2"
+ }
+ ui.submit{ text = _"Set new password" }
+ end
+ }
+
+end
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/initiative/_action/create.lua
--- a/app/main/initiative/_action/create.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/initiative/_action/create.lua Sat Jan 02 12:00:00 2010 +0100
@@ -29,6 +29,15 @@
area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec()
end
+local name = param.get("name")
+
+local name = util.trim(name)
+
+if #name < 3 then
+ slot.put_into("error", _"This name is really too short!")
+ return false
+end
+
local initiative = Initiative:new()
if not issue then
@@ -38,11 +47,9 @@
issue:save()
end
-
-
initiative.issue_id = issue.id
-
-param.update(initiative, "name", "discussion_url")
+initiative.name = name
+param.update(initiative, "discussion_url")
initiative:save()
local draft = Draft:new()
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/initiative/show.lua
--- a/app/main/initiative/show.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/initiative/show.lua Sat Jan 02 12:00:00 2010 +0100
@@ -188,10 +188,19 @@
end
-ui.tabs{
+local current_draft_name = _"Current draft"
+if initiative.issue.half_frozen then
+ current_draft_name = _"Voting proposal"
+end
+
+if initiative.issue.state == "finished" then
+ current_draft_name = _"Voted proposal"
+end
+
+local tabs = {
{
name = "current_draft",
- label = _"Current draft",
+ label = current_draft_name,
content = function()
if initiator then
ui.link{
@@ -206,108 +215,136 @@
end
execute.view{ module = "draft", view = "_show", params = { draft = initiative.current_draft } }
end
- },
- {
- name = "suggestion",
- label = _"Suggestions",
- content = function()
- execute.view{
- module = "suggestion",
- view = "_list",
- params = {
- initiative = initiative,
- suggestions_selector = initiative:get_reference_selector("suggestions")
- }
- }
- slot.put("
")
- if not initiative.issue.fully_frozen and not initiative.issue.closed then
- ui.link{
- content = function()
- ui.image{ static = "icons/16/comment_add.png" }
- slot.put(_"Add new suggestion")
- end,
- attr = { onclick = "document.getElementById('add_suggestion_form').style.display='block';return(false)" },
- static = "#"
- }
- end
- end
- },
- {
- name = "satisfied_supporter",
- label = _"Supporter",
+ }
+}
+
+if initiative.issue.ranks_available then
+ tabs[#tabs+1] = {
+ name = "voter",
+ label = _"Voter",
content = function()
execute.view{
module = "member",
view = "_list",
params = {
initiative = initiative,
- members_selector = initiative:get_reference_selector("supporting_members_snapshot")
- :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
- :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")
- :add_field("direct_interest_snapshot.weight")
- :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
- :add_where("direct_supporter_snapshot.satisfied")
- }
- }
- end
- },
- {
- name = "supporter",
- label = _"Potential supporter",
- content = function()
- execute.view{
- module = "member",
- view = "_list",
- params = {
- initiative = initiative,
- members_selector = initiative:get_reference_selector("supporting_members_snapshot")
- :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
- :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")
- :add_field("direct_interest_snapshot.weight")
- :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
- :add_where("NOT direct_supporter_snapshot.satisfied")
+ members_selector = initiative.issue:get_reference_selector("direct_voters")
+ :left_join("vote", nil, { "vote.initiative_id = ? AND vote.member_id = member.id", initiative.id })
+ :add_field("direct_voter.weight as voter_weight")
+ :add_field("coalesce(vote.grade, 0) as grade")
}
}
end
- },
- {
- name = "initiators",
- label = _"Initiators",
- content = function()
- execute.view{ module = "member", view = "_list", params = { members_selector = initiative:get_reference_selector("initiating_members") } }
- end
- },
- {
- name = "drafts",
- label = _"Old drafts",
- content = function()
- execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } }
- end
- },
- {
- name = "details",
- label = _"Details",
- content = function()
- ui.form{
- attr = { class = "vertical" },
- record = initiative,
- readonly = true,
+ }
+end
+
+tabs[#tabs+1] = {
+ name = "suggestion",
+ label = _"Suggestions",
+ content = function()
+ execute.view{
+ module = "suggestion",
+ view = "_list",
+ params = {
+ initiative = initiative,
+ suggestions_selector = initiative:get_reference_selector("suggestions")
+ }
+ }
+ slot.put("
")
+ if not initiative.issue.fully_frozen and not initiative.issue.closed then
+ ui.link{
content = function()
- ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
- ui.field.text{
- label = _"Created at",
- value = tostring(initiative.created)
- }
- ui.field.text{
- label = _"Created at",
- value = format.timestamp(initiative.created)
- }
- ui.field.date{ label = _"Revoked at", name = "revoked" }
- ui.field.boolean{ label = _"Admitted", name = "admitted" }
- end
+ ui.image{ static = "icons/16/comment_add.png" }
+ slot.put(_"Add new suggestion")
+ end,
+ attr = { onclick = "document.getElementById('add_suggestion_form').style.display='block';return(false)" },
+ static = "#"
}
end
- },
+ end
+}
+
+tabs[#tabs+1] = {
+ name = "satisfied_supporter",
+ label = _"Supporter",
+ content = function()
+ execute.view{
+ module = "member",
+ view = "_list",
+ params = {
+ initiative = initiative,
+ members_selector = initiative:get_reference_selector("supporting_members_snapshot")
+ :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
+ :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")
+ :add_field("direct_interest_snapshot.weight")
+ :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
+ :add_where("direct_supporter_snapshot.satisfied")
+ }
+ }
+ end
+}
+
+tabs[#tabs+1] = {
+ name = "supporter",
+ label = _"Potential supporter",
+ content = function()
+ execute.view{
+ module = "member",
+ view = "_list",
+ params = {
+ initiative = initiative,
+ members_selector = initiative:get_reference_selector("supporting_members_snapshot")
+ :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
+ :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")
+ :add_field("direct_interest_snapshot.weight")
+ :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
+ :add_where("NOT direct_supporter_snapshot.satisfied")
+ }
+ }
+ end
+}
+
+tabs[#tabs+1] = {
+ name = "initiators",
+ label = _"Initiators",
+ content = function()
+ execute.view{ module = "member", view = "_list", params = { members_selector = initiative:get_reference_selector("initiating_members") } }
+ end
+}
+
+tabs[#tabs+1] = {
+ name = "drafts",
+ label = _"Old drafts",
+ content = function()
+ execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } }
+ end
+}
+
+tabs[#tabs+1] = {
+ name = "details",
+ label = _"Details",
+ content = function()
+ ui.form{
+ attr = { class = "vertical" },
+ record = initiative,
+ readonly = true,
+ content = function()
+ ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
+ ui.field.text{
+ label = _"Created at",
+ value = tostring(initiative.created)
+ }
+ ui.field.text{
+ label = _"Created at",
+ value = format.timestamp(initiative.created)
+ }
+ ui.field.date{ label = _"Revoked at", name = "revoked" }
+ ui.field.boolean{ label = _"Admitted", name = "admitted" }
+ end
+ }
+ end
}
+ui.tabs(tabs)
+
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/issue/_list.lua
--- a/app/main/issue/_list.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/issue/_list.lua Sat Jan 02 12:00:00 2010 +0100
@@ -11,9 +11,13 @@
filters = {
{
type = "boolean",
- name = "any",
- label = _"Any",
- selector_modifier = function() end
+ name = "open",
+ label = _"Open",
+ selector_modifier = function(selector, value)
+ if value then
+ selector:add_where("issue.closed ISNULL")
+ end
+ end
},
{
type = "boolean",
@@ -150,14 +154,14 @@
name = "max_potential_support",
label = _"Max potential support",
selector_modifier = function(selector)
- selector:add_order_by("(SELECT max(supporter_count) FROM initiative WHERE initiative.issue_id = issue.id)")
+ selector:add_order_by("(SELECT max(supporter_count) FROM initiative WHERE initiative.issue_id = issue.id) DESC")
end
},
{
name = "max_support",
label = _"Max support",
selector_modifier = function(selector)
- selector:add_order_by("(SELECT max(satisfied_supporter_count) FROM initiative WHERE initiative.issue_id = issue.id)")
+ selector:add_order_by("(SELECT max(satisfied_supporter_count) FROM initiative WHERE initiative.issue_id = issue.id) DESC")
end
},
{
@@ -214,7 +218,16 @@
{
label = _"State",
content = function(record)
- ui.field.issue_state{ value = record.state }
+ if record.state == "voting" then
+ ui.link{
+ content = _"Voting",
+ module = "vote",
+ view = "list",
+ params = { issue_id = record.id }
+ }
+ else
+ ui.field.issue_state{ value = record.state }
+ end
end
},
{
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/issue/_show_head.lua
--- a/app/main/issue/_show_head.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/issue/_show_head.lua Sat Jan 02 12:00:00 2010 +0100
@@ -85,3 +85,24 @@
}
-- ui.twitter("http://example.com/t" .. tostring(issue.id))
+
+
+if issue.state == 'voting' then
+ ui.container{
+ attr = { class = "voting_active_info" },
+ content = function()
+ slot.put(_"Voting for this issue is currently running!")
+ slot.put(" ")
+ ui.link{
+ content = function()
+ slot.put(_"Vote now")
+ end,
+ module = "vote",
+ view = "list",
+ params = { issue_id = issue.id }
+ }
+ end
+ }
+ slot.put("
")
+end
+
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/member/_show_thumb.lua
--- a/app/main/member/_show_thumb.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/member/_show_thumb.lua Sat Jan 02 12:00:00 2010 +0100
@@ -17,16 +17,27 @@
ui.container{
attr = { class = "flags" },
content = function()
- if (issue or initiative) and member.weight > 1 then
+ local weight = 0
+ if member.weight then
+ weight = member.weight
+ end
+ if member.voter_weight then
+ weight = member.voter_weight
+ end
+ if (issue or initiative) and weight > 1 then
local module
if issue then
module = "interest"
elseif initiative then
- module = "supporter"
+ if member.voter_weight then
+ module = "vote"
+ else
+ module = "supporter"
+ end
end
ui.link{
attr = { title = _"Number of incoming delegations, follow link to see more details" },
- content = _("+ #{weight}", { weight = member.weight - 1 }),
+ content = _("+ #{weight}", { weight = weight - 1 }),
module = module,
view = "show_incoming",
params = {
@@ -35,6 +46,39 @@
issue_id = issue and issue.id or nil
}
}
+ else
+ slot.put(" ")
+ end
+ if member.grade then
+ ui.container{
+ content = function()
+ if member.grade > 0 then
+ ui.image{
+ attr = {
+ alt = _"Voted yes",
+ title = _"Voted yes"
+ },
+ static = "icons/16/thumb_up_green.png"
+ }
+ elseif member.grade < 0 then
+ ui.image{
+ attr = {
+ alt = _"Voted no",
+ title = _"Voted no"
+ },
+ static = "icons/16/thumb_down_red.png"
+ }
+ else
+ ui.image{
+ attr = {
+ alt = _"Abstention",
+ title = _"Abstention"
+ },
+ static = "icons/16/bullet_yellow.png"
+ }
+ end
+ end
+ }
end
if member.admin then
ui.image{
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/member/edit.lua
--- a/app/main/member/edit.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/member/edit.lua Sat Jan 02 12:00:00 2010 +0100
@@ -29,7 +29,7 @@
ui.field.text{ label = _"Organizational unit", name = "organizational_unit" }
ui.field.text{ label = _"Internal posts", name = "internal_posts" }
ui.field.text{ label = _"Real name", name = "realname" }
- ui.field.text{ label = _"Birthday", name = "birthday" }
+ ui.field.text{ label = _"Birthday" .. " YYYY-MM-DD ", name = "birthday" }
ui.field.text{ label = _"Address", name = "address", multiline = true }
ui.field.text{ label = _"email", name = "email" }
ui.field.text{ label = _"xmpp", name = "xmpp_address" }
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/opinion/_action/update.lua
--- a/app/main/opinion/_action/update.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/opinion/_action/update.lua Sat Jan 02 12:00:00 2010 +0100
@@ -4,8 +4,15 @@
local opinion = Opinion:by_pk(member_id, suggestion_id)
+local suggestion = Suggestion:by_id(suggestion_id)
+
+if not suggestion then
+ slot.put_into("error", _"This suggestion has been meanwhile deleted")
+ return false
+end
+
-- TODO important m1 selectors returning result _SET_!
-local issue = opinion.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec()
+local issue = suggestion.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec()
if issue.closed then
slot.put_into("error", _"This issue is already closed.")
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/opinion/_list.lua
--- a/app/main/opinion/_list.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/opinion/_list.lua Sat Jan 02 12:00:00 2010 +0100
@@ -4,20 +4,32 @@
records = opinions_selector:exec(),
columns = {
{
- label = _"Member login",
- name = "member_login"
- },
- {
label = _"Member name",
name = "member_name"
},
{
label = _"Degree",
- name = "degree"
+ content = function(record)
+ if record.degree == -2 then
+ slot.put(_"must not")
+ elseif record.degree == -1 then
+ slot.put(_"should not")
+ elseif record.degree == 1 then
+ slot.put(_"should")
+ elseif record.degree == 2 then
+ slot.put(_"must")
+ end
+ end
},
{
- label = _"Fulfilled",
- name = "fulfilled"
+ label = _"Suggestion currently implemented",
+ content = function(record)
+ if record.fulfilled then
+ slot.put(_"Yes")
+ else
+ slot.put(_"No")
+ end
+ end
},
}
}
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/suggestion/_action/add.lua
--- a/app/main/suggestion/_action/add.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/suggestion/_action/add.lua Sat Jan 02 12:00:00 2010 +0100
@@ -4,10 +4,19 @@
return false
end
+local name = param.get("name")
+local name = util.trim(name)
+
+if #name < 3 then
+ slot.put_into("error", _"This title is really too short!")
+ return false
+end
+
local suggestion = Suggestion:new()
suggestion.author_id = app.session.member.id
-param.update(suggestion, "name", "description", "initiative_id")
+suggestion.name = name
+param.update(suggestion, "description", "initiative_id")
suggestion:save()
-- TODO important m1 selectors returning result _SET_!
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/suggestion/show.lua
--- a/app/main/suggestion/show.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/suggestion/show.lua Sat Jan 02 12:00:00 2010 +0100
@@ -2,6 +2,19 @@
slot.put_into("title", encode.html(_"Suggestion for initiative: '#{name}'":gsub("#{name}", suggestion.initiative.name) ))
+slot.select("actions", function()
+ ui.link{
+ content = function()
+ ui.image{ static = "icons/16/resultset_previous.png" }
+ slot.put(_"Back")
+ end,
+ module = "initiative",
+ view = "show",
+ id = suggestion.initiative.id,
+ params = { tab = "suggestion" }
+ }
+end)
+
ui.form{
attr = { class = "vertical" },
record = suggestion,
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/vote/list.lua
--- a/app/main/vote/list.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/app/main/vote/list.lua Sat Jan 02 12:00:00 2010 +0100
@@ -109,13 +109,46 @@
id = "entry_" .. tostring(initiative.id)
},
content = function()
- ui.link{
- attr = { class = "clickable" },
- content = initiative.name,
- module = "initiative",
- view = "show",
- id = initiative.id
+ local initiators = initiative.initiating_members
+ local initiator_names = {}
+ for i, initiator in ipairs(initiators) do
+ initiator_names[#initiator_names+1] = initiator.name
+ end
+ local initiator_names_string = table.concat(initiator_names, ", ")
+ ui.container{
+ attr = { style = "float: right;" },
+ content = function()
+ ui.link{
+ attr = { class = "clickable" },
+ content = _"Show",
+ module = "initiative",
+ view = "show",
+ id = initiative.id
+ }
+ slot.put(" ")
+ ui.link{
+ attr = { class = "clickable", target = "_blank" },
+ content = _"(new window)",
+ module = "initiative",
+ view = "show",
+ id = initiative.id
+ }
+ slot.put(" ")
+ ui.image{ attr = { class = "grabber" }, static = "icons/grabber.png" }
+ end
}
+ slot.put(encode.html(initiative.shortened_name))
+ if #initiators > 1 then
+ ui.container{
+ attr = { style = "font-size: 80%;" },
+ content = _"Initiators" .. ": " .. initiator_names_string
+ }
+ else
+ ui.container{
+ attr = { style = "font-size: 80%;" },
+ content = _"Initiator" .. ": " .. initiator_names_string
+ }
+ end
end
}
end
diff -r afd9f769c7ae -r 8d91bccab0bf app/main/vote/show_incoming.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/app/main/vote/show_incoming.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,19 @@
+local initiative = Initiative:by_id(param.get("initiative_id", atom.integer))
+local issue = initiative.issue
+local member = Member:by_id(param.get("member_id", atom.integer))
+
+local members_selector = Member:new_selector()
+ :join("delegating_voter", nil, "delegating_voter.member_id = member.id")
+ :add_where{ "delegating_voter.issue_id = ?", issue.id }
+ :add_where{ "delegating_voter.delegate_member_ids[1] = ?", member.id }
+ :add_field{ "delegating_voter.weight" }
+
+execute.view{
+ module = "member",
+ view = "_list",
+ params = {
+ members_selector = members_selector,
+ issue = issue,
+ trustee = member
+ }
+}
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf config/default.lua
--- a/config/default.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/config/default.lua Sat Jan 02 12:00:00 2010 +0100
@@ -1,5 +1,5 @@
config.app_name = "LiquidFeedback"
-config.app_version = "beta1"
+config.app_version = "beta2"
config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)"
@@ -7,6 +7,8 @@
config.app_service_provider = "Snake Oil
10000 Berlin
Germany"
+config.use_terms = "=== Nutzungsbedingungen ===\nAlles ist verboten"
+
config.member_image_convert_func = {
avatar = function(data) return os.pfilter(data, "convert", "jpeg:-", "-thumbnail", "48x48", "jpeg:-") end,
photo = function(data) return os.pfilter(data, "convert", "jpeg:-", "-thumbnail", "240x240", "jpeg:-") end
@@ -17,8 +19,13 @@
photo = nil
}
+config.mail_subject_prefix = "[LiquidFeedback] "
+
config.fastpath_url_func = nil
+config.download_dir = nil
+
+config.download_use_terms = "=== Nutzungsbedingungen ===\nAlles ist verboten"
-- uncomment the following two lines to use C implementations of chosen
-- functions and to disable garbage collection during the request, to
@@ -45,10 +52,7 @@
end
end
--- 'request.get_relative_baseurl()' should be replaced by the absolute
--- base URL of the application, as otherwise HTTP redirects will not be
--- standard compliant
-request.set_absolute_baseurl(request.get_relative_baseurl())
+request.set_absolute_baseurl(config.absolute_base_url)
diff -r afd9f769c7ae -r 8d91bccab0bf config/development.lua
--- a/config/development.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/config/development.lua Sat Jan 02 12:00:00 2010 +0100
@@ -1,6 +1,11 @@
+config.absolute_base_url = "http://10.8.33.34/lf/"
+
execute.config("default")
config.formatting_engine_executeables = {
rocketwiki= "/opt/rocketwiki/rocketwiki-lqfb",
compat = "/opt/rocketwiki/rocketwiki-lqfb-compat"
}
+
+config.mail_from = "LiquidFeedback"
+config.mail_reply_to = "liquid-support@localhost"
diff -r afd9f769c7ae -r 8d91bccab0bf config/testing.lua
--- a/config/testing.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/config/testing.lua Sat Jan 02 12:00:00 2010 +0100
@@ -1,3 +1,5 @@
+config.absolute_base_url = "http://www.public-software-group.org/liquid_feedback_testing/"
+
execute.config("default")
config.formatting_engine_executeables = {
diff -r afd9f769c7ae -r 8d91bccab0bf env/util/trim.lua
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/env/util/trim.lua Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,3 @@
+function util.trim(string)
+ return (string:gsub("^%s*", ""):gsub("%s*$", ""))
+end
\ No newline at end of file
diff -r afd9f769c7ae -r 8d91bccab0bf locale/help/index.download.de.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/locale/help/index.download.de.txt Sat Jan 02 12:00:00 2010 +0100
@@ -0,0 +1,2 @@
+=Download der Datenbank=
+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.
diff -r afd9f769c7ae -r 8d91bccab0bf locale/help/initiative.show.de.txt
--- a/locale/help/initiative.show.de.txt Fri Dec 25 12:00:00 2009 +0100
+++ b/locale/help/initiative.show.de.txt Sat Jan 02 12:00:00 2010 +0100
@@ -5,6 +5,6 @@
=überarbeiteter Entwurf, Umsetzungsvermerk=
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.
=wenn du nicht gehört wirst=
-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.
+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.
=wenn du diese Initiative ablehnst=
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.
diff -r afd9f769c7ae -r 8d91bccab0bf locale/translations.de.lua
--- a/locale/translations.de.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/locale/translations.de.lua Sat Jan 02 12:00:00 2010 +0100
@@ -5,6 +5,7 @@
["#{number} Image(s) has been deleted"] = "Es wurde(n) #{number} Bild(er) gelöscht";
["#{number} Image(s) has been updated"] = "Es wurde(n) #{number} Bild(er) aktualisiert";
["(change URL)"] = "(URL ändern)";
+["(new window)"] = "(neues Fenster)";
["+ #{weight}"] = "+ #{weight}";
["A-Z"] = "A-Z";
["About"] = "About";
@@ -35,10 +36,14 @@
["Autoreject is off."] = "Auto-Ablehnen ist aus";
["Autoreject is on."] = "Auto-Ablehnen ist an";
["Avatar"] = "Avatar";
+["Back"] = "Zurück";
["Become a member"] = "Mitglied werden";
["Birthday"] = "Geburtstag";
["Bug report"] = "Fehlerbericht";
+["Can't send confirmation email"] = "Bestätigungs-E-Mail kann nicht versendet werden.";
["Cancel"] = "Abbrechen";
+["Cancel password reset"] = "Kennwort-Rücksetzung abbrechen";
+["Cancel registration"] = "Registration abbrechen";
["Cancelled"] = "Abgebrochen";
["Change area delegation"] = "Delegation für Themengebiet ändern";
["Change global delegation"] = "Globale Delegation ändern";
@@ -50,6 +55,9 @@
["Collective opinion"] = "Meinungsbild";
["Commit suggestion"] = "Anregung speichern";
["Compare"] = "Vergleichen";
+["Confirm"] = "Bestätigen";
+["Confirmation code"] = "Bestätigungscode";
+["Confirmation code invalid!"] = "Bestätigungscode ist ungültig!";
["Contacts"] = "Kontakte";
["Content"] = "Inhalt";
["Counting of votes"] = "Auszählung";
@@ -75,6 +83,11 @@
["Edit initiative"] = "Initiative bearbeiten";
["Edit my page"] = "Meine Seite bearbeiten";
["Edit my profile"] = "Mein Profil bearbeiten";
+["Email address"] = "E-Mail-Adresse";
+["Email address confirmation"] = "Bestätigung der E-Mail-Adresse";
+["Email address is confirmed now"] = "E-Mail-Adresse ist jetzt bestätigt";
+["Email address too short!"] = "E-Mail-Adresse ist zu kurz!";
+["Email confirmation request"] = "Bestätigung Deiner E-Mail-Adresse";
["Empty help text: #{id}.#{lang}.txt"] = "Leerer Hilfe-Text: #{id}.#{lang}.txt";
["Error while updating member, database reported:
(#{errormessage})"] = "Fehler beim aktualisieren des Mitglieds, die Datenbank berichtet folgenden Fehler:
(#{errormessage})";
["External memberships"] = "Externe Mitgliedschaften";
@@ -88,12 +101,15 @@
["Global delegation"] = "Globale Delegation";
["Global delegation active"] = "Globale Delegation aktiv";
["Half frozen at"] = "Halb eingefroren am/um";
+["Hello "] = "Hallo ";
["Help for: #{text}"] = "Hilfe zu: #{text}";
["Hide"] = "Verstecken";
["Hide this help message"] = "Diesen Hilfetext ausblenden";
["Home"] = "Startseite";
+["I accept the terms of use by checking the following checkbox:"] = "Ich akzeptiere die Nutzungsbedingungen durch Auswahl der folgenden Ankreuzbox:";
["Id"] = "Id";
["Ident number"] = "Ident-Nummer";
+["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";
["Images"] = "Bilder";
["In discussion"] = "In Diskussion";
["Incoming delegations"] = "Eingehende Delegationen";
@@ -103,6 +119,7 @@
["Initiative successfully updated"] = "Initiative erfolgreich aktualisiert";
["Initiative: '#{name}'"] = "Initiative: '#{name}'";
["Initiatives"] = "Initiativen";
+["Initiator"] = "Initiator";
["Initiators"] = "Initiatoren";
["Interest not existant"] = "Interesse existiert nicht";
["Interest removed"] = "Interesse entfernt";
@@ -112,7 +129,6 @@
["Internal posts"] = "Interne Ämter";
["Invalid username or password!"] = "Ungültiger Benutzername oder Kennwort";
["Invite code"] = "Invite-Code";
-["Invite code valid!"] = "Invite-Code gültig!";
["Issue"] = "Thema";
["Issue ##{id}"] = "Issue ##{id}";
["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})";
@@ -127,7 +143,6 @@
["License"] = "Lizenz";
["Locked?"] = "Gesperrt?";
["Login"] = "Anmeldung";
-["Login is available"] = "Anmeldename ist verfügbar";
["Login name"] = "Anmeldename";
["Login successful!"] = "Anmeldung erfolgreich";
["Logout"] = "Abmelden";
@@ -159,7 +174,6 @@
["Mobile phone"] = "Mobiltelefon";
["My opinion"] = "Meine Meinung";
["Name"] = "Name";
-["Name is available"] = "Name ist verfügbar";
["New"] = "Neu";
["New draft has been added to initiative"] = "Neuer Entwurf wurde der Initiative hinzugefügt";
["New draft revision"] = "Neue Revision des Entwurfs";
@@ -182,13 +196,19 @@
["Old password"] = "Altes Kennwort";
["Old password is wrong"] = "Das alte Kennwort ist falsch";
["Oldest"] = "Älteste";
+["On that page please enter the confirmation code:\n\n"] = "Auf dieser Seite gebe bitte folgenden Bestätigungscode ein:\n\n";
+["On that page please enter the reset code:\n\n"] = "Auf dieser Seite gebe bitte den folgenden Rücksetzcode ein:\n\n";
["One issue"] = "Ein Thema";
["One issue you are interested in"] = "Ein Thema, das Dich interessiert";
+["One step back"] = "Ein Schritt zurück";
+["Open"] = "Offen";
["Order by"] = "Sortieren nach";
["Organizational unit"] = "Organisationseinheit";
["Outgoing delegations"] = "Ausgehende Delegationen";
["Password"] = "Kennwort";
["Password (repeat)"] = "Kennwort (wiederholen)";
+["Password has been reset successfully"] = "Kennwort wurde erfolgreich zurückgesetzt";
+["Password reset request"] = "Kennwort-Rücksetzung anfordern";
["Passwords don't match!"] = "Kennwörter stimmen nicht überein!";
["Passwords must consist of at least 8 characters!"] = "Das Kennwort muß zumindest 8 Zeichen lang sein!";
["Phone"] = "Telefon";
@@ -196,7 +216,12 @@
["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.";
["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!";
["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.";
+["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";
+["Please enter the email reset code you have received:"] = "Bitte gib den Rücksetzcode ein, den Du erhalten hast:";
["Please enter the invite code you've received."] = "Bitte gib den Invite-Code ein, den Du erhalten hast.";
+["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.";
+["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.";
+["Please enter your new password twice."] = "Bitte gib Dein neues Kennwort zweimal ein:";
["Policy"] = "Regelwerk";
["Population"] = "Grundgesamtheit";
["Posts"] = "Ämter";
@@ -220,6 +245,11 @@
["Remove my membership"] = "Mitgliedschaft aufgeben";
["Remove my support from this initiative"] = "Meine Unterstützung der Initiative entziehen";
["Repeat new password"] = "Neues Kennwort wiederholen";
+["Request password reset link"] = "Link zum Rücksetzen des Kennworts anfordern";
+["Reset code"] = "Rücksetzcode";
+["Reset code is invalid!"] = "Rücksetzcode ist ungültig";
+["Reset link has been send for this member"] = "Rücksetz-Link wurde versendet";
+["Reset password"] = "Kennwort zurücksetzen";
["Revoke"] = "Widerrufen";
["Revoked at"] = "Zurückgezogen am/um";
["Save"] = "Speichern";
@@ -235,6 +265,7 @@
["Set delegation for Issue ##{number} in Area '#{area_name}'"] = "Delegation für Thema ##{number} im Themenbereich '#{area_name}' festlegen";
["Set global delegation"] = "Globale Delegation festlegen";
["Set issue delegation"] = "Delegation für Thema festlegen";
+["Set new password"] = "Neues Kennwort setzen";
["Show"] = "Zeige";
["Show active members"] = "Zeige aktive Mitglieder";
["Show all initiatives"] = "Zeige alle Initiativen";
@@ -245,10 +276,16 @@
["Show member"] = "Mitglied anzeigen";
["Software"] = "Software";
["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.";
+["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.";
["Sorry, you have reached your personal flood limit. Please be slower..."] = "Sorry, Du hast Dein persönliches Flood-Limit erreicht. Bitte sei langsamer...";
["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!";
["State"] = "Zustand";
["Statement"] = "Statement";
+["Step 1/5: Invite code"] = "Schritt 1/5: Invite-Code";
+["Step 2/5: Email address"] = "Schritt 2/5: E-Mail-Adresse";
+["Step 3/5: Username"] = "Schritt 3/5: Benutzername";
+["Step 4/5: Login name"] = "Schritt 4/5: Anmeldename";
+["Step 5/5: Terms of use and password"] = "Schritt 5/5: Nutzungsbedingungen und Kennwort";
["Suggestion"] = "Anregung";
["Suggestion currently implemented"] = "Anregung zur Zeit umgesetzt";
["Suggestion currently not implemented"] = "Anregung zur Zeit nicht umgesetzt";
@@ -258,13 +295,19 @@
["Support this initiative"] = "Diese Initiative unterstützen";
["Supported initiatives"] = "Unterstützte Initiativen";
["Supporter"] = "Unterstützer";
+["Terms accepted"] = "Bedingungen akzeptiert";
["That's me!"] = "Das bin ich";
["The code you've entered is invalid"] = "Den Code den Du eingeben hast ist nicht gültig!";
["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht";
["This issue is already closed."] = "Das Thema ist schon geschlossen.";
["This issue is already frozen."] = "Das Thema ist schon eingefroren";
["This login is already taken, please choose another one!"] = "Dieser Anmeldename ist bereits vergeben, bitte wähle einen anderen!";
+["This login is too short!"] = "Dieser Anmeldename ist zu kurz!";
["This name is already taken, please choose another one!"] = "Dieser Name ist bereits vergeben, bitte wähle einen anderen!";
+["This name is really too short!"] = "Dieser Name ist wirklich zu kurz!";
+["This suggestion has been meanwhile deleted"] = "Diese Anregung wurde zwischenzeitlich gelöscht";
+["This title is really too short!"] = "Dieser Titel ist wirklich zu kurz!";
+["This username is too short!"] = "Dieser Benutzername ist zu kurz!";
["Time left"] = "Restzeit";
["Title (80 chars max)"] = "Title (max. 80 Zeichen)";
["Traditional wiki syntax"] = "Traditionaller Wiki-Syntax";
@@ -277,9 +320,15 @@
["Vote now"] = "Jetzt abstimmen";
["Vote now/later"] = "Jetzt/später abstimmen";
["Voted"] = "Abgestimmt";
+["Voted no"] = "Mit Nein gestimmt";
+["Voted proposal"] = "Abgestimmte Vorlage";
+["Voted yes"] = "Mit Ja gestimmt";
+["Voter"] = "Abstimmende";
["Voting"] = "Abstimmung";
["Voting for this issue has already begun."] = "Die Abstimmung für dieses Thema hat schon begonnen.";
+["Voting for this issue is currently running!"] = "Über dieses Thema wird gerade abgestimmt!";
["Voting has not started yet."] = "Die Abstimmung hat noch nicht begonnen.";
+["Voting proposal"] = "Abstimmungsvorlage";
["Voting requests"] = "Abstimmanträge";
["Voting time"] = "Zeit für die Abstimmung";
["Website"] = "Webseite";
@@ -292,6 +341,7 @@
["You didn't saved any member as contact yet."] = "Du hast noch kein Mitglied als Kontakt gespeichert!";
["You have saved this member as contact"] = "Du hast das Mitglied als Kontakt gespeichert";
["You have saved this member as contact."] = "Du hast das Mitglied als Kontakt gespeichert.";
+["You have to accept the terms of use to complete registration."] = "Du musst die Nutzungsbedingungen akzeptieren um die Registration abzuschliessen.";
["You need to be logged in, to use this system."] = "Du musst eingeloggt sein, um das System zu benutzen";
["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!";
["Your are interested"] = "Du bist interessiert";
@@ -325,5 +375,6 @@
["not implemented"] = "nicht umgesetzt";
["should"] = "soll";
["should not"] = "soll nicht";
+["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";
["xmpp"] = "Jabber (XMPP)";
}
diff -r afd9f769c7ae -r 8d91bccab0bf model/initiative.lua
--- a/model/initiative.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/model/initiative.lua Sat Jan 02 12:00:00 2010 +0100
@@ -101,10 +101,29 @@
ref = 'supporting_members_snapshot'
}
+
function Initiative:get_search_selector(search_string)
return self:new_selector()
+ :join("draft", nil, "draft.initiative_id = initiative.id")
:add_field( {'"highlight"("initiative"."name", ?)', search_string }, "name_highlighted")
- :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string }
+ :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?) OR "draft"."text_search_data" @@ "text_search_query"(?)', search_string, search_string }
+ :add_group_by('"initiative"."id"')
+ :add_group_by('"initiative"."issue_id"')
+ :add_group_by('"initiative"."name"')
+ :add_group_by('"initiative"."discussion_url"')
+ :add_group_by('"initiative"."created"')
+ :add_group_by('"initiative"."revoked"')
+ :add_group_by('"initiative"."admitted"')
+ :add_group_by('"initiative"."supporter_count"')
+ :add_group_by('"initiative"."informed_supporter_count"')
+ :add_group_by('"initiative"."satisfied_supporter_count"')
+ :add_group_by('"initiative"."satisfied_informed_supporter_count"')
+ :add_group_by('"initiative"."positive_votes"')
+ :add_group_by('"initiative"."negative_votes"')
+ :add_group_by('"initiative"."agreed"')
+ :add_group_by('"initiative"."rank"')
+ :add_group_by('"initiative"."text_search_data"')
+ :add_group_by('"issue"."population"')
end
function Member:get_search_selector(search_string)
diff -r afd9f769c7ae -r 8d91bccab0bf model/issue.lua
--- a/model/issue.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/model/issue.lua Sat Jan 02 12:00:00 2010 +0100
@@ -97,6 +97,19 @@
ref = 'interested_members_snapshot'
}
+Issue:add_reference{
+ mode = 'mm',
+ to = "Member",
+ this_key = 'id',
+ that_key = 'id',
+ connected_by_table = 'direct_voter',
+ connected_by_this_key = 'issue_id',
+ connected_by_that_key = 'member_id',
+ ref = 'direct_voters'
+}
+
+
+
function Issue:get_state_name_for_state(value)
local state_name_table = {
new = _"New",
@@ -112,8 +125,24 @@
function Issue:get_search_selector(search_string)
return self:new_selector()
:join('"initiative"', nil, '"initiative"."issue_id" = "issue"."id"')
- :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string }
- :set_distinct()
+ :join('"draft"', nil, '"draft"."initiative_id" = "initiative"."id"')
+ :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?) OR "draft"."text_search_data" @@ "text_search_query"(?)', search_string, search_string }
+ :add_group_by('"issue"."id"')
+ :add_group_by('"issue"."area_id"')
+ :add_group_by('"issue"."policy_id"')
+ :add_group_by('"issue"."created"')
+ :add_group_by('"issue"."accepted"')
+ :add_group_by('"issue"."half_frozen"')
+ :add_group_by('"issue"."fully_frozen"')
+ :add_group_by('"issue"."closed"')
+ :add_group_by('"issue"."ranks_available"')
+ :add_group_by('"issue"."snapshot"')
+ :add_group_by('"issue"."latest_snapshot_event"')
+ :add_group_by('"issue"."population"')
+ :add_group_by('"issue"."vote_now"')
+ :add_group_by('"issue"."vote_later"')
+ :add_group_by('"issue"."voter_count"')
+ --:set_distinct()
end
function Issue.object_get:state()
diff -r afd9f769c7ae -r 8d91bccab0bf model/member.lua
--- a/model/member.lua Fri Dec 25 12:00:00 2009 +0100
+++ b/model/member.lua Sat Jan 02 12:00:00 2010 +0100
@@ -277,3 +277,28 @@
:add_where("active")
end
+function Member.object:set_notify_email(notify_email)
+ local expiry = db:query("SELECT now() + '7 days'::interval as expiry", "object").expiry
+ self.notify_email_unconfirmed = notify_email
+ self.notify_email_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" )
+ self.notify_email_secret_expiry = expiry
+ local content = slot.use_temporary(function()
+ slot.put(_"Hello " .. self.name .. ",\n\n")
+ slot.put(_"Please confirm your email address by clicking the following link:\n\n")
+ slot.put(config.absolute_base_url .. "index/confirm_notify_email.html?secret=" .. self.notify_email_secret .. "\n\n")
+ slot.put(_"If this link is not working, please open following url in your web browser:\n\n")
+ slot.put(config.absolute_base_url .. "index/confirm_notify_email.html\n\n")
+ slot.put(_"On that page please enter the confirmation code:\n\n")
+ slot.put(self.notify_email_secret .. "\n\n")
+ end)
+ local success = net.send_mail{
+ envelope_from = config.mail_envelope_from,
+ from = config.mail_from,
+ reply_to = config.mail_reply_to,
+ to = self.notify_email_unconfirmed,
+ subject = config.mail_subject_prefix .. _"Email confirmation request",
+ content_type = "text/plain; charset=UTF-8",
+ content = content
+ }
+ return success
+end
diff -r afd9f769c7ae -r 8d91bccab0bf static/icons/16/bullet_yellow.png
Binary file static/icons/16/bullet_yellow.png has changed
diff -r afd9f769c7ae -r 8d91bccab0bf static/icons/16/database_save.png
Binary file static/icons/16/database_save.png has changed
diff -r afd9f769c7ae -r 8d91bccab0bf static/icons/16/key_forgot.png
Binary file static/icons/16/key_forgot.png has changed
diff -r afd9f769c7ae -r 8d91bccab0bf static/icons/16/resultset_previous.png
Binary file static/icons/16/resultset_previous.png has changed
diff -r afd9f769c7ae -r 8d91bccab0bf static/icons/grabber.png
Binary file static/icons/grabber.png has changed
diff -r afd9f769c7ae -r 8d91bccab0bf static/js/dragdrop.js
--- a/static/js/dragdrop.js Fri Dec 25 12:00:00 2009 +0100
+++ b/static/js/dragdrop.js Sat Jan 02 12:00:00 2010 +0100
@@ -59,7 +59,7 @@
element.addEventListener("mousedown", function(event) {
event.target.style.cursor = "move";
dragElement(event.currentTarget, function(element, dropX, dropY) {
- event.target.style.cursor = null;
+ event.target.style.cursor = '';
elementDropped(element, dropX, dropY);
});
event.preventDefault();
diff -r afd9f769c7ae -r 8d91bccab0bf static/style.css
--- a/static/style.css Fri Dec 25 12:00:00 2009 +0100
+++ b/static/style.css Sat Jan 02 12:00:00 2010 +0100
@@ -792,9 +792,10 @@
margin-right: 0.7em;
}
-.draft_updated_info {
- border: 2px solid #faa;
- background-color: #fee;
+.draft_updated_info,
+.voting_active_info {
+ background-color: #fec;
+ border: 2px solid #b96;
padding: 1ex;
}
@@ -806,7 +807,8 @@
line-height: 120%;
}
-.help {
+.help,
+.use_terms {
border: 1px solid #bcd;
background-color: #def;
color: #000;
@@ -918,3 +920,8 @@
#voting a.clickable {
cursor: pointer;
}
+
+#voting .grabber {
+ vertical-align: middle;
+ cursor: move;
+}
\ No newline at end of file