# HG changeset patch # User bsw # Date 1263121200 -3600 # Node ID 72c5e0ee7c984f7ee7e7e24e126bb5dcbdade600 # Parent 0ee1e0c42d4c5a73dcc44a8373ac5e1ed21fcfd9 Version beta6 Bugfixes: - Security fix: Every user was able to change the discussion URL of an initiative - Creation of new issues in areas without default policies is now possible - Members can now be sorted in different ways - No error when trying to compare a draft with itself - Added missing local statement to variable initialization in app/main/delegation/new.lua - CSS flaw in initiative action bar fixed New features: - Possiblity to invite other users to become initiator - Revokation of initiatives implemented - Number of suggestions, supporters, etc. is shown on corresponding tabs of initiative view - Members can now be sorted by account creation (default sorting is "newest first") - Configuration option to create an automatic discussion link for all issues - First draft of global timeline feature (not accessible via link yet) - Custom stylesheet URL for users marked as developers In area listing the number of closed issues is shown too Renamed "author" field of initiative to "last author" Removed wrongly included file app/main/member/_show_thumb.lua.orig in the distribution Help texts updated diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/_filter_view/34_stylesheet.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/_filter_view/34_stylesheet.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,14 @@ +local value +if app.session.member then + local setting_key = "liquidfeedback_frontend_stylesheet_url" + local setting = Setting:by_pk(app.session.member.id, setting_key) + value = setting and setting.value +end + +if value then + slot.put_into("stylesheet_url", value) +else + slot.put_into("stylesheet_url", config.absolute_base_url .. "static/style.css") +end + +execute.inner() \ No newline at end of file diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/_layout/default.html --- a/app/main/_layout/default.html Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/_layout/default.html Sun Jan 10 12:00:00 2010 +0100 @@ -3,7 +3,7 @@ <!-- WEBMCP SLOTNODIV app_name --> - + diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/area/_list.lua --- a/app/main/area/_list.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/area/_list.lua Sun Jan 10 12:00:00 2010 +0100 @@ -11,6 +11,8 @@ :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.half_frozen NOTNULL AND issue.fully_frozen ISNULL AND issue.closed ISNULL)", "issues_frozen_count") :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed ISNULL)", "issues_voting_count") :add_field({ "(SELECT COUNT(*) FROM issue LEFT JOIN direct_voter ON direct_voter.issue_id = issue.id AND direct_voter.member_id = ? WHERE issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed ISNULL AND direct_voter.member_id ISNULL)", app.session.member.id }, "issues_to_vote_count") + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed NOTNULL)", "issues_finished_count") + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.fully_frozen ISNULL AND issue.closed NOTNULL)", "issues_cancelled_count") ui.order{ name = name, @@ -132,7 +134,33 @@ params = { filter = "frozen", filter_voting = "not_voted" } } end - } + }, + { + label = _"Finished", + field_attr = { style = "text-align: right;" }, + content = function(record) + ui.link{ + text = tostring(record.issues_finished_count), + module = "area", + view = "show", + id = record.id, + params = { filter = "finished", issue_list = "newest" } + } + end + }, + { + label = _"Cancelled", + field_attr = { style = "text-align: right;" }, + content = function(record) + ui.link{ + text = tostring(record.issues_cancelled_count), + module = "area", + view = "show", + id = record.id, + params = { filter = "cancelled", issue_list = "newest" } + } + end + }, } } end diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/delegation/new.lua --- a/app/main/delegation/new.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/delegation/new.lua Sun Jan 10 12:00:00 2010 +0100 @@ -74,7 +74,7 @@ } }, content = function() - records = { + local records = { { id = "-1", name = _"No delegation" diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/draft/_action/add.lua --- a/app/main/draft/_action/add.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/draft/_action/add.lua Sun Jan 10 12:00:00 2010 +0100 @@ -17,26 +17,27 @@ return false end -if Initiator:by_pk(initiative.id, app.session.member.id) then - local draft = Draft:new() - draft.author_id = app.session.member.id - draft.initiative_id = initiative.id - local formatting_engine = param.get("formatting_engine") - local formatting_engine_valid = false - for fe, dummy in pairs(config.formatting_engine_executeables) do - if formatting_engine == fe then - formatting_engine_valid = true - end +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) +if not initiator or not initiator.accepted then + error("access denied") +end + +local draft = Draft:new() +draft.author_id = app.session.member.id +draft.initiative_id = initiative.id +local formatting_engine = param.get("formatting_engine") +local formatting_engine_valid = false +for fe, dummy in pairs(config.formatting_engine_executeables) do + if formatting_engine == fe then + formatting_engine_valid = true end - if not formatting_engine_valid then - error("invalid formatting engine!") - end - draft.formatting_engine = formatting_engine - draft.content = param.get("content") - draft:save() +end +if not formatting_engine_valid then + error("invalid formatting engine!") +end +draft.formatting_engine = formatting_engine +draft.content = param.get("content") +draft:save() - slot.put_into("notice", _"New draft has been added to initiative") +slot.put_into("notice", _"New draft has been added to initiative") -else - error('access denied') -end diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/draft/_show.lua --- a/app/main/draft/_show.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/draft/_show.lua Sun Jan 10 12:00:00 2010 +0100 @@ -6,7 +6,7 @@ readonly = true, content = function() - ui.field.text{ label = _"Author", name = "author_name" } + ui.field.text{ label = _"Last author", name = "author_name" } ui.field.timestamp{ label = _"Created at", name = "created" } ui.container{ attr = { class = "draft_content wiki" }, diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/draft/diff.lua --- a/app/main/draft/diff.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/draft/diff.lua Sun Jan 10 12:00:00 2010 +0100 @@ -3,6 +3,16 @@ local old_draft_id = param.get("old_draft_id", atom.integer) local new_draft_id = param.get("new_draft_id", atom.integer) +if not old_draft_id or not new_draft_id then + slot.put( _"Please choose two versions of the draft to compare") + return +end + +if old_draft_id == new_draft_id then + slot.put( _"Please choose two different versions of the draft to compare") + return +end + if old_draft_id > new_draft_id then local tmp = old_draft_id old_draft_id = new_draft_id diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/index/index.lua --- a/app/main/index/index.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/index/index.lua Sun Jan 10 12:00:00 2010 +0100 @@ -1,12 +1,14 @@ slot.select("title", function() - execute.view{ - module = "member_image", - view = "_show", - params = { - member = app.session.member, - image_type = "avatar" + if app.session.member then + execute.view{ + module = "member_image", + view = "_show", + params = { + member = app.session.member, + image_type = "avatar" + } } - } + end end) slot.select("title", function() @@ -43,48 +45,50 @@ slot.select("actions", function() - ui.link{ - content = function() - ui.image{ static = "icons/16/application_form.png" } - slot.put(_"Edit my profile") - end, - module = "member", - view = "edit" - } - - ui.link{ - content = function() - ui.image{ static = "icons/16/user_gray.png" } - slot.put(_"Upload images") - end, - module = "member", - view = "edit_images" - } - - execute.view{ - module = "delegation", - view = "_show_box" - } - - ui.link{ - content = function() - ui.image{ static = "icons/16/wrench.png" } - slot.put(_"Settings") - end, - module = "member", - view = "settings" - } - - if config.download_dir then + if app.session.member then + ui.link{ + content = function() + ui.image{ static = "icons/16/application_form.png" } + slot.put(_"Edit my profile") + end, + module = "member", + view = "edit" + } + ui.link{ content = function() - ui.image{ static = "icons/16/database_save.png" } - slot.put(_"Download") + ui.image{ static = "icons/16/user_gray.png" } + slot.put(_"Upload images") end, - module = "index", - view = "download" + module = "member", + view = "edit_images" + } + + execute.view{ + module = "delegation", + view = "_show_box" } - end + + ui.link{ + content = function() + ui.image{ static = "icons/16/wrench.png" } + slot.put(_"Settings") + end, + module = "member", + view = "settings" + } + + 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 end) local lang = locale.get("lang") @@ -106,24 +110,25 @@ util.help("index.index", _"Home") - -local selector = Area:new_selector() - :reset_fields() - :add_field("area.id", nil, { "grouped" }) - :add_field("area.name", nil, { "grouped" }) - :add_field("membership.member_id NOTNULL", "is_member", { "grouped" }) - :add_field("count(issue.id)", "issues_to_vote_count") - :add_field("count(interest.member_id)", "interested_issues_to_vote_count") - :join("issue", nil, "issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed ISNULL") - :left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id }) - :add_where{ "direct_voter.member_id ISNULL" } - :left_join("interest", nil, { "interest.issue_id = issue.id AND interest.member_id = ?", app.session.member.id }) - :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ? ", app.session.member.id }) - local areas = {} -for i, area in ipairs(selector:exec()) do - if area.is_member or area.interested_issues_to_vote_count > 0 then - areas[#areas+1] = area +if app.session.member then + local selector = Area:new_selector() + :reset_fields() + :add_field("area.id", nil, { "grouped" }) + :add_field("area.name", nil, { "grouped" }) + :add_field("membership.member_id NOTNULL", "is_member", { "grouped" }) + :add_field("count(issue.id)", "issues_to_vote_count") + :add_field("count(interest.member_id)", "interested_issues_to_vote_count") + :join("issue", nil, "issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed ISNULL") + :left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id }) + :add_where{ "direct_voter.member_id ISNULL" } + :left_join("interest", nil, { "interest.issue_id = issue.id AND interest.member_id = ?", app.session.member.id }) + :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ? ", app.session.member.id }) + + for i, area in ipairs(selector:exec()) do + if area.is_member or area.interested_issues_to_vote_count > 0 then + areas[#areas+1] = area + end end end @@ -190,9 +195,27 @@ } end -execute.view{ - module = "member", - view = "_show", - params = { member = app.session.member } -} +local initiatives_selector = Initiative:new_selector() + :join("initiator", nil, { "initiator.initiative_id = initiative.id AND initiator.member_id = ? AND initiator.accepted ISNULL", app.session.member.id }) + +if initiatives_selector:count() > 0 then + ui.container{ + attr = { style = "font-weight: bold;" }, + content = _"Initiatives that invited you to become initiator:" + } + execute.view{ + module = "initiative", + view = "_list", + params = { initiatives_selector = initiatives_selector } + } +end + + +if app.session.member then + execute.view{ + module = "member", + view = "_show", + params = { member = app.session.member } + } +end diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/accept_invitation.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/_action/accept_invitation.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,32 @@ +local initiator = Initiator:by_pk(param.get_id(), app.session.member.id) + +if not initiator then + slot.put_into("error", _"Sorry, but you are currently not invited") + return +end + +-- TODO important m1 selectors returning result _SET_! +local issue = initiator.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() + +if issue.closed then + slot.put_into("error", _"This issue is already closed.") + return false +elseif issue.half_frozen then + slot.put_into("error", _"This issue is already frozen.") + return false +end + +if initiator.initiative.revoked then + slot.put_into("error", _"This initiative is revoked") + return false +end + +if initiator.accepted then + slot.put_into("error", _"You are already initator") + return +end + +initiator.accepted = true +initiator:save() + +slot.put_into("notice", _"You are now initiator of this initiative") diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/add_initiator.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/_action/add_initiator.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,49 @@ +local initiative = Initiative:by_id(param.get("initiative_id")) +local member = Member:by_id(param.get("member_id")) + +if not member then + slot.put_into("error", _"Please choose a member") + return false +end + +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) +if not initiator or initiator.accepted ~= true then + error("access denied") +end + +-- TODO important m1 selectors returning result _SET_! +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() + +if issue.closed then + slot.put_into("error", _"This issue is already closed.") + return false +elseif issue.half_frozen then + slot.put_into("error", _"This issue is already frozen.") + return false +end + +if initiative.revoked then + slot.put_into("error", _"This initiative is revoked") + return false +end + +local initiator = Initiator:by_pk(initiative.id, member.id) +if initiator then + if initiator.accepted == true then + slot.put_into("error", _"This member is already initiator of this initiative") + elseif initiator.accepted == false then + slot.put_into("error", _"This member has rejected to become initiator of this initiative") + elseif initiator.accepted == nil then + slot.put_into("error", _"This member is already invited to become initiator of this initiative") + end + return false +end + +local initiator = Initiator:new() +initiator.initiative_id = initiative.id +initiator.member_id = member.id +initiator.accepted = nil +initiator:save() + +slot.put_into("notice", _"Member is now invited to be initiator") + diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/add_support.lua --- a/app/main/initiative/_action/add_support.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/initiative/_action/add_support.lua Sun Jan 10 12:00:00 2010 +0100 @@ -11,6 +11,11 @@ return false end +if initiative.revoked then + slot.put_into("error", _"This initiative is revoked") + return false +end + local member = app.session.member local supporter = Supporter:by_pk(initiative.id, member.id) diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/create.lua --- a/app/main/initiative/_action/create.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/initiative/_action/create.lua Sun Jan 10 12:00:00 2010 +0100 @@ -29,6 +29,13 @@ area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec() end +local policy_id = param.get("policy_id", atom.integer) + +if policy_id == -1 then + slot.put_into("error", _"Please choose a policy") + return false +end + local name = param.get("name") local name = util.trim(name) @@ -41,7 +48,6 @@ local initiative = Initiative:new() if not issue then - local policy_id = param.get("policy_id", atom.integer) if not area:get_reference_selector("allowed_policies") :add_where{ "policy.id = ?", policy_id } :optional_object_mode() @@ -80,6 +86,7 @@ local initiator = Initiator:new() initiator.initiative_id = initiative.id initiator.member_id = app.session.member.id +initiator.accepted = true initiator:save() local supporter = Supporter:new() diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/reject_initiator_invitation.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/_action/reject_initiator_invitation.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,24 @@ +local initiative = Initiative:by_id(param.get("initiative_id")) +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) + +-- TODO important m1 selectors returning result _SET_! +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() + +if issue.closed then + slot.put_into("error", _"This issue is already closed.") + return false +elseif issue.half_frozen then + slot.put_into("error", _"This issue is already frozen.") + return false +end + +if initiator.accepted ~= nil then + error("access denied") +end + +initiator.accepted = false +initiator:save() + +slot.put_into("notice", _"Invitation has been refused") + + diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/remove_initiator.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/_action/remove_initiator.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,44 @@ +local initiative = Initiative:by_id(param.get("initiative_id")) +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) + +-- TODO important m1 selectors returning result _SET_! +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() + +if issue.closed then + slot.put_into("error", _"This issue is already closed.") + return false +elseif issue.half_frozen then + slot.put_into("error", _"This issue is already frozen.") + return false +end + +if initiative.revoked then + slot.put_into("error", _"This initiative is revoked") + return false +end + +local initiator_todelete = Initiator:by_pk(initiative.id, param.get("member_id", atom.integer)) + +if not (initiator and initiator.accepted) and not (initiator.member_id == initiator_todelete.member_id) then + error("access denied") +end + +if initiator_todelete.accepted == false and initiator.member_id ~= initiator_todelete.member_id then + error("access denied") +end + +local initiators = initiative + :get_reference_selector("initiators") + :add_where("accepted") + :for_update() + :exec() + +if #initiators > 1 or initiator_todelete.accepted ~= true then + initiator_todelete:destroy() + slot.put_into("notice", _"Member has been removed from initiators") +else + slot.put_into("error", _"Can't remove last initiator") + return false +end + + diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/revoke.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/_action/revoke.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,47 @@ +local initiative = Initiative:by_id(param.get_id()) + +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) +if not initiator or initiator.accepted ~= true then + error("access denied") +end + +-- TODO important m1 selectors returning result _SET_! +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() + +if issue.closed then + slot.put_into("error", _"This issue is already closed.") + return false +elseif issue.half_frozen then + slot.put_into("error", _"This issue is already frozen.") + return false +end + +if initiative.revoked then + slot.put_into("error", _"This initiative is already revoked") + return false +end + +local suggested_initiative_id = param.get("suggested_initiative_id", atom.integer) + +if suggested_initiative_id ~= -1 then + local suggested_initiative = Initiative:by_id(suggested_initiative_id) + if not suggested_initiative then + error("object not found") + end + if initiative.id == suggested_initiative.id then + slot.put_into("error", _"You can't suggest the initiative you are revoking") + return false + end + initiative.suggested_initiative_id = suggested_initiative.id +end + +if not param.get("are_you_sure", atom.boolean) then + slot.put_into("error", _"You have to mark 'Are you sure' to revoke!") + return false +end + +initiative.revoked = "now" +initiative:save() + +slot.put_into("notice", _"Initiative is revoked now") + diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_action/update.lua --- a/app/main/initiative/_action/update.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/initiative/_action/update.lua Sun Jan 10 12:00:00 2010 +0100 @@ -1,4 +1,26 @@ local initiative = Initiative:by_id(param.get_id()) + +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) +if not initiator or not initiator.accepted then + error("access denied") +end + +-- TODO important m1 selectors returning result _SET_! +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() + +if issue.closed then + slot.put_into("error", _"This issue is already closed.") + return false +elseif issue.half_frozen then + slot.put_into("error", _"This issue is already frozen.") + return false +end + +if initiative.revoked then + slot.put_into("error", _"This initiative is revoked") + return false +end + param.update(initiative, "discussion_url") initiative:save() diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/_list.lua --- a/app/main/initiative/_list.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/initiative/_list.lua Sun Jan 10 12:00:00 2010 +0100 @@ -42,13 +42,25 @@ name = "issue_" .. tostring(issue.id) .. "_initiative_list" end -ui.order{ +ui_order = ui.order + +if param.get("no_sort", atom.boolean) then + ui_order = function(args) args.content() end + if issue.ranks_available then + initiatives_selector:add_order_by("initiative.rank, initiative.admitted DESC, vote_ratio(initiative.positive_votes, initiative.negative_votes) DESC, initiative.id") + else + initiatives_selector:add_order_by("initiative.supporter_count::float / issue.population::float DESC, initiative.id") + end +end + +ui_order{ name = name, selector = initiatives_selector, options = order_options, content = function() ui.paginate{ selector = initiatives_selector, + per_page = param.get("per_page", atom.number), content = function() local initiatives = initiatives_selector:exec() local columns = {} @@ -94,7 +106,12 @@ } columns[#columns+1] = { content = function(record) + local link_class + if record.revoked then + link_class = "revoked" + end ui.link{ + attr = { class = link_class }, content = function() local name if record.name_highlighted then diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/add_initiator.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/add_initiator.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,60 @@ +local initiative = Initiative:by_id(param.get("initiative_id")) + +slot.put_into("title", _"Invite an initiator to initiative") + +slot.select("actions", function() + ui.link{ + content = function() + ui.image{ static = "icons/16/cancel.png" } + slot.put(_"Cancel") + end, + module = "initiative", + view = "show", + id = initiative.id, + params = { + tab = "initiators" + } + } +end) + +util.help("initiative.add_initiator", _"Invite an initiator to initiative") + +ui.form{ + attr = { class = "vertical" }, + module = "initiative", + action = "add_initiator", + params = { + initiative_id = initiative.id, + }, + routing = { + ok = { + mode = "redirect", + module = "initiative", + view = "show", + id = initiative.id, + params = { + tab = "initiators", + } + } + }, + content = function() + local records = { + { + id = "-1", + name = _"Choose member" + } + } + local contact_members = app.session.member:get_reference_selector("saved_members"):add_order_by("name"):exec() + for i, record in ipairs(contact_members) do + records[#records+1] = record + end + ui.field.select{ + label = _"Member", + name = "member_id", + foreign_records = records, + foreign_id = "id", + foreign_name = "name" + } + ui.submit{ text = _"Save" } + end +} \ No newline at end of file diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/new.lua --- a/app/main/initiative/new.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/initiative/new.lua Sun Jan 10 12:00:00 2010 +0100 @@ -30,14 +30,17 @@ if issue_id then ui.field.text{ label = _"Issue", value = issue_id } else - local value + tmp = { { id = -1, name = _"Please choose a policy" } } + for i, allowed_policy in ipairs(area.allowed_policies) do + tmp[#tmp+1] = allowed_policy + end ui.field.select{ label = _"Policy", name = "policy_id", - foreign_records = area.allowed_policies, + foreign_records = tmp, foreign_id = "id", foreign_name = "name", - value = area.default_policy.id + value = (area.default_policy or {}).id } end ui.field.text{ label = _"Name", name = "name" } diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/remove_initiator.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/remove_initiator.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,65 @@ +local initiative = Initiative:by_id(param.get("initiative_id")) + +local initiator = Initiator:by_pk(initiative.id, app.session.member.id) +if not initiator or initiator.accepted ~= true then + error("access denied") +end + +slot.put_into("title", _"Remove initiator from initiative") + +slot.select("actions", function() + ui.link{ + content = function() + ui.image{ static = "icons/16/cancel.png" } + slot.put(_"Cancel") + end, + module = "initiative", + view = "show", + id = initiative.id, + params = { + tab = "initiators" + } + } +end) + +util.help("initiative.remove_initiator", _"Remove initiator from initiative") + +ui.form{ + attr = { class = "vertical" }, + module = "initiative", + action = "remove_initiator", + params = { + initiative_id = initiative.id, + }, + routing = { + ok = { + mode = "redirect", + module = "initiative", + view = "show", + id = initiative.id, + params = { + tab = "initiators", + } + } + }, + content = function() + local records = { + { + id = "-1", + name = _"Choose initiator" + } + } + local members = initiative:get_reference_selector("initiating_members"):add_where("accepted OR accepted ISNULL"):exec() + for i, record in ipairs(members) do + records[#records+1] = record + end + ui.field.select{ + label = _"Member", + name = "member_id", + foreign_records = records, + foreign_id = "id", + foreign_name = "name" + } + ui.submit{ text = _"Save" } + end +} \ No newline at end of file diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/revoke.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/initiative/revoke.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,62 @@ +local initiative = Initiative:by_id(param.get_id()) + +slot.put_into("title", _"Revoke initiative") + +slot.select("actions", function() + ui.link{ + content = function() + ui.image{ static = "icons/16/cancel.png" } + slot.put(_"Cancel") + end, + module = "initiative", + view = "show", + id = initiative.id, + params = { + tab = "initiators" + } + } +end) + +util.help("initiative.revoke") + +ui.form{ + attr = { class = "vertical" }, + module = "initiative", + action = "revoke", + id = initiative.id, + routing = { + ok = { + mode = "redirect", + module = "initiative", + view = "show", + id = initiative.id + } + }, + content = function() + local initiatives = app.session.member + :get_reference_selector("supported_initiatives") + :join("issue", nil, "issue.id = initiative.issue_id") + :add_field("'Issue #' || issue.id || ': ' || initiative.name", "myname") + :exec() + + local tmp = { { id = -1, myname = _"Suggest no initiative" }} + for i, initiative in ipairs(initiatives) do + tmp[#tmp+1] = initiative + end + ui.field.select{ + label = _"Suggested initiative", + name = "suggested_initiative_id", + foreign_records = tmp, + foreign_id = "id", + foreign_name = "myname", + value = param.get("suggested_initiative_id", atom.integer) + } + slot.put("") + ui.field.boolean{ + label = _"Are you sure?", + name = "are_you_sure", + } + + ui.submit{ text = _"Revoke initiative" } + end +} \ No newline at end of file diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/initiative/show.lua --- a/app/main/initiative/show.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/initiative/show.lua Sun Jan 10 12:00:00 2010 +0100 @@ -18,17 +18,31 @@ params = { issue = initiative.issue } } +if initiative.revoked then + ui.container{ + attr = { class = "revoked_info" }, + content = function() + slot.put(_("This initiative has been revoked at #{revoked}", { revoked = format.timestamp(initiative.revoked) })) + local suggested_initiative = initiative.suggested_initiative + if suggested_initiative then + slot.put("

") + slot.put(_("The initiators suggest to support the following initiative:")) + slot.put("
") + ui.link{ + content = _("Issue ##{id}", { id = suggested_initiative.issue.id } ) .. ": " .. encode.html(suggested_initiative.name), + module = "initiative", + view = "show", + id = suggested_initiative.id + } + end + end + } +end + local initiator = Initiator:by_pk(initiative.id, app.session.member.id) --slot.put_into("html_head", '') -execute.view{ - module = "supporter", - view = "_show_box", - params = { initiative = initiative } -} - -slot.put_into("sub_title", encode.html(_"Initiative: '#{name}'":gsub("#{name}", initiative.shortened_name) )) slot.select("actions", function() if not initiative.issue.fully_frozen and not initiative.issue.closed then @@ -45,48 +59,126 @@ end end) +slot.put_into("sub_title", encode.html(_"Initiative: '#{name}'":gsub("#{name}", initiative.shortened_name) )) + +slot.select("support", function() + ui.container{ + attr = { class = "actions" }, + content = function() + execute.view{ + module = "supporter", + view = "_show_box", + params = { initiative = initiative } + } + if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then + ui.link{ + attr = { class = "action", style = "float: left;" }, + content = function() + ui.image{ static = "icons/16/script_delete.png" } + slot.put(_"Revoke initiative") + end, + module = "initiative", + view = "revoke", + id = initiative.id + } + end + end + } +end) util.help("initiative.show") +if initiator and initiator.accepted == nil then + ui.container{ + attr = { class = "initiator_invite_info" }, + content = function() + slot.put(_"You are invited to become initiator of this initiative.") + slot.put(" ") + ui.link{ + content = function() + ui.image{ static = "icons/16/tick.png" } + slot.put(_"Accept invitation") + end, + module = "initiative", + action = "accept_invitation", + id = initiative.id, + routing = { + default = { + mode = "redirect", + module = request.get_module(), + view = request.get_view(), + id = param.get_id_cgi(), + params = param.get_all_cgi() + } + } + } + slot.put(" ") + ui.link{ + content = function() + ui.image{ static = "icons/16/cross.png" } + slot.put(_"Refuse invitation") + end, + module = "initiative", + action = "reject_initiator_invitation", + params = { + initiative_id = initiative.id, + member_id = app.session.member.id + }, + routing = { + default = { + mode = "redirect", + module = request.get_module(), + view = request.get_view(), + id = param.get_id_cgi(), + params = param.get_all_cgi() + } + } + } + end + } + slot.put("
") +end -ui.container{ - attr = { class = "vertical" }, - content = function() - ui.container{ - attr = { class = "ui_field_label" }, - content = _"Discussion URL" - } - ui.tag{ - tag = "span", - content = function() - if initiative.discussion_url and #initiative.discussion_url > 0 then - ui.link{ - attr = { - class = "actions", - target = "_blank", - title = initiative.discussion_url - }, - content = function() - slot.put(encode.html(initiative.discussion_url)) - end, - external = initiative.discussion_url - } +if (initiative.discussion_url and #initiative.discussion_url > 0) + or (initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked) then + ui.container{ + attr = { class = "vertical" }, + content = function() + ui.container{ + attr = { class = "ui_field_label" }, + content = _"Discussion with initiators" + } + ui.tag{ + tag = "span", + content = function() + if initiative.discussion_url and #initiative.discussion_url > 0 then + ui.link{ + attr = { + class = "actions", + target = "_blank", + title = initiative.discussion_url + }, + content = function() + slot.put(encode.html(initiative.discussion_url)) + end, + external = initiative.discussion_url + } + end + slot.put(" ") + if initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked then + ui.link{ + attr = { class = "actions" }, + content = _"(change URL)", + module = "initiative", + view = "edit", + id = initiative.id + } + end end - slot.put(" ") - if initiator then - ui.link{ - attr = { class = "actions" }, - content = _"(change URL)", - module = "initiative", - view = "edit", - id = initiative.id - } - end - end - } - end -} - + } + end + } +end ui.container{ @@ -202,7 +294,7 @@ name = "current_draft", label = current_draft_name, content = function() - if initiator then + if initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked then ui.link{ content = function() ui.image{ static = "icons/16/script_add.png" } @@ -238,9 +330,11 @@ } end +local suggestion_count = initiative:get_reference_selector("suggestions"):count() + tabs[#tabs+1] = { name = "suggestion", - label = _"Suggestions", + label = _"Suggestions" .. " (" .. tostring(suggestion_count) .. ")", content = function() execute.view{ module = "suggestion", @@ -251,7 +345,7 @@ } } slot.put("
") - if not initiative.issue.fully_frozen and not initiative.issue.closed then + if not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then ui.link{ content = function() ui.image{ static = "icons/16/comment_add.png" } @@ -264,57 +358,126 @@ end } +local 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") + +local satisfied_supporter_count = members_selector:count() + tabs[#tabs+1] = { name = "satisfied_supporter", - label = _"Supporter", + label = _"Supporter" .. " (" .. tostring(satisfied_supporter_count) .. ")", + content = function() + execute.view{ + module = "member", + view = "_list", + params = { + initiative = initiative, + members_selector = members_selector + } + } + end +} + +local 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") + +local potential_supporter_count = members_selector:count() + +tabs[#tabs+1] = { + name = "supporter", + label = _"Potential supporter" .. " (" .. tostring(potential_supporter_count) .. ")", 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") + members_selector = members_selector } } end } +local initiator_count = initiative:get_reference_selector("initiators"):add_where("accepted"):count() + tabs[#tabs+1] = { - name = "supporter", - label = _"Potential supporter", + name = "initiators", + label = _"Initiators" .. " (" .. tostring(initiator_count) .. ")", content = function() + if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then + ui.link{ + attr = { class = "action" }, + content = function() + ui.image{ static = "icons/16/user_add.png" } + slot.put(_"Invite initiator") + end, + module = "initiative", + view = "add_initiator", + params = { initiative_id = initiative.id } + } + if initiator_count > 1 then + ui.link{ + content = function() + ui.image{ static = "icons/16/user_delete.png" } + slot.put(_"Remove initiator") + end, + module = "initiative", + view = "remove_initiator", + params = { initiative_id = initiative.id } + } + end + end + if initiator and initiator.accepted == false then + ui.link{ + content = function() + ui.image{ static = "icons/16/user_delete.png" } + slot.put(_"Cancel refuse of invitation") + end, + module = "initiative", + action = "remove_initiator", + params = { + initiative_id = initiative.id, + member_id = app.session.member.id + }, + routing = { + ok = { + mode = "redirect", + module = "initiative", + view = "show", + id = initiative.id + } + } + } + end + local members_selector = initiative:get_reference_selector("initiating_members") + :add_field("initiator.accepted", "accepted") + if not (initiator and initiator.accepted) then + members_selector:add_where("accepted") + end 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 = members_selector, + initiator = initiator } } 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 -} +local drafts_count = initiative:get_reference_selector("drafts"):count() tabs[#tabs+1] = { name = "drafts", - label = _"Old drafts", + label = _"Draft history" .. " (" .. tostring(drafts_count) .. ")", content = function() execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } } end @@ -338,7 +501,7 @@ label = _"Created at", value = format.timestamp(initiative.created) } - ui.field.date{ label = _"Revoked at", name = "revoked" } +-- ui.field.date{ label = _"Revoked at", name = "revoked" } ui.field.boolean{ label = _"Admitted", name = "admitted" } end } diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/issue/_list.lua --- a/app/main/issue/_list.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/issue/_list.lua Sun Jan 10 12:00:00 2010 +0100 @@ -5,6 +5,10 @@ ui_filter = function(args) args.content() end end +if param.get("no_filter", atom.boolean) then + ui_filter = function(args) args.content() end +end + local filter_voting = false ui_filter{ selector = issues_selector, @@ -86,6 +90,9 @@ if not filter_voting then ui_filter = function(args) args.content() end end + if param.get("no_filter", atom.boolean) then + ui_filter = function(args) args.content() end + end ui_filter{ selector = issues_selector, name = "filter_voting", @@ -145,8 +152,11 @@ }, }, content = function() - - ui.order{ + local ui_order = ui.order + if param.get("no_sort", atom.boolean) then + ui_order = function(args) args.content() end + end + ui_order{ name = "issue_list", selector = issues_selector, options = { @@ -181,7 +191,12 @@ } }, content = function() - ui.paginate{ + local ui_paginate = ui.paginate + if param.get("per_page") == "all" then + ui_paginate = function(args) args.content() end + end + ui_paginate{ + per_page = tonumber(param.get("per_page")), selector = issues_selector, content = function() local highlight_string = param.get("highlight_string", "string") @@ -201,7 +216,7 @@ slot.put("
") end ui.link{ - text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)), + text = _("Issue ##{id}", { id = tostring(record.id) }), module = "issue", view = "show", id = record.id @@ -213,6 +228,11 @@ end slot.put("
") slot.put("
") + if record.old_state then + ui.field.text{ value = format.time(record.sort) } + ui.field.text{ value = Issue:get_state_name_for_state(record.old_state) .. " > " .. Issue:get_state_name_for_state(record.new_state) } + else + end end }, { @@ -245,7 +265,9 @@ issue = record, initiatives_selector = initiatives_selector, highlight_string = highlight_string, - limit = 3 + limit = 3, + per_page = param.get("initiatives_per_page", atom.number), + no_sort = param.get("initiatives_no_sort", atom.boolean) } } end diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/issue/_show_head.lua --- a/app/main/issue/_show_head.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/issue/_show_head.lua Sun Jan 10 12:00:00 2010 +0100 @@ -77,6 +77,17 @@ end --]] + if config.issue_discussion_url_func then + local url = config.issue_discussion_url_func(issue) + ui.link{ + attr = { target = "_blank" }, + external = url, + content = function() + ui.image{ static = "icons/16/comments.png" } + slot.put(_"Discussion on issue") + end, + } + end end) diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/_action/update_stylesheet_url.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/member/_action/update_stylesheet_url.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,25 @@ + +local setting_key = "liquidfeedback_frontend_developer_features" +local setting = Setting:by_pk(app.session.member.id, setting_key) + +if not setting then + error("access denied") +end + +local stylesheet_url = util.trim(param.get("stylesheet_url")) +local setting_key = "liquidfeedback_frontend_stylesheet_url" +local setting = Setting:by_pk(app.session.member.id, setting_key) + +if stylesheet_url and #stylesheet_url > 0 then + if not setting then + setting = Setting:new() + setting.member_id = app.session.member.id + setting.key = setting_key + end + setting.value = stylesheet_url + setting:save() +elseif setting then + setting:destroy() +end + +slot.put_into("notice", _"Stylesheet URL has been updated") diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/_list.lua --- a/app/main/member/_list.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/member/_list.lua Sun Jan 10 12:00:00 2010 +0100 @@ -2,9 +2,20 @@ local initiative = param.get("initiative", "table") local issue = param.get("issue", "table") local trustee = param.get("trustee", "table") +local initiator = param.get("initiator", "table") local options = { { + name = "newest", + label = _"Newest", + order_by = "created DESC, id DESC" + }, + { + name = "oldest", + label = _"Oldest", + order_by = "created, id" + }, + { name = "name", label = _"A-Z", order_by = "name" @@ -117,7 +128,13 @@ execute.view{ module = "member", view = "_show_thumb", - params = { member = member, initiative = initiative, issue = issue, trustee = trustee } + params = { + member = member, + initiative = initiative, + issue = issue, + trustee = trustee, + initiator = initiator + } } end ---]] diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/_show.lua --- a/app/main/member/_show.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/member/_show.lua Sun Jan 10 12:00:00 2010 +0100 @@ -151,7 +151,7 @@ execute.view{ module = "initiative", view = "_list", - params = { initiatives_selector = member:get_reference_selector("initiated_initiatives") } + params = { initiatives_selector = member:get_reference_selector("initiated_initiatives"):add_where("initiator.accepted = true") } } end }, diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/_show_thumb.lua --- a/app/main/member/_show_thumb.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/member/_show_thumb.lua Sun Jan 10 12:00:00 2010 +0100 @@ -1,3 +1,4 @@ +local initiator = param.get("initiator", "table") local member = param.get("member", "table") local issue = param.get("issue", "table") @@ -11,8 +12,13 @@ name = encode.html(member.name) end +local container_class = "member_thumb" +if initiator and member.accepted ~= true then + container_class = container_class .. " not_accepted" +end + ui.container{ - attr = { class = "member_thumb" }, + attr = { class = container_class }, content = function() ui.container{ attr = { class = "flags" }, @@ -49,6 +55,13 @@ else slot.put(" ") end + if initiator and initiator.accepted then + if member.accepted == nil then + slot.put(_"Invited") + elseif member.accepted == false then + slot.put(_"Rejected") + end + end if member.grade then ui.container{ content = function() diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/_show_thumb.lua.orig --- a/app/main/member/_show_thumb.lua.orig Mon Jan 04 12:00:00 2010 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -local member = param.get("member", "table") - -local issue = param.get("issue", "table") -local initiative = param.get("initiative", "table") -local trustee = param.get("trustee", "table") - -local name -if member.name_highlighted then - name = encode.highlight(member.name_highlighted) -else - name = encode.html(member.name) -end - -ui.container{ - attr = { class = "member_thumb" }, - content = function() - ui.container{ - attr = { class = "flags" }, - content = function() - if (issue or initiative) and member.weight > 1 then - local module - if issue then - module = "interest" - elseif initiative then - module = "supporter" - end - ui.link{ - attr = { title = _"Number of incoming delegations, follow link to see more details" }, - content = _("+ #{weight}", { weight = member.weight - 1 }), - module = module, - view = "show_incoming", - params = { - member_id = member.id, - initiative_id = initiative and initiative.id or nil, - issue_id = issue and issue.id or nil - } - } - end - -- TODO performance - local contact = Contact:by_pk(app.session.member.id, member.id) - if contact then - ui.image{ - attr = { - alt = _"You have saved this member as contact", - title = _"You have saved this member as contact" - }, - static = "icons/16/bullet_disk.png" - } - end - end - } - - ui.link{ - attr = { title = _"Show member" }, - module = "member", - view = "show", - id = member.id, - content = function() - execute.view{ - module = "member_image", - view = "_show", - params = { - member = member, - image_type = "avatar", - show_dummy = true - } - } - end - } - - ui.link{ - attr = { title = _"Show member" }, - module = "member", - view = "show", - id = member.id, - content = function() - ui.container{ - attr = { class = "member_name" }, - content = function() - slot.put(name) - end - } - end - } - end -} diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/developer_settings.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/member/developer_settings.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,36 @@ +slot.put_into("title", _"Developer features") + +slot.select("actions", function() + ui.link{ + content = function() + ui.image{ static = "icons/16/cancel.png" } + slot.put(_"Cancel") + end, + module = "member", + view = "settings" + } +end) + +ui.form{ + attr = { class = "vertical" }, + module = "member", + action = "update_stylesheet_url", + routing = { + ok = { + mode = "redirect", + module = "index", + view = "index" + } + }, + content = function() + local setting_key = "liquidfeedback_frontend_stylesheet_url" + local setting = Setting:by_pk(app.session.member.id, setting_key) + local value = setting and setting.value + ui.field.text{ + label = _"Stylesheet URL", + name = "stylesheet_url", + value = value + } + ui.submit{ value = _"Set URL" } + end +} diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/list.lua --- a/app/main/member/list.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/member/list.lua Sun Jan 10 12:00:00 2010 +0100 @@ -5,5 +5,5 @@ execute.view{ module = "member", view = "_list", - params = { members_selector = Member:new_selector():add_order_by("name") } + params = { members_selector = Member:new_selector() } } diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/member/settings.lua --- a/app/main/member/settings.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/member/settings.lua Sun Jan 10 12:00:00 2010 +0100 @@ -10,6 +10,20 @@ module = "index", view = "index" } + + local setting_key = "liquidfeedback_frontend_developer_features" + local setting = Setting:by_pk(app.session.member.id, setting_key) + + if setting then + ui.link{ + content = function() + ui.image{ static = "icons/16/wrench.png" } + slot.put(_"Developer features") + end, + module = "member", + view = "developer_settings" + } + end end) ui.heading{ content = _"Change your name" } @@ -72,4 +86,4 @@ ui.field.password{ label = _"Repeat new password", name = "new_password2" } ui.submit{ value = _"Change password" } end -} +} \ No newline at end of file diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/supporter/_show_box.lua --- a/app/main/supporter/_show_box.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/app/main/supporter/_show_box.lua Sun Jan 10 12:00:00 2010 +0100 @@ -3,103 +3,101 @@ local initiative = param.get("initiative", "table") local supporter = Supporter:by_pk(initiative.id, app.session.member.id) - ui.container{ - attr = { class = "actions" }, - content = function() - if not initiative.issue.fully_frozen and not initiative.issue.closed then - if supporter then - if not supporter:has_critical_opinion() then - ui.container{ - attr = { - class = "head head_supporter", - style = "cursor: pointer;", - onclick = "document.getElementById('support_content').style.display = 'block';" - }, - content = function() - ui.image{ - static = "icons/16/thumb_up_green.png" - } - slot.put(_"Your are supporter") - ui.image{ - static = "icons/16/dropdown.png" - } - end + if not initiative.issue.fully_frozen and not initiative.issue.closed then + if supporter then + if not supporter:has_critical_opinion() then + ui.container{ + attr = { + class = "head head_supporter", + style = "cursor: pointer;", + onclick = "document.getElementById('support_content').style.display = 'block';" + }, + content = function() + ui.image{ + static = "icons/16/thumb_up_green.png" + } + slot.put(_"Your are supporter") + ui.image{ + static = "icons/16/dropdown.png" } - else - ui.container{ - attr = { - class = "head head_potential_supporter", - style = "cursor: pointer;", - onclick = "document.getElementById('support_content').style.display = 'block';" - }, - content = function() - ui.image{ - static = "icons/16/thumb_up.png" - } - slot.put(_"Your are potential supporter") - ui.image{ - static = "icons/16/dropdown.png" - } - end + end + } + else + ui.container{ + attr = { + class = "head head_potential_supporter", + style = "cursor: pointer;", + onclick = "document.getElementById('support_content').style.display = 'block';" + }, + content = function() + ui.image{ + static = "icons/16/thumb_up.png" + } + slot.put(_"Your are potential supporter") + ui.image{ + static = "icons/16/dropdown.png" } end + } + end + ui.container{ + attr = { class = "content", id = "support_content" }, + content = function() ui.container{ - attr = { class = "content", id = "support_content" }, + attr = { + class = "close", + style = "cursor: pointer;", + onclick = "document.getElementById('support_content').style.display = 'none';" + }, content = function() - ui.container{ - attr = { - class = "close", - style = "cursor: pointer;", - onclick = "document.getElementById('support_content').style.display = 'none';" - }, - content = function() - ui.image{ static = "icons/16/cross.png" } - end - } - if supporter then - ui.link{ - content = function() - ui.image{ static = "icons/16/thumb_down_red.png" } - slot.put(_"Remove my support from this initiative") - end, - module = "initiative", - action = "remove_support", - id = initiative.id, - routing = { - default = { - mode = "redirect", - module = request.get_module(), - view = request.get_view(), - id = param.get_id_cgi(), - params = param.get_all_cgi() - } - } - } - else - end + ui.image{ static = "icons/16/cross.png" } end } - else - ui.link{ - content = function() - ui.image{ static = "icons/16/thumb_up_green.png" } - slot.put(_"Support this initiative") - end, - module = "initiative", - action = "add_support", - id = initiative.id, - routing = { - default = { - mode = "redirect", - module = request.get_module(), - view = request.get_view(), - id = param.get_id_cgi(), - params = param.get_all_cgi() + if supporter then + ui.link{ + content = function() + ui.image{ static = "icons/16/thumb_down_red.png" } + slot.put(_"Remove my support from this initiative") + end, + module = "initiative", + action = "remove_support", + id = initiative.id, + routing = { + default = { + mode = "redirect", + module = request.get_module(), + view = request.get_view(), + id = param.get_id_cgi(), + params = param.get_all_cgi() + } } } + else + end + end + } + else + if not initiative.revoked then + ui.link{ + content = function() + ui.image{ static = "icons/16/thumb_up_green.png" } + slot.put(_"Support this initiative") + end, + module = "initiative", + action = "add_support", + id = initiative.id, + routing = { + default = { + mode = "redirect", + module = request.get_module(), + view = request.get_view(), + id = param.get_id_cgi(), + params = param.get_all_cgi() + } } - end + } end end - } + end + end) diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 app/main/timeline/index.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/timeline/index.lua Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,151 @@ + +local function format_dow(dow) + local dows = { + _"Monday", + _"Tuesday", + _"Wednesday", + _"Thursday", + _"Friday", + _"Saturday", + _"Sunday" + } + return dows[dow+1] +end + +slot.put_into("title", _"Global timeline") + + +ui.form{ + attr = { class = "vertical" }, + module = "timeline", + view = "index", + method = "get", + content = function() + local tmp = db:query("select EXTRACT(DOW FROM date) as dow, date FROM (SELECT (now() - (to_char(days_before, '0') || ' days')::interval)::date as date from (select generate_series(0,7) as days_before) as series) as date; ") + local today = tmp[1].date + for i, record in ipairs(tmp) do + local content + if i == 1 then + content = _"Today" + elseif i == 2 then + content = _"Yesterday" + else + content = format_dow(record.dow) + end + ui.link{ + content = content, + attr = { onclick = "el = document.getElementById('timeline_search_date'); el.value = '" .. tostring(record.date) .. "'; el.form.submit(); return(false);" }, + module = "timeline", + view = "index", + params = { date = record.date } + } + slot.put(" ") + end + ui.field.hidden{ + attr = { id = "timeline_search_date" }, + name = "date", + value = param.get("date") or today + } + ui.field.select{ + attr = { onchange = "this.form.submit();" }, + name = "per_page", + label = _"Issues per page", + foreign_records = { + { id = "10", name = "10" }, + { id = "25", name = "25" }, + { id = "50", name = "50" }, + { id = "100", name = "100" }, + { id = "250", name = "250" }, + { id = "all", name = _"All" }, + }, + foreign_id = "id", + foreign_name = "name", + value = param.get("per_page") + } + local initiatives_per_page = param.get("initiatives_per_page", atom.integer) or 3 + + ui.field.select{ + attr = { onchange = "this.form.submit();" }, + name = "initiatives_per_page", + label = _"Initiatives per page", + foreign_records = { + { id = 1, name = "1" }, + { id = 3, name = "3" }, + { id = 5, name = "5" }, + { id = 10, name = "10" }, + { id = 25, name = "25" }, + { id = 50, name = "50" }, + }, + foreign_id = "id", + foreign_name = "name", + value = initiatives_per_page + } + end +} + +local date = param.get("date") +if not date then + date = "today" +end +local issues_selector = db:new_selector() +issues_selector._class = Issue + +issues_selector + :add_field("*") + :add_where{ "sort::date = ?", date } + :add_from{ "($) as issue", { + Issue:new_selector() + :add_field("''", "old_state") + :add_field("'new'", "new_state") + :add_field("created", "sort") + :union(Issue:new_selector() + :add_field("'new'", "old_state") + :add_field("'accepted'", "new_state") + :add_field("accepted", "sort") + :add_where("accepted NOTNULL") + ):union(Issue:new_selector() + :add_field("'accepted'", "old_state") + :add_field("'frozen'", "new_state") + :add_field("half_frozen", "sort") + :add_where("half_frozen NOTNULL") + ):union(Issue:new_selector() + :add_field("'frozen'", "old_state") + :add_field("'voting'", "new_state") + :add_field("fully_frozen", "sort") + :add_where("fully_frozen NOTNULL") + ):union(Issue:new_selector() + :add_field("'new'", "old_state") + :add_field("'cancelled'", "new_state") + :add_field("closed", "sort") + :add_where("closed NOTNULL AND accepted ISNULL") + ):union(Issue:new_selector() + :add_field("'accepted'", "old_state") + :add_field("'cancelled'", "new_state") + :add_field("closed", "sort") + :add_where("closed NOTNULL AND half_frozen ISNULL AND accepted NOTNULL") + ):union(Issue:new_selector() + :add_field("'frozen'", "old_state") + :add_field("'cancelled'", "new_state") + :add_field("closed", "sort") + :add_where("closed NOTNULL AND fully_frozen ISNULL AND half_frozen NOTNULL") + ):union(Issue:new_selector() + :add_field("'voting'", "old_state") + :add_field("'finished'", "new_state") + :add_field("closed", "sort") + :add_where("closed NOTNULL AND fully_frozen NOTNULL AND half_frozen ISNULL") + ) + } +} + +execute.view{ + module = "issue", + view = "_list", + params = { + issues_selector = issues_selector, + initiatives_per_page = param.get("initiatives_per_page", atom.number), + initiatives_no_sort = true, + no_filter = true, + no_sort = true, + per_page = param.get("per_page"), + } +} diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 config/default.lua --- a/config/default.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/config/default.lua Sun Jan 10 12:00:00 2010 +0100 @@ -1,5 +1,5 @@ config.app_name = "LiquidFeedback" -config.app_version = "beta5" +config.app_version = "beta6" config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)" diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 config/development.lua --- a/config/development.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/config/development.lua Sun Jan 10 12:00:00 2010 +0100 @@ -9,3 +9,5 @@ config.mail_from = "LiquidFeedback" config.mail_reply_to = "liquid-support@localhost" + +config.issue_discussion_url_func = function(issue) return "http://example.com/issue_" .. tostring(issue.id) end \ No newline at end of file diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 locale/help/initiative.add_initiator.de.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale/help/initiative.add_initiator.de.txt Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,2 @@ +=Initiator einladen= +Hier kannst du eine Person aus deiner Kontaktliste zur gleichberechtigten Mitarbeit am Entwurf einladen. Der eingeladene muss die Einladung erst akzeptieren, bevor er als weiterer Initiator gilt (und angezeigt wird). **Vorsicht:** Alle Initiatoren haben die gleichen Rechte und können andere Initiatoren entfernen. diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 locale/help/initiative.remove_initiator.de.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale/help/initiative.remove_initiator.de.txt Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,2 @@ +=Initiator entfernen= +Du kannst dich oder einen anderen Initiator entfernen, sofern nach dem Entfernen noch mindestens ein Initiator existiert. Um die Initiative insgesamt aufzugeben, wähle bitte ,,Initiative zurückziehen''. diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 locale/help/initiative.revoke.de.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/locale/help/initiative.revoke.de.txt Sun Jan 10 12:00:00 2010 +0100 @@ -0,0 +1,2 @@ +=Initiative zurückziehen= +Du kannst diese Initiative zurückziehen. Dies kann nicht rückgängig gemacht werden. Natürlich hast du jederzeit die Möglichkeit, eine neue Initiative zu starten. Den Unterstützern kannst du eine alternative Initiative empfehlen. diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 locale/help/initiative.show.de.txt --- a/locale/help/initiative.show.de.txt Mon Jan 04 12:00:00 2010 +0100 +++ b/locale/help/initiative.show.de.txt Sun Jan 10 12:00:00 2010 +0100 @@ -2,9 +2,11 @@ Während der Diskussionsphase kannst du diese Initiative unterstützen und gibst damit den Initiatoren eine wichtige Rückmeldung, inwieweit der aktuelle Entwurf auf Zustimmung stößt. Darüber hinaus kannst du durch Anregungen (Änderungsvorschläge) mitteilen, was noch verbessert werden kann bzw. unter welchen Bedingungen du dir eine Unterstützung vorstellen kannst. Du kannst dich den Anregungen anderer Mitglieder anschließen (und damit das Gewicht dieser Anregungen erhöhen) und eigene (zusätzliche) Anregungen einbringen. Anregungen, die aus deiner Sicht unbedingt eingearbeitet werden müssen, damit du zustimmst, kennzeichnest du mit ,,muss'', wünschenswerte mit ,,soll''. Du kannst Anregungen auch kritisch gegenüber stehen und sie mit ,,soll nicht'' kennzeichnen. Eine Anregung, die bei Umsetzung zum Entzug deiner Zustimmung führen würde, kennzeichnest du mit ,,darf nicht''. -=überarbeiteter Entwurf, Umsetzungsvermerk= +=überarbeiteter Entwurf, Umsetzungsvermerkn= 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. +=weitere Initiatoren einladen, Zurückziehen einer Initiative= +Ein Initiator kann weitere Initiatoren einladen und verleiht ihnen damit gleiche Rechte für die Bearbeitung des Entwurfs. Jeder Initiator einer Initiative kann die Initiative zurückziehen und auf Wunsch den Unterstützern eine andere Initiative als Alternative empfehlen. =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 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. +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. Sofern du nicht ohnehin schon Mitglied des Themenbereichs bist, kannst du durch eine Mitgliedschaft im Themenbereich oder durch Anmeldung von Interesse am Thema die den erforderlichen Unterstützerquorum zugrundeliegende Grundgesamtheit erhöhen. Den gleichen Effekt erreichst du auch, wenn du an ein Mitglied des Themenbereichs oder einen Interessenten am Thema delegierst. **Nur bei Status ,,Neu'':** Falls das Thema, zu dem diese Initiative gehört, noch im Zustand ,,Neu'' ist und du dieses Thema eigentlich gar nicht diskutieren möchtest, solltest du zunächst nur Mitglied des Themenbereichs werden oder Interesse am Thema anmelden und keine Anregungen eingeben, die dich zum (potentiellen) Unterstützer machen würden. Ebenso solltest du noch keine Gegeninitiative starten, die das Thema über die Zulassungshürde heben könnte. diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 locale/translations.de.lua --- a/locale/translations.de.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/locale/translations.de.lua Sun Jan 10 12:00:00 2010 +0100 @@ -1,4 +1,4 @@ --- #!/usr/bin/env lua +#!/usr/bin/env lua return { ["#{interested_issues_to_vote_count} issue(s) you are interested in"] = "#{interested_issues_to_vote_count} Themen, die Dich interessieren"; ["#{issues_to_vote_count} issue(s)"] = "#{issues_to_vote_count} Themen"; @@ -11,6 +11,7 @@ ["About"] = "About"; ["About LiquidFeedback"] = "Über LiquidFeedback"; ["Abstention"] = "Enthaltung"; +["Accept invitation"] = "Einladung annehmen"; ["Accepted at"] = "Angenommen am/um"; ["Active?"] = "Aktiv?"; ["Add my interest"] = "Mein Interesse anmelden"; @@ -24,7 +25,9 @@ ["Administrator"] = "Administrator"; ["Admission time"] = "Zeit für die Zulassung"; ["Admitted"] = "zugelassen"; +["All"] = "Alle"; ["Any"] = "Alle"; +["Are you sure?"] = "Sicher?"; ["Area"] = "Themenbereich"; ["Area '#{name}'"] = "Themenbereich '#{name}'"; ["Area delegation"] = "Area-Delegation"; @@ -39,9 +42,11 @@ ["Back"] = "Zurück"; ["Become a member"] = "Mitglied werden"; ["Birthday"] = "Geburtstag"; +["Can't remove last initiator"] = "Der letzte Initiator kann nicht entfernt werden"; ["Can't send confirmation email"] = "Bestätigungs-E-Mail kann nicht versendet werden."; ["Cancel"] = "Abbrechen"; ["Cancel password reset"] = "Kennwort-Rücksetzung abbrechen"; +["Cancel refuse of invitation"] = "Ablehnung der Einladung aufheben"; ["Cancel registration"] = "Registration abbrechen"; ["Cancelled"] = "Abgebrochen"; ["Change area delegation"] = "Delegation für Themengebiet ändern"; @@ -53,6 +58,8 @@ ["Change your login"] = "Deinen Anmeldenamen ändern"; ["Change your name"] = "Deinen Namen ändern"; ["Change your password"] = "Dein Kennwort ändern"; +["Choose initiator"] = "Initiator auswählen"; +["Choose member"] = "Mitglied auswählen"; ["Click for details"] = "Klicke für Details"; ["Close"] = "Schließen"; ["Closed"] = "geschlossen"; @@ -75,15 +82,19 @@ ["Delegations"] = "Delegationen"; ["Description"] = "Beschreibung"; ["Details"] = "Details"; +["Developer features"] = "Entwicklerfunktionen"; ["Diff"] = "Differenz"; ["Direct member count"] = "Anzahl Direktmitglieder"; ["Direct membership"] = "Direkte Mitgliedschaft"; ["Discussion"] = "Diskussion"; ["Discussion URL"] = "Diskussions-URL"; +["Discussion on issue"] = "Diskussion zum Thema"; ["Discussion time"] = "Zeit für die Diskussions"; +["Discussion with initiators"] = "Diskussion mit den Initiatoren"; ["Download"] = "Download"; ["Download database export"] = "Datenbankexport herunterladen"; ["Draft"] = "Entwurf"; +["Draft history"] = "Entwurfshistorie"; ["Edit"] = "Bearbeiten"; ["Edit draft"] = "Entwurf bearbeiten"; ["Edit initiative"] = "Initiative bearbeiten"; @@ -101,10 +112,12 @@ ["Filter"] = "Filter"; ["Finish voting"] = "Stimmabgabe abschließen"; ["Finished"] = "Abgeschlossen"; +["Friday"] = "Freitag"; ["Frozen"] = "Eingefroren"; ["Fully frozen at"] = "Ganz eingefroren am/um"; ["Global delegation"] = "Globale Delegation"; ["Global delegation active"] = "Globale Delegation aktiv"; +["Global timeline"] = "Globale Zeitlinie"; ["Half frozen at"] = "Halb eingefroren am/um"; ["Hello "] = "Hallo "; ["Help for: #{text}"] = "Hilfe zu: #{text}"; @@ -119,11 +132,14 @@ ["In discussion"] = "In Diskussion"; ["Incoming delegations"] = "Eingehende Delegationen"; ["Initiated initiatives"] = "Initierte Initiativen"; +["Initiative is revoked now"] = "Initiative ist jetzt zurückgezogen"; ["Initiative quorum"] = "Quorum Inititive"; ["Initiative successfully created"] = "Initiative erfolgreich erzeugt"; ["Initiative successfully updated"] = "Initiative erfolgreich aktualisiert"; ["Initiative: '#{name}'"] = "Initiative: '#{name}'"; ["Initiatives"] = "Initiativen"; +["Initiatives per page"] = "Initiativen je Seite"; +["Initiatives that invited you to become initiator:"] = "Initiative, die Dich eingeladen haben, Initiator zu werden:"; ["Initiator"] = "Initiator"; ["Initiators"] = "Initiatoren"; ["Interest not existant"] = "Interesse existiert nicht"; @@ -133,16 +149,23 @@ ["Interested members"] = "Interessierte Mitglieder"; ["Internal posts"] = "Interne Ämter"; ["Invalid username or password!"] = "Ungültiger Benutzername oder Kennwort"; +["Invitation has been refused"] = "Einladung wurde widerrufen"; +["Invite an initiator to initiative"] = "Initiator zur Initiative einladen"; ["Invite code"] = "Invite-Code"; +["Invite initiator"] = "Initiator einladen"; +["Invited"] = "Eingeladen"; +["Inviting initiator"] = "Initiatoren einladen"; ["Issue"] = "Thema"; -["Issue ##{id}"] = "Issue ##{id}"; +["Issue ##{id}"] = "Thema ##{id}"; ["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})"; ["Issue delegation"] = "Issue-Delegation"; ["Issue delegation active"] = "Delegation für Thema aktiv"; ["Issue policy"] = "Regelwerk für Thema"; ["Issue quorum"] = "Quorum Thema"; ["Issues"] = "Themen"; +["Issues per page"] = "Themen je Seite"; ["JavaScript is disabled or not available."] = "JavaScript ist abgeschaltet oder nicht verfügbar."; +["Last author"] = "Letzter Autor"; ["Last snapshot:"] = "Letzte Auszählung:"; ["Legend:"] = "Legende:"; ["License"] = "Lizenz"; @@ -158,10 +181,13 @@ ["Mark suggestion as not implemented and express satisfaction"] = "Anregung als nicht umgesetzt markieren und Zufriedenheit ausdrücken"; ["Max potential support"] = "Max. potentielle Unterstützer"; ["Max support"] = "Max. Unterstützer"; +["Member"] = "Mitglied"; ["Member '#{member}'"] = "Mitglied '#{member}'"; +["Member has been removed from initiators"] = "Mitglied wurde von den Initiatoren entfernt"; ["Member has been removed from your contacts"] = "Mitglied wurde aus Deinen Kontakten entfernt"; ["Member is administrator"] = "Mitglied ist Administrator"; ["Member is already saved in your contacts!"] = "Mitglied ist schon in Deinen Kontakten!"; +["Member is now invited to be initiator"] = "Mitglied ist jetzt als Initiator eingeladen"; ["Member list"] = "Mitgliederliste"; ["Member name"] = "Mitglied Name"; ["Member name history for '#{name}'"] = "Namenshistorie für '#{name}'"; @@ -177,6 +203,7 @@ ["Memberships"] = "Mitgliedschaften"; ["Missing help text: #{id}.#{lang}.txt"] = "Fehlender Hilfe-Text: #{id}.#{lang}.txt"; ["Mobile phone"] = "Mobiltelefon"; +["Monday"] = "Montag"; ["My opinion"] = "Meine Meinung"; ["Name"] = "Name"; ["New"] = "Neu"; @@ -198,7 +225,6 @@ ["Number of incoming delegations, follow link to see more details"] = "Anzahl eingehender Delegationen, Link folgen für mehr Details"; ["OK"] = "OK"; ["Old draft revision"] = "Alte Revision des Entwurfs"; -["Old drafts"] = "Alte Entwürfe"; ["Old password"] = "Altes Kennwort"; ["Old password is wrong"] = "Das alte Kennwort ist falsch"; ["Oldest"] = "Älteste"; @@ -220,8 +246,12 @@ ["Phone"] = "Telefon"; ["Photo"] = "Foto"; ["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 member"] = "Bitte wähle ein Mitglied"; ["Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you."] = "Wähle einen Namen, z. B. Deinen Real- oder Nicknamen. Dieser wird anderen angezeigt um Dich zu identifizieren."; ["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 choose a policy"] = "Bitte wähle ein Regelwerk"; +["Please choose two different versions of the draft to compare"] = "Bitte wähle zwei verschiedene Versionen des Drafts, um sie zu vergleichen."; +["Please choose two versions of the draft to compare"] = "Bitte wähle zwei Versionen des Drafts, um sie zu vergleichen."; ["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."; @@ -241,12 +271,16 @@ ["Rank"] = "Rang"; ["Real name"] = "Realname"; ["Refresh support to current draft"] = "Unterstützung auf aktuellen Entwurf aktualisieren"; +["Refuse invitation"] = "Einladung ablehnen"; ["Register"] = "Registrieren"; ["Register new member"] = "Neues Mitglied registrieren"; ["Registration"] = "Registrierung"; +["Rejected"] = "Abgelehnt"; ["Remove"] = "Entfernen"; ["Remove autoreject"] = "Auto-Ablehnen abschalten"; ["Remove from contacts"] = "Aus den Kontakten entfernen"; +["Remove initiator"] = "Initiator entfernen"; +["Remove initiator from initiative"] = "Initiator von der Initiative entfernen"; ["Remove my interest"] = "Interesse abmelden"; ["Remove my membership"] = "Mitgliedschaft aufgeben"; ["Remove my support from this initiative"] = "Meine Unterstützung der Initiative entziehen"; @@ -257,7 +291,9 @@ ["Reset link has been send for this member"] = "Rücksetz-Link wurde versendet"; ["Reset password"] = "Kennwort zurücksetzen"; ["Revoke"] = "Widerrufen"; +["Revoke initiative"] = "Initiative zurückziehen"; ["Revoked at"] = "Zurückgezogen am/um"; +["Saturday"] = "Samstag"; ["Save"] = "Speichern"; ["Saved as contact"] = "Als Kontakt speichern"; ["Search"] = "Suchen"; @@ -265,6 +301,7 @@ ["Search issues"] = "Suche Themen"; ["Search members"] = "Suche Mitglieder"; ["Search results for: '#{search}'"] = "Suchergebnisse für: '#{search}'"; +["Set URL"] = "URL setzen"; ["Set area delegation"] = "Delegation für Themengebiet festlegen"; ["Set autoreject"] = "Auto-Ablehnen anschalten"; ["Set delegation for Area '#{name}'"] = "Delegation für Themengebiet '#{name}' festlegen"; @@ -285,6 +322,7 @@ ["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, but you are currently not invited"] = "Sorry, aber Du bist zur Zeit nicht eingeladen"; ["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"; @@ -294,11 +332,16 @@ ["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"; +["Stylesheet URL"] = "Stylesheet URL"; +["Stylesheet URL has been updated"] = "Stylesheet URL wurde aktualisiert"; +["Suggest no initiative"] = "Keine Initiative empfehlen"; +["Suggested initiative"] = "Empfohlene Initiative"; ["Suggestion"] = "Anregung"; ["Suggestion currently implemented"] = "Anregung zur Zeit umgesetzt"; ["Suggestion currently not implemented"] = "Anregung zur Zeit nicht umgesetzt"; ["Suggestion for initiative: '#{name}'"] = "Anregung für Initiative '#{name}'"; ["Suggestions"] = "Anregungen"; +["Sunday"] = "Sonntag"; ["Support"] = "Unterstützung"; ["Support this initiative"] = "Diese Initiative unterstützen"; ["Supported initiatives"] = "Unterstützte Initiativen"; @@ -306,21 +349,31 @@ ["Terms accepted"] = "Bedingungen akzeptiert"; ["The code you've entered is invalid"] = "Der Code, den Du eingeben hast, ist nicht gültig!"; ["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht"; +["The initiators suggest to support the following initiative:"] = "Die Initiatoren empfehlen folgende Initiative zu unterstützen:"; +["This initiative has been revoked at #{revoked}"] = "Diese Initiative wurde am/um #{revoked} zurückgezogen"; +["This initiative is already revoked"] = "Diese Initiative ist schon zurückgezogen"; +["This initiative is revoked"] = "Diese Initiative wurde zurückgezogen"; ["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 member account has been created at #{created}"] = "Dieser Mitgliedszugang wurde am/um #{created} angelegt."; +["This member has rejected to become initiator of this initiative"] = "Dieses Mitglied hat die Einladung, Initiator zu werden, abgelehnt"; +["This member is already initiator of this initiative"] = "Dieses Mitglied ist bereits Initiator dieser Initiative"; +["This member is already invited to become initiator of this initiative"] = "Dieses Mitglied ist bereits eingeladen Initiator dieser Initiative zu werden"; ["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 name is too short!"] = "Dieser Name ist 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!"; +["Thursday"] = "Donnerstag"; ["Time left"] = "Restzeit"; ["Title (80 chars max)"] = "Title (max. 80 Zeichen)"; +["Today"] = "Heute"; ["Traditional wiki syntax"] = "Traditionaller Wiki-Syntax"; ["Trustee"] = "Bevollmächtigter"; +["Tuesday"] = "Dienstag"; ["Unknown author"] = "Unbekannter Autor"; ["Upload images"] = "Bilder hochladen"; ["Used until"] = "Benutzt bis"; @@ -342,16 +395,23 @@ ["Voting requests"] = "Abstimmanträge"; ["Voting time"] = "Zeit für die Abstimmung"; ["Website"] = "Webseite"; +["Wednesday"] = "Mittwoch"; ["Wiki engine"] = "Wiki engine"; ["Yes"] = "Ja"; +["Yesterday"] = "Gestern"; +["You are already initator"] = "Du bist bereits Initiator"; ["You are already not supporting this initiative"] = "Diese Initiative hat bereits keine Unterstützung von Dir"; ["You are already supporting the latest draft"] = "Du unterstützt bereits den neuesten Entwurf"; ["You are currently not supporting this initiative. By adding suggestions to this initiative you will automatically become a potential supporter."] = "Du bist zur Zeit kein Unterstützer dieser Initiative. Wenn Du eine Anregung hinzufügst wirst Du automatisch potentieller Unterstützer!"; +["You are invited to become initiator of this initiative."] = "Du bist eingeladen Initiator dieser Initiative zu werden."; ["You are member"] = "Du bist Mitglied"; +["You are now initiator of this initiative"] = "Du bist jetzt Initiator dieser Initiative"; +["You can't suggest the initiative you are revoking"] = "Du kannst nicht die Initiative empfehlen, die Du löschen möchtest"; ["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 have to mark 'Are you sure' to revoke!"] = "Zum Zurückziehen musst Du 'Sicher?' auswählen"; ["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"; diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 model/area.lua --- a/model/area.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/model/area.lua Sun Jan 10 12:00:00 2010 +0100 @@ -54,7 +54,7 @@ return Policy:new_selector() :join("allowed_policy", nil, "allowed_policy.policy_id = policy.id") :add_where{ "allowed_policy.area_id = ? AND allowed_policy.default_policy", self.id } - :single_object_mode() + :optional_object_mode() :exec() end diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 model/initiative.lua --- a/model/initiative.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/model/initiative.lua Sun Jan 10 12:00:00 2010 +0100 @@ -34,8 +34,7 @@ this_key = 'id', that_key = 'initiative_id', ref = 'initiators', - back_ref = 'initiative', - default_order = '"id"' + back_ref = 'initiative' } Initiative:add_reference{ @@ -69,6 +68,14 @@ } Initiative:add_reference{ + mode = 'm1', + to = "Initiative", + this_key = 'suggested_initiative_id', + that_key = 'id', + ref = 'suggested_initiative', +} + +Initiative:add_reference{ mode = 'mm', to = "Member", this_key = 'id', diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 model/issue.lua --- a/model/issue.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/model/issue.lua Sun Jan 10 12:00:00 2010 +0100 @@ -119,7 +119,7 @@ finished = _"Finished", cancelled = _"Cancelled" } - return state_name_table[value] or value + return state_name_table[value] or value or '' end function Issue:get_search_selector(search_string) diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 model/member.lua --- a/model/member.lua Mon Jan 04 12:00:00 2010 +0100 +++ b/model/member.lua Sun Jan 10 12:00:00 2010 +0100 @@ -95,8 +95,7 @@ this_key = 'id', that_key = 'member_id', ref = 'initiators', - back_ref = 'member', - default_order = '"id"' + back_ref = 'member' } Member:add_reference{ diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 static/icons/16/script_delete.png Binary file static/icons/16/script_delete.png has changed diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 static/icons/16/user_delete.png Binary file static/icons/16/user_delete.png has changed diff -r 0ee1e0c42d4c -r 72c5e0ee7c98 static/style.css --- a/static/style.css Mon Jan 04 12:00:00 2010 +0100 +++ b/static/style.css Sun Jan 10 12:00:00 2010 +0100 @@ -40,6 +40,10 @@ color: #fff; } +.revoked { + text-decoration: line-through; +} + .highlighted { background-color: #fa7; color: #000; @@ -62,7 +66,6 @@ } - /************************************************************************* * Notices, warnings and errors */ @@ -274,7 +277,7 @@ } .interest .head_active, -.slot_support .head_potential_supporter { +.slot_support .head_potential_supporter{ background-color: #fec; border: 1px solid #b96; } @@ -284,6 +287,11 @@ border: 1px solid #8b8; } +.slot_support .head_initiator { + background-color: #eee; + border: 1px solid #999; +} + .delegation .head_active { background-color: #ddf; border: 1px solid #88b; @@ -759,6 +767,10 @@ float: right; } +.member_thumb.not_accepted { + opacity: 0.5; +} + .draft_content, .member_statement { background-color: #eee; @@ -803,7 +815,9 @@ } .draft_updated_info, -.voting_active_info { +.voting_active_info, +.revoked_info, +.initiator_invite_info { background-color: #fec; border: 2px solid #b96; padding: 1ex;