liquid_feedback_frontend
changeset 11:77d58efe99fd beta7
Version beta7
Important security fixes:
- Added missing HTML encoding to postal address of member
- Link to discussion URL only if it starts with http(s)://
Other bugfixes:
- Fixed wrong display of 2nd level delegating voters for an initiative
- Do not display invited initiators as initiators while voting
- Added missing translation
New features:
- Public message of the day
- Both direct and indirect supporter count is shown in tab heads
- Support shown in initiative lists
Language chooser at the login page has been added (again)
Important security fixes:
- Added missing HTML encoding to postal address of member
- Link to discussion URL only if it starts with http(s)://
Other bugfixes:
- Fixed wrong display of 2nd level delegating voters for an initiative
- Do not display invited initiators as initiators while voting
- Added missing translation
New features:
- Public message of the day
- Both direct and indirect supporter count is shown in tab heads
- Support shown in initiative lists
Language chooser at the login page has been added (again)
line diff
1.1 --- a/app/main/_filter/21_auth.lua Sun Jan 10 12:00:00 2010 +0100 1.2 +++ b/app/main/_filter/21_auth.lua Fri Jan 22 12:00:00 2010 +0100 1.3 @@ -10,6 +10,7 @@ 1.4 or request.get_action() == "reset_password" 1.5 or request.get_view() == "confirm_notify_email" 1.6 or request.get_action() == "confirm_notify_email" 1.7 + or request.get_action() == "set_lang" 1.8 ) 1.9 ) 1.10
2.1 --- a/app/main/_filter_view/30_navigation.lua Sun Jan 10 12:00:00 2010 +0100 2.2 +++ b/app/main/_filter_view/30_navigation.lua Fri Jan 22 12:00:00 2010 +0100 2.3 @@ -42,50 +42,77 @@ 2.4 2.5 slot.select('navigation', function() 2.6 2.7 - ui.link{ 2.8 - content = function() 2.9 - ui.image{ static = "icons/16/house.png" } 2.10 - slot.put(_"Home") 2.11 - end, 2.12 - module = 'index', 2.13 - view = 'index' 2.14 - } 2.15 + ui.link{ 2.16 + content = function() 2.17 + ui.image{ static = "icons/16/house.png" } 2.18 + slot.put(_"Home") 2.19 + end, 2.20 + module = 'index', 2.21 + view = 'index' 2.22 + } 2.23 + 2.24 + local setting_key = "liquidfeedback_frontend_timeline_current_options" 2.25 + local setting = Setting:by_pk(app.session.member.id, setting_key) 2.26 2.27 - ui.link{ 2.28 - content = function() 2.29 - ui.image{ static = "icons/16/package.png" } 2.30 - slot.put(_"Areas") 2.31 - end, 2.32 - module = 'area', 2.33 - view = 'list' 2.34 - } 2.35 + timeline_params = {} 2.36 + if setting then 2.37 + for event_ident, filter_idents in setting.value:gmatch("(%S+):(%S+)") do 2.38 + timeline_params["option_" .. event_ident] = true 2.39 + if filter_idents ~= "*" then 2.40 + for filter_ident in filter_idents:gmatch("([^\|]+)") do 2.41 + timeline_params["option_" .. event_ident .. "_" .. filter_ident] = true 2.42 + end 2.43 + end 2.44 + end 2.45 + end 2.46 + 2.47 + timeline_params.date = param.get("date") or today 2.48 + 2.49 + ui.link{ 2.50 + content = function() 2.51 + ui.image{ static = "icons/16/time.png" } 2.52 + slot.put(_"Timeline") 2.53 + end, 2.54 + module = "timeline", 2.55 + action = "update" 2.56 +-- params = timeline_params 2.57 + } 2.58 2.59 - ui.link{ 2.60 - content = function() 2.61 - ui.image{ static = "icons/16/group.png" } 2.62 - slot.put(_"Members") 2.63 - end, 2.64 - module = 'member', 2.65 - view = 'list' 2.66 - } 2.67 + ui.link{ 2.68 + content = function() 2.69 + ui.image{ static = "icons/16/package.png" } 2.70 + slot.put(_"Areas") 2.71 + end, 2.72 + module = 'area', 2.73 + view = 'list' 2.74 + } 2.75 + 2.76 + ui.link{ 2.77 + content = function() 2.78 + ui.image{ static = "icons/16/group.png" } 2.79 + slot.put(_"Members") 2.80 + end, 2.81 + module = 'member', 2.82 + view = 'list' 2.83 + } 2.84 2.85 - ui.link{ 2.86 - content = function() 2.87 - ui.image{ static = "icons/16/book_edit.png" } 2.88 - slot.put(_"Contacts") 2.89 - end, 2.90 - module = 'contact', 2.91 - view = 'list' 2.92 - } 2.93 + ui.link{ 2.94 + content = function() 2.95 + ui.image{ static = "icons/16/book_edit.png" } 2.96 + slot.put(_"Contacts") 2.97 + end, 2.98 + module = 'contact', 2.99 + view = 'list' 2.100 + } 2.101 2.102 - ui.link{ 2.103 - content = function() 2.104 - ui.image{ static = "icons/16/information.png" } 2.105 - slot.put(_"About") 2.106 - end, 2.107 - module = 'index', 2.108 - view = 'about' 2.109 - } 2.110 + ui.link{ 2.111 + content = function() 2.112 + ui.image{ static = "icons/16/information.png" } 2.113 + slot.put(_"About") 2.114 + end, 2.115 + module = 'index', 2.116 + view = 'about' 2.117 + } 2.118 2.119 if app.session.member.admin then 2.120
3.1 --- a/app/main/_layout/default.html Sun Jan 10 12:00:00 2010 +0100 3.2 +++ b/app/main/_layout/default.html Fri Jan 22 12:00:00 2010 +0100 3.3 @@ -3,6 +3,7 @@ 3.4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 3.5 <title><!-- WEBMCP SLOTNODIV app_name --></title> 3.6 <link rel="stylesheet" type="text/css" media="screen" href="__BASEURL__/static/trace.css" /> 3.7 + <link rel="stylesheet" type="text/css" media="screen" href="__BASEURL__/static/gregor.js/gregor.css" /> 3.8 <link rel="stylesheet" type="text/css" media="screen" href="<!-- WEBMCP SLOTNODIV stylesheet_url -->" /> 3.9 <!-- WEBMCP SLOTNODIV html_head --> 3.10 </head>
4.1 --- a/app/main/index/login.lua Sun Jan 10 12:00:00 2010 +0100 4.2 +++ b/app/main/index/login.lua Fri Jan 22 12:00:00 2010 +0100 4.3 @@ -12,6 +12,55 @@ 4.4 4.5 slot.put_into("title", encode.html(config.app_title)) 4.6 4.7 +slot.select("title", function() 4.8 + ui.container{ 4.9 + attr = { class = "lang_chooser" }, 4.10 + content = function() 4.11 + for i, lang in ipairs{"en", "de"} do 4.12 + ui.link{ 4.13 + content = function() 4.14 + ui.image{ 4.15 + static = "lang/" .. lang .. ".png", 4.16 + attr = { style = "margin-left: 0.5em;", alt = lang } 4.17 + } 4.18 + end, 4.19 + module = "index", 4.20 + action = "set_lang", 4.21 + params = { lang = lang }, 4.22 + routing = { 4.23 + default = { 4.24 + mode = "redirect", 4.25 + module = request.get_module(), 4.26 + view = request.get_view(), 4.27 + id = param.get_id_cgi(), 4.28 + params = param.get_all_cgi() 4.29 + } 4.30 + } 4.31 + } 4.32 + end 4.33 + end 4.34 + } 4.35 +end) 4.36 + 4.37 + 4.38 +local lang = locale.get("lang") 4.39 +local basepath = request.get_app_basepath() 4.40 +local file_name = basepath .. "/locale/motd/" .. lang .. "_public.txt" 4.41 +local file = io.open(file_name) 4.42 +if file ~= nil then 4.43 + local help_text = file:read("*a") 4.44 + if #help_text > 0 then 4.45 + ui.container{ 4.46 + attr = { class = "motd wiki" }, 4.47 + content = function() 4.48 + slot.put(format.wiki_text(help_text)) 4.49 + end 4.50 + } 4.51 + end 4.52 +end 4.53 + 4.54 + 4.55 + 4.56 ui.tag{ 4.57 tag = 'p', 4.58 content = _'You need to be logged in, to use this system.' 4.59 @@ -51,3 +100,4 @@ 4.60 } 4.61 end 4.62 } 4.63 +
5.1 --- a/app/main/initiative/_list.lua Sun Jan 10 12:00:00 2010 +0100 5.2 +++ b/app/main/initiative/_list.lua Fri Jan 22 12:00:00 2010 +0100 5.3 @@ -1,6 +1,18 @@ 5.4 local initiatives_selector = param.get("initiatives_selector", "table") 5.5 initiatives_selector:join("issue", nil, "issue.id = initiative.issue_id") 5.6 5.7 +local limit = param.get("limit", atom.number) 5.8 + 5.9 +local more_initiatives_count 5.10 +if limit then 5.11 + local initiatives_count = initiatives_selector:count() 5.12 + if initiatives_count > limit then 5.13 + more_initiatives_count = initiatives_count - limit 5.14 + end 5.15 + initiatives_selector:limit(limit) 5.16 +end 5.17 + 5.18 + 5.19 local issue = param.get("issue", "table") 5.20 5.21 local order_options = {} 5.22 @@ -53,12 +65,22 @@ 5.23 end 5.24 end 5.25 5.26 +initiatives_selector 5.27 + :left_join("initiator", "_initiator", { "_initiator.initiative_id = initiative.id AND _initiator.member_id = ?", app.session.member.id} ) 5.28 + :left_join("supporter", "_supporter", { "_supporter.initiative_id = initiative.id AND _supporter.member_id = ?", app.session.member.id} ) 5.29 + 5.30 + :add_field("(_initiator.member_id NOTNULL)", "is_initiator") 5.31 + :add_field({"(_supporter.member_id NOTNULL) AND NOT EXISTS(SELECT 1 FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)))", app.session.member.id }, "is_supporter") 5.32 + :add_field({"EXISTS(SELECT 1 FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)))", app.session.member.id }, "is_potential_supporter") 5.33 + 5.34 + 5.35 ui_order{ 5.36 name = name, 5.37 selector = initiatives_selector, 5.38 options = order_options, 5.39 content = function() 5.40 ui.paginate{ 5.41 + name = issue and "issue_" .. tostring(issue.id) .. "_page" or nil, 5.42 selector = initiatives_selector, 5.43 per_page = param.get("per_page", atom.number), 5.44 content = function() 5.45 @@ -130,6 +152,30 @@ 5.46 static = "icons/16/new.png" 5.47 } 5.48 end 5.49 + if record.is_supporter then 5.50 + slot.put(" ") 5.51 + local label = _"You are supporting this initiative" 5.52 + ui.image{ 5.53 + attr = { alt = label, title = label }, 5.54 + static = "icons/16/thumb_up_green.png" 5.55 + } 5.56 + end 5.57 + if record.is_potential_supporter then 5.58 + slot.put(" ") 5.59 + local label = _"You are potential supporter of this initiative" 5.60 + ui.image{ 5.61 + attr = { alt = label, title = label }, 5.62 + static = "icons/16/thumb_up.png" 5.63 + } 5.64 + end 5.65 + if record.is_initiator then 5.66 + slot.put(" ") 5.67 + local label = _"You are iniator of this initiative" 5.68 + ui.image{ 5.69 + attr = { alt = label, title = label }, 5.70 + static = "icons/16/user_edit.png" 5.71 + } 5.72 + end 5.73 end 5.74 } 5.75 5.76 @@ -141,4 +187,14 @@ 5.77 end 5.78 } 5.79 end 5.80 -} 5.81 \ No newline at end of file 5.82 +} 5.83 + 5.84 +if more_initiatives_count then 5.85 + ui.link{ 5.86 + attr = { style = "font-size: 75%; font-style: italic;" }, 5.87 + content = _("#{count} more initiatives", { count = more_initiatives_count }), 5.88 + module = "issue", 5.89 + view = "show", 5.90 + id = issue.id, 5.91 + } 5.92 +end
6.1 --- a/app/main/initiative/show.lua Sun Jan 10 12:00:00 2010 +0100 6.2 +++ b/app/main/initiative/show.lua Fri Jan 22 12:00:00 2010 +0100 6.3 @@ -151,18 +151,22 @@ 6.4 ui.tag{ 6.5 tag = "span", 6.6 content = function() 6.7 - if initiative.discussion_url and #initiative.discussion_url > 0 then 6.8 - ui.link{ 6.9 - attr = { 6.10 - class = "actions", 6.11 - target = "_blank", 6.12 - title = initiative.discussion_url 6.13 - }, 6.14 - content = function() 6.15 - slot.put(encode.html(initiative.discussion_url)) 6.16 - end, 6.17 - external = initiative.discussion_url 6.18 - } 6.19 + if initiative.discussion_url:find("^https?://") then 6.20 + if initiative.discussion_url and #initiative.discussion_url > 0 then 6.21 + ui.link{ 6.22 + attr = { 6.23 + class = "actions", 6.24 + target = "_blank", 6.25 + title = initiative.discussion_url 6.26 + }, 6.27 + content = function() 6.28 + slot.put(encode.html(initiative.discussion_url)) 6.29 + end, 6.30 + external = initiative.discussion_url 6.31 + } 6.32 + end 6.33 + else 6.34 + slot.put(encode.html(initiative.discussion_url)) 6.35 end 6.36 slot.put(" ") 6.37 if initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked then 6.38 @@ -248,7 +252,7 @@ 6.39 ui.container{ 6.40 attr = { class = "draft_updated_info" }, 6.41 content = function() 6.42 - slot.put("The draft of this initiative has been updated!") 6.43 + slot.put(_"The draft of this initiative has been updated!") 6.44 slot.put(" ") 6.45 ui.link{ 6.46 content = _"Show diff", 6.47 @@ -358,18 +362,27 @@ 6.48 end 6.49 } 6.50 6.51 -local members_selector = initiative:get_reference_selector("supporting_members_snapshot") 6.52 +local members_selector = initiative:get_reference_selector("supporting_members_snapshot") 6.53 :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id") 6.54 :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") 6.55 :add_field("direct_interest_snapshot.weight") 6.56 :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event") 6.57 :add_where("direct_supporter_snapshot.satisfied") 6.58 6.59 -local satisfied_supporter_count = members_selector:count() 6.60 +local tmp = db:query("SELECT count(1) AS count, sum(weight) AS weight FROM (" .. tostring(members_selector) .. ") as subquery", "object") 6.61 +local direct_satisfied_supporter_count = tmp.count 6.62 +local indirect_satisfied_supporter_count = (tmp.weight or 0) - tmp.count 6.63 + 6.64 +local count_string 6.65 +if indirect_satisfied_supporter_count > 0 then 6.66 + count_string = "(" .. tostring(direct_satisfied_supporter_count) .. "+" .. tostring(indirect_satisfied_supporter_count) .. ")" 6.67 +else 6.68 + count_string = "(" .. tostring(direct_satisfied_supporter_count) .. ")" 6.69 +end 6.70 6.71 tabs[#tabs+1] = { 6.72 name = "satisfied_supporter", 6.73 - label = _"Supporter" .. " (" .. tostring(satisfied_supporter_count) .. ")", 6.74 + label = _"Supporter" .. " " .. count_string, 6.75 content = function() 6.76 execute.view{ 6.77 module = "member", 6.78 @@ -389,11 +402,20 @@ 6.79 :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event") 6.80 :add_where("NOT direct_supporter_snapshot.satisfied") 6.81 6.82 -local potential_supporter_count = members_selector:count() 6.83 +local tmp = db:query("SELECT count(1) AS count, sum(weight) AS weight FROM (" .. tostring(members_selector) .. ") as subquery", "object") 6.84 +local direct_potential_supporter_count = tmp.count 6.85 +local indirect_potential_supporter_count = (tmp.weight or 0) - tmp.count 6.86 + 6.87 +local count_string 6.88 +if indirect_potential_supporter_count > 0 then 6.89 + count_string = "(" .. tostring(direct_potential_supporter_count) .. "+" .. tostring(indirect_potential_supporter_count) .. ")" 6.90 +else 6.91 + count_string = "(" .. tostring(direct_potential_supporter_count) .. ")" 6.92 +end 6.93 6.94 tabs[#tabs+1] = { 6.95 name = "supporter", 6.96 - label = _"Potential supporter" .. " (" .. tostring(potential_supporter_count) .. ")", 6.97 + label = _"Potential supporter" .. " " .. count_string, 6.98 content = function() 6.99 execute.view{ 6.100 module = "member",
7.1 --- a/app/main/issue/_list.lua Sun Jan 10 12:00:00 2010 +0100 7.2 +++ b/app/main/issue/_list.lua Fri Jan 22 12:00:00 2010 +0100 7.3 @@ -1,5 +1,9 @@ 7.4 local issues_selector = param.get("issues_selector", "table") 7.5 7.6 +issues_selector 7.7 + :left_join("interest", "_interest", { "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id} ) 7.8 + :add_field("(_interest.member_id NOTNULL)", "is_interested") 7.9 + 7.10 local ui_filter = ui.filter 7.11 if param.get("filter", atom.boolean) == false then 7.12 ui_filter = function(args) args.content() end 7.13 @@ -150,6 +154,36 @@ 7.14 end 7.15 end 7.16 }, 7.17 + { 7.18 + type = "boolean", 7.19 + name = "supported", 7.20 + label = _"Supported", 7.21 + selector_modifier = function(selector, value) 7.22 + if value then 7.23 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? LEFT JOIN opinion ON opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) WHERE initiative.issue_id = issue.id AND opinion.member_id ISNULL LIMIT 1)", app.session.member.id, app.session.member.id }) 7.24 + end 7.25 + end 7.26 + }, 7.27 + { 7.28 + type = "boolean", 7.29 + name = "potentially_supported", 7.30 + label = _"Potential supported", 7.31 + selector_modifier = function(selector, value) 7.32 + if value then 7.33 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN supporter ON supporter.initiative_id = initiative.id AND supporter.member_id = ? JOIN opinion ON opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) WHERE initiative.issue_id = issue.id LIMIT 1)", app.session.member.id, app.session.member.id }) 7.34 + end 7.35 + end 7.36 + }, 7.37 + { 7.38 + type = "boolean", 7.39 + name = "initiated", 7.40 + label = _"Initiated", 7.41 + selector_modifier = function(selector, value) 7.42 + if value then 7.43 + selector:add_where({ "EXISTS (SELECT 1 FROM initiative JOIN initiator ON initiator.initiative_id = initiative.id AND initiator.member_id = ? WHERE initiative.issue_id = issue.id)", app.session.member.id }) 7.44 + end 7.45 + end 7.46 + }, 7.47 }, 7.48 content = function() 7.49 local ui_order = ui.order 7.50 @@ -215,6 +249,14 @@ 7.51 } 7.52 slot.put("<br />") 7.53 end 7.54 + if record.is_interested then 7.55 + local label = _"You are interested in this issue", 7.56 + ui.image{ 7.57 + attr = { alt = label, title = label }, 7.58 + static = "icons/16/eye.png" 7.59 + } 7.60 + slot.put(" ") 7.61 + end 7.62 ui.link{ 7.63 text = _("Issue ##{id}", { id = tostring(record.id) }), 7.64 module = "issue", 7.65 @@ -265,7 +307,6 @@ 7.66 issue = record, 7.67 initiatives_selector = initiatives_selector, 7.68 highlight_string = highlight_string, 7.69 - limit = 3, 7.70 per_page = param.get("initiatives_per_page", atom.number), 7.71 no_sort = param.get("initiatives_no_sort", atom.boolean) 7.72 }
8.1 --- a/app/main/member/_show.lua Sun Jan 10 12:00:00 2010 +0100 8.2 +++ b/app/main/member/_show.lua Fri Jan 22 12:00:00 2010 +0100 8.3 @@ -73,7 +73,7 @@ 8.4 ui.tag{ 8.5 tag = "span", 8.6 content = function() 8.7 - slot.put(encode.html_newlines(member.address)) 8.8 + slot.put(encode.html_newlines(html.encode(member.address))) 8.9 end 8.10 } 8.11 end
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/app/main/timeline/_action/delete_filter.lua Fri Jan 22 12:00:00 2010 +0100 9.3 @@ -0,0 +1,3 @@ 9.4 +local timeline_filter = app.session.member:get_setting_map_by_key_and_subkey("timeline_filters", param.get("name")) 9.5 + 9.6 +timeline_filter:destroy() 9.7 \ No newline at end of file
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/app/main/timeline/_action/save.lua Fri Jan 22 12:00:00 2010 +0100 10.3 @@ -0,0 +1,41 @@ 10.4 +local id = param.get("id", atom.number) 10.5 + 10.6 +local setting_key = "liquidfeedback_frontend_timeline_current_options" 10.7 +local setting = Setting:by_pk(app.session.member.id, setting_key) 10.8 +local options_string = setting.value 10.9 + 10.10 +local timeline_filter 10.11 + 10.12 +local subkey = param.get("name") 10.13 + 10.14 +setting_map = SettingMap:new() 10.15 +setting_map.member_id = app.session.member.id 10.16 +setting_map.key = "timeline_filters" 10.17 +setting_map.subkey = subkey 10.18 +setting_map.value = options_string 10.19 +setting_map:save() 10.20 + 10.21 +local timeline_params = {} 10.22 +if options_string then 10.23 + for event_ident, filter_idents in setting.value:gmatch("(%S+):(%S+)") do 10.24 + timeline_params["option_" .. event_ident] = true 10.25 + if filter_idents ~= "*" then 10.26 + for filter_ident in filter_idents:gmatch("([^\|]+)") do 10.27 + timeline_params["option_" .. event_ident .. "_" .. filter_ident] = true 10.28 + end 10.29 + end 10.30 + end 10.31 +end 10.32 + 10.33 +local setting_key = "liquidfeedback_frontend_timeline_current_date" 10.34 +local setting = Setting:by_pk(app.session.member.id, setting_key) 10.35 + 10.36 +if setting then 10.37 + timeline_params.date = setting.value 10.38 +end 10.39 + 10.40 +request.redirect{ 10.41 + module = "timeline", 10.42 + view = "index", 10.43 + params = timeline_params 10.44 +} 10.45 \ No newline at end of file
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/app/main/timeline/_action/update.lua Fri Jan 22 12:00:00 2010 +0100 11.3 @@ -0,0 +1,101 @@ 11.4 +execute.view{ 11.5 + module = "timeline", 11.6 + view = "_constants" 11.7 +} 11.8 + 11.9 +local options_string = param.get("options_string") 11.10 + 11.11 +if not options_string then 11.12 + local active_options = "" 11.13 + for event_ident, event_name in pairs(event_names) do 11.14 + if param.get("option_" .. event_ident, atom.boolean) then 11.15 + active_options = active_options .. event_ident .. ":" 11.16 + local filter_idents = {} 11.17 + for filter_ident, filter_name in pairs(filter_names) do 11.18 + if param.get("option_" .. event_ident .. "_" .. filter_ident, atom.boolean) then 11.19 + filter_idents[#filter_idents+1] = filter_ident 11.20 + end 11.21 + end 11.22 + if #filter_idents > 0 then 11.23 + active_options = active_options .. table.concat(filter_idents, "|") .. " " 11.24 + else 11.25 + active_options = active_options .. "* " 11.26 + end 11.27 + end 11.28 + end 11.29 + if #active_options > 0 then 11.30 + options_string = active_options 11.31 + end 11.32 +end 11.33 + 11.34 +if not options_string then 11.35 + options_string = "issue_created:* issue_finished_after_voting:* issue_accepted:* issue_voting_started:* suggestion_created:* issue_canceled:* initiative_created:* issue_finished_without_voting:* draft_created:* initiative_revoked:* issue_half_frozen:* " 11.36 +end 11.37 + 11.38 +local setting_key = "liquidfeedback_frontend_timeline_current_options" 11.39 +local setting = Setting:by_pk(app.session.member.id, setting_key) 11.40 + 11.41 +if not setting or setting.value ~= options_string then 11.42 + if not setting then 11.43 + setting = Setting:new() 11.44 + setting.member_id = app.session.member_id 11.45 + setting.key = setting_key 11.46 + end 11.47 + if options_string then 11.48 + setting.value = options_string 11.49 + setting:save() 11.50 + end 11.51 +end 11.52 + 11.53 +local date = param.get("date") 11.54 + 11.55 +if date and #date > 0 then 11.56 + local setting_key = "liquidfeedback_frontend_timeline_current_date" 11.57 + local setting = Setting:by_pk(app.session.member.id, setting_key) 11.58 + if not setting or setting.value ~= date then 11.59 + if not setting then 11.60 + setting = Setting:new() 11.61 + setting.member_id = app.session.member.id 11.62 + setting.key = setting_key 11.63 + end 11.64 + setting.value = date 11.65 + setting:save() 11.66 + end 11.67 +end 11.68 + 11.69 +local setting_key = "liquidfeedback_frontend_timeline_current_options" 11.70 +local setting = Setting:by_pk(app.session.member.id, setting_key) 11.71 + 11.72 +local timeline_params = {} 11.73 +if setting and setting.value then 11.74 + for event_ident, filter_idents in setting.value:gmatch("(%S+):(%S+)") do 11.75 + timeline_params["option_" .. event_ident] = true 11.76 + if filter_idents ~= "*" then 11.77 + for filter_ident in filter_idents:gmatch("([^\|]+)") do 11.78 + timeline_params["option_" .. event_ident .. "_" .. filter_ident] = true 11.79 + end 11.80 + end 11.81 + end 11.82 +end 11.83 + 11.84 +local setting_key = "liquidfeedback_frontend_timeline_current_date" 11.85 +local setting = Setting:by_pk(app.session.member.id, setting_key) 11.86 + 11.87 +if setting then 11.88 + timeline_params.date = setting.value 11.89 +end 11.90 + 11.91 +timeline_params.show_options = param.get("show_options", atom.boolean) 11.92 + 11.93 +if param.get("save", atom.boolean) then 11.94 + request.redirect{ 11.95 + module = "timeline", 11.96 + view = "save_filter" 11.97 + } 11.98 +else 11.99 + request.redirect{ 11.100 + module = "timeline", 11.101 + view = "index", 11.102 + params = timeline_params 11.103 + } 11.104 +end 11.105 \ No newline at end of file
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/app/main/timeline/_constants.lua Fri Jan 22 12:00:00 2010 +0100 12.3 @@ -0,0 +1,31 @@ 12.4 +event_names = { 12.5 + issue_created = _"New issue", 12.6 + issue_canceled = _"Issue canceled", 12.7 + issue_accepted = _"Issue accepted", 12.8 + issue_half_frozen = _"Issue frozen", 12.9 + issue_finished_without_voting = _"Issue finished without voting", 12.10 + issue_voting_started = _"Voting started", 12.11 + issue_finished_after_voting = _"Issue finished", 12.12 + initiative_created = _"New initiative", 12.13 + initiative_revoked = _"Initiative revoked", 12.14 + draft_created = _"New draft", 12.15 + suggestion_created = _"New suggestion" 12.16 +} 12.17 + 12.18 +filter_names = { 12.19 + contact = _"Saved as contact", 12.20 + interested = _"Interested", 12.21 + supporter = _"Supported", 12.22 + potential_supporter = _"Potential supported", 12.23 + initiator = _"Initiated", 12.24 + membership = _"Member of area" 12.25 +} 12.26 + 12.27 +option_names = {} 12.28 +for key, val in pairs(event_names) do 12.29 + option_names[key] = val 12.30 +end 12.31 +for key, val in pairs(filter_names) do 12.32 + option_names[key] = val 12.33 +end 12.34 +
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/app/main/timeline/_list.lua Fri Jan 22 12:00:00 2010 +0100 13.3 @@ -0,0 +1,124 @@ 13.4 +local timeline_selector = param.get("timeline_selector", "table") 13.5 +local event_names = param.get("event_names", "table") 13.6 +local initiatives_per_page = param.get("initiatives_per_page", atom.number) or 3 13.7 + 13.8 +ui.paginate{ 13.9 + per_page = param.get("per_page", atom.number) or 25, 13.10 + selector = timeline_selector, 13.11 + content = function() 13.12 + local timelines = timeline_selector:exec() 13.13 + timelines:load("issue") 13.14 + timelines:load("initiative") 13.15 + timelines:load("member") 13.16 + ui.list{ 13.17 + attr = { class = "nohover" }, 13.18 + records = timelines, 13.19 + columns = { 13.20 + { 13.21 + field_attr = { style = "width: 10em;" }, 13.22 + content = function(timeline) 13.23 + ui.field.text{ 13.24 + attr = { style = "font-size: 75%; font-weight: bold; background-color: #ccc; display: block; margin-bottom: 1ex;"}, 13.25 + value = format.time(timeline.occurrence) 13.26 + } 13.27 + ui.field.text{ 13.28 + attr = { style = "font-size: 75%; font-weight: bold;"}, 13.29 + value = event_names[timeline.event] or timeline.event 13.30 + } 13.31 + end 13.32 + }, 13.33 + { 13.34 + content = function(timeline) 13.35 + local issue 13.36 + local initiative 13.37 + if timeline.issue then 13.38 + issue = timeline.issue 13.39 + elseif timeline.initiative then 13.40 + initiative = timeline.initiative 13.41 + issue = initiative.issue 13.42 + elseif timeline.draft then 13.43 + initiative = timeline.draft.initiative 13.44 + issue = initiative.issue 13.45 + elseif timeline.suggestion then 13.46 + initiative = timeline.suggestion.initiative 13.47 + issue = initiative.issue 13.48 + end 13.49 + if issue then 13.50 + if timeline.is_interested then 13.51 + local label = _"You are interested in this issue", 13.52 + ui.image{ 13.53 + attr = { alt = label, title = label, style = "float: left; margin-right: 0.5em;" }, 13.54 + static = "icons/16/eye.png" 13.55 + } 13.56 + end 13.57 + slot.put(" ") 13.58 + ui.tag{ 13.59 + tag = "span", 13.60 + attr = { style = "font-size: 75%; font-weight: bold; background-color: #ccc; display: block; margin-bottom: 1ex;"}, 13.61 + content = issue.area.name .. ", " .. _("Issue ##{id}", { id = issue.id }) 13.62 + } 13.63 + else 13.64 + ui.tag{ 13.65 + tag = "span", 13.66 + attr = { style = "font-size: 75%; background-color: #ccc; display: block; margin-bottom: 1ex;"}, 13.67 + content = function() slot.put(" ") end 13.68 + } 13.69 + end 13.70 + 13.71 + if timeline.member then 13.72 + execute.view{ 13.73 + module = "member_image", 13.74 + view = "_show", 13.75 + params = { 13.76 + member = timeline.member, 13.77 + image_type = "avatar", 13.78 + show_dummy = true 13.79 + } 13.80 + } 13.81 + ui.link{ 13.82 + content = timeline.member.name, 13.83 + module = "member", 13.84 + view = "show", 13.85 + id = timeline.member.id 13.86 + } 13.87 + end 13.88 + if timeline.issue then 13.89 + local initiatives_selector = timeline.issue 13.90 + :get_reference_selector("initiatives") 13.91 + execute.view{ 13.92 + module = "initiative", 13.93 + view = "_list", 13.94 + params = { 13.95 + issue = timeline.issue, 13.96 + initiatives_selector = initiatives_selector, 13.97 + per_page = initiatives_per_page, 13.98 + no_sort = true, 13.99 + limit = initiatives_per_page 13.100 + } 13.101 + } 13.102 + elseif initiative then 13.103 + execute.view{ 13.104 + module = "initiative", 13.105 + view = "_list", 13.106 + params = { 13.107 + issue = initiative.issue, 13.108 + initiatives_selector = Initiative:new_selector():add_where{ "initiative.id = ?", initiative.id }, 13.109 + per_page = initiatives_per_page, 13.110 + no_sort = true 13.111 + } 13.112 + } 13.113 + end 13.114 + if timeline.suggestion then 13.115 + ui.link{ 13.116 + module = "suggestion", 13.117 + view = "show", 13.118 + id = timeline.suggestion.id, 13.119 + content = timeline.suggestion.name 13.120 + } 13.121 + end 13.122 + end 13.123 + }, 13.124 + } 13.125 + } 13.126 + end 13.127 +} 13.128 \ No newline at end of file
14.1 --- a/app/main/timeline/index.lua Sun Jan 10 12:00:00 2010 +0100 14.2 +++ b/app/main/timeline/index.lua Fri Jan 22 12:00:00 2010 +0100 14.3 @@ -1,3 +1,12 @@ 14.4 +execute.view{ 14.5 + module = "timeline", 14.6 + view = "_constants" 14.7 +} 14.8 + 14.9 +local options_box_count = param.get("options_box_count", atom.number) or 1 14.10 +if options_box_count > 10 then 14.11 + options_box_count = 10 14.12 +end 14.13 14.14 local function format_dow(dow) 14.15 local dows = { 14.16 @@ -11,141 +20,331 @@ 14.17 } 14.18 return dows[dow+1] 14.19 end 14.20 +slot.put_into("title", _"Timeline") 14.21 14.22 -slot.put_into("title", _"Global timeline") 14.23 +slot.select("actions", function() 14.24 + local setting_key = "liquidfeedback_frontend_timeline_current_options" 14.25 + local setting = Setting:by_pk(app.session.member.id, setting_key) 14.26 + local current_options = "" 14.27 + if setting then 14.28 + current_options = setting.value 14.29 + end 14.30 + local setting_maps = app.session.member:get_setting_maps_by_key("timeline_filters") 14.31 + for i, setting_map in ipairs(setting_maps) do 14.32 + local active 14.33 + local options_string = setting_map.value 14.34 + local name = setting_map.subkey 14.35 + if options_string == current_options then 14.36 + active = true 14.37 + end 14.38 + timeline_params.date = param.get("date") 14.39 + ui.link{ 14.40 + attr = { class = active and "action_active" or nil }, 14.41 + content = function() 14.42 + ui.image{ static = "icons/16/time.png" } 14.43 + slot.put(encode.html(name)) 14.44 + end, 14.45 + module = 'timeline', 14.46 + action = 'update', 14.47 + params = { 14.48 + options_string = options_string 14.49 + }, 14.50 + } 14.51 + end 14.52 + if #setting_maps > 0 then 14.53 + ui.link{ 14.54 + content = function() 14.55 + ui.image{ static = "icons/16/wrench.png" } 14.56 + slot.put(_"Manage filter") 14.57 + end, 14.58 + module = "timeline", 14.59 + view = "list_filter", 14.60 + } 14.61 + end 14.62 + ui.link{ 14.63 + content = function() 14.64 + ui.image{ static = "icons/16/bullet_disk.png" } 14.65 + slot.put(_"Save current filter") 14.66 + end, 14.67 + module = "timeline", 14.68 + view = "save_filter", 14.69 + attr = { 14.70 + onclick = "el=document.getElementById('timeline_save');el.checked=true;el.form.submit();return(false);" 14.71 + } 14.72 + } 14.73 +end) 14.74 14.75 +util.help("timeline.index", _"Timeline") 14.76 14.77 ui.form{ 14.78 - attr = { class = "vertical" }, 14.79 module = "timeline", 14.80 - view = "index", 14.81 - method = "get", 14.82 + action = "update", 14.83 content = function() 14.84 - 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; ") 14.85 - local today = tmp[1].date 14.86 - for i, record in ipairs(tmp) do 14.87 - local content 14.88 - if i == 1 then 14.89 - content = _"Today" 14.90 - elseif i == 2 then 14.91 - content = _"Yesterday" 14.92 - else 14.93 - content = format_dow(record.dow) 14.94 - end 14.95 - ui.link{ 14.96 - content = content, 14.97 - attr = { onclick = "el = document.getElementById('timeline_search_date'); el.value = '" .. tostring(record.date) .. "'; el.form.submit(); return(false);" }, 14.98 - module = "timeline", 14.99 - view = "index", 14.100 - params = { date = record.date } 14.101 - } 14.102 - slot.put(" ") 14.103 + 14.104 + 14.105 + ui.tag{ 14.106 + tag = "label", 14.107 + attr = { style = "font-size: 130%;" }, 14.108 + content = _"Date" .. ":" 14.109 + } 14.110 + slot.put(" ") 14.111 + local date = param.get("date") 14.112 + if not date or #date == 0 then 14.113 + date = tostring(db:query("select now()::date as date")[1].date) 14.114 end 14.115 - ui.field.hidden{ 14.116 - attr = { id = "timeline_search_date" }, 14.117 - name = "date", 14.118 - value = param.get("date") or today 14.119 + ui.tag{ 14.120 + tag = "input", 14.121 + attr = { 14.122 + type = "text", 14.123 + id = "timeline_search_date", 14.124 + style = "width: 10em;", 14.125 + onchange = "this.form.submit();", 14.126 + name = "date", 14.127 + value = date 14.128 + }, 14.129 + content = function() end 14.130 + } 14.131 + 14.132 + ui.script{ static = "gregor.js/gregor.js" } 14.133 + util.gregor("timeline_search_date", "document.getElementById('timeline_search_date').form.submit();") 14.134 + 14.135 + 14.136 + ui.link{ 14.137 + attr = { style = "margin-left: 1em; font-size: 130%; font-weight: bold;", onclick = "document.getElementById('timeline_search_date').form.submit();return(false);" }, 14.138 + content = function() 14.139 + ui.image{ 14.140 + attr = { style = "margin-right: 0.25em;" }, 14.141 + static = "icons/16/magnifier.png" 14.142 + } 14.143 + slot.put(_"Search") 14.144 + end, 14.145 + external = "#", 14.146 + } 14.147 + local show_options = param.get("show_options", atom.boolean) 14.148 + ui.link{ 14.149 + attr = { style = "margin-left: 1em; font-size: 130%;", onclick = "el=document.getElementById('timeline_show_options');el.checked=" .. tostring(not show_options) .. ";el.form.submit();return(false);" }, 14.150 + content = function() 14.151 + ui.image{ 14.152 + attr = { style = "margin-right: 0.25em;" }, 14.153 + static = "icons/16/text_list_bullets.png" 14.154 + } 14.155 + slot.put(not show_options and _"Show filter details" or _"Hide filter details") 14.156 + end, 14.157 + external = "#", 14.158 + } 14.159 + 14.160 + ui.field.boolean{ 14.161 + attr = { id = "timeline_show_options", style = "display: none;", onchange="this.form.submit();" }, 14.162 + name = "show_options", 14.163 + value = param.get("show_options", atom.boolean) 14.164 + } 14.165 + 14.166 + ui.field.boolean{ 14.167 + attr = { id = "timeline_save", style = "display: none;", onchange="this.form.submit();" }, 14.168 + name = "save", 14.169 + value = false 14.170 } 14.171 - ui.field.select{ 14.172 - attr = { onchange = "this.form.submit();" }, 14.173 - name = "per_page", 14.174 - label = _"Issues per page", 14.175 - foreign_records = { 14.176 - { id = "10", name = "10" }, 14.177 - { id = "25", name = "25" }, 14.178 - { id = "50", name = "50" }, 14.179 - { id = "100", name = "100" }, 14.180 - { id = "250", name = "250" }, 14.181 - { id = "all", name = _"All" }, 14.182 + 14.183 + ui.container{ 14.184 + attr = { 14.185 + id = "timeline_options_boxes", 14.186 + class = "vertical", 14.187 + style = not param.get("show_options", atom.boolean) and "display: none;" or nil 14.188 }, 14.189 - foreign_id = "id", 14.190 - foreign_name = "name", 14.191 - value = param.get("per_page") 14.192 - } 14.193 - local initiatives_per_page = param.get("initiatives_per_page", atom.integer) or 3 14.194 + content = function() 14.195 + 14.196 + local function option_field(event_ident, filter_ident) 14.197 + local param_name 14.198 + if not filter_ident then 14.199 + param_name = "option_" .. event_ident 14.200 + else 14.201 + param_name = "option_" .. event_ident .. "_" .. filter_ident 14.202 + end 14.203 + local value = param.get(param_name, atom.boolean) 14.204 + ui.field.boolean{ 14.205 + attr = { id = param_name }, 14.206 + name = param_name, 14.207 + value = value, 14.208 + } 14.209 + end 14.210 + 14.211 + local function filter_option_fields(event_ident, filter_idents) 14.212 + 14.213 + for i, filter_ident in ipairs(filter_idents) do 14.214 + slot.put("<td>") 14.215 + option_field(event_ident, filter_ident) 14.216 + slot.put("</td><td><div class='ui_field_label label_right'>") 14.217 + ui.tag{ 14.218 + attr = { ["for"] = "option_" .. event_ident .. "_" .. filter_ident }, 14.219 + tag = "label", 14.220 + content = filter_names[filter_ident] 14.221 + } 14.222 + slot.put("</div></td>") 14.223 + end 14.224 + 14.225 + end 14.226 14.227 - ui.field.select{ 14.228 - attr = { onchange = "this.form.submit();" }, 14.229 - name = "initiatives_per_page", 14.230 - label = _"Initiatives per page", 14.231 - foreign_records = { 14.232 - { id = 1, name = "1" }, 14.233 - { id = 3, name = "3" }, 14.234 - { id = 5, name = "5" }, 14.235 - { id = 10, name = "10" }, 14.236 - { id = 25, name = "25" }, 14.237 - { id = 50, name = "50" }, 14.238 - }, 14.239 - foreign_id = "id", 14.240 - foreign_name = "name", 14.241 - value = initiatives_per_page 14.242 + local event_groups = { 14.243 + { 14.244 + title = _"Issue events", 14.245 + event_idents = { 14.246 + "issue_created", 14.247 + "issue_canceled", 14.248 + "issue_accepted", 14.249 + "issue_half_frozen", 14.250 + "issue_finished_without_voting", 14.251 + "issue_voting_started", 14.252 + "issue_finished_after_voting", 14.253 + }, 14.254 + filter_idents = { 14.255 + "membership", 14.256 + "interested" 14.257 + } 14.258 + }, 14.259 + { 14.260 + title = _"Initiative events", 14.261 + event_idents = { 14.262 + "initiative_created", 14.263 + "initiative_revoked", 14.264 + "draft_created", 14.265 + "suggestion_created", 14.266 + }, 14.267 + filter_idents = { 14.268 + "membership", 14.269 + "interested", 14.270 + "supporter", 14.271 + "potential_supporter", 14.272 + "initiator" 14.273 + } 14.274 + } 14.275 + } 14.276 + 14.277 + slot.put("<br />") 14.278 + 14.279 + slot.put("<table>") 14.280 + 14.281 + for i_event_group, event_group in ipairs(event_groups) do 14.282 + slot.put("<tr>") 14.283 + slot.put("<th colspan='2'>") 14.284 + slot.put(event_group.title) 14.285 + slot.put("</th><th colspan='10'>") 14.286 + slot.put(_"Show only events which match... (or associtated)") 14.287 + slot.put("</th>") 14.288 + slot.put("</tr>") 14.289 + local event_idents = event_group.event_idents 14.290 + for i, event_ident in ipairs(event_idents) do 14.291 + slot.put("<tr><td>") 14.292 + option_field(event_ident) 14.293 + slot.put("</td><td><div class='ui_field_label label_right'>") 14.294 + ui.tag{ 14.295 + attr = { ["for"] = "option_" .. event_ident }, 14.296 + tag = "label", 14.297 + content = event_names[event_ident] 14.298 + } 14.299 + slot.put("</div></td>") 14.300 + filter_option_fields(event_ident, event_group.filter_idents) 14.301 + slot.put("</tr>") 14.302 + end 14.303 + end 14.304 + 14.305 + slot.put("</table>") 14.306 + 14.307 + end 14.308 } 14.309 end 14.310 } 14.311 14.312 local date = param.get("date") 14.313 -if not date then 14.314 +if not date or #date == 0 then 14.315 date = "today" 14.316 end 14.317 -local issues_selector = db:new_selector() 14.318 -issues_selector._class = Issue 14.319 + 14.320 +local timeline_selector 14.321 + 14.322 +for event, event_name in pairs(event_names) do 14.323 + 14.324 + if param.get("option_" .. event, atom.boolean) then 14.325 + 14.326 + local tmp = Timeline:new_selector() 14.327 + :add_where{ "occurrence::date = ?", date } 14.328 + 14.329 + :left_join("draft", nil, "draft.id = timeline.draft_id") 14.330 + :left_join("suggestion", nil, "suggestion.id = timeline.suggestion_id") 14.331 + :left_join("initiative", nil, "initiative.id = timeline.initiative_id or initiative.id = draft.initiative_id or initiative.id = suggestion.initiative_id") 14.332 + :left_join("issue", nil, "issue.id = timeline.issue_id or issue.id = initiative.issue_id") 14.333 + :left_join("area", nil, "area.id = issue.area_id") 14.334 + 14.335 + :left_join("interest", "_interest", { "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id} ) 14.336 + :left_join("membership", "_membership", { "_membership.area_id = area.id AND _membership.member_id = ?", app.session.member.id} ) 14.337 + :left_join("initiator", "_initiator", { "_initiator.initiative_id = initiative.id AND _initiator.member_id = ?", app.session.member.id} ) 14.338 + :left_join("supporter", "_supporter", { "_supporter.initiative_id = initiative.id AND _supporter.member_id = ?", app.session.member.id} ) 14.339 + 14.340 + :add_field("(_interest.member_id NOTNULL)", "is_interested") 14.341 + :add_field("(_initiator.member_id NOTNULL)", "is_initiator") 14.342 + :add_field({"(_supporter.member_id NOTNULL) AND NOT EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1)", app.session.member.id }, "is_supporter") 14.343 + :add_field({"EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1)", app.session.member.id }, "is_potential_supporter") 14.344 + -- :left_join("member", nil, "member.id = timeline.member_id") 14.345 + 14.346 + tmp:add_where{ "event = ?", event } 14.347 + 14.348 + local filters = {} 14.349 + if param.get("option_" .. event .. "_membership", atom.boolean) then 14.350 + filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR _membership.member_id NOTNULL" 14.351 + end 14.352 + 14.353 + if param.get("option_" .. event .. "_supporter", atom.boolean) then 14.354 + filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR ((_supporter.member_id NOTNULL) AND NOT EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1))" 14.355 + end 14.356 + 14.357 + if param.get("option_" .. event .. "_potential_supporter", atom.boolean) then 14.358 + filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR ((_supporter.member_id NOTNULL) AND EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1))" 14.359 + end 14.360 14.361 -issues_selector 14.362 - :add_field("*") 14.363 - :add_where{ "sort::date = ?", date } 14.364 - :add_from{ "($) as issue", { 14.365 - Issue:new_selector() 14.366 - :add_field("''", "old_state") 14.367 - :add_field("'new'", "new_state") 14.368 - :add_field("created", "sort") 14.369 - :union(Issue:new_selector() 14.370 - :add_field("'new'", "old_state") 14.371 - :add_field("'accepted'", "new_state") 14.372 - :add_field("accepted", "sort") 14.373 - :add_where("accepted NOTNULL") 14.374 - ):union(Issue:new_selector() 14.375 - :add_field("'accepted'", "old_state") 14.376 - :add_field("'frozen'", "new_state") 14.377 - :add_field("half_frozen", "sort") 14.378 - :add_where("half_frozen NOTNULL") 14.379 - ):union(Issue:new_selector() 14.380 - :add_field("'frozen'", "old_state") 14.381 - :add_field("'voting'", "new_state") 14.382 - :add_field("fully_frozen", "sort") 14.383 - :add_where("fully_frozen NOTNULL") 14.384 - ):union(Issue:new_selector() 14.385 - :add_field("'new'", "old_state") 14.386 - :add_field("'cancelled'", "new_state") 14.387 - :add_field("closed", "sort") 14.388 - :add_where("closed NOTNULL AND accepted ISNULL") 14.389 - ):union(Issue:new_selector() 14.390 - :add_field("'accepted'", "old_state") 14.391 - :add_field("'cancelled'", "new_state") 14.392 - :add_field("closed", "sort") 14.393 - :add_where("closed NOTNULL AND half_frozen ISNULL AND accepted NOTNULL") 14.394 - ):union(Issue:new_selector() 14.395 - :add_field("'frozen'", "old_state") 14.396 - :add_field("'cancelled'", "new_state") 14.397 - :add_field("closed", "sort") 14.398 - :add_where("closed NOTNULL AND fully_frozen ISNULL AND half_frozen NOTNULL") 14.399 - ):union(Issue:new_selector() 14.400 - :add_field("'voting'", "old_state") 14.401 - :add_field("'finished'", "new_state") 14.402 - :add_field("closed", "sort") 14.403 - :add_where("closed NOTNULL AND fully_frozen NOTNULL AND half_frozen ISNULL") 14.404 - ) 14.405 + if param.get("option_" .. event .. "_interested", atom.boolean) then 14.406 + filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR _interest.member_id NOTNULL" 14.407 + end 14.408 + 14.409 + if param.get("option_" .. event .. "_initiator", atom.boolean) then 14.410 + filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR _initiator.member_id NOTNULL" 14.411 + end 14.412 + 14.413 + if #filters > 0 then 14.414 + local filter_string = "(" .. table.concat(filters, ") OR (") .. ")" 14.415 + tmp:add_where{ filter_string, app.session.member.id } 14.416 + end 14.417 + 14.418 + if not timeline_selector then 14.419 + timeline_selector = tmp 14.420 + else 14.421 + timeline_selector:union_all(tmp) 14.422 + end 14.423 + end 14.424 +end 14.425 + 14.426 +if timeline_selector then 14.427 + 14.428 + local initiatives_per_page = param.get("initiatives_per_page", atom.number) 14.429 + 14.430 + local outer_timeline_selector = db:new_selector() 14.431 + outer_timeline_selector._class = Timeline 14.432 + outer_timeline_selector:add_field{ "timeline.*" } 14.433 + outer_timeline_selector:from({"($)", { timeline_selector }}, "timeline" ) 14.434 + outer_timeline_selector:add_order_by("occurrence DESC") 14.435 + 14.436 + slot.put("<br />") 14.437 + execute.view{ 14.438 + module = "timeline", 14.439 + view = "_list", 14.440 + params = { 14.441 + timeline_selector = outer_timeline_selector, 14.442 + per_page = param.get("per_page", atom.number), 14.443 + event_names = event_names, 14.444 + initiatives_per_page = initiatives_per_page 14.445 + } 14.446 } 14.447 -} 14.448 + 14.449 +else 14.450 14.451 -execute.view{ 14.452 - module = "issue", 14.453 - view = "_list", 14.454 - params = { 14.455 - issues_selector = issues_selector, 14.456 - initiatives_per_page = param.get("initiatives_per_page", atom.number), 14.457 - initiatives_no_sort = true, 14.458 - no_filter = true, 14.459 - no_sort = true, 14.460 - per_page = param.get("per_page"), 14.461 - } 14.462 -} 14.463 + slot.put(_"No events selected to list") 14.464 + 14.465 +end 14.466 \ No newline at end of file
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/app/main/timeline/list_filter.lua Fri Jan 22 12:00:00 2010 +0100 15.3 @@ -0,0 +1,38 @@ 15.4 +slot.put_into("title", _"Manage timeline filters") 15.5 + 15.6 +slot.select("actions", function() 15.7 + ui.link{ 15.8 + content = function() 15.9 + ui.image{ static = "icons/16/cancel.png" } 15.10 + slot.put(_"Back to timeline") 15.11 + end, 15.12 + module = "timeline", 15.13 + action = "update" 15.14 + } 15.15 +end) 15.16 + 15.17 +local timeline_filters = app.session.member:get_setting_maps_by_key("timeline_filters") 15.18 + 15.19 +ui.list{ 15.20 + records = timeline_filters, 15.21 + columns = { 15.22 + { 15.23 + name = "subkey" 15.24 + }, 15.25 + { 15.26 + content = function(timeline_filter) 15.27 + ui.link{ 15.28 + attr = { class = "action" }, 15.29 + content = function() 15.30 + slot.put(_"Delete filter") 15.31 + end, 15.32 + module = "timeline", 15.33 + action = "delete_filter", 15.34 + params = { 15.35 + name = timeline_filter.subkey 15.36 + } 15.37 + } 15.38 + end 15.39 + } 15.40 + } 15.41 +} 15.42 \ No newline at end of file
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/app/main/timeline/save_filter.lua Fri Jan 22 12:00:00 2010 +0100 16.3 @@ -0,0 +1,27 @@ 16.4 +slot.put_into("title", _"Save timeline filters") 16.5 + 16.6 +slot.select("actions", function() 16.7 + ui.link{ 16.8 + content = function() 16.9 + ui.image{ static = "icons/16/cancel.png" } 16.10 + slot.put(_"Cancel") 16.11 + end, 16.12 + module = "timeline", 16.13 + view = "index" 16.14 + } 16.15 +end) 16.16 + 16.17 +ui.form{ 16.18 + attr = { class = "vertical" }, 16.19 + module = "timeline", 16.20 + action = "save", 16.21 + content = function() 16.22 + ui.field.text{ 16.23 + label = _"Name", 16.24 + name = "name", 16.25 + } 16.26 + ui.submit{ 16.27 + text = _"Save" 16.28 + } 16.29 + end 16.30 +}
17.1 --- a/app/main/vote/list.lua Sun Jan 10 12:00:00 2010 +0100 17.2 +++ b/app/main/vote/list.lua Fri Jan 22 12:00:00 2010 +0100 17.3 @@ -109,7 +109,9 @@ 17.4 id = "entry_" .. tostring(initiative.id) 17.5 }, 17.6 content = function() 17.7 - local initiators = initiative.initiating_members 17.8 + local initiators_selector = initiative:get_reference_selector("initiating_members") 17.9 + :add_where("accepted") 17.10 + local initiators = initiators_selector:exec() 17.11 local initiator_names = {} 17.12 for i, initiator in ipairs(initiators) do 17.13 initiator_names[#initiator_names+1] = initiator.name
18.1 --- a/app/main/vote/show_incoming.lua Sun Jan 10 12:00:00 2010 +0100 18.2 +++ b/app/main/vote/show_incoming.lua Fri Jan 22 12:00:00 2010 +0100 18.3 @@ -6,14 +6,14 @@ 18.4 :join("delegating_voter", nil, "delegating_voter.member_id = member.id") 18.5 :add_where{ "delegating_voter.issue_id = ?", issue.id } 18.6 :add_where{ "delegating_voter.delegate_member_ids[1] = ?", member.id } 18.7 - :add_field{ "delegating_voter.weight" } 18.8 + :add_field("delegating_voter.weight", "voter_weight") 18.9 18.10 execute.view{ 18.11 module = "member", 18.12 view = "_list", 18.13 - params = { 18.14 + params = { 18.15 members_selector = members_selector, 18.16 - issue = issue, 18.17 + initiative = initiative, 18.18 trustee = member 18.19 } 18.20 } 18.21 \ No newline at end of file
19.1 --- a/config/default.lua Sun Jan 10 12:00:00 2010 +0100 19.2 +++ b/config/default.lua Fri Jan 22 12:00:00 2010 +0100 19.3 @@ -1,5 +1,5 @@ 19.4 config.app_name = "LiquidFeedback" 19.5 -config.app_version = "beta6" 19.6 +config.app_version = "beta7" 19.7 19.8 config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)" 19.9
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/env/util/gregor.lua Fri Jan 22 12:00:00 2010 +0100 20.3 @@ -0,0 +1,11 @@ 20.4 +function util.gregor(el_id) 20.5 + ui.script{ script = 20.6 + 'gregor_addGui({' .. 20.7 + 'element_id: "' .. el_id .. '",' .. 20.8 + 'month_names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],' .. 20.9 + 'weekday_names: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],' .. 20.10 + 'week_mode: "iso",' .. 20.11 + 'week_numbers: "left",' .. 20.12 + '});' 20.13 + } 20.14 +end 20.15 \ No newline at end of file
21.1 --- a/locale/help/initiative.revoke.de.txt Sun Jan 10 12:00:00 2010 +0100 21.2 +++ b/locale/help/initiative.revoke.de.txt Fri Jan 22 12:00:00 2010 +0100 21.3 @@ -1,2 +1,2 @@ 21.4 =Initiative zurückziehen= 21.5 -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. 21.6 +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. Angeboten werden dir hierfür alle von dir unterstützten Initiativen.
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/locale/help/timeline.index.de.txt Fri Jan 22 12:00:00 2010 +0100 22.3 @@ -0,0 +1,4 @@ 22.4 +=Zeitachse= 22.5 +Hier kannst du dich über Ereignisse im System informieren. Über Filter-Einstellungen kannst du steuern, welche Ereignisarten dir angezeigt werden sollen. Du kannst die Anzeige für einzelne Ereignisarten auch von weiteren Bedingungen abhängig machen. Um zum Beispiel alle Themen anzuzeigen, die nach Erreichen des Unterstützerquorums in den Status ,,Diskussion'' gelangen, wählst du das Themen-Ereignis ,,Thema akzeptiert'' und kannst zum Beispiel zusätzlich festlegen, dass diese Ereignisse nur dann angezeigt werden sollen, wenn du Mitglied des jeweiligen Themenbereichs bist. 22.6 +=Filter speichern= 22.7 +Du kannst Filtereinstellungen unter einem Namen abspeichern und die jeweilige Abfrage dann mit einem Klick ausführen. Wenn du kein anderes Datum auswählst, beziehen sich Abfragen standardmäßig auf heute.
24.1 --- a/locale/translations.de.lua Sun Jan 10 12:00:00 2010 +0100 24.2 +++ b/locale/translations.de.lua Fri Jan 22 12:00:00 2010 +0100 24.3 @@ -1,5 +1,6 @@ 24.4 #!/usr/bin/env lua 24.5 return { 24.6 +["#{count} more initiatives"] = "#{count} weitere Initiativen"; 24.7 ["#{interested_issues_to_vote_count} issue(s) you are interested in"] = "#{interested_issues_to_vote_count} Themen, die Dich interessieren"; 24.8 ["#{issues_to_vote_count} issue(s)"] = "#{issues_to_vote_count} Themen"; 24.9 ["#{number} Image(s) has been deleted"] = "Es wurde(n) #{number} Bild(er) gelöscht"; 24.10 @@ -25,7 +26,6 @@ 24.11 ["Administrator"] = "Administrator"; 24.12 ["Admission time"] = "Zeit für die Zulassung"; 24.13 ["Admitted"] = "zugelassen"; 24.14 -["All"] = "Alle"; 24.15 ["Any"] = "Alle"; 24.16 ["Are you sure?"] = "Sicher?"; 24.17 ["Area"] = "Themenbereich"; 24.18 @@ -40,6 +40,7 @@ 24.19 ["Autoreject is on."] = "Auto-Ablehnen ist an"; 24.20 ["Avatar"] = "Avatar"; 24.21 ["Back"] = "Zurück"; 24.22 +["Back to timeline"] = "Zurück zur Zeitachse"; 24.23 ["Become a member"] = "Mitglied werden"; 24.24 ["Birthday"] = "Geburtstag"; 24.25 ["Can't remove last initiator"] = "Der letzte Initiator kann nicht entfernt werden"; 24.26 @@ -78,8 +79,10 @@ 24.27 ["Created at"] = "Erzeugt am/um"; 24.28 ["Current draft"] = "Aktueller Entwurf"; 24.29 ["Current votings in areas you are member of and issues you are interested in:"] = "Jetzt laufende Abstimmungen zu Themen aus Deinen Themenbereichen oder solchen an denen Du interessiert bist:"; 24.30 +["Date"] = "Datum"; 24.31 ["Degree"] = "Grad"; 24.32 ["Delegations"] = "Delegationen"; 24.33 +["Delete filter"] = "Filter löschen"; 24.34 ["Description"] = "Beschreibung"; 24.35 ["Details"] = "Details"; 24.36 ["Developer features"] = "Entwicklerfunktionen"; 24.37 @@ -100,6 +103,7 @@ 24.38 ["Edit initiative"] = "Initiative bearbeiten"; 24.39 ["Edit my page"] = "Meine Seite bearbeiten"; 24.40 ["Edit my profile"] = "Mein Profil bearbeiten"; 24.41 +["Edit timeline filter"] = "Zeitachsen-Filter bearbeiten"; 24.42 ["Email address"] = "E-Mail-Adresse"; 24.43 ["Email address confirmation"] = "Bestätigung der E-Mail-Adresse"; 24.44 ["Email address is confirmed now"] = "E-Mail-Adresse ist jetzt bestätigt"; 24.45 @@ -117,11 +121,11 @@ 24.46 ["Fully frozen at"] = "Ganz eingefroren am/um"; 24.47 ["Global delegation"] = "Globale Delegation"; 24.48 ["Global delegation active"] = "Globale Delegation aktiv"; 24.49 -["Global timeline"] = "Globale Zeitlinie"; 24.50 ["Half frozen at"] = "Halb eingefroren am/um"; 24.51 ["Hello "] = "Hallo "; 24.52 ["Help for: #{text}"] = "Hilfe zu: #{text}"; 24.53 ["Hide"] = "Verstecken"; 24.54 +["Hide filter details"] = "Filter-Details verstecken"; 24.55 ["Hide this help message"] = "Diesen Hilfetext ausblenden"; 24.56 ["Home"] = "Startseite"; 24.57 ["I accept the terms of use by checking the following checkbox:"] = "Ich akzeptiere die Nutzungsbedingungen durch Auswahl der folgenden Ankreuzbox:"; 24.58 @@ -131,14 +135,16 @@ 24.59 ["Images"] = "Bilder"; 24.60 ["In discussion"] = "In Diskussion"; 24.61 ["Incoming delegations"] = "Eingehende Delegationen"; 24.62 +["Initiated"] = "Initiert"; 24.63 ["Initiated initiatives"] = "Initierte Initiativen"; 24.64 +["Initiative events"] = "Initiativen-Ereignisse"; 24.65 ["Initiative is revoked now"] = "Initiative ist jetzt zurückgezogen"; 24.66 ["Initiative quorum"] = "Quorum Inititive"; 24.67 +["Initiative revoked"] = "Initiative zurückgezogen"; 24.68 ["Initiative successfully created"] = "Initiative erfolgreich erzeugt"; 24.69 ["Initiative successfully updated"] = "Initiative erfolgreich aktualisiert"; 24.70 ["Initiative: '#{name}'"] = "Initiative: '#{name}'"; 24.71 ["Initiatives"] = "Initiativen"; 24.72 -["Initiatives per page"] = "Initiativen je Seite"; 24.73 ["Initiatives that invited you to become initiator:"] = "Initiative, die Dich eingeladen haben, Initiator zu werden:"; 24.74 ["Initiator"] = "Initiator"; 24.75 ["Initiators"] = "Initiatoren"; 24.76 @@ -154,16 +160,20 @@ 24.77 ["Invite code"] = "Invite-Code"; 24.78 ["Invite initiator"] = "Initiator einladen"; 24.79 ["Invited"] = "Eingeladen"; 24.80 -["Inviting initiator"] = "Initiatoren einladen"; 24.81 ["Issue"] = "Thema"; 24.82 ["Issue ##{id}"] = "Thema ##{id}"; 24.83 ["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})"; 24.84 +["Issue accepted"] = "Thema akzeptiert"; 24.85 +["Issue canceled"] = "Thema abgebrochen"; 24.86 ["Issue delegation"] = "Issue-Delegation"; 24.87 ["Issue delegation active"] = "Delegation für Thema aktiv"; 24.88 +["Issue events"] = "Themen-Ergeignisse"; 24.89 +["Issue finished"] = "Thema abgeschlossen"; 24.90 +["Issue finished without voting"] = "Thema ohne Abstimmung abgeschlossen"; 24.91 +["Issue frozen"] = "Thema eingefroren"; 24.92 ["Issue policy"] = "Regelwerk für Thema"; 24.93 ["Issue quorum"] = "Quorum Thema"; 24.94 ["Issues"] = "Themen"; 24.95 -["Issues per page"] = "Themen je Seite"; 24.96 ["JavaScript is disabled or not available."] = "JavaScript ist abgeschaltet oder nicht verfügbar."; 24.97 ["Last author"] = "Letzter Autor"; 24.98 ["Last snapshot:"] = "Letzte Auszählung:"; 24.99 @@ -175,6 +185,8 @@ 24.100 ["Login successful!"] = "Anmeldung erfolgreich"; 24.101 ["Logout"] = "Abmelden"; 24.102 ["Logout successful"] = "Abmeldung erfolgreich"; 24.103 +["Manage filter"] = "Filter verwalten"; 24.104 +["Manage timeline filters"] = "Zeitachsen-Filter verwalten"; 24.105 ["Mark suggestion as implemented and express dissatisfaction"] = "Anregung als umgesetzt markieren und Unzufriedenheit ausdrücken"; 24.106 ["Mark suggestion as implemented and express satisfaction"] = "Anregung als umgesetzt markieren und Zufriedenheit ausdrücken"; 24.107 ["Mark suggestion as not implemented and express dissatisfaction"] = "Anregung als nicht umgesetzt markieren und Unzufriedenheit ausdrücken"; 24.108 @@ -191,6 +203,7 @@ 24.109 ["Member list"] = "Mitgliederliste"; 24.110 ["Member name"] = "Mitglied Name"; 24.111 ["Member name history for '#{name}'"] = "Namenshistorie für '#{name}'"; 24.112 +["Member of area"] = "Mitglied des Themenbereichs"; 24.113 ["Member page"] = "Mitgliederseite"; 24.114 ["Member successfully registered"] = "Mitglied erfolgreich registriert"; 24.115 ["Member successfully updated"] = "Mitglied erfolgreich aktualisert"; 24.116 @@ -207,16 +220,21 @@ 24.117 ["My opinion"] = "Meine Meinung"; 24.118 ["Name"] = "Name"; 24.119 ["New"] = "Neu"; 24.120 +["New draft"] = "Neuer Entwurf"; 24.121 ["New draft has been added to initiative"] = "Neuer Entwurf wurde der Initiative hinzugefügt"; 24.122 ["New draft revision"] = "Neue Revision des Entwurfs"; 24.123 +["New initiative"] = "Neue Initiative"; 24.124 +["New issue"] = "Neues Thema"; 24.125 ["New password"] = "Neues Kennwort"; 24.126 ["New passwords does not match."] = "Du hast nicht zweimal das gleiche Kennwort eingegeben"; 24.127 ["New passwords is too short."] = "Das neue Kennwort ist zu kurz"; 24.128 +["New suggestion"] = "Neue Anregung"; 24.129 ["Newest"] = "Neueste"; 24.130 ["Next state"] = "Nächster Zustand"; 24.131 ["No"] = "Nein"; 24.132 ["No changes to your images were made"] = "An Deinen Bildern wurde nichts geändert"; 24.133 ["No delegation"] = "Keine Delegation"; 24.134 +["No events selected to list"] = "Keine Ereignisse ausgewählt"; 24.135 ["No membership at all"] = "Gar keine Mitgliedschaft"; 24.136 ["No support at all"] = "Gar keine Unterstützung"; 24.137 ["Not a member"] = "Kein Mitglied"; 24.138 @@ -262,6 +280,7 @@ 24.139 ["Population"] = "Grundgesamtheit"; 24.140 ["Posts"] = "Ämter"; 24.141 ["Potential support"] = "Potentielle Unterstützung"; 24.142 +["Potential supported"] = "Potentiell unterstützt"; 24.143 ["Potential supporter"] = "Potentielle Unterstützer"; 24.144 ["Profession"] = "Beruf"; 24.145 ["Profile"] = "Profil"; 24.146 @@ -284,7 +303,10 @@ 24.147 ["Remove my interest"] = "Interesse abmelden"; 24.148 ["Remove my membership"] = "Mitgliedschaft aufgeben"; 24.149 ["Remove my support from this initiative"] = "Meine Unterstützung der Initiative entziehen"; 24.150 +["Rename"] = "Umbenennen"; 24.151 +["Rename filter"] = "Filter umbenennen"; 24.152 ["Repeat new password"] = "Neues Kennwort wiederholen"; 24.153 +["Replace filter"] = "Filter ersetzen"; 24.154 ["Request password reset link"] = "Link zum Rücksetzen des Kennworts anfordern"; 24.155 ["Reset code"] = "Rücksetzcode"; 24.156 ["Reset code is invalid!"] = "Rücksetzcode ist ungültig"; 24.157 @@ -295,12 +317,16 @@ 24.158 ["Revoked at"] = "Zurückgezogen am/um"; 24.159 ["Saturday"] = "Samstag"; 24.160 ["Save"] = "Speichern"; 24.161 -["Saved as contact"] = "Als Kontakt speichern"; 24.162 +["Save as new filter"] = "Als neuen Filter speichern"; 24.163 +["Save current filter"] = "Aktuellen Filter speichern"; 24.164 +["Save timeline filters"] = "Zeitachsen-Filter speichern"; 24.165 +["Saved as contact"] = "Als Kontakt gespeichert"; 24.166 ["Search"] = "Suchen"; 24.167 ["Search initiatives"] = "Suche Initiativen"; 24.168 ["Search issues"] = "Suche Themen"; 24.169 ["Search members"] = "Suche Mitglieder"; 24.170 ["Search results for: '#{search}'"] = "Suchergebnisse für: '#{search}'"; 24.171 +["Select filter to replace"] = "Wähle zu ersetzenden Filter"; 24.172 ["Set URL"] = "URL setzen"; 24.173 ["Set area delegation"] = "Delegation für Themengebiet festlegen"; 24.174 ["Set autoreject"] = "Auto-Ablehnen anschalten"; 24.175 @@ -316,9 +342,11 @@ 24.176 ["Show areas in use"] = "Zeige verwendete Themenbereiche"; 24.177 ["Show areas not in use"] = "Zeige nicht verwendente Themenbereiche"; 24.178 ["Show diff"] = "Änderungen anzeigen"; 24.179 +["Show filter details"] = "Zeige Filter-Details"; 24.180 ["Show locked members"] = "Zeige gesperrte Mitglieder"; 24.181 ["Show member"] = "Mitglied anzeigen"; 24.182 ["Show name history"] = "Namenshistorie zeigen"; 24.183 +["Show only events which match... (or associtated)"] = "Zeige nur Ereignisse welche folgendes erfüllen... (oder-verknüpft)"; 24.184 ["Software"] = "Software"; 24.185 ["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."; 24.186 ["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."; 24.187 @@ -344,10 +372,12 @@ 24.188 ["Sunday"] = "Sonntag"; 24.189 ["Support"] = "Unterstützung"; 24.190 ["Support this initiative"] = "Diese Initiative unterstützen"; 24.191 +["Supported"] = "Unterstützt"; 24.192 ["Supported initiatives"] = "Unterstützte Initiativen"; 24.193 ["Supporter"] = "Unterstützer"; 24.194 ["Terms accepted"] = "Bedingungen akzeptiert"; 24.195 ["The code you've entered is invalid"] = "Der Code, den Du eingeben hast, ist nicht gültig!"; 24.196 +["The draft of this initiative has been updated!"] = "Der Entwurfstext der Initiative wurde aktualisiert!"; 24.197 ["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht"; 24.198 ["The initiators suggest to support the following initiative:"] = "Die Initiatoren empfehlen folgende Initiative zu unterstützen:"; 24.199 ["This initiative has been revoked at #{revoked}"] = "Diese Initiative wurde am/um #{revoked} zurückgezogen"; 24.200 @@ -369,8 +399,8 @@ 24.201 ["This username is too short!"] = "Dieser Benutzername ist zu kurz!"; 24.202 ["Thursday"] = "Donnerstag"; 24.203 ["Time left"] = "Restzeit"; 24.204 +["Timeline"] = "Zeitachse"; 24.205 ["Title (80 chars max)"] = "Title (max. 80 Zeichen)"; 24.206 -["Today"] = "Heute"; 24.207 ["Traditional wiki syntax"] = "Traditionaller Wiki-Syntax"; 24.208 ["Trustee"] = "Bevollmächtigter"; 24.209 ["Tuesday"] = "Dienstag"; 24.210 @@ -393,19 +423,23 @@ 24.211 ["Voting has not started yet."] = "Die Abstimmung hat noch nicht begonnen."; 24.212 ["Voting proposal"] = "Abstimmungsvorlage"; 24.213 ["Voting requests"] = "Abstimmanträge"; 24.214 +["Voting started"] = "Abstimmung begonnen"; 24.215 ["Voting time"] = "Zeit für die Abstimmung"; 24.216 ["Website"] = "Webseite"; 24.217 ["Wednesday"] = "Mittwoch"; 24.218 ["Wiki engine"] = "Wiki engine"; 24.219 ["Yes"] = "Ja"; 24.220 -["Yesterday"] = "Gestern"; 24.221 ["You are already initator"] = "Du bist bereits Initiator"; 24.222 ["You are already not supporting this initiative"] = "Diese Initiative hat bereits keine Unterstützung von Dir"; 24.223 ["You are already supporting the latest draft"] = "Du unterstützt bereits den neuesten Entwurf"; 24.224 ["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!"; 24.225 +["You are iniator of this initiative"] = "Du bist Initiator dieser Initiative"; 24.226 +["You are interested in this issue"] = "Du bist an diesem Thema interessiert"; 24.227 ["You are invited to become initiator of this initiative."] = "Du bist eingeladen Initiator dieser Initiative zu werden."; 24.228 ["You are member"] = "Du bist Mitglied"; 24.229 ["You are now initiator of this initiative"] = "Du bist jetzt Initiator dieser Initiative"; 24.230 +["You are potential supporter of this initiative"] = "Du bist potentieller Unterstützer dieser Initiative"; 24.231 +["You are supporting this initiative"] = "Du unterstützt diese Initiative"; 24.232 ["You can't suggest the initiative you are revoking"] = "Du kannst nicht die Initiative empfehlen, die Du löschen möchtest"; 24.233 ["You didn't saved any member as contact yet."] = "Du hast noch kein Mitglied als Kontakt gespeichert!"; 24.234 ["You have saved this member as contact"] = "Du hast das Mitglied als Kontakt gespeichert";
25.1 --- a/model/member.lua Sun Jan 10 12:00:00 2010 +0100 25.2 +++ b/model/member.lua Fri Jan 22 12:00:00 2010 +0100 25.3 @@ -310,3 +310,31 @@ 25.4 } 25.5 return success 25.6 end 25.7 + 25.8 +function Member.object:get_setting_by_key(key) 25.9 +end 25.10 + 25.11 +function Member.object:set_setting(key, value) 25.12 +end 25.13 + 25.14 +function Member.object:get_setting_maps_by_key(key) 25.15 + return SettingMap:new_selector() 25.16 + :add_where{ "member_id = ?", self.id } 25.17 + :add_where{ "key = ?", key } 25.18 + :add_order_by("subkey") 25.19 + :exec() 25.20 +end 25.21 + 25.22 +function Member.object:get_setting_map_by_key_and_subkey(key, subkey) 25.23 + return SettingMap:new_selector() 25.24 + :add_where{ "member_id = ?", self.id } 25.25 + :add_where{ "key = ?", key } 25.26 + :add_where{ "subkey = ?", subkey } 25.27 + :add_order_by("subkey") 25.28 + :optional_object_mode() 25.29 + :exec() 25.30 +end 25.31 + 25.32 +function Member.object:set_setting_map(key, subkey, value) 25.33 + 25.34 +end
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/model/setting_map.lua Fri Jan 22 12:00:00 2010 +0100 26.3 @@ -0,0 +1,3 @@ 26.4 +SettingMap = mondelefant.new_class() 26.5 +SettingMap.table = 'setting_map' 26.6 +SettingMap.primary_key = { "key", "subkey" } 26.7 \ No newline at end of file
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 27.2 +++ b/model/timeline.lua Fri Jan 22 12:00:00 2010 +0100 27.3 @@ -0,0 +1,42 @@ 27.4 +Timeline = mondelefant.new_class() 27.5 +Timeline.table = 'timeline' 27.6 + 27.7 +Timeline:add_reference{ 27.8 + mode = '11', 27.9 + to = "Member", 27.10 + this_key = 'member_id', 27.11 + that_key = 'id', 27.12 + ref = 'member' 27.13 +} 27.14 + 27.15 +Timeline:add_reference{ 27.16 + mode = '11', 27.17 + to = "Issue", 27.18 + this_key = 'issue_id', 27.19 + that_key = 'id', 27.20 + ref = 'issue' 27.21 +} 27.22 + 27.23 +Timeline:add_reference{ 27.24 + mode = '11', 27.25 + to = "Initiative", 27.26 + this_key = 'initiative_id', 27.27 + that_key = 'id', 27.28 + ref = 'initiative' 27.29 +} 27.30 + 27.31 +Timeline:add_reference{ 27.32 + mode = '11', 27.33 + to = "Draft", 27.34 + this_key = 'draft_id', 27.35 + that_key = 'id', 27.36 + ref = 'draft' 27.37 +} 27.38 + 27.39 +Timeline:add_reference{ 27.40 + mode = '11', 27.41 + to = "Suggestion", 27.42 + this_key = 'suggestion_id', 27.43 + that_key = 'id', 27.44 + ref = 'suggestion' 27.45 +}
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/static/gregor.js/gregor.css Fri Jan 22 12:00:00 2010 +0100 28.3 @@ -0,0 +1,92 @@ 28.4 +.gregor_sheet { 28.5 + border: 1px solid black; 28.6 + border-collapse: collapse; 28.7 + margin: 0; 28.8 + padding: 0; 28.9 + background: #fff; 28.10 + color: #000; 28.11 +} 28.12 +.gregor_sheet th, .gregor_sheet td { 28.13 + margin: 0; 28.14 + font-size: 70%; 28.15 +} 28.16 +.gregor_year, .gregor_month { 28.17 + background-color: #ddd; 28.18 + border-bottom: 1px solid black; 28.19 + font-weight: normal; 28.20 + text-align: center; 28.21 +} 28.22 +.gregor_year a, .gregor_month a { 28.23 + color: #000; 28.24 + text-decoration: none; 28.25 +} 28.26 +.gregor_year a:hover, .gregor_month a:hover { 28.27 + color: #f00; 28.28 +} 28.29 +.gregor_weekday { 28.30 + font-size: 80%; 28.31 + padding-top: 0.5em; 28.32 +} 28.33 +.gregor_day { 28.34 + padding-bottom: 0.35em; 28.35 +} 28.36 + 28.37 +.gregor_weekday, 28.38 +.gregor_day { 28.39 + padding-left: 0.25em; 28.40 + padding-right: 0.25em; 28.41 +} 28.42 +.gregor_weeks_left .gregor_weekday, 28.43 +.gregor_weeks_left .gregor_day { 28.44 + padding-left: 0; 28.45 + padding-right: 0.5em; 28.46 +} 28.47 +.gregor_weeks_right .gregor_weekday, 28.48 +.gregor_weeks_right .gregor_day { 28.49 + padding-left: 0.5em; 28.50 + padding-right: 0; 28.51 +} 28.52 +.gregor_weekday { 28.53 + text-align: center; 28.54 +} 28.55 +.gregor_weeks_left .gregor_week { 28.56 + padding-left: 0.25em; 28.57 + padding-right: 0.5em; 28.58 +} 28.59 +.gregor_weeks_right .gregor_week { 28.60 + padding-left: 1em; 28.61 + padding-right: 0.25em; 28.62 +} 28.63 +.gregor_week { 28.64 + color: #666; 28.65 + font-size: 80%; 28.66 + font-weight: normal; 28.67 + text-align: right; 28.68 +} 28.69 +td.gregor_day { 28.70 + text-align: right; 28.71 +} 28.72 +td.gregor_day a { 28.73 + background-color: #ddd; 28.74 + color: #000; 28.75 + display: block; 28.76 + padding-bottom: 0.25ex; 28.77 + padding-right: 0.25em; 28.78 + padding-top: 0.25ex; 28.79 + text-decoration: none; 28.80 + width: 2em; 28.81 +} 28.82 +td.gregor_day.gregor_sat a { 28.83 + background-color: #bbb; 28.84 +} 28.85 +td.gregor_day.gregor_sun a { 28.86 + background-color: #ea9; 28.87 +} 28.88 +td.gregor_day.gregor_today a { 28.89 + font-weight: bold; 28.90 +} 28.91 +td.gregor_day.gregor_selected a { 28.92 + background-color: #444; 28.93 + color: #fff; 28.94 +} 28.95 +
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 29.2 +++ b/static/gregor.js/gregor.js Fri Jan 22 12:00:00 2010 +0100 29.3 @@ -0,0 +1,845 @@ 29.4 +// 29.5 +// Copyright (c) 2009 Public Software Group e. V., Berlin 29.6 +// 29.7 +// Permission is hereby granted, free of charge, to any person obtaining a 29.8 +// copy of this software and associated documentation files (the 29.9 +// "Software"), to deal in the Software without restriction, including 29.10 +// without limitation the rights to use, copy, modify, merge, publish, 29.11 +// distribute, sublicense, and/or sell copies of the Software, and to 29.12 +// permit persons to whom the Software is furnished to do so, subject to 29.13 +// the following conditions: 29.14 +// 29.15 +// The above copyright notice and this permission notice shall be included 29.16 +// in all copies or substantial portions of the Software. 29.17 +// 29.18 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 29.19 +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29.20 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 29.21 +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 29.22 +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 29.23 +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 29.24 +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29.25 +// 29.26 + 29.27 +// 29.28 +// All date calculations are based on the gregorian calender, also for 29.29 +// dates before 1582 (before the gegorian calendar was introduced). 29.30 +// The supported range is from January 1st 0001 up to December 31st 9999. 29.31 +// 29.32 +// gregor_daycount({year: <year>, month: <month>, day: <day>}) returns 29.33 +// the number of days between the given date and January 1st 0001 (greg.). 29.34 +// 29.35 +// gregor_completeDate({year: <year>, month: <month>, day: <day>}) returns 29.36 +// a structure (an object) with the following properties: 29.37 +// - daycount (days since January 1st 0001, see gregor_daycount) 29.38 +// - year (with century) 29.39 +// - month (from 1 to 12) 29.40 +// - day (from 1 to 28, 29, 30 or 31) 29.41 +// - iso_string (string with format YYYY-MM-DD) 29.42 +// - us_weekday (from 0 for Sunday to 6 for Monday) 29.43 +// - iso_weekday (from 0 for Monday to 6 for Sunday) 29.44 +// - iso_weekyear (Year containing greater part of week st. w. Monday) 29.45 +// - iso_week (from 1 to 52 or 53) 29.46 +// - us_week (from 1 to 53 or 54) 29.47 +// 29.48 +// The structure (the object) passed as parameter to gregor_daycount or 29.49 +// gregor_completeDate may describe a date in the following ways: 29.50 +// - daycount 29.51 +// - year, month, day 29.52 +// - year, us_week, us_weekday 29.53 +// - year, iso_week, iso_weekday 29.54 +// - iso_weekyear, iso_week, iso_weekday 29.55 +// 29.56 +// gregor_sheet({...}) returns a calendar sheet as DOM object. The 29.57 +// structure (the object) passed to the function as an argument is altered 29.58 +// by the function and used to store state variables. Initially it can 29.59 +// contain the following fields: 29.60 +// - year (year to show, defaults to todays year) 29.61 +// - month (month to show, defaults to todays month) 29.62 +// - today (structure describing a day, e.g. year, month, day) 29.63 +// - selected (structure describing a day, e.g. year, month, day) 29.64 +// - navigation ("enabled", "disabled", "hidden", default "enabled") 29.65 +// - week_mode ("iso" or "us", defaults to "iso") 29.66 +// - week_numbers ("left", "right" or null, defaults to null) 29.67 +// - month_names (e.g. ["January", "Feburary", ...]) 29.68 +// - weekday_names (e.g. ["Mon", "Tue", ...] or ["Sun", "Mon", ...]) 29.69 +// - day_callback (function to render a cell) 29.70 +// - select_callback (function to be called, when a date was selected) 29.71 +// - element (for internal use only) 29.72 +// If "today" is undefined, it is automatically intitialized with the 29.73 +// current clients date. If "selected" is undefined or null, no date is 29.74 +// be initially selected. It is mandatory to provide month_names and 29.75 +// weekday_names. 29.76 +// 29.77 +// gregor_addGui({...}) alters a referenced input field in a way that 29.78 +// focussing on it causes a calendar being shown at its right side, where a 29.79 +// date can be selected. The structure (the object) passed to this function 29.80 +// is only evaluated once, and never modified. All options except "element" 29.81 +// of the gregor_sheet({...}) function may be used as options to 29.82 +// gregor_addGui({...}) as well. In addition an "element_id" must be 29.83 +// provided as argument, containing the id of a text input field. The 29.84 +// behaviour caused by the options "selected" and "select_callback" are 29.85 +// slightly different: If "selected" === undefined, then the current value 29.86 +// of the text field referenced by "element_id" will be parsed and used as 29.87 +// date selection. If "selected" === null, then no date will be initially 29.88 +// selected, and the text field will be cleared. The "select_callback" 29.89 +// function is always called once with the pre-selected date (or with null, 29.90 +// if no date is initially selected). Whenever the selected date is changed 29.91 +// or unselected later, the callback function is called again with the new 29.92 +// date (or with null, in case of deselection). 29.93 +// 29.94 +// EXAMPLE: 29.95 +// 29.96 +// <input type="text" id="test_field" name="test_field" value=""/> 29.97 +// <script type="text/javascript"> 29.98 +// gregor_addGui({ 29.99 +// element_id: 'test_field', 29.100 +// month_names: [ 29.101 +// "January", "February", "March", "April", "May", "June", 29.102 +// "July", "August", "September", "October", "November", "December" 29.103 +// ], 29.104 +// weekday_names: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], 29.105 +// week_numbers: "left" 29.106 +// }); 29.107 +// </script> 29.108 +// 29.109 + 29.110 + 29.111 + 29.112 + 29.113 +// Internal constants and helper functions for date calculation 29.114 + 29.115 + 29.116 +gregor_c1 = 365; // days of a non-leap year 29.117 +gregor_c4 = 4 * gregor_c1 + 1; // days of a full 4 year cycle 29.118 +gregor_c100 = 25 * gregor_c4 - 1; // days of a full 100 year cycle 29.119 +gregor_c400 = 4 * gregor_c100 + 1; // days of a full 400 year cycle 29.120 + 29.121 +gregor_normalMonthOffsets = [ 29.122 + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 29.123 +]; 29.124 + 29.125 +function gregor_getMonthOffset(year, month) { 29.126 + if ( 29.127 + (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) && 29.128 + month > 2 29.129 + ) return gregor_normalMonthOffsets[month-1] + 1; 29.130 + else return gregor_normalMonthOffsets[month-1]; 29.131 +} 29.132 + 29.133 +function gregor_formatInteger(int, digits) { 29.134 + var result = int.toFixed(); 29.135 + if (digits != null) { 29.136 + while (result.length < digits) result = "0" + result; 29.137 + } 29.138 + return result; 29.139 +} 29.140 + 29.141 + 29.142 + 29.143 +// Calculate days since January 1st 0001 (Gegorian) for a given date 29.144 + 29.145 + 29.146 +function gregor_daycount(obj) { 29.147 + if ( 29.148 + obj.daycount >= 0 && obj.daycount <= 3652058 && obj.daycount % 1 == 0 29.149 + ) { 29.150 + return obj.daycount; 29.151 + } else if ( 29.152 + obj.year >= 1 && obj.year <= 9999 && obj.year % 1 == 0 && 29.153 + obj.month >= 1 && obj.month <= 12 && obj.month % 1 == 0 && 29.154 + obj.day >= 0 && obj.day <= 31 && obj.day % 1 == 0 29.155 + ) { 29.156 + var n400 = Math.floor((obj.year-1) / 400); 29.157 + var r400 = (obj.year-1) % 400; 29.158 + var n100 = Math.floor(r400 / 100); 29.159 + var r100 = r400 % 100; 29.160 + var n4 = Math.floor(r100 / 4); 29.161 + var n1 = r100 % 4; 29.162 + return ( 29.163 + gregor_c400 * n400 + 29.164 + gregor_c100 * n100 + 29.165 + gregor_c4 * n4 + 29.166 + gregor_c1 * n1 + 29.167 + gregor_getMonthOffset(obj.year, obj.month) + (obj.day - 1) 29.168 + ); 29.169 + } else if ( 29.170 + ( 29.171 + ( 29.172 + obj.year >= 1 && obj.year <= 9999 && 29.173 + obj.year % 1 == 0 && obj.iso_weekyear == null 29.174 + ) || ( 29.175 + obj.iso_weekyear >= 1 && obj.iso_weekyear <= 9999 && 29.176 + obj.iso_weekyear % 1 == 0 && obj.year == null 29.177 + ) 29.178 + ) && 29.179 + obj.iso_week >= 0 && obj.iso_week <= 53 && obj.iso_week % 1 == 0 && 29.180 + obj.iso_weekday >= 0 && obj.iso_weekday <= 6 && 29.181 + obj.iso_weekday % 1 == 0 29.182 + ) { 29.183 + var jan4th = gregor_daycount({ 29.184 + year: (obj.year != null) ? obj.year : obj.iso_weekyear, 29.185 + month: 1, 29.186 + day: 4 29.187 + }); 29.188 + var monday0 = jan4th - (jan4th % 7) - 7; // monday of week 0 29.189 + return monday0 + 7 * obj.iso_week + obj.iso_weekday; 29.190 + } else if ( 29.191 + obj.year >= 1 && obj.year <= 9999 && obj.year % 1 == 0 && 29.192 + obj.us_week >= 0 && obj.us_week <= 54 && obj.us_week % 1 == 0 && 29.193 + obj.us_weekday >= 0 && obj.us_weekday <= 6 && obj.us_weekday % 1 == 0 29.194 + ) { 29.195 + var jan1st = gregor_daycount({ 29.196 + year: obj.year, 29.197 + month: 1, 29.198 + day: 1 29.199 + }); 29.200 + var sunday0 = jan1st - ((jan1st+1) % 7) - 7; // sunday of week 0 29.201 + return sunday0 + 7 * obj.us_week + obj.us_weekday; 29.202 + } 29.203 +} 29.204 + 29.205 + 29.206 + 29.207 +// Calculate all calendar related numbers for a given date 29.208 + 29.209 + 29.210 +function gregor_completeDate(obj) { 29.211 + if ( 29.212 + obj.daycount >= 0 && obj.daycount <= 3652058 && obj.daycount % 1 == 0 29.213 + ) { 29.214 + var daycount = obj.daycount; 29.215 + var n400 = Math.floor(daycount / gregor_c400); 29.216 + var r400 = daycount % gregor_c400; 29.217 + var n100 = Math.floor(r400 / gregor_c100); 29.218 + var r100 = r400 % gregor_c100; 29.219 + if (n100 == 4) { n100 = 3; r100 = gregor_c100; } 29.220 + var n4 = Math.floor(r100 / gregor_c4); 29.221 + var r4 = r100 % gregor_c4; 29.222 + var n1 = Math.floor(r4 / gregor_c1); 29.223 + var r1 = r4 % gregor_c1; 29.224 + if (n1 == 4) { n1 = 3; r1 = gregor_c1; } 29.225 + var year = 1 + 400 * n400 + 100 * n100 + 4 * n4 + n1; 29.226 + var month = 1 + Math.floor(r1 / 31); 29.227 + var monthOffset = gregor_getMonthOffset(year, month); 29.228 + if (month < 12) { 29.229 + var nextMonthOffset = gregor_getMonthOffset(year, month + 1); 29.230 + if (r1 >= nextMonthOffset) { 29.231 + month++; 29.232 + monthOffset = nextMonthOffset; 29.233 + } 29.234 + } 29.235 + var day = 1 + r1 - monthOffset; 29.236 + var iso_string = ("" + 29.237 + gregor_formatInteger(year, 4) + "-" + 29.238 + gregor_formatInteger(month, 2) + "-" + 29.239 + gregor_formatInteger(day, 2) 29.240 + ); 29.241 + var us_weekday = (daycount+1) % 7; 29.242 + var iso_weekday = daycount % 7; 29.243 + var iso_weekyear = year; 29.244 + if ( 29.245 + month == 1 && ( 29.246 + (day == 3 && iso_weekday == 6) || 29.247 + (day == 2 && iso_weekday >= 5) || 29.248 + (day == 1 && iso_weekday >= 4) 29.249 + ) 29.250 + ) iso_weekyear--; 29.251 + else if ( 29.252 + month == 12 && ( 29.253 + (day == 29 && iso_weekday == 0) || 29.254 + (day == 30 && iso_weekday <= 1) || 29.255 + (day == 31 && iso_weekday <= 2) 29.256 + ) 29.257 + ) iso_weekyear++; 29.258 + var jan4th = gregor_daycount({year: iso_weekyear, month: 1, day: 4}); 29.259 + var monday0 = jan4th - (jan4th % 7) - 7; // monday of week 0 29.260 + var iso_week = Math.floor((daycount - monday0) / 7); 29.261 + var jan1st = gregor_daycount({year: year, month: 1, day: 1}); 29.262 + var sunday0 = jan1st - ((jan1st+1) % 7) - 7; // sunday of week 0 29.263 + var us_week = Math.floor((daycount - sunday0) / 7); 29.264 + return { 29.265 + daycount: daycount, 29.266 + year: year, 29.267 + month: month, 29.268 + day: day, 29.269 + iso_string: iso_string, 29.270 + us_weekday: (daycount+1) % 7, // 0 = Sunday 29.271 + iso_weekday: daycount % 7, // 0 = Monday 29.272 + iso_weekyear: iso_weekyear, 29.273 + iso_week: iso_week, 29.274 + us_week: us_week 29.275 + }; 29.276 + } else if (obj.daycount == null) { 29.277 + var daycount = gregor_daycount(obj); 29.278 + if (daycount != null) { 29.279 + return gregor_completeDate({daycount: gregor_daycount(obj)}); 29.280 + } 29.281 + } 29.282 +} 29.283 + 29.284 + 29.285 + 29.286 +// Test gregor_daycount and gregor_completeDate for consistency 29.287 +// (Debugging only) 29.288 + 29.289 + 29.290 +function gregor_test() { 29.291 + for (i=650000; i<900000; i++) { 29.292 + var obj = gregor_completeDate({daycount: i}); 29.293 + var j; 29.294 + j = gregor_daycount({ 29.295 + year: obj.year, 29.296 + month: obj.month, 29.297 + day: obj.day 29.298 + }); 29.299 + if (i != j) { alert("ERROR"); return; } 29.300 + j = gregor_daycount({ 29.301 + iso_weekyear: obj.iso_weekyear, 29.302 + iso_week: obj.iso_week, 29.303 + iso_weekday: obj.iso_weekday 29.304 + }); 29.305 + if (i != j) { alert("ERROR"); return; } 29.306 + j = gregor_daycount({ 29.307 + year: obj.year, 29.308 + iso_week: 29.309 + (obj.iso_weekyear == obj.year + 1) ? 53 : 29.310 + (obj.iso_weekyear == obj.year - 1) ? 0 : 29.311 + obj.iso_week, 29.312 + iso_weekday: obj.iso_weekday 29.313 + }); 29.314 + if (i != j) { alert("ERROR"); return; } 29.315 + j = gregor_daycount({ 29.316 + year: obj.year, 29.317 + us_week: obj.us_week, 29.318 + us_weekday: obj.us_weekday 29.319 + }); 29.320 + if (i != j) { alert("ERROR"); return; } 29.321 + } 29.322 + alert("SUCCESS"); 29.323 +} 29.324 + 29.325 + 29.326 + 29.327 +// Graphical calendar 29.328 + 29.329 + 29.330 +gregor_iso_weekday_css = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]; 29.331 + 29.332 +function gregor_sheet(args) { 29.333 + 29.334 + // setting args.today and args.selected 29.335 + if (args.today === undefined) { 29.336 + var js_date = new Date(); 29.337 + args.today = gregor_completeDate({ 29.338 + year: js_date.getFullYear(), 29.339 + month: js_date.getMonth() + 1, 29.340 + day: js_date.getDate() 29.341 + }); 29.342 + } else if (args.today != null) { 29.343 + args.today = gregor_completeDate(args.today); 29.344 + } 29.345 + if (args.selected == "today") { 29.346 + args.selected = args.today; 29.347 + } else if (args.selected != null) { 29.348 + args.selected = gregor_completeDate(args.selected); 29.349 + } 29.350 + 29.351 + // setting args.year and args.month 29.352 + if (args.year == null) { 29.353 + if (args.selected != null) args.year = args.selected.year; 29.354 + else args.year = args.today.year; 29.355 + } 29.356 + if (args.month == null) { 29.357 + if (args.selected != null) args.month = args.selected.month; 29.358 + else args.month = args.today.month; 29.359 + } 29.360 + 29.361 + // setting first_day 29.362 + var first_day = gregor_completeDate({ 29.363 + year: args.year, 29.364 + month: args.month, 29.365 + day: 1 29.366 + }); 29.367 + if (first_day == null) return; 29.368 + 29.369 + // checking args.navigation, args.week_mode and args.week_numbers 29.370 + if (args.navigation == null) args.navigation = "enabled"; 29.371 + else if ( 29.372 + args.navigation != "enabled" && 29.373 + args.navigation != "disabled" && 29.374 + args.navigation != "hidden" 29.375 + ) return; 29.376 + if (args.week_mode == null) args.week_mode = "iso"; 29.377 + else if (args.week_mode != "iso" && args.week_mode != "us") return; 29.378 + if ( 29.379 + args.week_numbers != null && 29.380 + args.week_numbers != "left" && 29.381 + args.week_numbers != "right" 29.382 + ) return; 29.383 + 29.384 + // checking args.month.names and args.weekday_names 29.385 + if (args.month_names.length != 12) return; 29.386 + if (args.weekday_names.length != 7) return; 29.387 + 29.388 + // calculating number of days in month 29.389 + var count; 29.390 + if (args.year < 9999 || args.month < 12) { 29.391 + count = gregor_daycount({ 29.392 + year: (args.month == 12) ? (args.year + 1) : args.year, 29.393 + month: (args.month == 12) ? 1 : (args.month + 1), 29.394 + day: 1 29.395 + }) - first_day.daycount; 29.396 + } else { 29.397 + // workaround for year 9999 29.398 + count = 31; 29.399 + } 29.400 + 29.401 + // locale variables for UI construction 29.402 + var table, row, cell, element; 29.403 + 29.404 + // take table element from args.element, 29.405 + // if neccessary create and store it 29.406 + if (args.element == null) { 29.407 + table = document.createElement("table"); 29.408 + args.element = table; 29.409 + } else { 29.410 + table = args.element; 29.411 + while (table.firstChild) table.removeChild(table.firstChild); 29.412 + } 29.413 + 29.414 + // set CSS class of table according to args.week_numbers 29.415 + if (args.week_numbers == "left") { 29.416 + table.className = "gregor_sheet gregor_weeks_left"; 29.417 + } else if (args.week_numbers == "right") { 29.418 + table.className = "gregor_sheet gregor_weeks_right"; 29.419 + } else { 29.420 + table.className = "gregor_sheet gregor_weeks_none"; 29.421 + } 29.422 + 29.423 + // begin of table head 29.424 + var thead = document.createElement("thead"); 29.425 + 29.426 + // navigation 29.427 + if (args.navigation != "hidden") { 29.428 + 29.429 + // UI head row containing the year 29.430 + row = document.createElement("tr"); 29.431 + row.className = "gregor_year_row"; 29.432 + cell = document.createElement("th"); 29.433 + cell.className = "gregor_year"; 29.434 + cell.colSpan = args.week_numbers ? 8 : 7; 29.435 + if (args.navigation == "enabled") { 29.436 + element = document.createElement("a"); 29.437 + element.className = "gregor_turn gregor_turn_left"; 29.438 + element.style.cssFloat = "left"; 29.439 + element.style.styleFloat = "left"; 29.440 + element.href = "#"; 29.441 + element.onclick = function() { 29.442 + if (args.year > 1) args.year--; 29.443 + gregor_sheet(args); 29.444 + return false; 29.445 + } 29.446 + element.ondblclick = element.onclick; 29.447 + element.appendChild(document.createTextNode("<<")); 29.448 + cell.appendChild(element); 29.449 + element = document.createElement("a"); 29.450 + element.className = "gregor_turn gregor_turn_right"; 29.451 + element.style.cssFloat = "right"; 29.452 + element.style.styleFloat = "right"; 29.453 + element.href = "#"; 29.454 + element.onclick = function() { 29.455 + if (args.year < 9999) args.year++; 29.456 + gregor_sheet(args); 29.457 + return false; 29.458 + } 29.459 + element.ondblclick = element.onclick; 29.460 + element.appendChild(document.createTextNode(">>")); 29.461 + cell.appendChild(element); 29.462 + } 29.463 + cell.appendChild(document.createTextNode(first_day.year)); 29.464 + row.appendChild(cell); 29.465 + thead.appendChild(row); 29.466 + 29.467 + // UI head row containing the month 29.468 + row = document.createElement("tr"); 29.469 + row.className = "gregor_month_row"; 29.470 + cell = document.createElement("th"); 29.471 + cell.className = "gregor_month"; 29.472 + cell.colSpan = args.week_numbers ? 8 : 7; 29.473 + if (args.navigation == "enabled") { 29.474 + element = document.createElement("a"); 29.475 + element.className = "gregor_turn gregor_turn_left"; 29.476 + element.style.cssFloat = "left"; 29.477 + element.style.styleFloat = "left"; 29.478 + element.href = "#"; 29.479 + element.onclick = function() { 29.480 + if (args.year > 1 || args.month > 1) { 29.481 + args.month--; 29.482 + if (args.month < 1) { 29.483 + args.month = 12; 29.484 + args.year--; 29.485 + } 29.486 + } 29.487 + gregor_sheet(args); 29.488 + return false; 29.489 + } 29.490 + element.ondblclick = element.onclick; 29.491 + element.appendChild(document.createTextNode("<<")); 29.492 + cell.appendChild(element); 29.493 + element = document.createElement("a"); 29.494 + element.className = "gregor_turn gregor_turn_right"; 29.495 + element.style.cssFloat = "right"; 29.496 + element.style.styleFloat = "right"; 29.497 + element.href = "#"; 29.498 + element.onclick = function() { 29.499 + if (args.year < 9999 || args.month < 12) { 29.500 + args.month++; 29.501 + if (args.month > 12) { 29.502 + args.month = 1; 29.503 + args.year++; 29.504 + } 29.505 + } 29.506 + gregor_sheet(args); 29.507 + return false; 29.508 + } 29.509 + element.ondblclick = element.onclick; 29.510 + element.appendChild(document.createTextNode(">>")); 29.511 + cell.appendChild(element); 29.512 + } 29.513 + cell.appendChild(document.createTextNode( 29.514 + args.month_names[first_day.month-1] 29.515 + )); 29.516 + row.appendChild(cell); 29.517 + thead.appendChild(row); 29.518 + 29.519 + // end of navigation 29.520 + } 29.521 + 29.522 + // UI weekday row 29.523 + row = document.createElement("tr"); 29.524 + row.className = "gregor_weekday_row"; 29.525 + if (args.week_numbers == "left") { 29.526 + cell = document.createElement("th"); 29.527 + cell.className = "gregor_corner"; 29.528 + row.appendChild(cell); 29.529 + } 29.530 + for (var i=0; i<7; i++) { 29.531 + cell = document.createElement("th"); 29.532 + cell.className = ( 29.533 + "gregor_weekday gregor_" + 29.534 + gregor_iso_weekday_css[(args.week_mode == "us") ? ((i+6)%7) : i] 29.535 + ); 29.536 + cell.appendChild(document.createTextNode(args.weekday_names[i])); 29.537 + row.appendChild(cell); 29.538 + } 29.539 + if (args.week_numbers == "right") { 29.540 + cell = document.createElement("th"); 29.541 + cell.className = "gregor_corner"; 29.542 + row.appendChild(cell); 29.543 + } 29.544 + thead.appendChild(row); 29.545 + 29.546 + // end of table head and begin of table body 29.547 + table.appendChild(thead); 29.548 + var tbody = document.createElement("tbody"); 29.549 + 29.550 + // definition of insert_week function 29.551 + var week = ( 29.552 + (args.week_mode == "us") ? first_day.us_week : first_day.iso_week 29.553 + ); 29.554 + insert_week = function() { 29.555 + cell = document.createElement("th"); 29.556 + cell.className = "gregor_week"; 29.557 + cell.appendChild(document.createTextNode( 29.558 + (week < 10) ? ("0" + week) : week) 29.559 + ); 29.560 + week++; 29.561 + if ( 29.562 + args.week_mode == "iso" && ( 29.563 + ( 29.564 + args.month == 1 && week > 52 29.565 + ) || ( 29.566 + args.month == 12 && week == 53 && ( 29.567 + first_day.iso_weekday == 0 || 29.568 + first_day.iso_weekday == 5 || 29.569 + first_day.iso_weekday == 6 29.570 + ) 29.571 + ) 29.572 + ) 29.573 + ) week = 1; 29.574 + row.appendChild(cell); 29.575 + } 29.576 + 29.577 + // output data fields 29.578 + row = document.createElement("tr"); 29.579 + if (args.week_numbers == "left") insert_week(); 29.580 + var filler_count = ( 29.581 + (args.week_mode == "us") ? first_day.us_weekday : first_day.iso_weekday 29.582 + ); 29.583 + for (var i=0; i<filler_count; i++) { 29.584 + cell = document.createElement("td"); 29.585 + cell.className = "gregor_empty"; 29.586 + row.appendChild(cell); 29.587 + } 29.588 + for (var i=0; i<count; i++) { 29.589 + var date = gregor_completeDate({daycount: first_day.daycount + i}); 29.590 + if (row == null) { 29.591 + row = document.createElement("tr"); 29.592 + if (args.week_numbers == "left") insert_week(); 29.593 + } 29.594 + cell = document.createElement("td"); 29.595 + var cssClass = ( 29.596 + "gregor_day gregor_" + gregor_iso_weekday_css[date.iso_weekday] 29.597 + ); 29.598 + if (args.today != null && date.daycount == args.today.daycount) { 29.599 + cssClass += " gregor_today"; 29.600 + } 29.601 + if (args.selected != null && date.daycount == args.selected.daycount) { 29.602 + cssClass += " gregor_selected"; 29.603 + } 29.604 + cell.className = cssClass; 29.605 + if (args.day_callback) { 29.606 + args.day_callback(cell, date); 29.607 + } else { 29.608 + element = document.createElement("a"); 29.609 + element.href = "#"; 29.610 + var generate_onclick = function(date) { 29.611 + return function() { 29.612 + args.selected = date; 29.613 + gregor_sheet(args); 29.614 + if (args.select_callback != null) { 29.615 + args.select_callback(date); 29.616 + } 29.617 + return false; 29.618 + } 29.619 + } 29.620 + element.onclick = generate_onclick(date); 29.621 + element.ondblclick = element.onclick; 29.622 + element.appendChild(document.createTextNode(first_day.day + i)); 29.623 + cell.appendChild(element); 29.624 + } 29.625 + row.appendChild(cell); 29.626 + if (row.childNodes.length == ((args.week_numbers == "left") ? 8 : 7)) { 29.627 + if (args.week_numbers == "right") insert_week(); 29.628 + tbody.appendChild(row); 29.629 + row = null; 29.630 + } 29.631 + } 29.632 + if (row != null) { 29.633 + while (row.childNodes.length < ((args.week_numbers == "left") ? 8 : 7)) { 29.634 + cell = document.createElement("td"); 29.635 + cell.className = "gregor_empty"; 29.636 + row.appendChild(cell); 29.637 + } 29.638 + if (args.week_numbers == "right") insert_week(); 29.639 + tbody.appendChild(row); 29.640 + } 29.641 + 29.642 + // end of table body 29.643 + table.appendChild(tbody); 29.644 + 29.645 + // return table 29.646 + return table; 29.647 +} 29.648 + 29.649 + 29.650 + 29.651 +// Rich form field support 29.652 + 29.653 + 29.654 +function gregor_getAbsoluteLeft(elem) { 29.655 + var result = 0; 29.656 + while (elem && elem.style.position != "static") { 29.657 + result += elem.offsetLeft; 29.658 + elem = elem.offsetParent; 29.659 + } 29.660 + return result; 29.661 +} 29.662 + 29.663 +function gregor_getAbsoluteTop(elem) { 29.664 + var result = 0; 29.665 + while (elem && elem.style.position != "static") { 29.666 + result += elem.offsetTop; 29.667 + elem = elem.offsetParent; 29.668 + } 29.669 + return result; 29.670 +} 29.671 + 29.672 +function gregor_formatDate(format, date) { 29.673 + if (date == null) return ""; 29.674 + var result = format; 29.675 + result = result.replace(/Y+/, function(s) { 29.676 + if (s.length == 2) return gregor_formatInteger(date.year % 100, 2); 29.677 + else return gregor_formatInteger(date.year, s.length); 29.678 + }); 29.679 + result = result.replace(/M+/, function(s) { 29.680 + return gregor_formatInteger(date.month, s.length); 29.681 + }); 29.682 + result = result.replace(/D+/, function(s) { 29.683 + return gregor_formatInteger(date.day, s.length); 29.684 + }); 29.685 + return result; 29.686 +} 29.687 + 29.688 +function gregor_map2digitYear(y2, maxYear) { 29.689 + var guess = Math.floor(maxYear / 100) * 100 + y2; 29.690 + if (guess <= maxYear) return guess; 29.691 + else return guess - 100; 29.692 +} 29.693 + 29.694 +function gregor_parseDate(format, string, terminated, maxYear) { 29.695 + var numericParts, formatParts; 29.696 + numericParts = string.match(/^\s*([0-9]{4})-([0-9]{2})-([0-9]{2})\s*$/); 29.697 + if (numericParts != null) { 29.698 + return gregor_completeDate({ 29.699 + year: numericParts[1], 29.700 + month: numericParts[2], 29.701 + day: numericParts[3] 29.702 + }); 29.703 + } 29.704 + numericParts = string.match(/[0-9]+/g); 29.705 + if (numericParts == null) return null; 29.706 + formatParts = format.match(/[YMD]+/g); 29.707 + if (numericParts.length != formatParts.length) return null; 29.708 + if ( 29.709 + !terminated && ( 29.710 + numericParts[numericParts.length-1].length < 29.711 + formatParts[formatParts.length-1].length 29.712 + ) 29.713 + ) return null; 29.714 + var year, month, day; 29.715 + for (var i=0; i<numericParts.length; i++) { 29.716 + var numericPart = numericParts[i]; 29.717 + var formatPart = formatParts[i]; 29.718 + if (formatPart.match(/^Y+$/)) { 29.719 + if (numericPart.length == 2 && maxYear != null) { 29.720 + year = gregor_map2digitYear(parseInt(numericPart, 10), maxYear); 29.721 + } else if (numericPart.length > 2) { 29.722 + year = parseInt(numericPart, 10); 29.723 + } 29.724 + } else if (formatPart.match(/^M+$/)) { 29.725 + month = parseInt(numericPart, 10); 29.726 + } else if (formatPart.match(/^D+$/)) { 29.727 + day = parseInt(numericPart, 10); 29.728 + } else { 29.729 + //alert("Not implemented."); 29.730 + return null; 29.731 + } 29.732 + } 29.733 + return gregor_completeDate({year: year, month: month, day: day}); 29.734 +} 29.735 + 29.736 +function gregor_addGui(args) { 29.737 + 29.738 + // copy argument structure 29.739 + var state = {}; 29.740 + for (key in args) state[key] = args[key]; 29.741 + 29.742 + // unset state.element, which should never be set anyway 29.743 + state.element = null; 29.744 + 29.745 + // save original values of "year" and "month" options 29.746 + var original_year = state.year; 29.747 + var original_month = state.month; 29.748 + 29.749 + // get text field element 29.750 + var element = document.getElementById(state.element_id); 29.751 + state.element_id = null; 29.752 + 29.753 + // setup state.today, state.selected and state.format options 29.754 + if (state.today === undefined) { 29.755 + var js_date = new Date(); 29.756 + state.today = gregor_completeDate({ 29.757 + year: js_date.getFullYear(), 29.758 + month: js_date.getMonth() + 1, 29.759 + day: js_date.getDate() 29.760 + }); 29.761 + } else if (state.today != null) { 29.762 + state.today = gregor_completeDate(args.today); 29.763 + } 29.764 + if (state.selected == "today") { 29.765 + state.selected = state.today; 29.766 + } else if (args.selected != null) { 29.767 + state.selected = gregor_completeDate(state.selected); 29.768 + } 29.769 + if (state.format == null) state.format = "YYYY-MM-DD"; 29.770 + 29.771 + // using state.future to calculate maxYear (for 2 digit year conversions) 29.772 + var maxYear = (state.today == null) ? null : ( 29.773 + state.today.year + 29.774 + ((state.future == null) ? 12 : state.future) 29.775 + ); 29.776 + 29.777 + // hook into state.select_callback 29.778 + var select_callback = state.select_callback; 29.779 + state.select_callback = function(date) { 29.780 + element.value = gregor_formatDate(state.format, date); 29.781 + if (select_callback) select_callback(date); 29.782 + }; 29.783 + 29.784 + // function to parse text field and update calendar sheet state 29.785 + var updateSheet = function(terminated) { 29.786 + var date = gregor_parseDate( 29.787 + state.format, element.value, terminated, maxYear 29.788 + ); 29.789 + if (date) { 29.790 + state.year = null; 29.791 + state.month = null; 29.792 + } 29.793 + state.selected = date; 29.794 + gregor_sheet(state); 29.795 + }; 29.796 + 29.797 + // Initial synchronization 29.798 + if (state.selected === undefined) updateSheet(true); 29.799 + element.value = gregor_formatDate(state.format, state.selected); 29.800 + if (select_callback) select_callback(state.selected); 29.801 + 29.802 + // variables storing popup status 29.803 + var visible = false; 29.804 + var focus = false; 29.805 + var protection = false; 29.806 + 29.807 + // event handlers for text field 29.808 + element.onfocus = function() { 29.809 + focus = true; 29.810 + if (!visible) { 29.811 + state.year = original_year; 29.812 + state.month = original_month; 29.813 + gregor_sheet(state); 29.814 + state.element.style.position = "absolute"; 29.815 + state.element.style.top = gregor_getAbsoluteTop(element) + element.offsetHeight; 29.816 + state.element.style.left = gregor_getAbsoluteLeft(element); 29.817 + state.element.onmousedown = function() { 29.818 + protection = true; 29.819 + }; 29.820 + state.element.onmouseup = function() { 29.821 + protection = false; 29.822 + element.focus(); 29.823 + }; 29.824 + state.element.onmouseout = state.element.onmouseup; 29.825 + element.parentNode.appendChild(state.element); 29.826 + visible = true; 29.827 + } 29.828 + }; 29.829 + element.onblur = function() { 29.830 + focus = false; 29.831 + window.setTimeout(function() { 29.832 + if (visible && !focus && !protection) { 29.833 + updateSheet(true); 29.834 + element.value = gregor_formatDate(state.format, state.selected); 29.835 + if (select_callback) select_callback(state.selected); 29.836 + state.element.parentNode.removeChild(state.element); 29.837 + visible = false; 29.838 + protection = false; 29.839 + } 29.840 + }, 1); 29.841 + }; 29.842 + element.onkeyup = function() { 29.843 + updateSheet(false); 29.844 + if (select_callback) select_callback(state.selected); 29.845 + }; 29.846 + 29.847 +} 29.848 +
30.1 Binary file static/icons/16/clock.png has changed
31.1 Binary file static/icons/16/magnifier.png has changed
32.1 Binary file static/icons/16/text_list_bullets.png has changed
33.1 Binary file static/icons/16/user_edit.png has changed
34.1 --- a/static/style.css Sun Jan 10 12:00:00 2010 +0100 34.2 +++ b/static/style.css Fri Jan 22 12:00:00 2010 +0100 34.3 @@ -438,6 +438,14 @@ 34.4 * ui.paginate 34.5 */ 34.6 34.7 +.ui_paginate_head { 34.8 + margin-bottom: 1ex; 34.9 +} 34.10 + 34.11 +.ui_paginate_foot { 34.12 + margin-top: 1ex; 34.13 +} 34.14 + 34.15 .ui_paginate_select a { 34.16 padding: 0.5ex; 34.17 } 34.18 @@ -517,6 +525,11 @@ 34.19 padding-right: 0.5em; 34.20 } 34.21 34.22 +.ui_field_label.label_right { 34.23 + text-align: left; 34.24 + width: auto; 34.25 +} 34.26 + 34.27 .login input[type=text], 34.28 .login input[type=password] { 34.29 width: 10em; 34.30 @@ -581,6 +594,14 @@ 34.31 background-color: #ddd; 34.32 } 34.33 34.34 +.nohover tr:hover td { 34.35 + background-color: #fff; 34.36 +} 34.37 + 34.38 +.nohover table tr:hover td { 34.39 + background-color: #ddd; 34.40 +} 34.41 + 34.42 34.43 tr table tr:hover td { 34.44 background-color: #fff; 34.45 @@ -817,7 +838,8 @@ 34.46 .draft_updated_info, 34.47 .voting_active_info, 34.48 .revoked_info, 34.49 -.initiator_invite_info { 34.50 +.initiator_invite_info, 34.51 +.motd { 34.52 background-color: #fec; 34.53 border: 2px solid #b96; 34.54 padding: 1ex; 34.55 @@ -901,6 +923,10 @@ 34.56 color: #000; 34.57 } 34.58 34.59 +.action_active { 34.60 + background-color: #fec; 34.61 +} 34.62 + 34.63 /************************************************************************* 34.64 * Voting 34.65 */