liquid_feedback_frontend

changeset 2:5c601807d397 alpha3

Version alpha3

Dark green part of issue supporter bargraph represents all satisfied supporters, regardless of having seen the latest draft

Wiki formatting for drafts

Showing differences between two drafts of the same initiative

Display of outgoing delegation chains

Many other improvements
author bsw
date Mon Nov 23 12:00:00 2009 +0100 (2009-11-23)
parents dd0109e81922
children 768faea1096d
files app/main/_layout/default.html app/main/area/_list.lua app/main/area/show.lua app/main/delegation/_list.lua app/main/delegation/_show_box.lua app/main/draft/_list.lua app/main/draft/_show.lua app/main/draft/diff.lua app/main/draft/new.lua app/main/index/search.lua app/main/initiative/_list.lua app/main/initiative/new.lua app/main/initiative/show.lua app/main/issue/_list.lua app/main/issue/_show_box.lua app/main/issue/show.lua app/main/member/_action/update.lua app/main/member/_action/update_avatar.lua app/main/member/_list.lua app/main/member/_show.lua app/main/member/_show_thumb.lua app/main/member/avatar.lua app/main/member/edit.lua app/main/membership/_show_box.lua app/main/suggestion/_list.lua app/main/supporter/_show_box.lua config/default.lua config/development.lua config/testing.lua env/encode/highlight.lua env/format/wiki_text.lua env/ui/bargraph.lua env/ui/filter.lua locale/translations.de.lua model/area.lua model/initiative.lua model/issue.lua model/member.lua model/member_image.lua static/avatar.jpg static/delegation_arrow.jpg static/delegation_arrow_vertical.jpg static/icons/16/new.png static/style.css
line diff
     1.1 --- a/app/main/_layout/default.html	Wed Nov 18 12:00:00 2009 +0100
     1.2 +++ b/app/main/_layout/default.html	Mon Nov 23 12:00:00 2009 +0100
     1.3 @@ -18,27 +18,29 @@
     1.4          <!-- WEBMCP SLOT navigation -->
     1.5        </div>
     1.6      </div>
     1.7 -    <div class="infobox" id="infobox">
     1.8 -      <div class="interest" id="interest">
     1.9 -        <!-- WEBMCP SLOT interest -->
    1.10 +    <div class="title_bar">
    1.11 +      <div class="issue_info" id="issue_info">
    1.12 +        <!-- WEBMCP SLOT issue_info -->
    1.13        </div>
    1.14 -      <div class="support" id="support">
    1.15 -        <!-- WEBMCP SLOT support -->
    1.16 -      </div>
    1.17 -      <div class="delegation" id="delegation">
    1.18 -        <!-- WEBMCP SLOT delegation -->
    1.19 -      </div>
    1.20 -    </div>
    1.21 -    <div class="title_bar">
    1.22        <div class="path" id="path">
    1.23          <!-- WEBMCP SLOT path -->
    1.24        </div>
    1.25        <div class="title" id="title">
    1.26          <!-- WEBMCP SLOT title -->
    1.27        </div>
    1.28 +      <div class="interest vote_info" id="interest">
    1.29 +        <!-- WEBMCP SLOT interest -->
    1.30 +      </div>
    1.31 +      <div class="support vote_info" id="support">
    1.32 +        <!-- WEBMCP SLOT support -->
    1.33 +      </div>
    1.34 +      <div class="delegation vote_info" id="delegation">
    1.35 +        <!-- WEBMCP SLOT delegation -->
    1.36 +      </div>
    1.37        <div class="actions" id="actions">
    1.38          <!-- WEBMCP SLOT actions -->
    1.39        </div>
    1.40 +      <br style="clear: left;" />
    1.41      </div>
    1.42      <div class="main" id="default">
    1.43        <!-- WEBMCP SLOT default -->
     2.1 --- a/app/main/area/_list.lua	Wed Nov 18 12:00:00 2009 +0100
     2.2 +++ b/app/main/area/_list.lua	Mon Nov 23 12:00:00 2009 +0100
     2.3 @@ -35,7 +35,7 @@
     2.4                local max_value = MemberCount:get()
     2.5                ui.bargraph{
     2.6                  max_value = max_value,
     2.7 -                width = 100,
     2.8 +                width = 200,
     2.9                  bars = {
    2.10                    { color = "#444", value = record.direct_member_count },
    2.11                    { color = "#777", value = record.member_weight - record.direct_member_count },
     3.1 --- a/app/main/area/show.lua	Wed Nov 18 12:00:00 2009 +0100
     3.2 +++ b/app/main/area/show.lua	Mon Nov 23 12:00:00 2009 +0100
     3.3 @@ -13,19 +13,19 @@
     3.4  slot.select("actions", function()
     3.5    ui.link{
     3.6      content = function()
     3.7 -      ui.image{ static = "icons/16/folder_add.png" }
     3.8 -      slot.put(_"Create new issue")
     3.9 +      ui.image{ static = "icons/16/table_go.png" }
    3.10 +      slot.put(_"Delegate")
    3.11      end,
    3.12 -    module = "initiative",
    3.13 +    module = "delegation",
    3.14      view = "new",
    3.15      params = { area_id = area.id }
    3.16    }
    3.17    ui.link{
    3.18      content = function()
    3.19 -      ui.image{ static = "icons/16/table_go.png" }
    3.20 -      slot.put(_"Delegate")
    3.21 +      ui.image{ static = "icons/16/folder_add.png" }
    3.22 +      slot.put(_"Create new issue")
    3.23      end,
    3.24 -    module = "delegation",
    3.25 +    module = "initiative",
    3.26      view = "new",
    3.27      params = { area_id = area.id }
    3.28    }
    3.29 @@ -45,68 +45,35 @@
    3.30  
    3.31  ui.tabs{
    3.32    {
    3.33 -    name = "new",
    3.34 -    label = _"New",
    3.35 -    content = function()
    3.36 -      execute.view{
    3.37 -        module = "issue",
    3.38 -        view = "_list",
    3.39 -        params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.accepted ISNULL AND issue.closed ISNULL"), for_area_list = true }
    3.40 -      }
    3.41 -    end
    3.42 -  },
    3.43 -  {
    3.44 -    name = "accepted",
    3.45 -    label = _"In discussion",
    3.46 -    content = function()
    3.47 -      execute.view{
    3.48 -        module = "issue",
    3.49 -        view = "_list",
    3.50 -        params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.accepted NOTNULL AND issue.half_frozen ISNULL AND issue.closed ISNULL"), for_area_list = true }
    3.51 -      }
    3.52 -    end
    3.53 -  },
    3.54 -  {
    3.55 -    name = "half_frozen",
    3.56 -    label = _"Frozen",
    3.57 +    name = "issues",
    3.58 +    label = _"Issues",
    3.59      content = function()
    3.60        execute.view{
    3.61          module = "issue",
    3.62          view = "_list",
    3.63 -        params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.half_frozen NOTNULL AND issue.closed ISNULL"), for_area_list = true }
    3.64 -      }
    3.65 -    end
    3.66 -  },
    3.67 -  {
    3.68 -    name = "frozen",
    3.69 -    label = _"Voting",
    3.70 -    content = function()
    3.71 -      execute.view{
    3.72 -        module = "issue",
    3.73 -        view = "_list",
    3.74 -        params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.fully_frozen NOTNULL AND issue.closed ISNULL"), for_area_list = true }
    3.75 +        params = { issues_selector = area:get_reference_selector("issues"), for_area_list = true }
    3.76        }
    3.77      end
    3.78    },
    3.79    {
    3.80 -    name = "finished",
    3.81 -    label = _"Finished",
    3.82 +    name = "members",
    3.83 +    label = _"Members",
    3.84      content = function()
    3.85        execute.view{
    3.86 -        module = "issue",
    3.87 +        module = "member",
    3.88          view = "_list",
    3.89 -        params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.closed NOTNULL AND ranks_available"), for_area_list = true }
    3.90 +        params = { members_selector = area:get_reference_selector("members") }
    3.91        }
    3.92      end
    3.93    },
    3.94    {
    3.95 -    name = "cancelled",
    3.96 -    label = _"Cancelled",
    3.97 +    name = "delegations",
    3.98 +    label = _"Delegations",
    3.99      content = function()
   3.100        execute.view{
   3.101 -        module = "issue",
   3.102 +        module = "delegation",
   3.103          view = "_list",
   3.104 -        params = { issues_selector = area:get_reference_selector("issues"):add_where("issue.closed NOTNULL AND NOT ranks_available"), for_area_list = true }
   3.105 +        params = { delegations_selector = area:get_reference_selector("delegations") }
   3.106        }
   3.107      end
   3.108    },
     4.1 --- a/app/main/delegation/_list.lua	Wed Nov 18 12:00:00 2009 +0100
     4.2 +++ b/app/main/delegation/_list.lua	Mon Nov 23 12:00:00 2009 +0100
     4.3 @@ -1,50 +1,73 @@
     4.4 -local selector = param.get("selector", "table")
     4.5 +local delegations_selector = param.get("delegations_selector", "table")
     4.6 +local outgoing = param.get("outgoing", atom.boolean)
     4.7 +local incoming = param.get("incoming", atom.boolean)
     4.8 +
     4.9 +local function delegation_scope(delegation)
    4.10 +  ui.container{
    4.11 +    attr = { class = "delegation_scope" },
    4.12 +    content = function()
    4.13 +      local area
    4.14 +      if delegation.issue then
    4.15 +        area = delegation.issue.area
    4.16 +      else
    4.17 +        area = delegation.area
    4.18 +      end
    4.19 +      if not area then
    4.20 +        ui.field.text{ value = _"Global delegation" }
    4.21 +      end
    4.22 +      if area then
    4.23 +        ui.link{
    4.24 +          content = _"Area '#{name}'":gsub("#{name}", area.name),
    4.25 +          module = "area",
    4.26 +          view = "show",
    4.27 +          id = area.id
    4.28 +        }
    4.29 +      end
    4.30 +      if delegation.issue then
    4.31 +        ui.link{
    4.32 +          content = _"Issue ##{id}":gsub("#{id}", delegation.issue.id),
    4.33 +          module = "issue",
    4.34 +          view = "show",
    4.35 +          id = delegation.issue.id
    4.36 +        }
    4.37 +      end
    4.38 +    end
    4.39 +  }
    4.40 +end
    4.41 +
    4.42  
    4.43  ui.paginate{
    4.44 -  selector = selector,
    4.45 +  selector = delegations_selector,
    4.46    content = function()
    4.47 -    ui.list{
    4.48 -      records = selector:exec(),
    4.49 -      columns = {
    4.50 -        {
    4.51 -          label = _"Truster",
    4.52 -          content = function(record)
    4.53 -            ui.link{
    4.54 -              content = record.truster.name,
    4.55 +    for i, delegation in ipairs(delegations_selector:exec()) do
    4.56 +      ui.container{
    4.57 +        attr = { class = "delegation_list_entry" },
    4.58 +        content = function()
    4.59 +          if outgoing then
    4.60 +            delegation_scope(delegation)
    4.61 +          else
    4.62 +            execute.view{
    4.63                module = "member",
    4.64 -              view = "show",
    4.65 -              id = record.truster.id
    4.66 +              view = "_show_thumb",
    4.67 +              params = { member = delegation.truster }
    4.68              }
    4.69            end
    4.70 -        },
    4.71 -        {
    4.72 -          label = _"Trustee",
    4.73 -          content = function(record)
    4.74 -            ui.link{
    4.75 -              content = record.trustee.name,
    4.76 +          ui.image{
    4.77 +            attr = { class = "delegation_arrow" },
    4.78 +            static = "delegation_arrow.jpg"
    4.79 +          }
    4.80 +          if incoming then
    4.81 +            delegation_scope(delegation)
    4.82 +          else
    4.83 +            execute.view{
    4.84                module = "member",
    4.85 -              view = "show",
    4.86 -              id = record.trustee.id
    4.87 +              view = "_show_thumb",
    4.88 +              params = { member = delegation.trustee }
    4.89              }
    4.90            end
    4.91 -        },
    4.92 -        {
    4.93 -          label = _"Area",
    4.94 -          content = function(record)
    4.95 -            if record.area then
    4.96 -              ui.field.text{ value = record.area.name }
    4.97 -            end
    4.98 -          end
    4.99 -        },
   4.100 -        {
   4.101 -          label = _"Issue",
   4.102 -          content = function(record)
   4.103 -            if record.issue then
   4.104 -              ui.field.text{ value = record.issue.id }
   4.105 -            end
   4.106 -          end
   4.107 -        },
   4.108 +        end
   4.109        }
   4.110 -    }
   4.111 +    end
   4.112 +    slot.put("<br style='clear: left;' />")
   4.113    end
   4.114  }
     5.1 --- a/app/main/delegation/_show_box.lua	Wed Nov 18 12:00:00 2009 +0100
     5.2 +++ b/app/main/delegation/_show_box.lua	Mon Nov 23 12:00:00 2009 +0100
     5.3 @@ -50,63 +50,70 @@
     5.4      ui.container{
     5.5        attr = { class = "content", id = "delegation_content" },
     5.6        content = function()
     5.7 +        ui.container{
     5.8 +          attr = {
     5.9 +            class = "close",
    5.10 +            style = "cursor: pointer;",
    5.11 +            onclick = "document.getElementById('delegation_content').style.display = 'none';"
    5.12 +          },
    5.13 +          content = _"X"
    5.14 +        }
    5.15  
    5.16          local delegation_chain = db:query{ "SELECT * FROM delegation_chain(?, ?, ?) JOIN member ON member.id = member_id ORDER BY index", app.session.member.id, area_id, issue_id }
    5.17  
    5.18          for i, record in ipairs(delegation_chain) do
    5.19            local style
    5.20 -          if record.participation then
    5.21 -            style = "font-weight: bold;"
    5.22 -          end
    5.23 -          if record.overridden then
    5.24 -            style = "color: #777;"
    5.25 -          end
    5.26 -          if not record.active then
    5.27 -            style = "text-decoration: line-through;"
    5.28 -          end
    5.29 -          if record.scope_in then
    5.30 -            ui.field.text{
    5.31 -              value = " v " .. record.scope_in .. " v "
    5.32 +          execute.view{
    5.33 +            module = "member",
    5.34 +            view = "_show_thumb",
    5.35 +            params = { member = record }
    5.36 +          }
    5.37 +          slot.put("<br style='clear: left'/>")
    5.38 +          if record.scope_out then
    5.39 +            ui.container{
    5.40 +              attr = { class = "delegation_info" },
    5.41 +              content = function()
    5.42 +                ui.image{
    5.43 +                  attr = { class = "delegation_arrow" },
    5.44 +                  static = "delegation_arrow_vertical.jpg"
    5.45 +                }
    5.46 +                ui.container{
    5.47 +                  attr = { class = "delegation_scope" },
    5.48 +                  content = function()
    5.49 +                    if record.scope_out == "global" then
    5.50 +                      slot.put(_"Global delegation")
    5.51 +                    elseif record.scope_out == "area" then
    5.52 +                      slot.put(_"Area delegation")
    5.53 +                    elseif record.scope_out == "issue" then
    5.54 +                      slot.put(_"Issue delegation")
    5.55 +                    end
    5.56 +                  end
    5.57 +                }
    5.58 +                if record.id == app.session.member.id then
    5.59 +                  ui.link{
    5.60 +                    attr = { class = "revoke" },
    5.61 +                    content = function()
    5.62 +                      ui.image{ static = "icons/16/delete.png" }
    5.63 +                      slot.put(_"Revoke")
    5.64 +                    end,
    5.65 +                    module = "delegation",
    5.66 +                    action = "update",
    5.67 +                    params = { issue_id = delegation.issue_id, area_id = delegation.area_id, delete = true },
    5.68 +                    routing = {
    5.69 +                      default = {
    5.70 +                        mode = "redirect",
    5.71 +                        module = request.get_module(),
    5.72 +                        view = request.get_view(),
    5.73 +                        id = param.get_id_cgi(),
    5.74 +                        params = param.get_all_cgi()
    5.75 +                      }
    5.76 +                    }
    5.77 +                  }
    5.78 +                end
    5.79 +              end
    5.80              }
    5.81            end
    5.82 -          local name = record.name
    5.83 -          if record.member_id == app.session.member.id then
    5.84 -            name = _"Me"
    5.85 -          end
    5.86 -          ui.field.text{
    5.87 -            attr = { style = style },
    5.88 -            value = name
    5.89 -          }
    5.90          end
    5.91 -
    5.92 -        ui.link{
    5.93 -          attr = { class = "revoke" },
    5.94 -          content = function()
    5.95 -            ui.image{ static = "icons/16/delete.png" }
    5.96 -            slot.put(_"Revoke")
    5.97 -          end,
    5.98 -          module = "delegation",
    5.99 -          action = "update",
   5.100 -          params = { issue_id = delegation.issue_id, area_id = delegation.area_id, delete = true },
   5.101 -          routing = {
   5.102 -            default = {
   5.103 -              mode = "redirect",
   5.104 -              module = request.get_module(),
   5.105 -              view = request.get_view(),
   5.106 -              id = param.get_id_cgi(),
   5.107 -              params = param.get_all_cgi()
   5.108 -            }
   5.109 -          }
   5.110 -        }
   5.111 -
   5.112 -        ui.container{
   5.113 -          attr = {
   5.114 -            class = "head",
   5.115 -            style = "cursor: pointer;",
   5.116 -            onclick = "document.getElementById('delegation_content').style.display = 'none';"
   5.117 -          },
   5.118 -          content = _"Click here to close."
   5.119 -        }
   5.120        end
   5.121      }
   5.122    end
     6.1 --- a/app/main/draft/_list.lua	Wed Nov 18 12:00:00 2009 +0100
     6.2 +++ b/app/main/draft/_list.lua	Mon Nov 23 12:00:00 2009 +0100
     6.3 @@ -1,30 +1,43 @@
     6.4 -ui.list{
     6.5 -  records = param.get("drafts", "table"),
     6.6 -  columns = {
     6.7 -    {
     6.8 -      label = _"Id",
     6.9 -      name = "id"
    6.10 -    },
    6.11 -    {
    6.12 -      label = _"Created at",
    6.13 -      content = function(record)
    6.14 -        ui.field.text{ value = format.timestamp(record.created) }
    6.15 -      end
    6.16 -    },
    6.17 -    {
    6.18 -      label = _"Author",
    6.19 -      name = "author_name"
    6.20 -    },
    6.21 -    {
    6.22 -      content = function(record)
    6.23 -        ui.link{
    6.24 -          attr = { class = "action" },
    6.25 -          text = _"Show",
    6.26 -          module = "draft",
    6.27 -          view = "show",
    6.28 -          id = record.id
    6.29 +ui.form{
    6.30 +  method = "get",
    6.31 +  module = "draft",
    6.32 +  view = "diff",
    6.33 +  content = function()
    6.34 +    ui.list{
    6.35 +      records = param.get("drafts", "table"),
    6.36 +      columns = {
    6.37 +        {
    6.38 +          label = _"Created at",
    6.39 +          content = function(record)
    6.40 +            ui.field.text{ readonly = true, value = format.timestamp(record.created) }
    6.41 +          end
    6.42 +        },
    6.43 +        {
    6.44 +          label = _"Author",
    6.45 +          content = function(record)
    6.46 +            ui.field.text{ readonly = true, value = record.author.name }
    6.47 +          end
    6.48 +        },
    6.49 +        {
    6.50 +          content = function(record)
    6.51 +            ui.link{
    6.52 +              attr = { class = "action" },
    6.53 +              text = _"Show",
    6.54 +              module = "draft",
    6.55 +              view = "show",
    6.56 +              id = record.id
    6.57 +            }
    6.58 +          end
    6.59 +        },
    6.60 +        {
    6.61 +          label = _"Compare",
    6.62 +          content = function(record)
    6.63 +            slot.put('<input type="radio" name="old_draft_id" value="' .. tostring(record.id) .. '">')
    6.64 +            slot.put('<input type="radio" name="new_draft_id" value="' .. tostring(record.id) .. '">')
    6.65 +          end
    6.66          }
    6.67 -      end
    6.68 +      }
    6.69      }
    6.70 -  }
    6.71 +    ui.submit{ text = _"Compare" }
    6.72 +  end
    6.73  }
     7.1 --- a/app/main/draft/_show.lua	Wed Nov 18 12:00:00 2009 +0100
     7.2 +++ b/app/main/draft/_show.lua	Mon Nov 23 12:00:00 2009 +0100
     7.3 @@ -6,9 +6,13 @@
     7.4    readonly = true,
     7.5    content = function()
     7.6  
     7.7 -    ui.field.text{ label = _"Initiative", value = draft.initiative.name }
     7.8      ui.field.text{ label = _"Author", name = "author_name" }
     7.9 -    ui.field.text{ label = _"Content", name = "content" }
    7.10 -
    7.11 +    ui.field.timestamp{ label = _"Created at", name = "created" }
    7.12 +    ui.container{
    7.13 +      attr = { class = "draft_content" },
    7.14 +      content = function()
    7.15 +        slot.put(format.wiki_text(draft.content))
    7.16 +      end
    7.17 +    }
    7.18    end
    7.19  }
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/app/main/draft/diff.lua	Mon Nov 23 12:00:00 2009 +0100
     8.3 @@ -0,0 +1,93 @@
     8.4 +slot.put_into("title", _"Diff")
     8.5 +
     8.6 +local old_draft_id = param.get("old_draft_id", atom.integer)
     8.7 +local new_draft_id = param.get("new_draft_id", atom.integer)
     8.8 +
     8.9 +if old_draft_id > new_draft_id then
    8.10 +  local tmp = old_draft_id
    8.11 +  old_draft_id = new_draft_id
    8.12 +  new_draft_id = tmp
    8.13 +end
    8.14 +
    8.15 +local old_draft = Draft:by_id(old_draft_id)
    8.16 +local new_draft = Draft:by_id(new_draft_id)
    8.17 +
    8.18 +local key = multirand.string(26, "123456789bcdfghjklmnpqrstvwxyz");
    8.19 +
    8.20 +local old_draft_filename = encode.file_path(request.get_app_basepath(), 'tmp', "diff-" .. key .. "-old.tmp")
    8.21 +local new_draft_filename = encode.file_path(request.get_app_basepath(), 'tmp', "diff-" .. key .. "-new.tmp")
    8.22 +
    8.23 +local old_draft_file = assert(io.open(old_draft_filename, "w"))
    8.24 +old_draft_file:write(old_draft.content)
    8.25 +old_draft_file:write("\n")
    8.26 +old_draft_file:close()
    8.27 +
    8.28 +local new_draft_file = assert(io.open(new_draft_filename, "w"))
    8.29 +new_draft_file:write(new_draft.content)
    8.30 +new_draft_file:write("\n")
    8.31 +new_draft_file:close()
    8.32 +
    8.33 +local output, err, status = os.pfilter(nil, "sh", "-c", "diff -U 100000 '" .. old_draft_filename .. "' '" .. new_draft_filename .. "' | grep -v ^--- | grep -v ^+++ | grep -v ^@")
    8.34 +
    8.35 +os.remove(old_draft_filename)
    8.36 +os.remove(new_draft_filename)
    8.37 +
    8.38 +if not status then
    8.39 +  ui.field.text{ value = _"The drafts do not differ" }
    8.40 +else
    8.41 +  slot.put('<table class="diff">')
    8.42 +  slot.put('<tr><th width="50%">' .. _"Old draft revision" .. '</th><th width="50%">' .. _"New draft revision" .. '</th></tr>')
    8.43 +  local last_state = "unchanged"
    8.44 +  local lines = {}
    8.45 +  local removed_lines = nil
    8.46 +  output = output .. " "
    8.47 +  output = output:gsub("[^\n\r]+", function(line)
    8.48 +    local state = "unchanged"
    8.49 +    local char = line:sub(1,1)
    8.50 +    line = line:sub(2)
    8.51 +    state = "unchanged"
    8.52 +    if char == "-" then
    8.53 +      state = "-"
    8.54 +    elseif char == "+" then
    8.55 +      state = "+"
    8.56 +    end
    8.57 +    if last_state == "unchanged" then
    8.58 +      if state == "unchanged" then
    8.59 +        lines[#lines+1] = line
    8.60 +      elseif (state == "-") or (state == "+") then
    8.61 +        local text = table.concat(lines, "<br />")
    8.62 +        slot.put("<tr><td>", text, "</td><td>", text, "</td></tr>")
    8.63 +        lines = { line }
    8.64 +      end
    8.65 +    elseif last_state == "-" then
    8.66 +      if state == "-" then
    8.67 +        lines[#lines+1] = line
    8.68 +      elseif state == "+" then
    8.69 +        removed_lines = lines
    8.70 +        lines = { line }
    8.71 +      elseif state == "unchanged" then
    8.72 +        local text = table.concat(lines,"<br />")
    8.73 +        slot.put('<tr><td class="removed">', text, "</td><td></td></tr>")
    8.74 +        lines = { line }
    8.75 +      end
    8.76 +    elseif last_state == "+" then
    8.77 +      if state == "+" then
    8.78 +        lines[#lines+1] = line
    8.79 +      elseif (state == "-") or (state == "unchanged") then
    8.80 +        if removed_lines then
    8.81 +          local text = table.concat(lines, "<br />")
    8.82 +          local removed_text = table.concat(removed_lines, "<br />")
    8.83 +          slot.put('<tr><td class="removed">', removed_text, '</td><td class="added">', text, "</td></tr>")
    8.84 +        else
    8.85 +          local text = table.concat(lines, "<br />")
    8.86 +          slot.put('<tr><td></td><td class="added">', text, "</td></tr>")
    8.87 +        end
    8.88 +        removed_lines = nil
    8.89 +        lines = { line }
    8.90 +      end
    8.91 +    end
    8.92 +    last_state = state
    8.93 +  end)
    8.94 +  slot.put("</table>")
    8.95 +end 
    8.96 +
     9.1 --- a/app/main/draft/new.lua	Wed Nov 18 12:00:00 2009 +0100
     9.2 +++ b/app/main/draft/new.lua	Mon Nov 23 12:00:00 2009 +0100
     9.3 @@ -1,24 +1,30 @@
     9.4  slot.put_into("title", _"Add new draft")
     9.5  
     9.6 -local initiative_id = param.get("initiative_id")
     9.7 +local initiative = Initiative:by_id(param.get("initiative_id"))
     9.8  
     9.9  ui.form{
    9.10 +  record = initiative.current_draft,
    9.11    attr = { class = "vertical" },
    9.12    module = "draft",
    9.13    action = "add",
    9.14 -  params = { initiative_id = initiative_id },
    9.15 +  params = { initiative_id = initiative.id },
    9.16    routing = {
    9.17      default = {
    9.18        mode = "redirect",
    9.19        module = "initiative",
    9.20        view = "show",
    9.21 -      id = initiative_id
    9.22 +      id = initiative.id
    9.23      }
    9.24    },
    9.25    content = function()
    9.26  
    9.27      ui.field.text{ label = _"Author", value = app.session.member.name, readonly = true }
    9.28 -    ui.field.text{ label = _"Content", name = "content", multiline = true }
    9.29 +    ui.field.text{
    9.30 +      label = _"Content",
    9.31 +      name = "content",
    9.32 +      multiline = true,
    9.33 +      attr = { style = "height: 50ex;" }
    9.34 +   }
    9.35  
    9.36      ui.submit{ text = _"Save" }
    9.37    end
    10.1 --- a/app/main/index/search.lua	Wed Nov 18 12:00:00 2009 +0100
    10.2 +++ b/app/main/index/search.lua	Mon Nov 23 12:00:00 2009 +0100
    10.3 @@ -5,49 +5,40 @@
    10.4  
    10.5  slot.put_into("title", _("Search results for: '#{search}'", { search  = search_string }))
    10.6  
    10.7 -
    10.8 -local members = {}
    10.9 -local issues = {}
   10.10 -local initiatives = {}
   10.11 -
   10.12 -
   10.13  if search_for == "global" or search_for == "member" then
   10.14 -  members = Member:search(search_string)
   10.15 -end
   10.16 -
   10.17 -if search_for == "global" or search_for == "issue" then
   10.18 -  issues = Issue:search(search_string)
   10.19 -end
   10.20 -
   10.21 -if search_for == "initiative" then
   10.22 -  initiatives = Initiative:search(search_string)
   10.23 -end
   10.24 -
   10.25 -
   10.26 -if #members > 0 then
   10.27 +  members_selector = Member:get_search_selector(search_string)
   10.28 +--if #members > 0 then
   10.29    ui.heading{ content = _"Members" }
   10.30    execute.view{
   10.31      module = "member",
   10.32      view = "_list",
   10.33 -    params = { members = members, highlight_string = search_string },
   10.34 +    params = { members_selector = members_selector },
   10.35    }
   10.36 +--end
   10.37  end
   10.38  
   10.39 -if #issues > 0 then
   10.40 +if search_for == "global" or search_for == "issue" then
   10.41 +  issues_selector = Issue:get_search_selector(search_string)
   10.42 +--if #issues > 0 then
   10.43    ui.heading{ content = _"Issues" }
   10.44    execute.view{
   10.45      module = "issue",
   10.46      view = "_list",
   10.47 -    params = { issues = issues, highlight_string = search_string },
   10.48 +    params = { issues_selector = issues_selector, highlight_string = search_string },
   10.49    }
   10.50 +--end
   10.51  end
   10.52  
   10.53 -if #initiatives > 0 then
   10.54 +if search_for == "initiative" then
   10.55 +  initiatives_selector = Initiative:get_search_selector(search_string)
   10.56 +--if #initiatives > 0 then
   10.57    ui.heading{ content = _"Initiatives" }
   10.58    execute.view{
   10.59      module = "initiative",
   10.60      view = "_list",
   10.61 -    params = { initiatives = initiatives, highlight_string = search_string },
   10.62 +    params = { initiatives_selector = initiatives_selector },
   10.63    }
   10.64 +--end
   10.65  end
   10.66  
   10.67 +
    11.1 --- a/app/main/initiative/_list.lua	Wed Nov 18 12:00:00 2009 +0100
    11.2 +++ b/app/main/initiative/_list.lua	Mon Nov 23 12:00:00 2009 +0100
    11.3 @@ -52,21 +52,15 @@
    11.4        content = function()
    11.5          local initiatives = initiatives_selector:exec()
    11.6          local columns = {}
    11.7 -        local issue = initiatives[1] and initiatives[1].issue or {}
    11.8 -        if issue.accepted and issue.closed and issue.ranks_available then 
    11.9 -          columns[#columns+1] = {
   11.10 -            content = function(record)
   11.11 +        columns[#columns+1] = {
   11.12 +          content = function(record)
   11.13 +            if record.issue.accepted and record.issue.closed and record.issue.ranks_available then 
   11.14                ui.field.rank{ value = record.rank }
   11.15 -            end
   11.16 -          }
   11.17 -          columns[#columns+1] = {
   11.18 -            content = function(record)
   11.19                if record.negative_votes and record.positive_votes then
   11.20                  local max_value = record.issue.voter_count
   11.21 - trace.debug(record.issue.voter_count)
   11.22                  ui.bargraph{
   11.23                    max_value = max_value,
   11.24 -                  width = 100,
   11.25 +                  width = 200,
   11.26                    bars = {
   11.27                      { color = "#0a0", value = record.positive_votes },
   11.28                      { color = "#aaa", value = max_value - record.negative_votes - record.positive_votes },
   11.29 @@ -74,34 +68,41 @@
   11.30                    }
   11.31                  }
   11.32                end
   11.33 -            end
   11.34 -          }
   11.35 -        else
   11.36 -          columns[#columns+1] = {
   11.37 -            content = function(record)
   11.38 +            else
   11.39                local max_value = (record.issue.population or 0)
   11.40                ui.bargraph{
   11.41                  max_value = max_value,
   11.42 -                width = 100,
   11.43 +                width = 200,
   11.44                  bars = {
   11.45 -                  { color = "#0a0", value = (record.satisfied_informed_supporter_count or 0) },
   11.46 -                  { color = "#8f8", value = (record.supporter_count or 0) - (record.satisfied_informed_supporter_count or 0) },
   11.47 +                  { color = "#0a0", value = (record.satisfied_supporter_count or 0) },
   11.48 +                  { color = "#8f8", value = (record.supporter_count or 0) - (record.satisfied_supporter_count or 0) },
   11.49                    { color = "#ddd", value = max_value - (record.supporter_count or 0) },
   11.50                  }
   11.51                }
   11.52              end
   11.53 -          }
   11.54 -        end
   11.55 +          end
   11.56 +        }
   11.57          columns[#columns+1] = {
   11.58            content = function(record)
   11.59              ui.link{
   11.60                content = function()
   11.61 -                util.put_highlighted_string(record.shortened_name)
   11.62 +                local name
   11.63 +                if record.name_highlighted then
   11.64 +                  name = encode.highlight(record.name_highlighted)
   11.65 +                else
   11.66 +                  name = encode.html(record.name)
   11.67 +                end
   11.68 +                slot.put(name)
   11.69                end,
   11.70                module = "initiative",
   11.71                view = "show",
   11.72                id = record.id
   11.73              }
   11.74 +            if record.issue.state == "new" then
   11.75 +              ui.image{
   11.76 +                static = "icons/16/new.png"
   11.77 +              }
   11.78 +            end
   11.79            end
   11.80          }
   11.81  
    12.1 --- a/app/main/initiative/new.lua	Wed Nov 18 12:00:00 2009 +0100
    12.2 +++ b/app/main/initiative/new.lua	Mon Nov 23 12:00:00 2009 +0100
    12.3 @@ -39,7 +39,7 @@
    12.4        }
    12.5      end
    12.6      ui.field.text{ label = _"Name",  name = "name" }
    12.7 -    ui.field.text{ label = _"Draft", name = "draft", multiline = true }
    12.8 +    ui.field.text{ label = _"Draft", name = "draft", multiline = true, attr = { style = "height: 50ex;" } }
    12.9      ui.submit{ text = _"Save" }
   12.10    end
   12.11  }
   12.12 \ No newline at end of file
    13.1 --- a/app/main/initiative/show.lua	Wed Nov 18 12:00:00 2009 +0100
    13.2 +++ b/app/main/initiative/show.lua	Mon Nov 23 12:00:00 2009 +0100
    13.3 @@ -14,6 +14,11 @@
    13.4    params = { issue_id = initiative.issue_id }
    13.5  }
    13.6  
    13.7 +execute.view{
    13.8 +  module = "issue",
    13.9 +  view = "_show_box",
   13.10 +  params = { issue = initiative.issue }
   13.11 +}
   13.12  
   13.13  slot.select("path", function()
   13.14    ui.link{
   13.15 @@ -24,7 +29,7 @@
   13.16    }
   13.17    ui.container{ content = "::" }
   13.18    ui.link{
   13.19 -    content = _"Issue ##{id} (#{policy_name})":gsub("#{id}", initiative.issue.id):gsub("#{policy_name}", initiative.issue.policy.name),
   13.20 +    content = _"Issue ##{id}":gsub("#{id}", initiative.issue.id),
   13.21      module = "issue",
   13.22      view = "show",
   13.23      id = initiative.issue.id
   13.24 @@ -35,6 +40,18 @@
   13.25  
   13.26  slot.select("actions", function()
   13.27  
   13.28 +  if Initiator:by_pk(initiative.id, app.session.member.id) then
   13.29 +    ui.link{
   13.30 +      content = function()
   13.31 +        ui.image{ static = "icons/16/script_add.png" }
   13.32 +        slot.put(_"Edit draft")
   13.33 +      end,
   13.34 +      module = "draft",
   13.35 +      view = "new",
   13.36 +      params = { initiative_id = initiative.id }
   13.37 +    }
   13.38 +  end
   13.39 +
   13.40    ui.twitter("http://example.com/i" .. tostring(initiative.id) .. " " .. initiative.name)
   13.41  
   13.42  end)
   13.43 @@ -88,6 +105,48 @@
   13.44    end
   13.45  }
   13.46  
   13.47 +local supporter = app.session.member:get_reference_selector("supporters")
   13.48 +  :add_where{ "initiative_id = ?", initiative.id }
   13.49 +  :optional_object_mode()
   13.50 +  :exec()
   13.51 +
   13.52 +if supporter then
   13.53 +  local old_draft_id = supporter.draft_id
   13.54 +  local new_draft_id = initiative.current_draft.id
   13.55 +  if old_draft_id ~= new_draft_id then
   13.56 +    ui.container{
   13.57 +      attr = { class = "draft_updated_info" },
   13.58 +      content = function()
   13.59 +        slot.put("The draft of this initiative has been updated!")
   13.60 +        slot.put(" ")
   13.61 +        ui.link{
   13.62 +          content = _"Show diff",
   13.63 +          module = "draft",
   13.64 +          view = "diff",
   13.65 +          params = {
   13.66 +            old_draft_id = old_draft_id,
   13.67 +            new_draft_id = new_draft_id
   13.68 +          }
   13.69 +        }
   13.70 +        slot.put(" ")
   13.71 +        ui.link{
   13.72 +          content = _"Refresh support to current draft",
   13.73 +          module = "initiative",
   13.74 +          action = "add_support",
   13.75 +          id = initiative.id,
   13.76 +          routing = {
   13.77 +            default = {
   13.78 +              mode = "redirect",
   13.79 +              module = "initiative",
   13.80 +              view = "show",
   13.81 +              id = initiative.id
   13.82 +            }
   13.83 +          }
   13.84 +        }
   13.85 +      end
   13.86 +    }
   13.87 +  end
   13.88 +end
   13.89  
   13.90  ui.tabs{
   13.91    {
   13.92 @@ -95,41 +154,6 @@
   13.93      label = _"Current draft",
   13.94      content = function()
   13.95        execute.view{ module = "draft", view = "_show", params = { draft = initiative.current_draft } }
   13.96 -      if Initiator:by_pk(initiative.id, app.session.member.id) then
   13.97 -        ui.link{
   13.98 -          content = function()
   13.99 -            ui.image{ static = "icons/16/script_add.png" }
  13.100 -            slot.put(_"Add new draft")
  13.101 -          end,
  13.102 -          module = "draft",
  13.103 -          view = "new",
  13.104 -          params = { initiative_id = initiative.id }
  13.105 -        }
  13.106 -      end
  13.107 -    end
  13.108 -  },
  13.109 -  {
  13.110 -    name = "details",
  13.111 -    label = _"Details",
  13.112 -    content = function()
  13.113 -      ui.form{
  13.114 -        attr = { class = "vertical" },
  13.115 -        record = initiative,
  13.116 -        readonly = true,
  13.117 -        content = function()
  13.118 -          ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
  13.119 -          ui.field.text{
  13.120 -            label = _"Created at",
  13.121 -            value = tostring(initiative.created)
  13.122 -          }
  13.123 -          ui.field.text{
  13.124 -            label = _"Created at",
  13.125 -            value = format.timestamp(initiative.created)
  13.126 -          }
  13.127 -          ui.field.date{ label = _"Revoked at", name = "revoked" }
  13.128 -          ui.field.boolean{ label = _"Admitted", name = "admitted" }
  13.129 -        end
  13.130 -      }
  13.131      end
  13.132    },
  13.133    {
  13.134 @@ -171,6 +195,30 @@
  13.135        execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } }
  13.136      end
  13.137    },
  13.138 +  {
  13.139 +    name = "details",
  13.140 +    label = _"Details",
  13.141 +    content = function()
  13.142 +      ui.form{
  13.143 +        attr = { class = "vertical" },
  13.144 +        record = initiative,
  13.145 +        readonly = true,
  13.146 +        content = function()
  13.147 +          ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
  13.148 +          ui.field.text{
  13.149 +            label = _"Created at",
  13.150 +            value = tostring(initiative.created)
  13.151 +          }
  13.152 +          ui.field.text{
  13.153 +            label = _"Created at",
  13.154 +            value = format.timestamp(initiative.created)
  13.155 +          }
  13.156 +          ui.field.date{ label = _"Revoked at", name = "revoked" }
  13.157 +          ui.field.boolean{ label = _"Admitted", name = "admitted" }
  13.158 +        end
  13.159 +      }
  13.160 +    end
  13.161 +  },
  13.162  }
  13.163  
  13.164  
    14.1 --- a/app/main/issue/_list.lua	Wed Nov 18 12:00:00 2009 +0100
    14.2 +++ b/app/main/issue/_list.lua	Mon Nov 23 12:00:00 2009 +0100
    14.3 @@ -1,89 +1,163 @@
    14.4  local issues_selector = param.get("issues_selector", "table")
    14.5  
    14.6 -local paginate = ui.paginate
    14.7  
    14.8 -local issues
    14.9 -
   14.10 -if not issues_selector then
   14.11 -  issues = param.get("issues", "table")
   14.12 -  paginate = function(args)
   14.13 -    args.content()
   14.14 -  end
   14.15 -end
   14.16 -
   14.17 -ui.order{
   14.18 -  name = "issue_list",
   14.19 +ui.filter{
   14.20    selector = issues_selector,
   14.21 -  options = {
   14.22 +  filters = {
   14.23 +    {
   14.24 +      type = "boolean",
   14.25 +      name = "any",
   14.26 +      label = _"Any",
   14.27 +      selector_modifier = function()  end
   14.28 +    },
   14.29      {
   14.30 -      name = "population",
   14.31 -      label = _"Population",
   14.32 -      order_by = "issue.population DESC"
   14.33 +      type = "boolean",
   14.34 +      name = "new",
   14.35 +      label = _"New",
   14.36 +      selector_modifier = function(selector, value)
   14.37 +        if value then
   14.38 +          selector:add_where("issue.accepted ISNULL AND issue.closed ISNULL")
   14.39 +        end
   14.40 +      end
   14.41 +    },
   14.42 +    {
   14.43 +      type = "boolean",
   14.44 +      name = "accepted",
   14.45 +      label = _"In discussion",
   14.46 +      selector_modifier = function(selector, value)
   14.47 +        if value then
   14.48 +          selector:add_where("issue.accepted NOTNULL AND issue.half_frozen ISNULL AND issue.closed ISNULL")
   14.49 +        end
   14.50 +      end
   14.51      },
   14.52      {
   14.53 -      name = "newest",
   14.54 -      label = _"Newest",
   14.55 -      order_by = "issue.created DESC"
   14.56 +      type = "boolean",
   14.57 +      name = "half_frozen",
   14.58 +      label = _"Frozen",
   14.59 +      selector_modifier = function(selector, value)
   14.60 +        if value then
   14.61 +          selector:add_where("issue.half_frozen NOTNULL AND issue.closed ISNULL")
   14.62 +        end
   14.63 +      end
   14.64 +    },
   14.65 +    {
   14.66 +      type = "boolean",
   14.67 +      name = "frozen",
   14.68 +      label = _"Voting",
   14.69 +      selector_modifier = function(selector, value)
   14.70 +        if value then
   14.71 +          selector:add_where("issue.fully_frozen NOTNULL AND issue.closed ISNULL")
   14.72 +        end
   14.73 +      end
   14.74      },
   14.75      {
   14.76 -      name = "oldest",
   14.77 -      label = _"Oldest",
   14.78 -      order_by = "issue.created"
   14.79 -    }
   14.80 +      type = "boolean",
   14.81 +      name = "finished",
   14.82 +      label = _"Finished",
   14.83 +      selector_modifier = function(selector, value)
   14.84 +        if value then
   14.85 +          selector:add_where("issue.closed NOTNULL AND ranks_available")
   14.86 +        end
   14.87 +      end
   14.88 +    },
   14.89 +    {
   14.90 +      type = "boolean",
   14.91 +      name = "cancelled",
   14.92 +      label = _"Cancelled",
   14.93 +      selector_modifier = function(selector, value)
   14.94 +        if value then
   14.95 +          selector:add_where("issue.closed NOTNULL AND NOT ranks_available")
   14.96 +        end
   14.97 +      end
   14.98 +    },
   14.99    },
  14.100    content = function()
  14.101 -    paginate{
  14.102 +    ui.order{
  14.103 +      name = "issue_list",
  14.104        selector = issues_selector,
  14.105 +      options = {
  14.106 +        {
  14.107 +          name = "population",
  14.108 +          label = _"Population",
  14.109 +          order_by = "issue.population DESC"
  14.110 +        },
  14.111 +        {
  14.112 +          name = "newest",
  14.113 +          label = _"Newest",
  14.114 +          order_by = "issue.created DESC"
  14.115 +        },
  14.116 +        {
  14.117 +          name = "oldest",
  14.118 +          label = _"Oldest",
  14.119 +          order_by = "issue.created"
  14.120 +        }
  14.121 +      },
  14.122        content = function()
  14.123 -        local highlight_string = param.get("highlight_string", "string")
  14.124 -        ui.list{
  14.125 -          attr = { class = "issues" },
  14.126 -          records = issues or issues_selector:exec(),
  14.127 -          columns = {
  14.128 -            {
  14.129 -              label = _"Issue",
  14.130 -              content = function(record)
  14.131 -                if not param.get("for_area_list", atom.boolean) then
  14.132 -                  ui.field.text{
  14.133 -                    value = record.area.name
  14.134 -                  }
  14.135 -                  slot.put("<br />")
  14.136 -                end
  14.137 -                ui.link{
  14.138 -                  text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)),
  14.139 -                  module = "issue",
  14.140 -                  view = "show",
  14.141 -                  id = record.id
  14.142 -                }
  14.143 -                slot.put("<br />")
  14.144 -                slot.put("<br />")
  14.145 -              end
  14.146 -            },
  14.147 -            {
  14.148 -              label = _"State",
  14.149 -              content = function(record)
  14.150 -                ui.field.issue_state{ value = record.state }
  14.151 -              end
  14.152 -            },
  14.153 -            {
  14.154 -              label = _"Initiatives",
  14.155 -              content = function(record)
  14.156 -                execute.view{
  14.157 -                  module = "initiative",
  14.158 -                  view = "_list",
  14.159 -                  params = {
  14.160 -                    issue = record,
  14.161 -                    initiatives_selector = record:get_reference_selector("initiatives"),
  14.162 -                    highlight_string = highlight_string,
  14.163 -                    limit = 3
  14.164 -                  }
  14.165 -                }
  14.166 -              end
  14.167 -            },
  14.168 -          }
  14.169 +        ui.paginate{
  14.170 +          selector = issues_selector,
  14.171 +          content = function()
  14.172 +            local highlight_string = param.get("highlight_string", "string")
  14.173 +            local issues = issues or issues_selector:exec()
  14.174 +--            issues:load(initiatives)
  14.175 +            ui.list{
  14.176 +              attr = { class = "issues" },
  14.177 +              records = issues,
  14.178 +              columns = {
  14.179 +                {
  14.180 +                  label = _"Issue",
  14.181 +                  content = function(record)
  14.182 +                    if not param.get("for_area_list", atom.boolean) then
  14.183 +                      ui.field.text{
  14.184 +                        value = record.area.name
  14.185 +                      }
  14.186 +                      slot.put("<br />")
  14.187 +                    end
  14.188 +                    ui.link{
  14.189 +                      text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)),
  14.190 +                      module = "issue",
  14.191 +                      view = "show",
  14.192 +                      id = record.id
  14.193 +                    }
  14.194 +                    if record.state == "new" then
  14.195 +                      ui.image{
  14.196 +                        static = "icons/16/new.png"
  14.197 +                      }
  14.198 +                    end
  14.199 +                    slot.put("<br />")
  14.200 +                    slot.put("<br />")
  14.201 +                  end
  14.202 +                },
  14.203 +                {
  14.204 +                  label = _"State",
  14.205 +                  content = function(record)
  14.206 +                    ui.field.issue_state{ value = record.state }
  14.207 +                  end
  14.208 +                },
  14.209 +                {
  14.210 +                  label = _"Initiatives",
  14.211 +                  content = function(record)
  14.212 +                    local initiatives_selector = record:get_reference_selector("initiatives")
  14.213 +                    local highlight_string = param.get("highlight_string")
  14.214 +                    if highlight_string then
  14.215 +                      initiatives_selector:add_field( {'"highlight"("initiative"."name", ?)', highlight_string }, "name_highlighted")
  14.216 +                    end
  14.217 +                    execute.view{
  14.218 +                      module = "initiative",
  14.219 +                      view = "_list",
  14.220 +                      params = {
  14.221 +                        issue = record,
  14.222 +                        initiatives_selector = initiatives_selector,
  14.223 +                        highlight_string = highlight_string,
  14.224 +                        limit = 3
  14.225 +                      }
  14.226 +                    }
  14.227 +                  end
  14.228 +                },
  14.229 +              }
  14.230 +            }
  14.231 +          end
  14.232          }
  14.233 -    
  14.234        end
  14.235      }
  14.236    end
  14.237 -}
  14.238 \ No newline at end of file
  14.239 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/app/main/issue/_show_box.lua	Mon Nov 23 12:00:00 2009 +0100
    15.3 @@ -0,0 +1,22 @@
    15.4 +local issue = param.get("issue", "table")
    15.5 +
    15.6 +slot.select("issue_info", function()
    15.7 +  ui.field.text{ 
    15.8 +    label = _"State",
    15.9 +    value = issue.state_name 
   15.10 +  }
   15.11 +  local time_left = issue.state_time_left
   15.12 +  if time_left then
   15.13 +    ui.field.text{ 
   15.14 +      label = "Time left",
   15.15 +      value = time_left
   15.16 +    }
   15.17 +  end
   15.18 +  local next_state_names = issue.next_states_names
   15.19 +  if next_state_names then
   15.20 +    ui.field.text{ 
   15.21 +      label = _"Next states", 
   15.22 +      value = next_state_names
   15.23 +    }
   15.24 +  end
   15.25 +end)
    16.1 --- a/app/main/issue/show.lua	Wed Nov 18 12:00:00 2009 +0100
    16.2 +++ b/app/main/issue/show.lua	Mon Nov 23 12:00:00 2009 +0100
    16.3 @@ -42,12 +42,17 @@
    16.4    params = { issue_id = issue.id }
    16.5  }
    16.6  
    16.7 +execute.view{
    16.8 +  module = "issue",
    16.9 +  view = "_show_box",
   16.10 +  params = { issue = issue }
   16.11 +}
   16.12 +
   16.13  ui.tabs{
   16.14    {
   16.15      name = "initiatives",
   16.16      label = _"Initiatives",
   16.17 -    content = function()
   16.18 -      execute.view{
   16.19 +    content = function()      execute.view{
   16.20          module = "initiative",
   16.21          view = "_list",
   16.22          params = { 
   16.23 @@ -83,24 +88,48 @@
   16.24    },
   16.25  --]]
   16.26    {
   16.27 +    name = "delegations",
   16.28 +    label = _"Delegations",
   16.29 +    content = function()
   16.30 +      execute.view{
   16.31 +        module = "delegation",
   16.32 +        view = "_list",
   16.33 +        params = { delegations_selector = issue:get_reference_selector("delegations") }
   16.34 +      }
   16.35 +    end
   16.36 +  },
   16.37 +  {
   16.38      name = "details",
   16.39      label = _"Details",
   16.40      content = function()
   16.41 +      local policy = issue.policy
   16.42        ui.form{
   16.43          record = issue,
   16.44          readonly = true,
   16.45          attr = { class = "vertical" },
   16.46          content = function()
   16.47 -          trace.debug(issue.created)
   16.48            ui.field.text{ label = _"State", name = "state" }
   16.49 -          ui.field.timestamp{ label = _"Created at", name = "created" }
   16.50 -          ui.field.timestamp{ label = _"Accepted", name = "accepted" }
   16.51 -          ui.field.timestamp{ label = _"Half frozen", name = "half_frozen" }
   16.52 -          ui.field.timestamp{ label = _"Fully frozen", name = "fully_frozen" }
   16.53 -          ui.field.timestamp{ label = _"Closed", name = "closed" }
   16.54 -          ui.field.potential_issue_weight{ label = _"Potential weight", name = "potential_weight" }
   16.55 -          ui.field.vote_now{ label = _"Vote now", name = "vote_now" }
   16.56 +          ui.field.timestamp{ label = _"Created at",            name = "created" }
   16.57 +          ui.field.text{      label = _"admission_time",        value = policy.admission_time }
   16.58 +          ui.field.integer{   label = _"issue_quorum_num",      value = policy.issue_quorum_num }
   16.59 +          ui.field.integer{   label = _"issue_quorum_den",      value = policy.issue_quorum_den }
   16.60 +          ui.field.timestamp{ label = _"Accepted",              name = "accepted" }
   16.61 +          ui.field.text{      label = _"discussion_time",       value = policy.discussion_time }
   16.62 +          ui.field.vote_now{   label = _"Vote now", name = "vote_now" }
   16.63            ui.field.vote_later{ label = _"Vote later", name = "vote_later" }
   16.64 +          ui.field.timestamp{ label = _"Half frozen",           name = "half_frozen" }
   16.65 +          ui.field.text{      label = _"verification_time",     value = policy.verification_time }
   16.66 +          ui.field.integer{ label   = _"initiative_quorum_num", value = policy.initiative_quorum_num }
   16.67 +          ui.field.integer{ label   = _"initiative_quorum_den", value = policy.initiative_quorum_den }
   16.68 +          ui.field.timestamp{ label = _"Fully frozen",          name = "fully_frozen" }
   16.69 +          ui.field.text{      label = _"voting_time",           value = policy.voting_time }
   16.70 +          ui.field.timestamp{ label = _"Closed",                name = "closed" }
   16.71 +        end
   16.72 +      }
   16.73 +      ui.form{
   16.74 +        record = issue.policy,
   16.75 +        readonly = true,
   16.76 +        content = function()
   16.77          end
   16.78        }
   16.79      end
    17.1 --- a/app/main/member/_action/update.lua	Wed Nov 18 12:00:00 2009 +0100
    17.2 +++ b/app/main/member/_action/update.lua	Mon Nov 23 12:00:00 2009 +0100
    17.3 @@ -1,4 +1,20 @@
    17.4 -param.update(app.session.member, "name")
    17.5 +param.update(app.session.member,
    17.6 +  "name",
    17.7 +  "organizational_unit",
    17.8 +  "internal_posts",
    17.9 +  "realname",
   17.10 +  "birthday",
   17.11 +  "address",
   17.12 +  "email",
   17.13 +  "xmpp_address",
   17.14 +  "website",
   17.15 +  "phone",
   17.16 +  "mobile_phone",
   17.17 +  "profession",
   17.18 +  "external_memberships",
   17.19 +  "external_posts",
   17.20 +  "statement"
   17.21 +)
   17.22  
   17.23  app.session.member:save()
   17.24  
    18.1 --- a/app/main/member/_action/update_avatar.lua	Wed Nov 18 12:00:00 2009 +0100
    18.2 +++ b/app/main/member/_action/update_avatar.lua	Mon Nov 23 12:00:00 2009 +0100
    18.3 @@ -1,20 +1,52 @@
    18.4 -local data = param.get("avatar")
    18.5 +local member_id = app.session.member_id
    18.6 +
    18.7 +local member_image = MemberImage:by_pk(member_id, "avatar", false)
    18.8 +local member_image_scaled = MemberImage:by_pk(member_id, "avatar", true)
    18.9  
   18.10  if param.get("avatar_delete", atom.boolean) then
   18.11 -  app.session.member.avatar = nil
   18.12 -  app.session.member:save()
   18.13 +  if member_image then
   18.14 +    member_image:destroy()
   18.15 +  end
   18.16 +  if member_image_scaled then
   18.17 +    member_image_scaled:destroy()
   18.18 +  end
   18.19    slot.put_into("notice", _"Avatar has been deleted")
   18.20    return
   18.21  end
   18.22  
   18.23 -local data, err, status = os.pfilter(data, "convert", "-", "-thumbnail", "48x48", "-")
   18.24 +local data = param.get("avatar")
   18.25  
   18.26 -if status ~= 0 or data == nil then
   18.27 +local data_scaled, err, status = os.pfilter(data, "convert", "-", "-thumbnail", "48x48", "-")
   18.28 +
   18.29 +if status ~= 0 or data_scaled == nil then
   18.30   error("error while converting image")
   18.31  end
   18.32  
   18.33 +if not member_image then
   18.34 +  member_image = MemberImage:new()
   18.35 +  member_image.member_id = member_id
   18.36 +  member_image.image_type = "avatar"
   18.37 +  member_image.scaled = false
   18.38 +  member_image.data = ""
   18.39 +  member_image:save()
   18.40 +end
   18.41 +
   18.42 +if not member_image_scaled then
   18.43 +  member_image_scaled = MemberImage:new()
   18.44 +  member_image_scaled.member_id = member_id
   18.45 +  member_image_scaled.image_type = "avatar"
   18.46 +  member_image_scaled.scaled = true
   18.47 +  member_image_scaled.content_type = true
   18.48 +  member_image_scaled.data = ""
   18.49 +  member_image_scaled:save()
   18.50 +end
   18.51 +
   18.52  if data and #data > 0 then
   18.53 -  db:query{ 'UPDATE member SET avatar = $ WHERE id = ?', { db:quote_binary(data) }, app.session.member.id }
   18.54 +  db:query{ "UPDATE member_image SET data = $ WHERE member_id = ? AND image_type='avatar' AND scaled=FALSE", { db:quote_binary(data) }, app.session.member.id }
   18.55 +end
   18.56 +
   18.57 +if data_scaled and #data_scaled > 0 then
   18.58 +  db:query{ "UPDATE member_image SET data = $ WHERE member_id = ? AND image_type='avatar' AND scaled=TRUE", { db:quote_binary(data_scaled) }, app.session.member.id }
   18.59  end
   18.60  
   18.61  slot.put_into("notice", _"Avatar has been updated")
    19.1 --- a/app/main/member/_list.lua	Wed Nov 18 12:00:00 2009 +0100
    19.2 +++ b/app/main/member/_list.lua	Mon Nov 23 12:00:00 2009 +0100
    19.3 @@ -1,81 +1,39 @@
    19.4  local members_selector = param.get("members_selector", "table")
    19.5  
    19.6 -ui.paginate{
    19.7 +ui.order{
    19.8 +  name = "member_list",
    19.9    selector = members_selector,
   19.10 -  content = function() 
   19.11 -    ui.list{
   19.12 -      records = members_selector:exec(),
   19.13 -      columns = {
   19.14 -        {
   19.15 -          content    = function(record)
   19.16 -            ui.image{
   19.17 -              attr = { style="height: 24px;" },
   19.18 -              module = "member",
   19.19 -              view = "avatar",
   19.20 -              extension = "jpg",
   19.21 -              id = record.id
   19.22 -            }
   19.23 -          end
   19.24 -        },
   19.25 -        {
   19.26 -          label = _"Login",
   19.27 -          content    = function(record)
   19.28 -            ui.link{
   19.29 -              text   = record.login,
   19.30 -              module = "member",
   19.31 -              view   = "show",
   19.32 -              id     = record.id
   19.33 -            }
   19.34 -          end
   19.35 -        },
   19.36 -        {
   19.37 -          label = _"Name",
   19.38 -          content = function(record)
   19.39 -            ui.link{
   19.40 -              content = function()
   19.41 -                util.put_highlighted_string(record.name)
   19.42 -              end,
   19.43 -              module = "member",
   19.44 -              view   = "show",
   19.45 -              id     = record.id
   19.46 -            }
   19.47 -          end
   19.48 -        },
   19.49 -        {
   19.50 -          label = _"Ident number",
   19.51 -          name = "ident_number"
   19.52 -        },
   19.53 -        {
   19.54 -          label = _"Admin?",
   19.55 -          name = "admin"
   19.56 -        },
   19.57 -        {
   19.58 -          label = "Locked?",
   19.59 -          content = function(record)
   19.60 -            ui.field.boolean{ value = record.locked }
   19.61 -          end
   19.62 -        },
   19.63 -        {
   19.64 -         content    = function(record)
   19.65 -            ui.link{
   19.66 -              attr   = { class = "action" },
   19.67 -              text   = _"Add to my contacts",
   19.68 -              module = "contact",
   19.69 -              action = "add_member",
   19.70 -              id     = record.id,
   19.71 -              routing = {
   19.72 -                default = {
   19.73 -                  mode = "redirect",
   19.74 -                  module = request.get_module(),
   19.75 -                  view = request.get_view(),
   19.76 -                  id = param.get_id_cgi(),
   19.77 -                  params = param.get_all_cgi()
   19.78 -                }
   19.79 +  options = {
   19.80 +    {
   19.81 +      name = "name",
   19.82 +      label = _"A-Z",
   19.83 +      order_by = "name"
   19.84 +    },
   19.85 +    {
   19.86 +      name = "name_desc",
   19.87 +      label = _"Z-A",
   19.88 +      order_by = "name DESC"
   19.89 +    },
   19.90 +  },
   19.91 +  content = function()
   19.92 +    ui.paginate{
   19.93 +      selector = members_selector,
   19.94 +      per_page = 100,
   19.95 +      content = function() 
   19.96 +        ui.container{
   19.97 +          attr = { class = "member_list" },
   19.98 +          content = function()
   19.99 +            for i, member in ipairs(members_selector:exec()) do
  19.100 +              execute.view{
  19.101 +                module = "member",
  19.102 +                view = "_show_thumb",
  19.103 +                params = { member = member }
  19.104                }
  19.105 -            }
  19.106 +            end
  19.107            end
  19.108          }
  19.109 -      }
  19.110 +            slot.put('<br style="clear: left;" />')
  19.111 +      end
  19.112      }
  19.113    end
  19.114  }
  19.115 \ No newline at end of file
    20.1 --- a/app/main/member/_show.lua	Wed Nov 18 12:00:00 2009 +0100
    20.2 +++ b/app/main/member/_show.lua	Mon Nov 23 12:00:00 2009 +0100
    20.3 @@ -5,11 +5,11 @@
    20.4    record = member,
    20.5    readonly = true,
    20.6    content = function()
    20.7 -    ui.field.text{    label = _"Login",        name = "login" }
    20.8 -    ui.field.text{    label = _"Name",         name = "name" }
    20.9      ui.field.boolean{ label = _"Admin?",       name = "admin" }
   20.10      ui.field.boolean{ label = _"Locked?",      name = "locked" }
   20.11 -    ui.field.text{    label = _"Ident number", name = "ident_number" }
   20.12 +    if member.ident_number then
   20.13 +      ui.field.text{    label = _"Ident number", name = "ident_number" }
   20.14 +    end
   20.15      ui.submit{        text  = _"Save" }
   20.16    end
   20.17  }
   20.18 @@ -55,7 +55,7 @@
   20.19        execute.view{
   20.20          module = "delegation",
   20.21          view = "_list",
   20.22 -        params = { selector = member:get_reference_selector("incoming_delegations") }
   20.23 +        params = { delegations_selector = member:get_reference_selector("incoming_delegations"), incoming = true }
   20.24        }
   20.25      end
   20.26    },
   20.27 @@ -66,7 +66,7 @@
   20.28        execute.view{
   20.29          module = "delegation",
   20.30          view = "_list",
   20.31 -        params = { selector = member:get_reference_selector("outgoing_delegations") }
   20.32 +        params = { delegations_selector = member:get_reference_selector("outgoing_delegations"), outgoing = true }
   20.33        }
   20.34      end
   20.35    },
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/app/main/member/_show_thumb.lua	Mon Nov 23 12:00:00 2009 +0100
    21.3 @@ -0,0 +1,25 @@
    21.4 +local member = param.get("member", "table")
    21.5 +
    21.6 +local name
    21.7 +if member.name_highlighted then
    21.8 +  name = encode.highlight(member.name_highlighted)
    21.9 +else
   21.10 +  name = encode.html(member.name)
   21.11 +end
   21.12 +
   21.13 +ui.link{
   21.14 +  attr = { class = "member_thumb" },
   21.15 +  module = "member",
   21.16 +  view = "show",
   21.17 +  id = member.id,
   21.18 +  content = function()
   21.19 +    ui.image{
   21.20 +      attr = { width = 48, height = 48 },
   21.21 +      module    = "member",
   21.22 +      view      = "avatar",
   21.23 +      id        = member.id,
   21.24 +      extension = "jpg"
   21.25 +    }
   21.26 +    slot.put(name)
   21.27 +  end
   21.28 +}
   21.29 \ No newline at end of file
    22.1 --- a/app/main/member/avatar.lua	Wed Nov 18 12:00:00 2009 +0100
    22.2 +++ b/app/main/member/avatar.lua	Mon Nov 23 12:00:00 2009 +0100
    22.3 @@ -1,14 +1,14 @@
    22.4 -local record = Member:by_id(param.get_id())
    22.5 +local record = MemberImage:by_pk(param.get_id(), "avatar", true)
    22.6  
    22.7 -if false and (not record or not record.avatar) then
    22.8 -  print('Location: ' .. encode.url{ static = 'no_image.png' } .. '\n\n')
    22.9 +if record == nil then
   22.10 +  print('Location: ' .. encode.url{ static = 'avatar.jpg' } .. '\n\n')
   22.11    exit()
   22.12  end
   22.13  
   22.14 -print('Content-type: image/jpg\n')
   22.15 +print('Content-type: ' .. record.content_type .. '\n')
   22.16  
   22.17  if record then
   22.18 -  io.stdout:write(record.avatar)
   22.19 +  io.stdout:write(record.data)
   22.20  else
   22.21  end
   22.22  
    23.1 --- a/app/main/member/edit.lua	Wed Nov 18 12:00:00 2009 +0100
    23.2 +++ b/app/main/member/edit.lua	Mon Nov 23 12:00:00 2009 +0100
    23.3 @@ -25,6 +25,20 @@
    23.4    },
    23.5    content = function()
    23.6      ui.field.text{ label = _"Name", name = "name" }
    23.7 +    ui.field.text{ label = _"Organizational unit", name = "organizational_unit" }
    23.8 +    ui.field.text{ label = _"Internal posts", name = "internal_posts" }
    23.9 +    ui.field.text{ label = _"Real name", name = "realname" }
   23.10 +    ui.field.text{ label = _"Birthday", name = "birthday" }
   23.11 +    ui.field.text{ label = _"Address", name = "address", multiline = true }
   23.12 +    ui.field.text{ label = _"email", name = "email" }
   23.13 +    ui.field.text{ label = _"xmpp", name = "xmpp_address" }
   23.14 +    ui.field.text{ label = _"Website", name = "website" }
   23.15 +    ui.field.text{ label = _"Phone", name = "phone" }
   23.16 +    ui.field.text{ label = _"Mobile phone", name = "mobile_phone" }
   23.17 +    ui.field.text{ label = _"Profession", name = "profession" }
   23.18 +    ui.field.text{ label = _"External memberships", name = "external_memberships", multiline = true }
   23.19 +    ui.field.text{ label = _"External posts", name = "external_posts", multiline = true }
   23.20 +    ui.field.text{ label = _"Statement", name = "statement", multiline = true }
   23.21      ui.submit{ value = _"Save" }
   23.22    end
   23.23  }
   23.24 \ No newline at end of file
    24.1 --- a/app/main/membership/_show_box.lua	Wed Nov 18 12:00:00 2009 +0100
    24.2 +++ b/app/main/membership/_show_box.lua	Mon Nov 23 12:00:00 2009 +0100
    24.3 @@ -6,7 +6,7 @@
    24.4    ui.container{
    24.5      attr = { 
    24.6        class = "head",
    24.7 -      onclick = "document.getElementById('interest_content').style.display = 'block';"
    24.8 +      onclick = "document.getElementById('membership_content').style.display = 'block';"
    24.9      },
   24.10      content = function()
   24.11        if membership then
   24.12 @@ -18,8 +18,16 @@
   24.13    }
   24.14  
   24.15    ui.container{
   24.16 -    attr = { class = "content", id = "interest_content" },
   24.17 +    attr = { class = "content", id = "membership_content" },
   24.18      content = function()
   24.19 +      ui.container{
   24.20 +        attr = {
   24.21 +          class = "close",
   24.22 +          style = "cursor: pointer;",
   24.23 +          onclick = "document.getElementById('membership_content').style.display = 'none';"
   24.24 +        },
   24.25 +        content = _"X"
   24.26 +      }
   24.27        if membership then
   24.28          ui.link{
   24.29            content = _"Remove my membership",
   24.30 @@ -56,14 +64,6 @@
   24.31            routing = { default = { mode = "redirect", module = "area", view = "show", id = area.id } }
   24.32          }
   24.33        end
   24.34 -        ui.container{
   24.35 -          attr = {
   24.36 -            class = "head",
   24.37 -            style = "cursor: pointer;",
   24.38 -            onclick = "document.getElementById('interest_content').style.display = 'none';"
   24.39 -          },
   24.40 -          content = _"Click here to close."
   24.41 -        }
   24.42      end
   24.43    }
   24.44  end)
    25.1 --- a/app/main/suggestion/_list.lua	Wed Nov 18 12:00:00 2009 +0100
    25.2 +++ b/app/main/suggestion/_list.lua	Mon Nov 23 12:00:00 2009 +0100
    25.3 @@ -4,6 +4,7 @@
    25.4    selector = suggestions_selector,
    25.5    content = function()
    25.6      ui.list{
    25.7 +      attr = { style = "table-layout: fixed;" },
    25.8        records = suggestions_selector:exec(),
    25.9        columns = {
   25.10          {
   25.11 @@ -24,7 +25,7 @@
   25.12                local max_value = record.initiative.issue.population
   25.13                ui.bargraph{
   25.14                  max_value = max_value,
   25.15 -                width = 50,
   25.16 +                width = 100,
   25.17                  bars = {
   25.18                    { color = "#ddd", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count - record.minus2_fulfilled_count - record.minus1_fulfilled_count },
   25.19                    { color = "#f88", value = record.minus1_unfulfilled_count + record.minus1_fulfilled_count },
   25.20 @@ -102,13 +103,14 @@
   25.21            end
   25.22          },
   25.23          {
   25.24 -          label = _"Not fullfilled",
   25.25 +          label = _"Suggestion currently not implemented",
   25.26 +          label_attr = { style = "width: 101px;" },
   25.27            content = function(record)
   25.28              if record.minus2_unfulfilled_count then
   25.29                local max_value = record.initiative.issue.population
   25.30                ui.bargraph{
   25.31                  max_value = max_value,
   25.32 -                width = 50,
   25.33 +                width = 100,
   25.34                  bars = {
   25.35                    { color = "#ddd", value = max_value - record.minus2_unfulfilled_count - record.minus1_unfulfilled_count },
   25.36                    { color = "#f88", value = record.minus1_unfulfilled_count },
   25.37 @@ -122,13 +124,14 @@
   25.38            end
   25.39          },
   25.40          {
   25.41 -          label = _"Fullfilled",
   25.42 +          label = _"Suggestion currently implemented",
   25.43 +          label_attr = { style = "width: 101px;" },
   25.44            content = function(record)
   25.45              if record.minus2_fulfilled_count then
   25.46                local max_value = record.initiative.issue.population
   25.47                ui.bargraph{
   25.48                  max_value = max_value,
   25.49 -                width = 50,
   25.50 +                width = 100,
   25.51                  bars = {
   25.52                    { color = "#ddd", value = max_value - record.minus2_fulfilled_count - record.minus1_fulfilled_count },
   25.53                    { color = "#f88", value = record.minus1_fulfilled_count },
   25.54 @@ -153,7 +156,7 @@
   25.55                  ui.image{ static = "icons/16/cross.png" }
   25.56                  ui.link{
   25.57                    attr = { class = "action" },
   25.58 -                  text = _"set fulfilled",
   25.59 +                  text = _"set implented",
   25.60                    module = "opinion",
   25.61                    action = "update",
   25.62                    routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } },
   25.63 @@ -166,7 +169,7 @@
   25.64                  ui.image{ static = "icons/16/tick.png" }
   25.65                  ui.link{
   25.66                    attr = { class = "action" },
   25.67 -                  text = _"remove fulfilled",
   25.68 +                  text = _"remove implemented",
   25.69                    module = "opinion",
   25.70                    action = "update",
   25.71                    routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } },
    26.1 --- a/app/main/supporter/_show_box.lua	Wed Nov 18 12:00:00 2009 +0100
    26.2 +++ b/app/main/supporter/_show_box.lua	Mon Nov 23 12:00:00 2009 +0100
    26.3 @@ -27,6 +27,14 @@
    26.4      ui.container{
    26.5        attr = { class = "content", id = "support_content" },
    26.6        content = function()
    26.7 +        ui.container{
    26.8 +          attr = {
    26.9 +            class = "close",
   26.10 +            style = "cursor: pointer;",
   26.11 +            onclick = "document.getElementById('support_content').style.display = 'none';"
   26.12 +          },
   26.13 +          content = _"X"
   26.14 +        }
   26.15          if supported then
   26.16            ui.link{
   26.17              content = function()
    27.1 --- a/config/default.lua	Wed Nov 18 12:00:00 2009 +0100
    27.2 +++ b/config/default.lua	Mon Nov 23 12:00:00 2009 +0100
    27.3 @@ -1,10 +1,14 @@
    27.4  config.app_name = "LiquidFeedback"
    27.5 -config.app_version = "alpha2"
    27.6 +config.app_version = "alpha3"
    27.7  
    27.8  config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)"
    27.9  
   27.10  config.app_service_provider = "Snake Oil<br/>10000 Berlin<br/>Germany"
   27.11  
   27.12 +config.member_image_convert = {
   27.13 +  avatar = { "convert", "-", "-thumbnail", "48x48", "-" }
   27.14 +}
   27.15 +
   27.16  -- uncomment the following two lines to use C implementations of chosen
   27.17  -- functions and to disable garbage collection during the request, to
   27.18  -- increase speed:
   27.19 @@ -37,7 +41,7 @@
   27.20  
   27.21  
   27.22  
   27.23 -
   27.24 +-- TODO abstraction
   27.25  -- get record by id
   27.26  function mondelefant.class_prototype:by_id(id)
   27.27    local selector = self:new_selector()
   27.28 @@ -46,3 +50,4 @@
   27.29    return selector:exec()
   27.30  end
   27.31  
   27.32 +
    28.1 --- a/config/development.lua	Wed Nov 18 12:00:00 2009 +0100
    28.2 +++ b/config/development.lua	Mon Nov 23 12:00:00 2009 +0100
    28.3 @@ -1,2 +1,3 @@
    28.4  execute.config("default")
    28.5  
    28.6 +config.wiki_parser_executeable = "/opt/rocketwiki/rocketwiki"
    29.1 --- a/config/testing.lua	Wed Nov 18 12:00:00 2009 +0100
    29.2 +++ b/config/testing.lua	Mon Nov 23 12:00:00 2009 +0100
    29.3 @@ -1,6 +1,3 @@
    29.4  execute.config("default")
    29.5  
    29.6 -
    29.7 -
    29.8 -
    29.9 -
   29.10 +config.wiki_parser_executeable = "/opt/liquid_feedback_testing/rocketwiki/rocketwiki"
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/env/encode/highlight.lua	Mon Nov 23 12:00:00 2009 +0100
    30.3 @@ -0,0 +1,10 @@
    30.4 +function encode.highlight(text)
    30.5 +  local text = encode.html(text)
    30.6 +  text = text:gsub("\027", "")
    30.7 +  text = text:gsub("\\\\", "\027b")
    30.8 +  text = text:gsub("\\%*", "\027a")
    30.9 +  text = text:gsub("%*([^%*]*)%*", '<span class="highlighted">%1</span>')
   30.10 +  text = text:gsub("\027a", "*")
   30.11 +  text = text:gsub("\027b", "\\")
   30.12 +  return text
   30.13 +end
   30.14 \ No newline at end of file
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/env/format/wiki_text.lua	Mon Nov 23 12:00:00 2009 +0100
    31.3 @@ -0,0 +1,11 @@
    31.4 +function format.wiki_text(wiki_text)
    31.5 +  local html, errmsg, exitcode = assert(
    31.6 +    os.pfilter(wiki_text, config.wiki_parser_executeable)
    31.7 +  )
    31.8 +  if exitcode > 0 then
    31.9 +    error("Wiki parser process returned with error code " .. tostring(exitcode))
   31.10 +  elseif exitcode < 0 then
   31.11 +    error("Wiki parser process was terminated by signal " .. tostring(-exitcode))
   31.12 +  end
   31.13 +  return html
   31.14 +end
    32.1 --- a/env/ui/bargraph.lua	Wed Nov 18 12:00:00 2009 +0100
    32.2 +++ b/env/ui/bargraph.lua	Mon Nov 23 12:00:00 2009 +0100
    32.3 @@ -5,14 +5,16 @@
    32.4      },
    32.5      content = function()
    32.6        for i, bar in ipairs(args.bars) do
    32.7 -        local value = bar.value * args.width / args.max_value / 2 
    32.8 -        ui.container{
    32.9 -          attr = {
   32.10 -            style = "width: " .. tostring(value) .. "px; background-color: " .. bar.color .. ";",
   32.11 -            title = tostring(bar.value)
   32.12 -          },
   32.13 -          content = function() slot.put("&nbsp;") end
   32.14 -        }
   32.15 +        if bar.value > 0 then
   32.16 +          local value = bar.value * args.width / args.max_value / 2 
   32.17 +          ui.container{
   32.18 +            attr = {
   32.19 +              style = "width: " .. tostring(value) .. "px; background-color: " .. bar.color .. ";",
   32.20 +              title = tostring(bar.value)
   32.21 +            },
   32.22 +            content = function() slot.put("&nbsp;") end
   32.23 +          }
   32.24 +        end
   32.25        end
   32.26      end
   32.27    }
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/env/ui/filter.lua	Mon Nov 23 12:00:00 2009 +0100
    33.3 @@ -0,0 +1,40 @@
    33.4 +function ui.filter(args)
    33.5 +  local name = args.name or "filter"
    33.6 +  local current_filter = atom.string:load(cgi.params[name]) or args.filters[1].name
    33.7 +  local id     = param.get_id_cgi()
    33.8 +  local params = param.get_all_cgi()
    33.9 +  ui.container{
   33.10 +    attr = { class = "ui_filter" },
   33.11 +    content = function()
   33.12 +      ui.container{
   33.13 +        attr = { class = "ui_filter_head" },
   33.14 +        content = function()
   33.15 +          slot.put(_"Filter")
   33.16 +          slot.put(": ")
   33.17 +          for i, filter in ipairs(args.filters) do
   33.18 +            params[name] = filter.name
   33.19 +            local attr = {}
   33.20 +            if current_filter == filter.name then
   33.21 +              attr.class = "active"
   33.22 +              filter.selector_modifier(args.selector, true)
   33.23 +            end
   33.24 +            ui.link{
   33.25 +              attr    = attr,
   33.26 +              module  = request.get_module(),
   33.27 +              view    = request.get_view(),
   33.28 +              id      = id,
   33.29 +              params  = params,
   33.30 +              content = filter.label
   33.31 +            }
   33.32 +          end
   33.33 +        end
   33.34 +      }
   33.35 +      ui.container{
   33.36 +        attr = { class = "ui_filter_content" },
   33.37 +        content = function()
   33.38 +          args.content()
   33.39 +        end
   33.40 +      }
   33.41 +    end
   33.42 +  }
   33.43 +end
    34.1 --- a/locale/translations.de.lua	Wed Nov 18 12:00:00 2009 +0100
    34.2 +++ b/locale/translations.de.lua	Mon Nov 23 12:00:00 2009 +0100
    34.3 @@ -11,25 +11,33 @@
    34.4  ["Add new initiative to issue"] = "Neue Initiative zum Thema hinzufügen";
    34.5  ["Add new suggestion"] = "Neue Anregung hinzufügen";
    34.6  ["Add to my contacts"] = "Zu meinen Kontakten hinzufügen";
    34.7 +["Address"] = "Anschrift";
    34.8  ["Admin"] = "Admin";
    34.9  ["Admin menu"] = "Admin Menü";
   34.10  ["Admin?"] = "Admin?";
   34.11  ["Admitted"] = "zugelassen";
   34.12 +["Any"] = "Alle";
   34.13  ["Area"] = "Themenbereich";
   34.14  ["Area '#{name}'"] = "Themenbereich '#{name}'";
   34.15 +["Area delegation"] = "Area-Delegation";
   34.16  ["Area list"] = "Liste der Themenbereiche";
   34.17  ["Area successfully updated"] = "Themenbereich erfolgreich aktualisiert";
   34.18  ["Areas"] = "Themenbereiche";
   34.19  ["Author"] = "Autor";
   34.20  ["Autoreject is off."] = "Auto-Ablehnen ist aus";
   34.21  ["Autoreject is on."] = "Auto-Ablehnen ist an";
   34.22 -["Cancel"] = false;
   34.23 +["Avatar"] = false;
   34.24 +["Avatar has been deleted"] = "Avatar wurde gelöscht";
   34.25 +["Avatar has been updated"] = "Avatar wurde aktualisiert";
   34.26 +["Birthday"] = "Geburtstag";
   34.27 +["Cancel"] = "Abbrechen";
   34.28  ["Cancelled"] = "Abgebrochen";
   34.29  ["Change password"] = "Kennwort ändern";
   34.30  ["Click here to close."] = "Zum Schließen hier klicken";
   34.31  ["Close"] = "Schließen";
   34.32  ["Closed"] = "geschlossen";
   34.33  ["Commit suggestion"] = "Anregung speichern";
   34.34 +["Compare"] = "Vergleichen";
   34.35  ["Contacts"] = "Kontakte";
   34.36  ["Content"] = "Inhalt";
   34.37  ["Create new area"] = "Neuen Themenbereich anlegen";
   34.38 @@ -38,39 +46,45 @@
   34.39  ["Current draft"] = "Aktueller Entwurf";
   34.40  ["Degree"] = "Grad";
   34.41  ["Delegate"] = "Delegieren";
   34.42 +["Delegations"] = "Delegationen";
   34.43  ["Description"] = "Beschreibung";
   34.44  ["Details"] = "Details";
   34.45 +["Diff"] = false;
   34.46  ["Direct member count"] = "Anzahl Direktmitglieder";
   34.47  ["Direct supporter [change]"] = "Direkte Unterstützung [ändern]";
   34.48  ["Draft"] = "Entwurf";
   34.49  ["Edit"] = "Bearbeiten";
   34.50 +["Edit draft"] = "Entwurf bearbeiten";
   34.51  ["Edit my page"] = "Meine Seite bearbeiten";
   34.52  ["Error while updating member, database reported:<br /><br /> (#{errormessage})"] = "Fehler beim aktualisieren des Mitglieds, die Datenbank berichtet folgenden Fehler:<br /><br /> (#{errormessage})";
   34.53 +["External memberships"] = "Externe Mitgliedschaften";
   34.54 +["External posts"] = "Externe Ämter";
   34.55 +["Filter"] = false;
   34.56  ["Finished"] = "Abgeschlossen";
   34.57  ["Frozen"] = "Eingefroren";
   34.58  ["Fulfilled"] = "Erfüllt";
   34.59 -["Fullfilled"] = "Erfüllt";
   34.60 -["Fully frozen"] = false;
   34.61 +["Fully frozen"] = "Ganz eingefroren";
   34.62  ["Global delegation"] = "Globale Delegation";
   34.63 -["Half frozen"] = false;
   34.64 +["Half frozen"] = "Halb eingefroren";
   34.65  ["Hide"] = "Verstecken";
   34.66  ["Home"] = "Startseite";
   34.67  ["Id"] = "Id";
   34.68  ["Ident number"] = "Ident-Nummer";
   34.69  ["In discussion"] = "In Diskussion";
   34.70  ["Incoming delegations"] = "Eingehende Delegationen";
   34.71 -["Initiative"] = "Initiative";
   34.72  ["Initiative successfully created"] = "Initiative erfolgreich erzeugt";
   34.73  ["Initiative: '#{name}'"] = "Initiative: '#{name}'";
   34.74  ["Initiatives"] = "Initiativen";
   34.75  ["Initiators"] = "Initiatoren";
   34.76 -["Interest not existant"] = false;
   34.77 +["Interest not existant"] = "Interesse existiert nicht";
   34.78  ["Interest removed"] = "Interesse entfernt";
   34.79  ["Interest updated"] = "Interesse aktualisiert";
   34.80 +["Internal posts"] = "Interne Ämter";
   34.81  ["Invalid username or password!"] = "Ungültiger Benutzername oder Kennwort";
   34.82  ["Issue"] = "Thema";
   34.83  ["Issue ##{id}"] = "Issue ##{id}";
   34.84  ["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})";
   34.85 +["Issue delegation"] = "Issue-Delegation";
   34.86  ["Issue policy"] = "Regelwerk für Thema";
   34.87  ["Issues"] = "Themen";
   34.88  ["License"] = "Lizenz";
   34.89 @@ -80,7 +94,6 @@
   34.90  ["Login successful!"] = "Anmeldung erfolgreich";
   34.91  ["Logout"] = "Abmelden";
   34.92  ["Logout successful"] = "Abmeldung erfolgreich";
   34.93 -["Me"] = "Ich";
   34.94  ["Member '#{member}'"] = "Mitglied '#{member}'";
   34.95  ["Member has been removed from your contacts"] = "Mitglied wurde aus Deinen Kontakten entfernt";
   34.96  ["Member has been saved as private contact"] = "Mitglied wurde als privater Kontakt gespeichert";
   34.97 @@ -93,33 +106,40 @@
   34.98  ["Member successfully updated"] = "Mitglied erfolgreich aktualisert";
   34.99  ["Member: '#{login}' (#{name})"] = "Mitlied: '#{login}' (#{name})";
  34.100  ["Members"] = "Mitglieder";
  34.101 -["Membership not existant"] = false;
  34.102 +["Membership not existant"] = "Mitgliedschaft exisitert nicht";
  34.103  ["Membership removed"] = "Mitgliedschaft entfernt";
  34.104  ["Membership updated"] = "Mitgliedschaft aktualisiert";
  34.105 +["Mobile phone"] = "Mobiltelefon";
  34.106  ["Name"] = "Name";
  34.107  ["New"] = "Neu";
  34.108  ["New draft has been added to initiative"] = "Neuer Entwurf wurde der Initiative hinzugefügt";
  34.109 +["New draft revision"] = "Neue Revision des Entwurfs";
  34.110  ["New password"] = "Neues Kennwort";
  34.111  ["New passwords does not match."] = "Du hast nicht zweimal das gleiche Kennwort eingegeben";
  34.112  ["New passwords is too short."] = "Das neue Kennwort ist zu kurz";
  34.113  ["Newest"] = "Neueste";
  34.114 +["Next states"] = "Nächste Statuse";
  34.115  ["No supporter [change]"] = "Keine Unterstützung (ändern)";
  34.116 -["Not fullfilled"] = "Nicht erfüllt";
  34.117  ["OK"] = "OK";
  34.118 +["Old draft revision"] = "Alte Revision des Entwurfs";
  34.119  ["Old drafts"] = "Alte Entwürfe";
  34.120  ["Old password"] = "Altes Kennwort";
  34.121  ["Old password is wrong"] = "Das alte Kennwort ist falsch";
  34.122  ["Oldest"] = "Älteste";
  34.123  ["Order by"] = "Sortieren nach";
  34.124 +["Organizational unit"] = "Organisationseinheit";
  34.125  ["Outgoing delegations"] = "Ausgehende Delegationen";
  34.126  ["Password"] = "Kennwort";
  34.127 +["Phone"] = "Telefon";
  34.128  ["Policy"] = "Regelwerk";
  34.129  ["Population"] = "Grundgesamtheit";
  34.130 -["Potential weight"] = "Potentielles Gewicht";
  34.131 +["Profession"] = "Beruf";
  34.132  ["Publish"] = "Veröffentlichen";
  34.133  ["Published"] = "veröffentlicht";
  34.134  ["Published contacts"] = "Veröffentlichte Kontakte";
  34.135  ["Rank"] = "Rang";
  34.136 +["Real name"] = "Realname";
  34.137 +["Refresh support to current draft"] = "Unterstützung auf aktuellen Entwurf aktualisieren";
  34.138  ["Register new member"] = "Neues Mitglied registrieren";
  34.139  ["Remove"] = "Entfernen";
  34.140  ["Remove autoreject"] = "Auto-Ablehnen abschalten";
  34.141 @@ -142,9 +162,13 @@
  34.142  ["Show active members"] = "Zeige aktive Mitglieder";
  34.143  ["Show areas in use"] = "Zeige verwendete Themenbereiche";
  34.144  ["Show areas not in use"] = "Zeige nicht verwendente Themenbereiche";
  34.145 +["Show diff"] = "Änderungen anzeigen";
  34.146  ["Show locked members"] = "Zeige gesperrte Mitglieder";
  34.147  ["Software"] = false;
  34.148  ["State"] = "Zustand";
  34.149 +["Statement"] = false;
  34.150 +["Suggestion currently implemented"] = "Anregung zur Zeit umgesetzt";
  34.151 +["Suggestion currently not implemented"] = "Anregung zur Zeit nicht umgesetzt";
  34.152  ["Suggestion for initiative: '#{name}'"] = "Anregung für Initiative '#{name}'";
  34.153  ["Suggestions"] = "Anregungen";
  34.154  ["Support"] = "Unterstützung";
  34.155 @@ -152,8 +176,8 @@
  34.156  ["Support this initiative"] = "Diese Initiative unterstützen";
  34.157  ["Supporter"] = "Unterstützer";
  34.158  ["That's me!"] = "Das bin ich";
  34.159 +["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht";
  34.160  ["Trustee"] = "Bevollmächtigter";
  34.161 -["Truster"] = "Delegierender";
  34.162  ["Unknown author"] = "Unbekannter Autor";
  34.163  ["Upload avatar"] = "Avatar hochladen";
  34.164  ["Username"] = "Benutzername";
  34.165 @@ -162,6 +186,8 @@
  34.166  ["Vote now"] = "Jetzt abstimmen";
  34.167  ["Voting"] = "Abstimmung";
  34.168  ["Voting requests"] = "Abstimmanträge";
  34.169 +["Website"] = "Webseite";
  34.170 +["X"] = false;
  34.171  ["You are already not supporting this initiative"] = "Diese Initiative hat bereits keine Unterstützung von Dir";
  34.172  ["You are already supporting the latest draft"] = "Du unterstützt bereits den neuesten Entwurf";
  34.173  ["You are interested. [more]"] = "Du bist interessiert. [mehr]";
  34.174 @@ -176,6 +202,7 @@
  34.175  ["Your global delegation has been deleted."] = "Deine globale Delegation wurde gelöscht";
  34.176  ["Your global delegation has been updated."] = "Deine globale Delegation würde geändert";
  34.177  ["Your opinion has been updated"] = "Deine Meinung wurde aktualisiert";
  34.178 +["Your page has been updated"] = "Deine Seite wurde aktualisiert";
  34.179  ["Your password has been updated successfully"] = "Das Kennwort wurde erfolgreich geändert";
  34.180  ["Your suggestion has been added"] = "Deine Anregung wurde hinzufügt";
  34.181  ["Your support has been added to this initiative"] = "Deine Unterstützung wurde der Initiative hinzugefügt";
  34.182 @@ -183,11 +210,22 @@
  34.183  ["Your support has been updated to the latest draft"] = "Deine Unterstützung wurde auf den neuesten Entwurf aktualisiert";
  34.184  ["Your vote is delegated. [more]"] = "Deine Stimme ist delegiert. [mehr]";
  34.185  ["Z-A"] = false;
  34.186 +["admission_time"] = false;
  34.187 +["delete<br /><br />"] = false;
  34.188 +["discussion_time"] = false;
  34.189 +["email"] = false;
  34.190 +["initiative_quorum_den"] = false;
  34.191 +["initiative_quorum_num"] = false;
  34.192 +["issue_quorum_den"] = false;
  34.193 +["issue_quorum_num"] = false;
  34.194  ["must"] = "muss";
  34.195  ["must not"] = "darf nicht";
  34.196  ["neutral"] = "neutral";
  34.197 -["remove fulfilled"] = "erfüllt entfernen";
  34.198 -["set fulfilled"] = "erfüllt setzten";
  34.199 +["remove implemented"] = "entferne umgesetzt";
  34.200 +["set implented"] = "setze umgesetzt";
  34.201  ["should"] = "soll";
  34.202  ["should not"] = "soll nicht";
  34.203 +["verification_time"] = false;
  34.204 +["voting_time"] = false;
  34.205 +["xmpp"] = false;
  34.206  }
    35.1 --- a/model/area.lua	Wed Nov 18 12:00:00 2009 +0100
    35.2 +++ b/model/area.lua	Mon Nov 23 12:00:00 2009 +0100
    35.3 @@ -20,6 +20,15 @@
    35.4  }
    35.5  
    35.6  Area:add_reference{
    35.7 +  mode          = '1m',
    35.8 +  to            = "Delegation",
    35.9 +  this_key      = 'id',
   35.10 +  that_key      = 'area_id',
   35.11 +  ref           = 'delegations',
   35.12 +  back_ref      = 'area'
   35.13 +}
   35.14 +
   35.15 +Area:add_reference{
   35.16    mode                  = 'mm',
   35.17    to                    = "Member",
   35.18    this_key              = 'id',
    36.1 --- a/model/initiative.lua	Wed Nov 18 12:00:00 2009 +0100
    36.2 +++ b/model/initiative.lua	Mon Nov 23 12:00:00 2009 +0100
    36.3 @@ -91,12 +91,18 @@
    36.4    ref                   = 'supporting_members'
    36.5  }
    36.6  
    36.7 -function Initiative:search(search_string)
    36.8 +function Initiative:get_search_selector(search_string)
    36.9    return self:new_selector()
   36.10 -    :add_where{ '"initiative"."name" ILIKE ?', "%" .. search_string:gsub("%%", "") .. "%" }
   36.11 -    :exec()
   36.12 +    :add_field( {'"highlight"("initiative"."name", ?)', search_string }, "name_highlighted")
   36.13 +    :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string }
   36.14  end
   36.15  
   36.16 +function Member:get_search_selector(search_string)
   36.17 +  return self:new_selector()
   36.18 +    :add_where("active")
   36.19 +end
   36.20 +
   36.21 +
   36.22  function Initiative.object_get:current_draft()
   36.23    return Draft:new_selector()
   36.24      :add_where{ '"initiative_id" = ?', self.id }
    37.1 --- a/model/issue.lua	Wed Nov 18 12:00:00 2009 +0100
    37.2 +++ b/model/issue.lua	Mon Nov 23 12:00:00 2009 +0100
    37.3 @@ -67,6 +67,15 @@
    37.4  }
    37.5  
    37.6  Issue:add_reference{
    37.7 +  mode          = '1m',
    37.8 +  to            = "Delegation",
    37.9 +  this_key      = 'id',
   37.10 +  that_key      = 'issue_id',
   37.11 +  ref           = 'delegations',
   37.12 +  back_ref      = 'issue'
   37.13 +}
   37.14 +
   37.15 +Issue:add_reference{
   37.16    mode                  = 'mm',
   37.17    to                    = "Member",
   37.18    this_key              = 'id',
   37.19 @@ -78,32 +87,95 @@
   37.20  }
   37.21  
   37.22  function Issue:get_state_name_for_state(value)
   37.23 -  local state_name_table = {}
   37.24 +  local state_name_table = {
   37.25 +    new          = _"New",
   37.26 +    accepted     = _"Accepted",
   37.27 +    frozen       = _"Frozen",
   37.28 +    voting       = _"Voting",
   37.29 +    finished     = _"Finished",
   37.30 +    cancelled    = _"Cancelled"
   37.31 +  }
   37.32    return state_name_table[value] or value
   37.33  end
   37.34  
   37.35 -function Issue:search(search_string)
   37.36 +function Issue:get_search_selector(search_string)
   37.37    return self:new_selector()
   37.38      :join('"initiative"', nil, '"initiative"."issue_id" = "issue"."id"')
   37.39 -    :add_where{ '"initiative"."name" ILIKE ?', "%" .. search_string:gsub("%%", "") .. "%" }
   37.40 +    :add_where{ '"initiative"."text_search_data" @@ "text_search_query"(?)', search_string }
   37.41      :set_distinct()
   37.42 -    :exec()
   37.43  end
   37.44  
   37.45  function Issue.object_get:state()
   37.46    if self.accepted then
   37.47 -    if self.frozen then
   37.48 +    if self.fully_frozen then
   37.49 +      return "voting"
   37.50 +    elseif self.half_frozen then
   37.51        return "frozen"
   37.52      elseif self.closed then
   37.53 -      return "closed"
   37.54 +      if self.ranks_available then
   37.55 +        return "finished"
   37.56 +      else
   37.57 +        return "cancelled"
   37.58 +      end
   37.59      else
   37.60        return "accepted"
   37.61      end
   37.62    else
   37.63      if self.closed then
   37.64 -      return "closed"
   37.65 +      return "cancelled"
   37.66      else
   37.67        return "new"
   37.68      end
   37.69    end
   37.70 +end
   37.71 +
   37.72 +function Issue.object_get:state_name()
   37.73 +  return Issue:get_state_name_for_state(self.state)
   37.74 +end
   37.75 +
   37.76 +function Issue.object_get:state_time_left()
   37.77 +  local state = self.state
   37.78 +  local last_event_time
   37.79 +  local duration
   37.80 +  if state == "new" then
   37.81 +    last_event_time = self.created
   37.82 +    duration = self.policy.admission_time
   37.83 +  elseif state == "accepted" then
   37.84 +    last_event_time = self.accepted
   37.85 +    duration =  self.policy.discussion_time
   37.86 +  elseif state == "frozen" then
   37.87 +    last_event_time = self.half_frozen
   37.88 +    duration = self.policy.verification_time
   37.89 +  elseif state == "voting" then
   37.90 +    last_event_time = self.fully_frozen
   37.91 +    duration = self.policy.voting_time
   37.92 +  end
   37.93 +  return db:query{ "SELECT ?::timestamp + ?::interval - now() as time_left", last_event_time, duration }[1].time_left
   37.94 +end
   37.95 +
   37.96 +function Issue.object_get:next_states()
   37.97 +  local state = self.state
   37.98 +  local next_states
   37.99 +  if state == "new" then
  37.100 +    next_states = { "accepted", "cancelled" }
  37.101 +  elseif state == "accepted" then
  37.102 +    next_states = { "frozen" }
  37.103 +  elseif state == "frozen" then
  37.104 +    next_states = { "voting" }
  37.105 +  elseif state == "voting" then
  37.106 +    next_states = { "finished" }
  37.107 +  end
  37.108 +  return next_states
  37.109 +end
  37.110 +
  37.111 +function Issue.object_get:next_states_names()
  37.112 +  local next_states = self.next_states
  37.113 +  if not next_states then
  37.114 +    return
  37.115 +  end
  37.116 +  local state_names = {}
  37.117 +  for i, state in ipairs(self.next_states) do
  37.118 +    state_names[#state_names+1] = Issue:get_state_name_for_state(state)
  37.119 +  end
  37.120 +  return table.concat(state_names, ", ")
  37.121  end
  37.122 \ No newline at end of file
    38.1 --- a/model/member.lua	Wed Nov 18 12:00:00 2009 +0100
    38.2 +++ b/model/member.lua	Mon Nov 23 12:00:00 2009 +0100
    38.3 @@ -2,6 +2,15 @@
    38.4  Member.table = 'member'
    38.5  
    38.6  Member:add_reference{
    38.7 +  mode          = '11',
    38.8 +  to            = "MemberImage",
    38.9 +  this_key      = 'id',
   38.10 +  that_key      = 'member_id',
   38.11 +  ref           = 'image',
   38.12 +  back_ref      = 'member'
   38.13 +}
   38.14 +
   38.15 +Member:add_reference{
   38.16    mode          = '1m',
   38.17    to            = "Contact",
   38.18    this_key      = 'id',
   38.19 @@ -87,8 +96,7 @@
   38.20    this_key      = 'id',
   38.21    that_key      = 'member_id',
   38.22    ref           = 'supporters',
   38.23 -  back_ref      = 'member',
   38.24 -  default_order = '"id"'
   38.25 +  back_ref      = 'member'
   38.26  }
   38.27  
   38.28  Member:add_reference{
   38.29 @@ -248,9 +256,10 @@
   38.30    end
   38.31  end
   38.32  
   38.33 -function Member:search(search_string)
   38.34 +function Member:get_search_selector(search_string)
   38.35    return self:new_selector()
   38.36 -    :add_where{ '"member"."name" ILIKE ?', "%" .. search_string:gsub("%%", "") .. "%" }
   38.37 +    :add_field( {'"highlight"("member"."name", ?)', search_string }, "name_highlighted")
   38.38 +    :add_where{ '"member"."text_search_data" @@ "text_search_query"(?)', search_string }
   38.39      :add_where("active")
   38.40 -    :exec()
   38.41  end
   38.42 +
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/model/member_image.lua	Mon Nov 23 12:00:00 2009 +0100
    39.3 @@ -0,0 +1,12 @@
    39.4 +MemberImage = mondelefant.new_class()
    39.5 +MemberImage.table = "member_image"
    39.6 +MemberImage.primary_key = { "member_id", "image_type" }
    39.7 +
    39.8 +function MemberImage:by_pk(member_id, image_type, scaled)
    39.9 +  return self:new_selector()
   39.10 +    :add_where{ "member_id = ?",  member_id }
   39.11 +    :add_where{ "image_type = ?", image_type }
   39.12 +    :add_where{ "scaled = ?", scaled }
   39.13 +    :optional_object_mode()
   39.14 +    :exec()
   39.15 +end
    40.1 Binary file static/avatar.jpg has changed
    41.1 Binary file static/delegation_arrow.jpg has changed
    42.1 Binary file static/delegation_arrow_vertical.jpg has changed
    43.1 Binary file static/icons/16/new.png has changed
    44.1 --- a/static/style.css	Wed Nov 18 12:00:00 2009 +0100
    44.2 +++ b/static/style.css	Mon Nov 23 12:00:00 2009 +0100
    44.3 @@ -23,8 +23,17 @@
    44.4  }
    44.5  
    44.6  td, th {
    44.7 +  padding: 0.5ex 0.5em 0.5ex 0.5em;
    44.8 +}
    44.9 +
   44.10 +td {
   44.11    vertical-align: top;
   44.12 -  padding: 0.5ex 0.5em 0.5ex 0.5em;
   44.13 +}
   44.14 +
   44.15 +th {
   44.16 +  vertical-align: bottom;
   44.17 +  font-size: 75%;
   44.18 +  font-weight: bold;
   44.19  }
   44.20  
   44.21  a.active {
   44.22 @@ -40,6 +49,10 @@
   44.23    font-style: italic;
   44.24  }
   44.25  
   44.26 +a {
   44.27 +  vertical-align: middle;
   44.28 +}
   44.29 +
   44.30  /*************************************************************************
   44.31   * Notices, warnings and errors
   44.32   */
   44.33 @@ -195,6 +208,8 @@
   44.34  .avatar {
   44.35    float: left;
   44.36    margin-right: 0.5em;
   44.37 +  width: 48px;
   44.38 +  height: 48px;
   44.39  }
   44.40  
   44.41  .actions {
   44.42 @@ -224,6 +239,62 @@
   44.43  }
   44.44  
   44.45  /*************************************************************************
   44.46 + * vote info / delegation 
   44.47 + */
   44.48 +
   44.49 +.slot_interest,
   44.50 +.slot_support,
   44.51 +.slot_delegation {
   44.52 +  float: left;
   44.53 +  font-size: 75%;
   44.54 +  margin-right: 1em;
   44.55 +}
   44.56 +
   44.57 +.vote_info .head {
   44.58 +  line-height: 200%;
   44.59 +}
   44.60 +
   44.61 +.vote_info .close {
   44.62 +  background-color: #f44;
   44.63 +  float: right;
   44.64 +  padding: 1ex;
   44.65 +}
   44.66 +
   44.67 +.vote_info .content {
   44.68 +  display: none;
   44.69 +  position: absolute;
   44.70 +  z-index: 10;
   44.71 +  background-color: #fff;
   44.72 +  border: 2px solid #444;
   44.73 +  padding: 1em;
   44.74 +}
   44.75 +
   44.76 +.vote_info .delegation_arrow {
   44.77 +  margin-top: 1ex;
   44.78 +  margin-bottom: 1ex;
   44.79 +  vertical-align: middle;
   44.80 +}
   44.81 +
   44.82 +.vote_info .delegation_scope {
   44.83 +  display: inline;
   44.84 +}
   44.85 +
   44.86 +.vote_info .delegation_info {
   44.87 +}
   44.88 +
   44.89 +.vote_info .member_thumb {
   44.90 +  clear: left;
   44.91 +}
   44.92 +
   44.93 +.delegation .revoke {
   44.94 +  margin: 0.5ex;
   44.95 +}
   44.96 +
   44.97 +.delegation .revoke img {
   44.98 +  vertical-align: middle;
   44.99 +}
  44.100 +
  44.101 +/*************************************************************************
  44.102   * Main content
  44.103   */
  44.104  
  44.105 @@ -268,6 +339,28 @@
  44.106   * ui.order
  44.107   */
  44.108  
  44.109 +.ui_filter_head {
  44.110 +  color: #777;
  44.111 +  float: left;
  44.112 +  margin-bottom: 1ex;
  44.113 +  font-size: 75%;
  44.114 +}
  44.115 +
  44.116 +.ui_filter_head a {
  44.117 +  color: #777;
  44.118 +  padding: 0.5ex;
  44.119 +}
  44.120 +
  44.121 +.ui_filter_head a.active{
  44.122 +  color: #fff;
  44.123 +  background-color: #777;
  44.124 +  padding: 0.5ex;
  44.125 +}
  44.126 +
  44.127 +/*************************************************************************
  44.128 + * ui.order
  44.129 + */
  44.130 +
  44.131  .ui_order_head {
  44.132    color: #777;
  44.133    text-align: right;
  44.134 @@ -299,13 +392,13 @@
  44.135   */
  44.136  
  44.137  .bargraph {
  44.138 -  width: 50px;
  44.139 +  width: 101px;
  44.140  }
  44.141  
  44.142  .bargraph div {
  44.143    float: left;
  44.144    margin-top: 0.5ex;
  44.145 -  height: 1ex;
  44.146 +  height: 1.3ex;
  44.147  }
  44.148  
  44.149  /*************************************************************************
  44.150 @@ -320,8 +413,8 @@
  44.151  .vertical select {
  44.152    font-family: sans-serif;
  44.153    font-size: 100%;
  44.154 +  width: 50em;
  44.155    border: 1px solid #444;
  44.156 -  width: 40em;
  44.157    padding: 0.2ex 0.2em 0.2ex 0.2em;
  44.158    margin-bottom: 1ex;
  44.159  }
  44.160 @@ -498,62 +591,109 @@
  44.161   */
  44.162  
  44.163  .issues tr {
  44.164 -  border-bottom: 1px solid #444;
  44.165 +  border: 1px solid #ccc;
  44.166  }
  44.167  
  44.168  .issues tr tr {
  44.169 -  border-bottom: none;
  44.170 -}
  44.171 -
  44.172 -/*************************************************************************
  44.173 - * delegation 
  44.174 - */
  44.175 -
  44.176 -.infobox {
  44.177 -  float: right;
  44.178 -  margin-right: 1em;
  44.179 -}
  44.180 -
  44.181 -.slot_interest,
  44.182 -.slot_support,
  44.183 -.slot_delegation {
  44.184 -  border: 2px solid #444;
  44.185 -  width: 20em;
  44.186 -  font-size: 75%;
  44.187 -  padding: 0;
  44.188 -  margin-bottom: 0.5ex;
  44.189 -}
  44.190 -
  44.191 -.infobox .head {
  44.192 -  margin: 0.5ex;
  44.193 -}
  44.194 -
  44.195 -.infobox .content {
  44.196 -  display: none;
  44.197 -  position: absolute;
  44.198 -  z-index: 10;
  44.199 -  width: 20em;
  44.200 -  background-color: #fff;
  44.201 -  border: 2px solid #444;
  44.202 -}
  44.203 -
  44.204 -.infobox .text {
  44.205 -  margin: 0.5ex;
  44.206 -  margin-bottom: 1ex;
  44.207 -}
  44.208 -
  44.209 -.infobox .member {
  44.210 -  margin: 0.5ex;
  44.211 -  display: block;
  44.212 -  font-weight: bold;
  44.213 -  margin-bottom: 1ex;
  44.214 -}
  44.215 -
  44.216 -.delegation .revoke {
  44.217 -  margin: 0.5ex;
  44.218 +  border: none;
  44.219  }
  44.220  
  44.221  .lang_chooser {
  44.222    float: right;
  44.223    margin-right: 0.5em;
  44.224  }
  44.225 +
  44.226 +.delegation_list_entry {
  44.227 +  margin-right: 2em;
  44.228 +  margin-bottom: 2ex;
  44.229 +  float: left;
  44.230 +  clear: left;
  44.231 +}
  44.232 +
  44.233 +.delegation_list_entry .delegation_arrow {
  44.234 +  float: left;
  44.235 +}
  44.236 +
  44.237 +.delegation_list_entry .delegation_scope {
  44.238 +  float: left;
  44.239 +  width: 25em;
  44.240 +}
  44.241 +
  44.242 +.delegation_list_entry .delegation_scope a {
  44.243 +  display: block;
  44.244 +}
  44.245 +
  44.246 +.member_list .member_thumb {
  44.247 +  float: left;
  44.248 +  margin-right: 1em;
  44.249 +  margin-bottom: 2ex;
  44.250 +}
  44.251 +
  44.252 +.member_thumb {
  44.253 +  text-decoration: none;
  44.254 +  min-width: 150px;
  44.255 +  display: block;
  44.256 +  float: left;
  44.257 +  border: 1px solid #ccc;
  44.258 +}
  44.259 +
  44.260 +.member_thumb:hover {
  44.261 +  border: 1px solid #000;
  44.262 +}
  44.263 +
  44.264 +.member_thumb img {
  44.265 +  margin-right: 0.5em;
  44.266 +  vertical-align: bottom;
  44.267 +}
  44.268 +
  44.269 +.member_thumb div {
  44.270 +  display: inline;
  44.271 +  margin-right: 0.5em;
  44.272 +}
  44.273 +
  44.274 +.draft_content {
  44.275 +  background-color: #eee;
  44.276 +  border: 1px solid #ccc;
  44.277 +  padding: 1ex;
  44.278 +}
  44.279 +
  44.280 +.diff {
  44.281 +  background-color: #eee;
  44.282 +  border: 1px solid #ccc;
  44.283 +  padding: 1ex;
  44.284 +}
  44.285 +
  44.286 +.diff .added {
  44.287 +  background-color: #cfc;
  44.288 +}
  44.289 +
  44.290 +.diff .removed {
  44.291 +  background-color: #fcc;
  44.292 +}
  44.293 +
  44.294 +.slot_issue_info {
  44.295 +  background-color: #eee;
  44.296 +  border: 1px solid #ccc;
  44.297 +  float: right;
  44.298 +  padding: 0.5ex;
  44.299 +  line-height: 130%;
  44.300 +  margin-right: 1em;
  44.301 +}
  44.302 +
  44.303 +.issue_info label {
  44.304 +  float: left;
  44.305 +  width: 8em;
  44.306 +  text-transform: uppercase;
  44.307 +  font-size: 70%;
  44.308 +  color: #777;
  44.309 +  font-weight: bold;
  44.310 +  clear: left;
  44.311 +  text-align: right;
  44.312 +  margin-right: 0.7em;
  44.313 +}
  44.314 +
  44.315 +.draft_updated_info {
  44.316 +  border: 2px solid #faa;
  44.317 +  background-color: #fee;
  44.318 +  padding: 1ex;
  44.319 +}
  44.320 \ No newline at end of file

Impressum / About Us