# HG changeset patch # User bsw # Date 1258974000 -3600 # Node ID 5c601807d39719860b6db47869204e52dfed4ffd # Parent dd0109e81922a7e050241d687d316a8ee3f05dad Version alpha3 Dark green part of issue supporter bargraph represents all satisfied supporters, regardless of having seen the latest draft Wiki formatting for drafts Showing differences between two drafts of the same initiative Display of outgoing delegation chains Many other improvements diff -r dd0109e81922 -r 5c601807d397 app/main/_layout/default.html --- a/app/main/_layout/default.html Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/_layout/default.html Mon Nov 23 12:00:00 2009 +0100 @@ -18,27 +18,29 @@ -
-
- +
+
+
-
- -
-
- -
-
-
+
+ +
+
+ +
+
+ +
+
diff -r dd0109e81922 -r 5c601807d397 app/main/area/_list.lua --- a/app/main/area/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/area/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -35,7 +35,7 @@ local max_value = MemberCount:get() ui.bargraph{ max_value = max_value, - width = 100, + width = 200, bars = { { color = "#444", value = record.direct_member_count }, { color = "#777", value = record.member_weight - record.direct_member_count }, diff -r dd0109e81922 -r 5c601807d397 app/main/area/show.lua --- a/app/main/area/show.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/area/show.lua Mon Nov 23 12:00:00 2009 +0100 @@ -13,19 +13,19 @@ slot.select("actions", function() ui.link{ content = function() - ui.image{ static = "icons/16/folder_add.png" } - slot.put(_"Create new issue") + ui.image{ static = "icons/16/table_go.png" } + slot.put(_"Delegate") end, - module = "initiative", + module = "delegation", view = "new", params = { area_id = area.id } } ui.link{ content = function() - ui.image{ static = "icons/16/table_go.png" } - slot.put(_"Delegate") + ui.image{ static = "icons/16/folder_add.png" } + slot.put(_"Create new issue") end, - module = "delegation", + module = "initiative", view = "new", params = { area_id = area.id } } @@ -45,68 +45,35 @@ ui.tabs{ { - name = "new", - label = _"New", - content = function() - execute.view{ - module = "issue", - view = "_list", - params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.accepted ISNULL AND issue.closed ISNULL"), for_area_list = true } - } - end - }, - { - name = "accepted", - label = _"In discussion", - content = function() - execute.view{ - module = "issue", - view = "_list", - params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.accepted NOTNULL AND issue.half_frozen ISNULL AND issue.closed ISNULL"), for_area_list = true } - } - end - }, - { - name = "half_frozen", - label = _"Frozen", + name = "issues", + label = _"Issues", content = function() execute.view{ module = "issue", view = "_list", - params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.half_frozen NOTNULL AND issue.closed ISNULL"), for_area_list = true } - } - end - }, - { - name = "frozen", - label = _"Voting", - content = function() - execute.view{ - module = "issue", - view = "_list", - params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.fully_frozen NOTNULL AND issue.closed ISNULL"), for_area_list = true } + params = { issues_selector = area:get_reference_selector("issues"), for_area_list = true } } end }, { - name = "finished", - label = _"Finished", + name = "members", + label = _"Members", content = function() execute.view{ - module = "issue", + module = "member", view = "_list", - params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.closed NOTNULL AND ranks_available"), for_area_list = true } + params = { members_selector = area:get_reference_selector("members") } } end }, { - name = "cancelled", - label = _"Cancelled", + name = "delegations", + label = _"Delegations", content = function() execute.view{ - module = "issue", + module = "delegation", view = "_list", - params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.closed NOTNULL AND NOT ranks_available"), for_area_list = true } + params = { delegations_selector = area:get_reference_selector("delegations") } } end }, diff -r dd0109e81922 -r 5c601807d397 app/main/delegation/_list.lua --- a/app/main/delegation/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/delegation/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,50 +1,73 @@ -local selector = param.get("selector", "table") +local delegations_selector = param.get("delegations_selector", "table") +local outgoing = param.get("outgoing", atom.boolean) +local incoming = param.get("incoming", atom.boolean) + +local function delegation_scope(delegation) + ui.container{ + attr = { class = "delegation_scope" }, + content = function() + local area + if delegation.issue then + area = delegation.issue.area + else + area = delegation.area + end + if not area then + ui.field.text{ value = _"Global delegation" } + end + if area then + ui.link{ + content = _"Area '#{name}'":gsub("#{name}", area.name), + module = "area", + view = "show", + id = area.id + } + end + if delegation.issue then + ui.link{ + content = _"Issue ##{id}":gsub("#{id}", delegation.issue.id), + module = "issue", + view = "show", + id = delegation.issue.id + } + end + end + } +end + ui.paginate{ - selector = selector, + selector = delegations_selector, content = function() - ui.list{ - records = selector:exec(), - columns = { - { - label = _"Truster", - content = function(record) - ui.link{ - content = record.truster.name, + for i, delegation in ipairs(delegations_selector:exec()) do + ui.container{ + attr = { class = "delegation_list_entry" }, + content = function() + if outgoing then + delegation_scope(delegation) + else + execute.view{ module = "member", - view = "show", - id = record.truster.id + view = "_show_thumb", + params = { member = delegation.truster } } end - }, - { - label = _"Trustee", - content = function(record) - ui.link{ - content = record.trustee.name, + ui.image{ + attr = { class = "delegation_arrow" }, + static = "delegation_arrow.jpg" + } + if incoming then + delegation_scope(delegation) + else + execute.view{ module = "member", - view = "show", - id = record.trustee.id + view = "_show_thumb", + params = { member = delegation.trustee } } end - }, - { - label = _"Area", - content = function(record) - if record.area then - ui.field.text{ value = record.area.name } - end - end - }, - { - label = _"Issue", - content = function(record) - if record.issue then - ui.field.text{ value = record.issue.id } - end - end - }, + end } - } + end + slot.put("
") end } diff -r dd0109e81922 -r 5c601807d397 app/main/delegation/_show_box.lua --- a/app/main/delegation/_show_box.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/delegation/_show_box.lua Mon Nov 23 12:00:00 2009 +0100 @@ -50,63 +50,70 @@ ui.container{ attr = { class = "content", id = "delegation_content" }, content = function() + ui.container{ + attr = { + class = "close", + style = "cursor: pointer;", + onclick = "document.getElementById('delegation_content').style.display = 'none';" + }, + content = _"X" + } local delegation_chain = db:query{ "SELECT * FROM delegation_chain(?, ?, ?) JOIN member ON member.id = member_id ORDER BY index", app.session.member.id, area_id, issue_id } for i, record in ipairs(delegation_chain) do local style - if record.participation then - style = "font-weight: bold;" - end - if record.overridden then - style = "color: #777;" - end - if not record.active then - style = "text-decoration: line-through;" - end - if record.scope_in then - ui.field.text{ - value = " v " .. record.scope_in .. " v " + execute.view{ + module = "member", + view = "_show_thumb", + params = { member = record } + } + slot.put("
") + if record.scope_out then + ui.container{ + attr = { class = "delegation_info" }, + content = function() + ui.image{ + attr = { class = "delegation_arrow" }, + static = "delegation_arrow_vertical.jpg" + } + ui.container{ + attr = { class = "delegation_scope" }, + content = function() + if record.scope_out == "global" then + slot.put(_"Global delegation") + elseif record.scope_out == "area" then + slot.put(_"Area delegation") + elseif record.scope_out == "issue" then + slot.put(_"Issue delegation") + end + end + } + if record.id == app.session.member.id then + ui.link{ + attr = { class = "revoke" }, + content = function() + ui.image{ static = "icons/16/delete.png" } + slot.put(_"Revoke") + end, + module = "delegation", + action = "update", + params = { issue_id = delegation.issue_id, area_id = delegation.area_id, delete = true }, + 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 - local name = record.name - if record.member_id == app.session.member.id then - name = _"Me" - end - ui.field.text{ - attr = { style = style }, - value = name - } end - - ui.link{ - attr = { class = "revoke" }, - content = function() - ui.image{ static = "icons/16/delete.png" } - slot.put(_"Revoke") - end, - module = "delegation", - action = "update", - params = { issue_id = delegation.issue_id, area_id = delegation.area_id, delete = true }, - routing = { - default = { - mode = "redirect", - module = request.get_module(), - view = request.get_view(), - id = param.get_id_cgi(), - params = param.get_all_cgi() - } - } - } - - ui.container{ - attr = { - class = "head", - style = "cursor: pointer;", - onclick = "document.getElementById('delegation_content').style.display = 'none';" - }, - content = _"Click here to close." - } end } end diff -r dd0109e81922 -r 5c601807d397 app/main/draft/_list.lua --- a/app/main/draft/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/draft/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,30 +1,43 @@ -ui.list{ - records = param.get("drafts", "table"), - columns = { - { - label = _"Id", - name = "id" - }, - { - label = _"Created at", - content = function(record) - ui.field.text{ value = format.timestamp(record.created) } - end - }, - { - label = _"Author", - name = "author_name" - }, - { - content = function(record) - ui.link{ - attr = { class = "action" }, - text = _"Show", - module = "draft", - view = "show", - id = record.id +ui.form{ + method = "get", + module = "draft", + view = "diff", + content = function() + ui.list{ + records = param.get("drafts", "table"), + columns = { + { + label = _"Created at", + content = function(record) + ui.field.text{ readonly = true, value = format.timestamp(record.created) } + end + }, + { + label = _"Author", + content = function(record) + ui.field.text{ readonly = true, value = record.author.name } + end + }, + { + content = function(record) + ui.link{ + attr = { class = "action" }, + text = _"Show", + module = "draft", + view = "show", + id = record.id + } + end + }, + { + label = _"Compare", + content = function(record) + slot.put('') + slot.put('') + end } - end + } } - } + ui.submit{ text = _"Compare" } + end } diff -r dd0109e81922 -r 5c601807d397 app/main/draft/_show.lua --- a/app/main/draft/_show.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/draft/_show.lua Mon Nov 23 12:00:00 2009 +0100 @@ -6,9 +6,13 @@ readonly = true, content = function() - ui.field.text{ label = _"Initiative", value = draft.initiative.name } ui.field.text{ label = _"Author", name = "author_name" } - ui.field.text{ label = _"Content", name = "content" } - + ui.field.timestamp{ label = _"Created at", name = "created" } + ui.container{ + attr = { class = "draft_content" }, + content = function() + slot.put(format.wiki_text(draft.content)) + end + } end } diff -r dd0109e81922 -r 5c601807d397 app/main/draft/diff.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/draft/diff.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,93 @@ +slot.put_into("title", _"Diff") + +local old_draft_id = param.get("old_draft_id", atom.integer) +local new_draft_id = param.get("new_draft_id", atom.integer) + +if old_draft_id > new_draft_id then + local tmp = old_draft_id + old_draft_id = new_draft_id + new_draft_id = tmp +end + +local old_draft = Draft:by_id(old_draft_id) +local new_draft = Draft:by_id(new_draft_id) + +local key = multirand.string(26, "123456789bcdfghjklmnpqrstvwxyz"); + +local old_draft_filename = encode.file_path(request.get_app_basepath(), 'tmp', "diff-" .. key .. "-old.tmp") +local new_draft_filename = encode.file_path(request.get_app_basepath(), 'tmp', "diff-" .. key .. "-new.tmp") + +local old_draft_file = assert(io.open(old_draft_filename, "w")) +old_draft_file:write(old_draft.content) +old_draft_file:write("\n") +old_draft_file:close() + +local new_draft_file = assert(io.open(new_draft_filename, "w")) +new_draft_file:write(new_draft.content) +new_draft_file:write("\n") +new_draft_file:close() + +local output, err, status = os.pfilter(nil, "sh", "-c", "diff -U 100000 '" .. old_draft_filename .. "' '" .. new_draft_filename .. "' | grep -v ^--- | grep -v ^+++ | grep -v ^@") + +os.remove(old_draft_filename) +os.remove(new_draft_filename) + +if not status then + ui.field.text{ value = _"The drafts do not differ" } +else + slot.put('') + slot.put('') + local last_state = "unchanged" + local lines = {} + local removed_lines = nil + output = output .. " " + output = output:gsub("[^\n\r]+", function(line) + local state = "unchanged" + local char = line:sub(1,1) + line = line:sub(2) + state = "unchanged" + if char == "-" then + state = "-" + elseif char == "+" then + state = "+" + end + if last_state == "unchanged" then + if state == "unchanged" then + lines[#lines+1] = line + elseif (state == "-") or (state == "+") then + local text = table.concat(lines, "
") + slot.put("") + lines = { line } + end + elseif last_state == "-" then + if state == "-" then + lines[#lines+1] = line + elseif state == "+" then + removed_lines = lines + lines = { line } + elseif state == "unchanged" then + local text = table.concat(lines,"
") + slot.put('") + lines = { line } + end + elseif last_state == "+" then + if state == "+" then + lines[#lines+1] = line + elseif (state == "-") or (state == "unchanged") then + if removed_lines then + local text = table.concat(lines, "
") + local removed_text = table.concat(removed_lines, "
") + slot.put('") + else + local text = table.concat(lines, "
") + slot.put('") + end + removed_lines = nil + lines = { line } + end + end + last_state = state + end) + slot.put("
' .. _"Old draft revision" .. '' .. _"New draft revision" .. '
", text, "", text, "
', text, "
', removed_text, '', text, "
', text, "
") +end + diff -r dd0109e81922 -r 5c601807d397 app/main/draft/new.lua --- a/app/main/draft/new.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/draft/new.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,24 +1,30 @@ slot.put_into("title", _"Add new draft") -local initiative_id = param.get("initiative_id") +local initiative = Initiative:by_id(param.get("initiative_id")) ui.form{ + record = initiative.current_draft, attr = { class = "vertical" }, module = "draft", action = "add", - params = { initiative_id = initiative_id }, + params = { initiative_id = initiative.id }, routing = { default = { mode = "redirect", module = "initiative", view = "show", - id = initiative_id + id = initiative.id } }, content = function() ui.field.text{ label = _"Author", value = app.session.member.name, readonly = true } - ui.field.text{ label = _"Content", name = "content", multiline = true } + ui.field.text{ + label = _"Content", + name = "content", + multiline = true, + attr = { style = "height: 50ex;" } + } ui.submit{ text = _"Save" } end diff -r dd0109e81922 -r 5c601807d397 app/main/index/search.lua --- a/app/main/index/search.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/index/search.lua Mon Nov 23 12:00:00 2009 +0100 @@ -5,49 +5,40 @@ slot.put_into("title", _("Search results for: '#{search}'", { search = search_string })) - -local members = {} -local issues = {} -local initiatives = {} - - if search_for == "global" or search_for == "member" then - members = Member:search(search_string) -end - -if search_for == "global" or search_for == "issue" then - issues = Issue:search(search_string) -end - -if search_for == "initiative" then - initiatives = Initiative:search(search_string) -end - - -if #members > 0 then + members_selector = Member:get_search_selector(search_string) +--if #members > 0 then ui.heading{ content = _"Members" } execute.view{ module = "member", view = "_list", - params = { members = members, highlight_string = search_string }, + params = { members_selector = members_selector }, } +--end end -if #issues > 0 then +if search_for == "global" or search_for == "issue" then + issues_selector = Issue:get_search_selector(search_string) +--if #issues > 0 then ui.heading{ content = _"Issues" } execute.view{ module = "issue", view = "_list", - params = { issues = issues, highlight_string = search_string }, + params = { issues_selector = issues_selector, highlight_string = search_string }, } +--end end -if #initiatives > 0 then +if search_for == "initiative" then + initiatives_selector = Initiative:get_search_selector(search_string) +--if #initiatives > 0 then ui.heading{ content = _"Initiatives" } execute.view{ module = "initiative", view = "_list", - params = { initiatives = initiatives, highlight_string = search_string }, + params = { initiatives_selector = initiatives_selector }, } +--end end + diff -r dd0109e81922 -r 5c601807d397 app/main/initiative/_list.lua --- a/app/main/initiative/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/initiative/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -52,21 +52,15 @@ content = function() local initiatives = initiatives_selector:exec() local columns = {} - local issue = initiatives[1] and initiatives[1].issue or {} - if issue.accepted and issue.closed and issue.ranks_available then - columns[#columns+1] = { - content = function(record) + columns[#columns+1] = { + content = function(record) + if record.issue.accepted and record.issue.closed and record.issue.ranks_available then ui.field.rank{ value = record.rank } - end - } - columns[#columns+1] = { - content = function(record) if record.negative_votes and record.positive_votes then local max_value = record.issue.voter_count - trace.debug(record.issue.voter_count) ui.bargraph{ max_value = max_value, - width = 100, + width = 200, bars = { { color = "#0a0", value = record.positive_votes }, { color = "#aaa", value = max_value - record.negative_votes - record.positive_votes }, @@ -74,34 +68,41 @@ } } end - end - } - else - columns[#columns+1] = { - content = function(record) + else local max_value = (record.issue.population or 0) ui.bargraph{ max_value = max_value, - width = 100, + width = 200, bars = { - { color = "#0a0", value = (record.satisfied_informed_supporter_count or 0) }, - { color = "#8f8", value = (record.supporter_count or 0) - (record.satisfied_informed_supporter_count or 0) }, + { color = "#0a0", value = (record.satisfied_supporter_count or 0) }, + { color = "#8f8", value = (record.supporter_count or 0) - (record.satisfied_supporter_count or 0) }, { color = "#ddd", value = max_value - (record.supporter_count or 0) }, } } end - } - end + end + } columns[#columns+1] = { content = function(record) ui.link{ content = function() - util.put_highlighted_string(record.shortened_name) + local name + if record.name_highlighted then + name = encode.highlight(record.name_highlighted) + else + name = encode.html(record.name) + end + slot.put(name) end, module = "initiative", view = "show", id = record.id } + if record.issue.state == "new" then + ui.image{ + static = "icons/16/new.png" + } + end end } diff -r dd0109e81922 -r 5c601807d397 app/main/initiative/new.lua --- a/app/main/initiative/new.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/initiative/new.lua Mon Nov 23 12:00:00 2009 +0100 @@ -39,7 +39,7 @@ } end ui.field.text{ label = _"Name", name = "name" } - ui.field.text{ label = _"Draft", name = "draft", multiline = true } + ui.field.text{ label = _"Draft", name = "draft", multiline = true, attr = { style = "height: 50ex;" } } ui.submit{ text = _"Save" } end } \ No newline at end of file diff -r dd0109e81922 -r 5c601807d397 app/main/initiative/show.lua --- a/app/main/initiative/show.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/initiative/show.lua Mon Nov 23 12:00:00 2009 +0100 @@ -14,6 +14,11 @@ params = { issue_id = initiative.issue_id } } +execute.view{ + module = "issue", + view = "_show_box", + params = { issue = initiative.issue } +} slot.select("path", function() ui.link{ @@ -24,7 +29,7 @@ } ui.container{ content = "::" } ui.link{ - content = _"Issue ##{id} (#{policy_name})":gsub("#{id}", initiative.issue.id):gsub("#{policy_name}", initiative.issue.policy.name), + content = _"Issue ##{id}":gsub("#{id}", initiative.issue.id), module = "issue", view = "show", id = initiative.issue.id @@ -35,6 +40,18 @@ slot.select("actions", function() + if Initiator:by_pk(initiative.id, app.session.member.id) then + ui.link{ + content = function() + ui.image{ static = "icons/16/script_add.png" } + slot.put(_"Edit draft") + end, + module = "draft", + view = "new", + params = { initiative_id = initiative.id } + } + end + ui.twitter("http://example.com/i" .. tostring(initiative.id) .. " " .. initiative.name) end) @@ -88,6 +105,48 @@ end } +local supporter = app.session.member:get_reference_selector("supporters") + :add_where{ "initiative_id = ?", initiative.id } + :optional_object_mode() + :exec() + +if supporter then + local old_draft_id = supporter.draft_id + local new_draft_id = initiative.current_draft.id + if old_draft_id ~= new_draft_id then + ui.container{ + attr = { class = "draft_updated_info" }, + content = function() + slot.put("The draft of this initiative has been updated!") + slot.put(" ") + ui.link{ + content = _"Show diff", + module = "draft", + view = "diff", + params = { + old_draft_id = old_draft_id, + new_draft_id = new_draft_id + } + } + slot.put(" ") + ui.link{ + content = _"Refresh support to current draft", + module = "initiative", + action = "add_support", + id = initiative.id, + routing = { + default = { + mode = "redirect", + module = "initiative", + view = "show", + id = initiative.id + } + } + } + end + } + end +end ui.tabs{ { @@ -95,41 +154,6 @@ label = _"Current draft", content = function() execute.view{ module = "draft", view = "_show", params = { draft = initiative.current_draft } } - if Initiator:by_pk(initiative.id, app.session.member.id) then - ui.link{ - content = function() - ui.image{ static = "icons/16/script_add.png" } - slot.put(_"Add new draft") - end, - module = "draft", - view = "new", - params = { initiative_id = initiative.id } - } - end - end - }, - { - 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 }, { @@ -171,6 +195,30 @@ 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, + 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 + }, } diff -r dd0109e81922 -r 5c601807d397 app/main/issue/_list.lua --- a/app/main/issue/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/issue/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,89 +1,163 @@ local issues_selector = param.get("issues_selector", "table") -local paginate = ui.paginate -local issues - -if not issues_selector then - issues = param.get("issues", "table") - paginate = function(args) - args.content() - end -end - -ui.order{ - name = "issue_list", +ui.filter{ selector = issues_selector, - options = { + filters = { + { + type = "boolean", + name = "any", + label = _"Any", + selector_modifier = function() end + }, { - name = "population", - label = _"Population", - order_by = "issue.population DESC" + type = "boolean", + name = "new", + label = _"New", + selector_modifier = function(selector, value) + if value then + selector:add_where("issue.accepted ISNULL AND issue.closed ISNULL") + end + end + }, + { + type = "boolean", + name = "accepted", + label = _"In discussion", + selector_modifier = function(selector, value) + if value then + selector:add_where("issue.accepted NOTNULL AND issue.half_frozen ISNULL AND issue.closed ISNULL") + end + end }, { - name = "newest", - label = _"Newest", - order_by = "issue.created DESC" + type = "boolean", + name = "half_frozen", + label = _"Frozen", + selector_modifier = function(selector, value) + if value then + selector:add_where("issue.half_frozen NOTNULL AND issue.closed ISNULL") + end + end + }, + { + type = "boolean", + name = "frozen", + label = _"Voting", + selector_modifier = function(selector, value) + if value then + selector:add_where("issue.fully_frozen NOTNULL AND issue.closed ISNULL") + end + end }, { - name = "oldest", - label = _"Oldest", - order_by = "issue.created" - } + type = "boolean", + name = "finished", + label = _"Finished", + selector_modifier = function(selector, value) + if value then + selector:add_where("issue.closed NOTNULL AND ranks_available") + end + end + }, + { + type = "boolean", + name = "cancelled", + label = _"Cancelled", + selector_modifier = function(selector, value) + if value then + selector:add_where("issue.closed NOTNULL AND NOT ranks_available") + end + end + }, }, content = function() - paginate{ + ui.order{ + name = "issue_list", selector = issues_selector, + options = { + { + name = "population", + label = _"Population", + order_by = "issue.population DESC" + }, + { + name = "newest", + label = _"Newest", + order_by = "issue.created DESC" + }, + { + name = "oldest", + label = _"Oldest", + order_by = "issue.created" + } + }, content = function() - local highlight_string = param.get("highlight_string", "string") - ui.list{ - attr = { class = "issues" }, - records = issues or issues_selector:exec(), - columns = { - { - label = _"Issue", - content = function(record) - if not param.get("for_area_list", atom.boolean) then - ui.field.text{ - value = record.area.name - } - slot.put("
") - end - ui.link{ - text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)), - module = "issue", - view = "show", - id = record.id - } - slot.put("
") - slot.put("
") - end - }, - { - label = _"State", - content = function(record) - ui.field.issue_state{ value = record.state } - end - }, - { - label = _"Initiatives", - content = function(record) - execute.view{ - module = "initiative", - view = "_list", - params = { - issue = record, - initiatives_selector = record:get_reference_selector("initiatives"), - highlight_string = highlight_string, - limit = 3 - } - } - end - }, - } + ui.paginate{ + selector = issues_selector, + content = function() + local highlight_string = param.get("highlight_string", "string") + local issues = issues or issues_selector:exec() +-- issues:load(initiatives) + ui.list{ + attr = { class = "issues" }, + records = issues, + columns = { + { + label = _"Issue", + content = function(record) + if not param.get("for_area_list", atom.boolean) then + ui.field.text{ + value = record.area.name + } + slot.put("
") + end + ui.link{ + text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)), + module = "issue", + view = "show", + id = record.id + } + if record.state == "new" then + ui.image{ + static = "icons/16/new.png" + } + end + slot.put("
") + slot.put("
") + end + }, + { + label = _"State", + content = function(record) + ui.field.issue_state{ value = record.state } + end + }, + { + label = _"Initiatives", + content = function(record) + local initiatives_selector = record:get_reference_selector("initiatives") + local highlight_string = param.get("highlight_string") + if highlight_string then + initiatives_selector:add_field( {'"highlight"("initiative"."name", ?)', highlight_string }, "name_highlighted") + end + execute.view{ + module = "initiative", + view = "_list", + params = { + issue = record, + initiatives_selector = initiatives_selector, + highlight_string = highlight_string, + limit = 3 + } + } + end + }, + } + } + end } - end } end -} \ No newline at end of file +} diff -r dd0109e81922 -r 5c601807d397 app/main/issue/_show_box.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/issue/_show_box.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,22 @@ +local issue = param.get("issue", "table") + +slot.select("issue_info", function() + ui.field.text{ + label = _"State", + value = issue.state_name + } + local time_left = issue.state_time_left + if time_left then + ui.field.text{ + label = "Time left", + value = time_left + } + end + local next_state_names = issue.next_states_names + if next_state_names then + ui.field.text{ + label = _"Next states", + value = next_state_names + } + end +end) diff -r dd0109e81922 -r 5c601807d397 app/main/issue/show.lua --- a/app/main/issue/show.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/issue/show.lua Mon Nov 23 12:00:00 2009 +0100 @@ -42,12 +42,17 @@ params = { issue_id = issue.id } } +execute.view{ + module = "issue", + view = "_show_box", + params = { issue = issue } +} + ui.tabs{ { name = "initiatives", label = _"Initiatives", - content = function() - execute.view{ + content = function() execute.view{ module = "initiative", view = "_list", params = { @@ -83,24 +88,48 @@ }, --]] { + name = "delegations", + label = _"Delegations", + content = function() + execute.view{ + module = "delegation", + view = "_list", + params = { delegations_selector = issue:get_reference_selector("delegations") } + } + end + }, + { name = "details", label = _"Details", content = function() + local policy = issue.policy ui.form{ record = issue, readonly = true, attr = { class = "vertical" }, content = function() - trace.debug(issue.created) ui.field.text{ label = _"State", name = "state" } - ui.field.timestamp{ label = _"Created at", name = "created" } - ui.field.timestamp{ label = _"Accepted", name = "accepted" } - ui.field.timestamp{ label = _"Half frozen", name = "half_frozen" } - ui.field.timestamp{ label = _"Fully frozen", name = "fully_frozen" } - ui.field.timestamp{ label = _"Closed", name = "closed" } - ui.field.potential_issue_weight{ label = _"Potential weight", name = "potential_weight" } - ui.field.vote_now{ label = _"Vote now", name = "vote_now" } + ui.field.timestamp{ label = _"Created at", name = "created" } + ui.field.text{ label = _"admission_time", value = policy.admission_time } + ui.field.integer{ label = _"issue_quorum_num", value = policy.issue_quorum_num } + ui.field.integer{ label = _"issue_quorum_den", value = policy.issue_quorum_den } + ui.field.timestamp{ label = _"Accepted", name = "accepted" } + ui.field.text{ label = _"discussion_time", value = policy.discussion_time } + ui.field.vote_now{ label = _"Vote now", name = "vote_now" } ui.field.vote_later{ label = _"Vote later", name = "vote_later" } + ui.field.timestamp{ label = _"Half frozen", name = "half_frozen" } + ui.field.text{ label = _"verification_time", value = policy.verification_time } + ui.field.integer{ label = _"initiative_quorum_num", value = policy.initiative_quorum_num } + ui.field.integer{ label = _"initiative_quorum_den", value = policy.initiative_quorum_den } + ui.field.timestamp{ label = _"Fully frozen", name = "fully_frozen" } + ui.field.text{ label = _"voting_time", value = policy.voting_time } + ui.field.timestamp{ label = _"Closed", name = "closed" } + end + } + ui.form{ + record = issue.policy, + readonly = true, + content = function() end } end diff -r dd0109e81922 -r 5c601807d397 app/main/member/_action/update.lua --- a/app/main/member/_action/update.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/member/_action/update.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,4 +1,20 @@ -param.update(app.session.member, "name") +param.update(app.session.member, + "name", + "organizational_unit", + "internal_posts", + "realname", + "birthday", + "address", + "email", + "xmpp_address", + "website", + "phone", + "mobile_phone", + "profession", + "external_memberships", + "external_posts", + "statement" +) app.session.member:save() diff -r dd0109e81922 -r 5c601807d397 app/main/member/_action/update_avatar.lua --- a/app/main/member/_action/update_avatar.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/member/_action/update_avatar.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,20 +1,52 @@ -local data = param.get("avatar") +local member_id = app.session.member_id + +local member_image = MemberImage:by_pk(member_id, "avatar", false) +local member_image_scaled = MemberImage:by_pk(member_id, "avatar", true) if param.get("avatar_delete", atom.boolean) then - app.session.member.avatar = nil - app.session.member:save() + if member_image then + member_image:destroy() + end + if member_image_scaled then + member_image_scaled:destroy() + end slot.put_into("notice", _"Avatar has been deleted") return end -local data, err, status = os.pfilter(data, "convert", "-", "-thumbnail", "48x48", "-") +local data = param.get("avatar") -if status ~= 0 or data == nil then +local data_scaled, err, status = os.pfilter(data, "convert", "-", "-thumbnail", "48x48", "-") + +if status ~= 0 or data_scaled == nil then error("error while converting image") end +if not member_image then + member_image = MemberImage:new() + member_image.member_id = member_id + member_image.image_type = "avatar" + member_image.scaled = false + member_image.data = "" + member_image:save() +end + +if not member_image_scaled then + member_image_scaled = MemberImage:new() + member_image_scaled.member_id = member_id + member_image_scaled.image_type = "avatar" + member_image_scaled.scaled = true + member_image_scaled.content_type = true + member_image_scaled.data = "" + member_image_scaled:save() +end + if data and #data > 0 then - db:query{ 'UPDATE member SET avatar = $ WHERE id = ?', { db:quote_binary(data) }, app.session.member.id } + db:query{ "UPDATE member_image SET data = $ WHERE member_id = ? AND image_type='avatar' AND scaled=FALSE", { db:quote_binary(data) }, app.session.member.id } +end + +if data_scaled and #data_scaled > 0 then + db:query{ "UPDATE member_image SET data = $ WHERE member_id = ? AND image_type='avatar' AND scaled=TRUE", { db:quote_binary(data_scaled) }, app.session.member.id } end slot.put_into("notice", _"Avatar has been updated") diff -r dd0109e81922 -r 5c601807d397 app/main/member/_list.lua --- a/app/main/member/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/member/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,81 +1,39 @@ local members_selector = param.get("members_selector", "table") -ui.paginate{ +ui.order{ + name = "member_list", selector = members_selector, - content = function() - ui.list{ - records = members_selector:exec(), - columns = { - { - content = function(record) - ui.image{ - attr = { style="height: 24px;" }, - module = "member", - view = "avatar", - extension = "jpg", - id = record.id - } - end - }, - { - label = _"Login", - content = function(record) - ui.link{ - text = record.login, - module = "member", - view = "show", - id = record.id - } - end - }, - { - label = _"Name", - content = function(record) - ui.link{ - content = function() - util.put_highlighted_string(record.name) - end, - module = "member", - view = "show", - id = record.id - } - end - }, - { - label = _"Ident number", - name = "ident_number" - }, - { - label = _"Admin?", - name = "admin" - }, - { - label = "Locked?", - content = function(record) - ui.field.boolean{ value = record.locked } - end - }, - { - content = function(record) - ui.link{ - attr = { class = "action" }, - text = _"Add to my contacts", - module = "contact", - action = "add_member", - id = record.id, - routing = { - default = { - mode = "redirect", - module = request.get_module(), - view = request.get_view(), - id = param.get_id_cgi(), - params = param.get_all_cgi() - } + options = { + { + name = "name", + label = _"A-Z", + order_by = "name" + }, + { + name = "name_desc", + label = _"Z-A", + order_by = "name DESC" + }, + }, + content = function() + ui.paginate{ + selector = members_selector, + per_page = 100, + content = function() + ui.container{ + attr = { class = "member_list" }, + content = function() + for i, member in ipairs(members_selector:exec()) do + execute.view{ + module = "member", + view = "_show_thumb", + params = { member = member } } - } + end end } - } + slot.put('
') + end } end } \ No newline at end of file diff -r dd0109e81922 -r 5c601807d397 app/main/member/_show.lua --- a/app/main/member/_show.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/member/_show.lua Mon Nov 23 12:00:00 2009 +0100 @@ -5,11 +5,11 @@ record = member, readonly = true, content = function() - ui.field.text{ label = _"Login", name = "login" } - ui.field.text{ label = _"Name", name = "name" } ui.field.boolean{ label = _"Admin?", name = "admin" } ui.field.boolean{ label = _"Locked?", name = "locked" } - ui.field.text{ label = _"Ident number", name = "ident_number" } + if member.ident_number then + ui.field.text{ label = _"Ident number", name = "ident_number" } + end ui.submit{ text = _"Save" } end } @@ -55,7 +55,7 @@ execute.view{ module = "delegation", view = "_list", - params = { selector = member:get_reference_selector("incoming_delegations") } + params = { delegations_selector = member:get_reference_selector("incoming_delegations"), incoming = true } } end }, @@ -66,7 +66,7 @@ execute.view{ module = "delegation", view = "_list", - params = { selector = member:get_reference_selector("outgoing_delegations") } + params = { delegations_selector = member:get_reference_selector("outgoing_delegations"), outgoing = true } } end }, diff -r dd0109e81922 -r 5c601807d397 app/main/member/_show_thumb.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/main/member/_show_thumb.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,25 @@ +local member = param.get("member", "table") + +local name +if member.name_highlighted then + name = encode.highlight(member.name_highlighted) +else + name = encode.html(member.name) +end + +ui.link{ + attr = { class = "member_thumb" }, + module = "member", + view = "show", + id = member.id, + content = function() + ui.image{ + attr = { width = 48, height = 48 }, + module = "member", + view = "avatar", + id = member.id, + extension = "jpg" + } + slot.put(name) + end +} \ No newline at end of file diff -r dd0109e81922 -r 5c601807d397 app/main/member/avatar.lua --- a/app/main/member/avatar.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/member/avatar.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,14 +1,14 @@ -local record = Member:by_id(param.get_id()) +local record = MemberImage:by_pk(param.get_id(), "avatar", true) -if false and (not record or not record.avatar) then - print('Location: ' .. encode.url{ static = 'no_image.png' } .. '\n\n') +if record == nil then + print('Location: ' .. encode.url{ static = 'avatar.jpg' } .. '\n\n') exit() end -print('Content-type: image/jpg\n') +print('Content-type: ' .. record.content_type .. '\n') if record then - io.stdout:write(record.avatar) + io.stdout:write(record.data) else end diff -r dd0109e81922 -r 5c601807d397 app/main/member/edit.lua --- a/app/main/member/edit.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/member/edit.lua Mon Nov 23 12:00:00 2009 +0100 @@ -25,6 +25,20 @@ }, content = function() ui.field.text{ label = _"Name", name = "name" } + 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 = _"Address", name = "address", multiline = true } + ui.field.text{ label = _"email", name = "email" } + ui.field.text{ label = _"xmpp", name = "xmpp_address" } + ui.field.text{ label = _"Website", name = "website" } + ui.field.text{ label = _"Phone", name = "phone" } + ui.field.text{ label = _"Mobile phone", name = "mobile_phone" } + ui.field.text{ label = _"Profession", name = "profession" } + ui.field.text{ label = _"External memberships", name = "external_memberships", multiline = true } + ui.field.text{ label = _"External posts", name = "external_posts", multiline = true } + ui.field.text{ label = _"Statement", name = "statement", multiline = true } ui.submit{ value = _"Save" } end } \ No newline at end of file diff -r dd0109e81922 -r 5c601807d397 app/main/membership/_show_box.lua --- a/app/main/membership/_show_box.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/membership/_show_box.lua Mon Nov 23 12:00:00 2009 +0100 @@ -6,7 +6,7 @@ ui.container{ attr = { class = "head", - onclick = "document.getElementById('interest_content').style.display = 'block';" + onclick = "document.getElementById('membership_content').style.display = 'block';" }, content = function() if membership then @@ -18,8 +18,16 @@ } ui.container{ - attr = { class = "content", id = "interest_content" }, + attr = { class = "content", id = "membership_content" }, content = function() + ui.container{ + attr = { + class = "close", + style = "cursor: pointer;", + onclick = "document.getElementById('membership_content').style.display = 'none';" + }, + content = _"X" + } if membership then ui.link{ content = _"Remove my membership", @@ -56,14 +64,6 @@ routing = { default = { mode = "redirect", module = "area", view = "show", id = area.id } } } end - ui.container{ - attr = { - class = "head", - style = "cursor: pointer;", - onclick = "document.getElementById('interest_content').style.display = 'none';" - }, - content = _"Click here to close." - } end } end) diff -r dd0109e81922 -r 5c601807d397 app/main/suggestion/_list.lua --- a/app/main/suggestion/_list.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/suggestion/_list.lua Mon Nov 23 12:00:00 2009 +0100 @@ -4,6 +4,7 @@ selector = suggestions_selector, content = function() ui.list{ + attr = { style = "table-layout: fixed;" }, records = suggestions_selector:exec(), columns = { { @@ -24,7 +25,7 @@ local max_value = record.initiative.issue.population ui.bargraph{ max_value = max_value, - width = 50, + width = 100, bars = { { color = "#ddd", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.minus2_fulfilled_count - record.minus1_fulfilled_count }, { color = "#f88", value = record.minus1_unfulfilled_count + record.minus1_fulfilled_count }, @@ -102,13 +103,14 @@ end }, { - label = _"Not fullfilled", + label = _"Suggestion currently not implemented", + label_attr = { style = "width: 101px;" }, content = function(record) if record.minus2_unfulfilled_count then local max_value = record.initiative.issue.population ui.bargraph{ max_value = max_value, - width = 50, + width = 100, bars = { { color = "#ddd", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count }, { color = "#f88", value = record.minus1_unfulfilled_count }, @@ -122,13 +124,14 @@ end }, { - label = _"Fullfilled", + label = _"Suggestion currently implemented", + label_attr = { style = "width: 101px;" }, content = function(record) if record.minus2_fulfilled_count then local max_value = record.initiative.issue.population ui.bargraph{ max_value = max_value, - width = 50, + width = 100, bars = { { color = "#ddd", value = max_value - record.minus2_fulfilled_count - record.minus1_fulfilled_count }, { color = "#f88", value = record.minus1_fulfilled_count }, @@ -153,7 +156,7 @@ ui.image{ static = "icons/16/cross.png" } ui.link{ attr = { class = "action" }, - text = _"set fulfilled", + text = _"set implented", module = "opinion", action = "update", routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, @@ -166,7 +169,7 @@ ui.image{ static = "icons/16/tick.png" } ui.link{ attr = { class = "action" }, - text = _"remove fulfilled", + text = _"remove implemented", module = "opinion", action = "update", routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, diff -r dd0109e81922 -r 5c601807d397 app/main/supporter/_show_box.lua --- a/app/main/supporter/_show_box.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/app/main/supporter/_show_box.lua Mon Nov 23 12:00:00 2009 +0100 @@ -27,6 +27,14 @@ ui.container{ attr = { class = "content", id = "support_content" }, content = function() + ui.container{ + attr = { + class = "close", + style = "cursor: pointer;", + onclick = "document.getElementById('support_content').style.display = 'none';" + }, + content = _"X" + } if supported then ui.link{ content = function() diff -r dd0109e81922 -r 5c601807d397 config/default.lua --- a/config/default.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/config/default.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,10 +1,14 @@ config.app_name = "LiquidFeedback" -config.app_version = "alpha2" +config.app_version = "alpha3" config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)" config.app_service_provider = "Snake Oil
10000 Berlin
Germany" +config.member_image_convert = { + avatar = { "convert", "-", "-thumbnail", "48x48", "-" } +} + -- uncomment the following two lines to use C implementations of chosen -- functions and to disable garbage collection during the request, to -- increase speed: @@ -37,7 +41,7 @@ - +-- TODO abstraction -- get record by id function mondelefant.class_prototype:by_id(id) local selector = self:new_selector() @@ -46,3 +50,4 @@ return selector:exec() end + diff -r dd0109e81922 -r 5c601807d397 config/development.lua --- a/config/development.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/config/development.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,2 +1,3 @@ execute.config("default") +config.wiki_parser_executeable = "/opt/rocketwiki/rocketwiki" diff -r dd0109e81922 -r 5c601807d397 config/testing.lua --- a/config/testing.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/config/testing.lua Mon Nov 23 12:00:00 2009 +0100 @@ -1,6 +1,3 @@ execute.config("default") - - - - +config.wiki_parser_executeable = "/opt/liquid_feedback_testing/rocketwiki/rocketwiki" diff -r dd0109e81922 -r 5c601807d397 env/encode/highlight.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/env/encode/highlight.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,10 @@ +function encode.highlight(text) + local text = encode.html(text) + text = text:gsub("\027", "") + text = text:gsub("\\\\", "\027b") + text = text:gsub("\\%*", "\027a") + text = text:gsub("%*([^%*]*)%*", '%1') + text = text:gsub("\027a", "*") + text = text:gsub("\027b", "\\") + return text +end \ No newline at end of file diff -r dd0109e81922 -r 5c601807d397 env/format/wiki_text.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/env/format/wiki_text.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,11 @@ +function format.wiki_text(wiki_text) + local html, errmsg, exitcode = assert( + os.pfilter(wiki_text, config.wiki_parser_executeable) + ) + if exitcode > 0 then + error("Wiki parser process returned with error code " .. tostring(exitcode)) + elseif exitcode < 0 then + error("Wiki parser process was terminated by signal " .. tostring(-exitcode)) + end + return html +end diff -r dd0109e81922 -r 5c601807d397 env/ui/bargraph.lua --- a/env/ui/bargraph.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/env/ui/bargraph.lua Mon Nov 23 12:00:00 2009 +0100 @@ -5,14 +5,16 @@ }, content = function() for i, bar in ipairs(args.bars) do - local value = bar.value * args.width / args.max_value / 2 - ui.container{ - attr = { - style = "width: " .. tostring(value) .. "px; background-color: " .. bar.color .. ";", - title = tostring(bar.value) - }, - content = function() slot.put(" ") end - } + if bar.value > 0 then + local value = bar.value * args.width / args.max_value / 2 + ui.container{ + attr = { + style = "width: " .. tostring(value) .. "px; background-color: " .. bar.color .. ";", + title = tostring(bar.value) + }, + content = function() slot.put(" ") end + } + end end end } diff -r dd0109e81922 -r 5c601807d397 env/ui/filter.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/env/ui/filter.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,40 @@ +function ui.filter(args) + local name = args.name or "filter" + local current_filter = atom.string:load(cgi.params[name]) or args.filters[1].name + local id = param.get_id_cgi() + local params = param.get_all_cgi() + ui.container{ + attr = { class = "ui_filter" }, + content = function() + ui.container{ + attr = { class = "ui_filter_head" }, + content = function() + slot.put(_"Filter") + slot.put(": ") + for i, filter in ipairs(args.filters) do + params[name] = filter.name + local attr = {} + if current_filter == filter.name then + attr.class = "active" + filter.selector_modifier(args.selector, true) + end + ui.link{ + attr = attr, + module = request.get_module(), + view = request.get_view(), + id = id, + params = params, + content = filter.label + } + end + end + } + ui.container{ + attr = { class = "ui_filter_content" }, + content = function() + args.content() + end + } + end + } +end diff -r dd0109e81922 -r 5c601807d397 locale/translations.de.lua --- a/locale/translations.de.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/locale/translations.de.lua Mon Nov 23 12:00:00 2009 +0100 @@ -11,25 +11,33 @@ ["Add new initiative to issue"] = "Neue Initiative zum Thema hinzufügen"; ["Add new suggestion"] = "Neue Anregung hinzufügen"; ["Add to my contacts"] = "Zu meinen Kontakten hinzufügen"; +["Address"] = "Anschrift"; ["Admin"] = "Admin"; ["Admin menu"] = "Admin Menü"; ["Admin?"] = "Admin?"; ["Admitted"] = "zugelassen"; +["Any"] = "Alle"; ["Area"] = "Themenbereich"; ["Area '#{name}'"] = "Themenbereich '#{name}'"; +["Area delegation"] = "Area-Delegation"; ["Area list"] = "Liste der Themenbereiche"; ["Area successfully updated"] = "Themenbereich erfolgreich aktualisiert"; ["Areas"] = "Themenbereiche"; ["Author"] = "Autor"; ["Autoreject is off."] = "Auto-Ablehnen ist aus"; ["Autoreject is on."] = "Auto-Ablehnen ist an"; -["Cancel"] = false; +["Avatar"] = false; +["Avatar has been deleted"] = "Avatar wurde gelöscht"; +["Avatar has been updated"] = "Avatar wurde aktualisiert"; +["Birthday"] = "Geburtstag"; +["Cancel"] = "Abbrechen"; ["Cancelled"] = "Abgebrochen"; ["Change password"] = "Kennwort ändern"; ["Click here to close."] = "Zum Schließen hier klicken"; ["Close"] = "Schließen"; ["Closed"] = "geschlossen"; ["Commit suggestion"] = "Anregung speichern"; +["Compare"] = "Vergleichen"; ["Contacts"] = "Kontakte"; ["Content"] = "Inhalt"; ["Create new area"] = "Neuen Themenbereich anlegen"; @@ -38,39 +46,45 @@ ["Current draft"] = "Aktueller Entwurf"; ["Degree"] = "Grad"; ["Delegate"] = "Delegieren"; +["Delegations"] = "Delegationen"; ["Description"] = "Beschreibung"; ["Details"] = "Details"; +["Diff"] = false; ["Direct member count"] = "Anzahl Direktmitglieder"; ["Direct supporter [change]"] = "Direkte Unterstützung [ändern]"; ["Draft"] = "Entwurf"; ["Edit"] = "Bearbeiten"; +["Edit draft"] = "Entwurf bearbeiten"; ["Edit my page"] = "Meine Seite bearbeiten"; ["Error while updating member, database reported:

(#{errormessage})"] = "Fehler beim aktualisieren des Mitglieds, die Datenbank berichtet folgenden Fehler:

(#{errormessage})"; +["External memberships"] = "Externe Mitgliedschaften"; +["External posts"] = "Externe Ämter"; +["Filter"] = false; ["Finished"] = "Abgeschlossen"; ["Frozen"] = "Eingefroren"; ["Fulfilled"] = "Erfüllt"; -["Fullfilled"] = "Erfüllt"; -["Fully frozen"] = false; +["Fully frozen"] = "Ganz eingefroren"; ["Global delegation"] = "Globale Delegation"; -["Half frozen"] = false; +["Half frozen"] = "Halb eingefroren"; ["Hide"] = "Verstecken"; ["Home"] = "Startseite"; ["Id"] = "Id"; ["Ident number"] = "Ident-Nummer"; ["In discussion"] = "In Diskussion"; ["Incoming delegations"] = "Eingehende Delegationen"; -["Initiative"] = "Initiative"; ["Initiative successfully created"] = "Initiative erfolgreich erzeugt"; ["Initiative: '#{name}'"] = "Initiative: '#{name}'"; ["Initiatives"] = "Initiativen"; ["Initiators"] = "Initiatoren"; -["Interest not existant"] = false; +["Interest not existant"] = "Interesse existiert nicht"; ["Interest removed"] = "Interesse entfernt"; ["Interest updated"] = "Interesse aktualisiert"; +["Internal posts"] = "Interne Ämter"; ["Invalid username or password!"] = "Ungültiger Benutzername oder Kennwort"; ["Issue"] = "Thema"; ["Issue ##{id}"] = "Issue ##{id}"; ["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})"; +["Issue delegation"] = "Issue-Delegation"; ["Issue policy"] = "Regelwerk für Thema"; ["Issues"] = "Themen"; ["License"] = "Lizenz"; @@ -80,7 +94,6 @@ ["Login successful!"] = "Anmeldung erfolgreich"; ["Logout"] = "Abmelden"; ["Logout successful"] = "Abmeldung erfolgreich"; -["Me"] = "Ich"; ["Member '#{member}'"] = "Mitglied '#{member}'"; ["Member has been removed from your contacts"] = "Mitglied wurde aus Deinen Kontakten entfernt"; ["Member has been saved as private contact"] = "Mitglied wurde als privater Kontakt gespeichert"; @@ -93,33 +106,40 @@ ["Member successfully updated"] = "Mitglied erfolgreich aktualisert"; ["Member: '#{login}' (#{name})"] = "Mitlied: '#{login}' (#{name})"; ["Members"] = "Mitglieder"; -["Membership not existant"] = false; +["Membership not existant"] = "Mitgliedschaft exisitert nicht"; ["Membership removed"] = "Mitgliedschaft entfernt"; ["Membership updated"] = "Mitgliedschaft aktualisiert"; +["Mobile phone"] = "Mobiltelefon"; ["Name"] = "Name"; ["New"] = "Neu"; ["New draft has been added to initiative"] = "Neuer Entwurf wurde der Initiative hinzugefügt"; +["New draft revision"] = "Neue Revision des Entwurfs"; ["New password"] = "Neues Kennwort"; ["New passwords does not match."] = "Du hast nicht zweimal das gleiche Kennwort eingegeben"; ["New passwords is too short."] = "Das neue Kennwort ist zu kurz"; ["Newest"] = "Neueste"; +["Next states"] = "Nächste Statuse"; ["No supporter [change]"] = "Keine Unterstützung (ändern)"; -["Not fullfilled"] = "Nicht erfüllt"; ["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"; ["Order by"] = "Sortieren nach"; +["Organizational unit"] = "Organisationseinheit"; ["Outgoing delegations"] = "Ausgehende Delegationen"; ["Password"] = "Kennwort"; +["Phone"] = "Telefon"; ["Policy"] = "Regelwerk"; ["Population"] = "Grundgesamtheit"; -["Potential weight"] = "Potentielles Gewicht"; +["Profession"] = "Beruf"; ["Publish"] = "Veröffentlichen"; ["Published"] = "veröffentlicht"; ["Published contacts"] = "Veröffentlichte Kontakte"; ["Rank"] = "Rang"; +["Real name"] = "Realname"; +["Refresh support to current draft"] = "Unterstützung auf aktuellen Entwurf aktualisieren"; ["Register new member"] = "Neues Mitglied registrieren"; ["Remove"] = "Entfernen"; ["Remove autoreject"] = "Auto-Ablehnen abschalten"; @@ -142,9 +162,13 @@ ["Show active members"] = "Zeige aktive Mitglieder"; ["Show areas in use"] = "Zeige verwendete Themenbereiche"; ["Show areas not in use"] = "Zeige nicht verwendente Themenbereiche"; +["Show diff"] = "Änderungen anzeigen"; ["Show locked members"] = "Zeige gesperrte Mitglieder"; ["Software"] = false; ["State"] = "Zustand"; +["Statement"] = false; +["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"; ["Support"] = "Unterstützung"; @@ -152,8 +176,8 @@ ["Support this initiative"] = "Diese Initiative unterstützen"; ["Supporter"] = "Unterstützer"; ["That's me!"] = "Das bin ich"; +["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht"; ["Trustee"] = "Bevollmächtigter"; -["Truster"] = "Delegierender"; ["Unknown author"] = "Unbekannter Autor"; ["Upload avatar"] = "Avatar hochladen"; ["Username"] = "Benutzername"; @@ -162,6 +186,8 @@ ["Vote now"] = "Jetzt abstimmen"; ["Voting"] = "Abstimmung"; ["Voting requests"] = "Abstimmanträge"; +["Website"] = "Webseite"; +["X"] = false; ["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 interested. [more]"] = "Du bist interessiert. [mehr]"; @@ -176,6 +202,7 @@ ["Your global delegation has been deleted."] = "Deine globale Delegation wurde gelöscht"; ["Your global delegation has been updated."] = "Deine globale Delegation würde geändert"; ["Your opinion has been updated"] = "Deine Meinung wurde aktualisiert"; +["Your page has been updated"] = "Deine Seite wurde aktualisiert"; ["Your password has been updated successfully"] = "Das Kennwort wurde erfolgreich geändert"; ["Your suggestion has been added"] = "Deine Anregung wurde hinzufügt"; ["Your support has been added to this initiative"] = "Deine Unterstützung wurde der Initiative hinzugefügt"; @@ -183,11 +210,22 @@ ["Your support has been updated to the latest draft"] = "Deine Unterstützung wurde auf den neuesten Entwurf aktualisiert"; ["Your vote is delegated. [more]"] = "Deine Stimme ist delegiert. [mehr]"; ["Z-A"] = false; +["admission_time"] = false; +["delete

"] = false; +["discussion_time"] = false; +["email"] = false; +["initiative_quorum_den"] = false; +["initiative_quorum_num"] = false; +["issue_quorum_den"] = false; +["issue_quorum_num"] = false; ["must"] = "muss"; ["must not"] = "darf nicht"; ["neutral"] = "neutral"; -["remove fulfilled"] = "erfüllt entfernen"; -["set fulfilled"] = "erfüllt setzten"; +["remove implemented"] = "entferne umgesetzt"; +["set implented"] = "setze umgesetzt"; ["should"] = "soll"; ["should not"] = "soll nicht"; +["verification_time"] = false; +["voting_time"] = false; +["xmpp"] = false; } diff -r dd0109e81922 -r 5c601807d397 model/area.lua --- a/model/area.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/model/area.lua Mon Nov 23 12:00:00 2009 +0100 @@ -20,6 +20,15 @@ } Area:add_reference{ + mode = '1m', + to = "Delegation", + this_key = 'id', + that_key = 'area_id', + ref = 'delegations', + back_ref = 'area' +} + +Area:add_reference{ mode = 'mm', to = "Member", this_key = 'id', diff -r dd0109e81922 -r 5c601807d397 model/initiative.lua --- a/model/initiative.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/model/initiative.lua Mon Nov 23 12:00:00 2009 +0100 @@ -91,12 +91,18 @@ ref = 'supporting_members' } -function Initiative:search(search_string) +function Initiative:get_search_selector(search_string) return self:new_selector() - :add_where{ '"initiative"."name" ILIKE ?', "%" .. search_string:gsub("%%", "") .. "%" } - :exec() + :add_field( {'"highlight"("initiative"."name", ?)', search_string }, "name_highlighted") + :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string } end +function Member:get_search_selector(search_string) + return self:new_selector() + :add_where("active") +end + + function Initiative.object_get:current_draft() return Draft:new_selector() :add_where{ '"initiative_id" = ?', self.id } diff -r dd0109e81922 -r 5c601807d397 model/issue.lua --- a/model/issue.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/model/issue.lua Mon Nov 23 12:00:00 2009 +0100 @@ -67,6 +67,15 @@ } Issue:add_reference{ + mode = '1m', + to = "Delegation", + this_key = 'id', + that_key = 'issue_id', + ref = 'delegations', + back_ref = 'issue' +} + +Issue:add_reference{ mode = 'mm', to = "Member", this_key = 'id', @@ -78,32 +87,95 @@ } function Issue:get_state_name_for_state(value) - local state_name_table = {} + local state_name_table = { + new = _"New", + accepted = _"Accepted", + frozen = _"Frozen", + voting = _"Voting", + finished = _"Finished", + cancelled = _"Cancelled" + } return state_name_table[value] or value end -function Issue:search(search_string) +function Issue:get_search_selector(search_string) return self:new_selector() :join('"initiative"', nil, '"initiative"."issue_id" = "issue"."id"') - :add_where{ '"initiative"."name" ILIKE ?', "%" .. search_string:gsub("%%", "") .. "%" } + :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string } :set_distinct() - :exec() end function Issue.object_get:state() if self.accepted then - if self.frozen then + if self.fully_frozen then + return "voting" + elseif self.half_frozen then return "frozen" elseif self.closed then - return "closed" + if self.ranks_available then + return "finished" + else + return "cancelled" + end else return "accepted" end else if self.closed then - return "closed" + return "cancelled" else return "new" end end +end + +function Issue.object_get:state_name() + return Issue:get_state_name_for_state(self.state) +end + +function Issue.object_get:state_time_left() + local state = self.state + local last_event_time + local duration + if state == "new" then + last_event_time = self.created + duration = self.policy.admission_time + elseif state == "accepted" then + last_event_time = self.accepted + duration = self.policy.discussion_time + elseif state == "frozen" then + last_event_time = self.half_frozen + duration = self.policy.verification_time + elseif state == "voting" then + last_event_time = self.fully_frozen + duration = self.policy.voting_time + end + return db:query{ "SELECT ?::timestamp + ?::interval - now() as time_left", last_event_time, duration }[1].time_left +end + +function Issue.object_get:next_states() + local state = self.state + local next_states + if state == "new" then + next_states = { "accepted", "cancelled" } + elseif state == "accepted" then + next_states = { "frozen" } + elseif state == "frozen" then + next_states = { "voting" } + elseif state == "voting" then + next_states = { "finished" } + end + return next_states +end + +function Issue.object_get:next_states_names() + local next_states = self.next_states + if not next_states then + return + end + local state_names = {} + for i, state in ipairs(self.next_states) do + state_names[#state_names+1] = Issue:get_state_name_for_state(state) + end + return table.concat(state_names, ", ") end \ No newline at end of file diff -r dd0109e81922 -r 5c601807d397 model/member.lua --- a/model/member.lua Wed Nov 18 12:00:00 2009 +0100 +++ b/model/member.lua Mon Nov 23 12:00:00 2009 +0100 @@ -2,6 +2,15 @@ Member.table = 'member' Member:add_reference{ + mode = '11', + to = "MemberImage", + this_key = 'id', + that_key = 'member_id', + ref = 'image', + back_ref = 'member' +} + +Member:add_reference{ mode = '1m', to = "Contact", this_key = 'id', @@ -87,8 +96,7 @@ this_key = 'id', that_key = 'member_id', ref = 'supporters', - back_ref = 'member', - default_order = '"id"' + back_ref = 'member' } Member:add_reference{ @@ -248,9 +256,10 @@ end end -function Member:search(search_string) +function Member:get_search_selector(search_string) return self:new_selector() - :add_where{ '"member"."name" ILIKE ?', "%" .. search_string:gsub("%%", "") .. "%" } + :add_field( {'"highlight"("member"."name", ?)', search_string }, "name_highlighted") + :add_where{ '"member"."text_search_data" @@ "text_search_query"(?)', search_string } :add_where("active") - :exec() end + diff -r dd0109e81922 -r 5c601807d397 model/member_image.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/model/member_image.lua Mon Nov 23 12:00:00 2009 +0100 @@ -0,0 +1,12 @@ +MemberImage = mondelefant.new_class() +MemberImage.table = "member_image" +MemberImage.primary_key = { "member_id", "image_type" } + +function MemberImage:by_pk(member_id, image_type, scaled) + return self:new_selector() + :add_where{ "member_id = ?", member_id } + :add_where{ "image_type = ?", image_type } + :add_where{ "scaled = ?", scaled } + :optional_object_mode() + :exec() +end diff -r dd0109e81922 -r 5c601807d397 static/avatar.jpg Binary file static/avatar.jpg has changed diff -r dd0109e81922 -r 5c601807d397 static/delegation_arrow.jpg Binary file static/delegation_arrow.jpg has changed diff -r dd0109e81922 -r 5c601807d397 static/delegation_arrow_vertical.jpg Binary file static/delegation_arrow_vertical.jpg has changed diff -r dd0109e81922 -r 5c601807d397 static/icons/16/new.png Binary file static/icons/16/new.png has changed diff -r dd0109e81922 -r 5c601807d397 static/style.css --- a/static/style.css Wed Nov 18 12:00:00 2009 +0100 +++ b/static/style.css Mon Nov 23 12:00:00 2009 +0100 @@ -23,8 +23,17 @@ } td, th { + padding: 0.5ex 0.5em 0.5ex 0.5em; +} + +td { vertical-align: top; - padding: 0.5ex 0.5em 0.5ex 0.5em; +} + +th { + vertical-align: bottom; + font-size: 75%; + font-weight: bold; } a.active { @@ -40,6 +49,10 @@ font-style: italic; } +a { + vertical-align: middle; +} + /************************************************************************* * Notices, warnings and errors */ @@ -195,6 +208,8 @@ .avatar { float: left; margin-right: 0.5em; + width: 48px; + height: 48px; } .actions { @@ -224,6 +239,62 @@ } /************************************************************************* + * vote info / delegation + */ + +.slot_interest, +.slot_support, +.slot_delegation { + float: left; + font-size: 75%; + margin-right: 1em; +} + +.vote_info .head { + line-height: 200%; +} + +.vote_info .close { + background-color: #f44; + float: right; + padding: 1ex; +} + +.vote_info .content { + display: none; + position: absolute; + z-index: 10; + background-color: #fff; + border: 2px solid #444; + padding: 1em; +} + +.vote_info .delegation_arrow { + margin-top: 1ex; + margin-bottom: 1ex; + vertical-align: middle; +} + +.vote_info .delegation_scope { + display: inline; +} + +.vote_info .delegation_info { +} + +.vote_info .member_thumb { + clear: left; +} + +.delegation .revoke { + margin: 0.5ex; +} + +.delegation .revoke img { + vertical-align: middle; +} + +/************************************************************************* * Main content */ @@ -268,6 +339,28 @@ * ui.order */ +.ui_filter_head { + color: #777; + float: left; + margin-bottom: 1ex; + font-size: 75%; +} + +.ui_filter_head a { + color: #777; + padding: 0.5ex; +} + +.ui_filter_head a.active{ + color: #fff; + background-color: #777; + padding: 0.5ex; +} + +/************************************************************************* + * ui.order + */ + .ui_order_head { color: #777; text-align: right; @@ -299,13 +392,13 @@ */ .bargraph { - width: 50px; + width: 101px; } .bargraph div { float: left; margin-top: 0.5ex; - height: 1ex; + height: 1.3ex; } /************************************************************************* @@ -320,8 +413,8 @@ .vertical select { font-family: sans-serif; font-size: 100%; + width: 50em; border: 1px solid #444; - width: 40em; padding: 0.2ex 0.2em 0.2ex 0.2em; margin-bottom: 1ex; } @@ -498,62 +591,109 @@ */ .issues tr { - border-bottom: 1px solid #444; + border: 1px solid #ccc; } .issues tr tr { - border-bottom: none; -} - -/************************************************************************* - * delegation - */ - -.infobox { - float: right; - margin-right: 1em; -} - -.slot_interest, -.slot_support, -.slot_delegation { - border: 2px solid #444; - width: 20em; - font-size: 75%; - padding: 0; - margin-bottom: 0.5ex; -} - -.infobox .head { - margin: 0.5ex; -} - -.infobox .content { - display: none; - position: absolute; - z-index: 10; - width: 20em; - background-color: #fff; - border: 2px solid #444; -} - -.infobox .text { - margin: 0.5ex; - margin-bottom: 1ex; -} - -.infobox .member { - margin: 0.5ex; - display: block; - font-weight: bold; - margin-bottom: 1ex; -} - -.delegation .revoke { - margin: 0.5ex; + border: none; } .lang_chooser { float: right; margin-right: 0.5em; } + +.delegation_list_entry { + margin-right: 2em; + margin-bottom: 2ex; + float: left; + clear: left; +} + +.delegation_list_entry .delegation_arrow { + float: left; +} + +.delegation_list_entry .delegation_scope { + float: left; + width: 25em; +} + +.delegation_list_entry .delegation_scope a { + display: block; +} + +.member_list .member_thumb { + float: left; + margin-right: 1em; + margin-bottom: 2ex; +} + +.member_thumb { + text-decoration: none; + min-width: 150px; + display: block; + float: left; + border: 1px solid #ccc; +} + +.member_thumb:hover { + border: 1px solid #000; +} + +.member_thumb img { + margin-right: 0.5em; + vertical-align: bottom; +} + +.member_thumb div { + display: inline; + margin-right: 0.5em; +} + +.draft_content { + background-color: #eee; + border: 1px solid #ccc; + padding: 1ex; +} + +.diff { + background-color: #eee; + border: 1px solid #ccc; + padding: 1ex; +} + +.diff .added { + background-color: #cfc; +} + +.diff .removed { + background-color: #fcc; +} + +.slot_issue_info { + background-color: #eee; + border: 1px solid #ccc; + float: right; + padding: 0.5ex; + line-height: 130%; + margin-right: 1em; +} + +.issue_info label { + float: left; + width: 8em; + text-transform: uppercase; + font-size: 70%; + color: #777; + font-weight: bold; + clear: left; + text-align: right; + margin-right: 0.7em; +} + +.draft_updated_info { + border: 2px solid #faa; + background-color: #fee; + padding: 1ex; +} \ No newline at end of file