liquid_feedback_frontend

changeset 558:18e8de7a2b6a

Show notifications on start page as ulli list with links instead of tabs
author bsw
date Tue Jun 19 21:20:46 2012 +0200 (2012-06-19)
parents fc01e21c3d41
children afd406d96044
files app/main/index/_index_member.lua app/main/index/_initiator_invites.lua app/main/index/_member_home.lua app/main/index/_notifications.lua app/main/index/_updated_drafts.lua app/main/index/broken_delegations.lua app/main/index/email_unconfirmed.lua app/main/index/index.lua app/main/index/initiator_invites.lua app/main/index/notification_level.lua app/main/index/updated_drafts.lua app/main/initiative/_list.lua app/main/member/_area_list.lua app/main/member/_email_unconfirmed.lua app/main/member/_notify_level_not_set.lua app/main/member/_show.lua model/delegation.lua model/initiative.lua model/initiator.lua static/style.css
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/app/main/index/_index_member.lua	Tue Jun 19 21:20:46 2012 +0200
     1.3 @@ -0,0 +1,68 @@
     1.4 +
     1.5 +local tabs = {
     1.6 +  module = "index",
     1.7 +  view = "index"
     1.8 +}
     1.9 +
    1.10 +local areas_selector = app.session.member:get_reference_selector("areas")
    1.11 +tabs[#tabs+1] = {
    1.12 +  name = "areas",
    1.13 +  label = _"Home",
    1.14 +  icon = { static = "icons/16/package.png" },
    1.15 +  module = "index",
    1.16 +  view = "_member_home",
    1.17 +  params = { areas_selector = areas_selector, member = app.session.member, for_member = true },
    1.18 +}
    1.19 +
    1.20 +tabs[#tabs+1] = {
    1.21 +  name = "timeline",
    1.22 +  label = _"Latest events",
    1.23 +  module = "member",
    1.24 +  view = "_event_list",
    1.25 +  params = { }
    1.26 +}
    1.27 +
    1.28 +
    1.29 +tabs[#tabs+1] = {
    1.30 +  name = "open",
    1.31 +  label = _"Open issues",
    1.32 +  module = "issue",
    1.33 +  view = "_list",
    1.34 +  link_params = { 
    1.35 +    filter_interest = not show_as_homepage and "issue" or nil,
    1.36 +  },
    1.37 +  params = {
    1.38 +    for_state = "open",
    1.39 +    issues_selector = Issue:new_selector()
    1.40 +      :add_where("issue.closed ISNULL")
    1.41 +      :add_order_by("coalesce(issue.fully_frozen + issue.voting_time, issue.half_frozen + issue.verification_time, issue.accepted + issue.discussion_time, issue.created + issue.admission_time) - now()")
    1.42 +  }
    1.43 +}
    1.44 +
    1.45 +tabs[#tabs+1] = {
    1.46 +  name = "closed",
    1.47 +  label = _"Closed issues",
    1.48 +  module = "issue",
    1.49 +  view = "_list",
    1.50 +  link_params = { 
    1.51 +    filter_interest = not show_as_homepage and "issue" or nil,
    1.52 +  },
    1.53 +  params = {
    1.54 +    for_state = "closed",
    1.55 +    issues_selector = Issue:new_selector()
    1.56 +      :add_where("issue.closed NOTNULL")
    1.57 +      :add_order_by("issue.closed DESC")
    1.58 +
    1.59 +  }
    1.60 +}
    1.61 +
    1.62 +tabs[#tabs+1] = {
    1.63 +  name = "members",
    1.64 +  label = _"Members",
    1.65 +  module = 'member',
    1.66 +  view   = '_list',
    1.67 +  params = { members_selector = Member:new_selector() }
    1.68 +}
    1.69 +
    1.70 +
    1.71 +ui.tabs(tabs)
     2.1 --- a/app/main/index/_initiator_invites.lua	Tue Jun 19 18:45:45 2012 +0200
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,16 +0,0 @@
     2.4 -local initiatives_selector = param.get("initiatives_selector", "table")
     2.5 -
     2.6 -if initiatives_selector:count() > 0 then
     2.7 -  ui.container{
     2.8 -    attr = { style = "font-weight: bold;" },
     2.9 -    content = _"Initiatives that invited you to become initiator:"
    2.10 -  }
    2.11 -
    2.12 -  execute.view{
    2.13 -    module = "initiative",
    2.14 -    view = "_list",
    2.15 -    params = { initiatives_selector = initiatives_selector }
    2.16 -  }
    2.17 -else
    2.18 -  ui.field.text{ value = _"You are currently not invited to any initiative." }
    2.19 -end
    2.20 \ No newline at end of file
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/app/main/index/_member_home.lua	Tue Jun 19 21:20:46 2012 +0200
     3.3 @@ -0,0 +1,113 @@
     3.4 +local member = param.get("member", "table")
     3.5 +local for_member = param.get("for_member", atom.boolean)
     3.6 +local filter_unit = param.get_all_cgi()["filter_unit"] or "personal"
     3.7 +
     3.8 +
     3.9 +execute.view{
    3.10 +  module = "index", view = "_notifications"
    3.11 +}
    3.12 +
    3.13 +    
    3.14 +ui.container{ attr = { class = "ui_filter_head" }, content = function()
    3.15 +
    3.16 +  ui.link{
    3.17 +    attr = { class = filter_unit == "personal" and "ui_tabs_link active" or nil },
    3.18 +    text = _"My units and areas",
    3.19 +    module = "index", view = "index", params = { filter_unit = "personal" }
    3.20 +  }
    3.21 +  
    3.22 +  slot.put(" ")
    3.23 +
    3.24 +  ui.link{
    3.25 +    attr = { class = filter_unit == "global" and "active" or nil },
    3.26 +    text = _"All units",
    3.27 +    module = "index", view = "index", params = { filter_unit = "global" }
    3.28 +  }
    3.29 +end }
    3.30 +
    3.31 +slot.put("<br />")
    3.32 +
    3.33 +
    3.34 +if filter_unit == "global" then
    3.35 +  execute.view{ module = "unit", view = "_list" }
    3.36 +  return
    3.37 +end
    3.38 +
    3.39 +local units = Unit:new_selector():exec()
    3.40 +
    3.41 +for i, unit in ipairs(units) do
    3.42 +  local trustee_member = Member:new_selector()
    3.43 +    :join("delegation", nil, { "delegation.scope = 'unit' AND delegation.unit_id = ? AND delegation.trustee_id = member.id AND delegation.truster_id = ?", unit.id, member.id })
    3.44 +    :optional_object_mode()
    3.45 +    :exec()
    3.46 +  
    3.47 +  local areas_selector = Area:new_selector()
    3.48 +    :join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id })
    3.49 +    :add_where{ "area.unit_id = ?", unit.id }
    3.50 +    :add_where{ "area.active" }
    3.51 +    :add_order_by("area.member_weight DESC")
    3.52 +  
    3.53 +  local area_count = areas_selector:count()
    3.54 +  
    3.55 +  ui.container{ attr = { class = "member_area_list" }, content = function()
    3.56 +    ui.container{ attr = { class = "xunit_head" }, content = function()
    3.57 +      ui.link{
    3.58 +        text = unit.name,
    3.59 +        module = "unit", view = "show", id = unit.id
    3.60 +      }
    3.61 +
    3.62 +      if trustee_member then
    3.63 +        local text = _("Unit delegated to '#{name}'", { name = trustee_member.name })
    3.64 +        ui.image{
    3.65 +          attr = { class = "delegation_arrow", alt = text, title = text },
    3.66 +          static = "delegation_arrow_24_horizontal.png"
    3.67 +        }
    3.68 +        execute.view{
    3.69 +          module = "member_image",
    3.70 +          view = "_show",
    3.71 +          params = {
    3.72 +            member = trustee_member,
    3.73 +            image_type = "avatar",
    3.74 +            show_dummy = true,
    3.75 +            class = "micro_avatar",
    3.76 +            popup_text = text
    3.77 +          }
    3.78 +        }
    3.79 +      end
    3.80 +    end }
    3.81 +
    3.82 +    if area_count > 0 then
    3.83 +      execute.view{
    3.84 +        module = "area", view = "_list",
    3.85 +        params = { areas_selector = areas_selector, hide_membership = true }
    3.86 +      }
    3.87 +    elseif member:has_voting_right_for_unit_id(unit.id) then
    3.88 +      if for_member then
    3.89 +        ui.container{ attr = { class = "voting_priv_info" }, content = _"This member has voting privileges for this unit, but you ist not member of any of its areas." }
    3.90 +      else
    3.91 +        ui.container{ attr = { class = "voting_priv_info" }, content = _"You have voting privileges for this unit, but you are not member of any of its areas." }
    3.92 +      end
    3.93 +    end
    3.94 +    local max_area_count = Area:new_selector()
    3.95 +      :add_where{ "area.unit_id = ?", unit.id }
    3.96 +      :add_where{ "area.active" }
    3.97 +      :count()
    3.98 +    local more_area_count = max_area_count - area_count
    3.99 +    local delegated_count = Area:new_selector()
   3.100 +      :add_where{ "area.unit_id = ?", unit.id }
   3.101 +      :add_where{ "area.active" }
   3.102 +      :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id } )
   3.103 +      :add_where{ "membership.member_id ISNULL" }
   3.104 +      :join("delegation", nil, { "delegation.area_id = area.id AND delegation.truster_id = ?", member.id } )
   3.105 +      :count()
   3.106 +    if more_area_count > 0 then
   3.107 +      slot.put("<br />")
   3.108 +      ui.container{ attr = { class = "more_areas" }, content = function()
   3.109 +        ui.link{ content = _("#{count} more areas in this unit, #{delegated_count} of them are delegated", { count = more_area_count, delegated_count = delegated_count }), module = "unit", view = "show", id = unit.id }
   3.110 +      end }
   3.111 +    end
   3.112 +  end }
   3.113 +
   3.114 +end
   3.115 +
   3.116 +
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/app/main/index/_notifications.lua	Tue Jun 19 21:20:46 2012 +0200
     4.3 @@ -0,0 +1,79 @@
     4.4 +local notification_links = {}
     4.5 +
     4.6 +if app.session.member.notify_email_unconfirmed then
     4.7 +  notification_links[#notification_links+1] = {
     4.8 +    module = "index", view = "email_unconfirmed",
     4.9 +    text = _"Please confirm your email address"
    4.10 +  }
    4.11 +end
    4.12 +
    4.13 +if app.session.member.notify_level == nil then
    4.14 +  notification_links[#notification_links+1] = {
    4.15 +    module = "member", view = "settings_notification",
    4.16 +    text = _"Please select your preferred notification level"
    4.17 +  }
    4.18 +end
    4.19 +
    4.20 +local broken_delegations_count = Delegation:selector_for_broken(app.session.member_id):count()
    4.21 +
    4.22 +if broken_delegations_count > 0 then
    4.23 +  notification_links[#notification_links+1] = {
    4.24 +    module = "index", view = "broken_delegations",
    4.25 +    text = _("#{count} of your outgoing delegation(s) are broken", { count = broken_delegations_count })
    4.26 +  }
    4.27 +  
    4.28 +end
    4.29 +
    4.30 +local selector = Issue:new_selector()
    4.31 +  :join("area", nil, "area.id = issue.area_id")
    4.32 +  :join("privilege", nil, { "privilege.unit_id = area.unit_id AND privilege.member_id = ? AND privilege.voting_right", app.session.member_id })
    4.33 +  :left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id })
    4.34 +  :left_join("interest", nil, { "interest.issue_id = issue.id AND interest.member_id = ?", app.session.member.id })
    4.35 +  :add_where{ "direct_voter.member_id ISNULL" }
    4.36 +  :add_where{ "interest.member_id NOTNULL" }
    4.37 +  :add_where{ "issue.fully_frozen NOTNULL" }
    4.38 +  :add_where{ "issue.closed ISNULL" }
    4.39 +  :add_order_by{ "issue.fully_frozen + issue.voting_time ASC" }
    4.40 +  
    4.41 +local issues_to_vote_count = selector:count()
    4.42 +if issues_to_vote_count > 0 then
    4.43 +    notification_links[#notification_links+1] = {
    4.44 +    module = "index", view = "index",
    4.45 +    params = {
    4.46 +      tab = "open", filter = "frozen", filter_interest = "issue", filter_voting = "not_voted"
    4.47 +    },
    4.48 +    text = _("You have not voted #{count} issue(s) you was interested in", { count = issues_to_vote_count })
    4.49 +  }
    4.50 +end
    4.51 +
    4.52 +local initiator_invites_count = Initiator:selector_for_invites(app.session.member_id):count()
    4.53 +  
    4.54 +if initiator_invites_count > 0 then
    4.55 +  notification_links[#notification_links+1] = {
    4.56 +    module = "index", view = "initiator_invites",
    4.57 +    text = _("You are invited to #{count} initiative(s)", { count = initiator_invites_count })
    4.58 +  }
    4.59 +end
    4.60 +
    4.61 +updated_drafts_count = Initiative:selector_for_updated_drafts(app.session.member_id):count()
    4.62 +
    4.63 +if updated_drafts_count > 0 then
    4.64 +  notification_links[#notification_links+1] = {
    4.65 +    module = "index", view = "updated_drafts",
    4.66 +    text = _("New drafts for #{count} initiative(s) you are supporting", { count = updated_drafts_count })
    4.67 +  }
    4.68 +end
    4.69 +
    4.70 +if #notification_links > 0 then
    4.71 +  ui.container{ attr = { class = "notifications" }, content = function()
    4.72 +    ui.tag{ tag = "ul", attr = { class = "notifications" }, content = function()
    4.73 +      for i, notification_link in ipairs(notification_links) do
    4.74 +        ui.tag{ tag = "li", content = function()
    4.75 +          ui.link(notification_link)
    4.76 +        end }
    4.77 +      end
    4.78 +    end }
    4.79 +  end }
    4.80 +end
    4.81 +
    4.82 +
     5.1 --- a/app/main/index/_updated_drafts.lua	Tue Jun 19 18:45:45 2012 +0200
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,15 +0,0 @@
     5.4 -local initiatives_selector = param.get("initiatives_selector", "table")
     5.5 -if initiatives_selector:count() > 0 then
     5.6 -  ui.container{
     5.7 -    attr = { class = "heading" },
     5.8 -    content = _"Open initiatives you are supporting which has been updated their draft:"
     5.9 -  }
    5.10 -  
    5.11 -  slot.put("<br />")
    5.12 -
    5.13 -  execute.view{
    5.14 -    module = "initiative",
    5.15 -    view = "_list",
    5.16 -    params = { initiatives_selector = initiatives_selector }
    5.17 -  }
    5.18 -end
    5.19 \ No newline at end of file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/app/main/index/broken_delegations.lua	Tue Jun 19 21:20:46 2012 +0200
     6.3 @@ -0,0 +1,8 @@
     6.4 +execute.view{
     6.5 +  module = "delegation",
     6.6 +  view = "_list",
     6.7 +  params = {
     6.8 +    delegations_selector = Delegation:selector_for_broken(app.session.member_id),
     6.9 +    outgoing = true
    6.10 +  }
    6.11 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/app/main/index/email_unconfirmed.lua	Tue Jun 19 21:20:46 2012 +0200
     7.3 @@ -0,0 +1,51 @@
     7.4 +if app.session.member.notify_email_unconfirmed then
     7.5 +
     7.6 +  local current = Member:new_selector()
     7.7 +    :add_where{ "id = ?", app.session.member_id }
     7.8 +    :add_where("notify_email_unconfirmed NOTNULL")
     7.9 +    :add_where("notify_email_secret_expiry > now()")
    7.10 +    :optional_object_mode()
    7.11 +    :exec()
    7.12 +
    7.13 +  slot.select("title", function()
    7.14 +    ui.tag{ content =  _"Notification address unconfirmed" }
    7.15 +  end )
    7.16 +
    7.17 +  if current then
    7.18 +    ui.tag{
    7.19 +      tag = "div",
    7.20 +      content = _("You didn't confirm your email address '#{email}'. You have received an email with an activation link.", { email = app.session.member.notify_email_unconfirmed })
    7.21 +    }
    7.22 +  else
    7.23 +    ui.tag{
    7.24 +      tag = "div",
    7.25 +      content = _("You didn't confirm your email address '#{email}' within 7 days.", { email = app.session.member.notify_email_unconfirmed })
    7.26 +    }
    7.27 +  end
    7.28 +  slot.put("<br />")
    7.29 +
    7.30 +  ui.link{
    7.31 +    text = _"Change email address",
    7.32 +    module = "member",
    7.33 +    view = "settings_email",
    7.34 +  }
    7.35 +  slot.put("<br />")
    7.36 +  slot.put("<br />")
    7.37 +
    7.38 +  ui.link{
    7.39 +    text = _("Resend activation email to '#{email}'", { email = app.session.member.notify_email_unconfirmed }),
    7.40 +    module = "member",
    7.41 +    action = "update_email",
    7.42 +    params = {
    7.43 +      resend = true
    7.44 +    },
    7.45 +    routing = {
    7.46 +      default = {
    7.47 +        mode = "redirect",
    7.48 +        module = "index",
    7.49 +        view = "index"
    7.50 +      }
    7.51 +    }
    7.52 +  }
    7.53 +
    7.54 +end
     8.1 --- a/app/main/index/index.lua	Tue Jun 19 18:45:45 2012 +0200
     8.2 +++ b/app/main/index/index.lua	Tue Jun 19 21:20:46 2012 +0200
     8.3 @@ -2,12 +2,7 @@
     8.4    util.help("index.index", _"Home")
     8.5  
     8.6    execute.view{
     8.7 -    module = "member",
     8.8 -    view = "_show",
     8.9 -    params = {
    8.10 -      member = app.session.member,
    8.11 -      show_as_homepage = true
    8.12 -    }
    8.13 +    module = "index", view = "_index_member"
    8.14    }
    8.15  
    8.16  elseif config.public_access then
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/app/main/index/initiator_invites.lua	Tue Jun 19 21:20:46 2012 +0200
     9.3 @@ -0,0 +1,18 @@
     9.4 +
     9.5 +
     9.6 +local initiatives_selector = Initiator:selector_for_invites(app.session.member_id)
     9.7 +
     9.8 +if initiatives_selector:count() > 0 then
     9.9 +  ui.container{
    9.10 +    attr = { style = "font-weight: bold;" },
    9.11 +    content = _"Initiatives that invited you to become initiator:"
    9.12 +  }
    9.13 +
    9.14 +  execute.view{
    9.15 +    module = "initiative",
    9.16 +    view = "_list",
    9.17 +    params = { initiatives_selector = initiatives_selector }
    9.18 +  }
    9.19 +else
    9.20 +  ui.field.text{ value = _"You are currently not invited to any initiative." }
    9.21 +end
    9.22 \ No newline at end of file
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/app/main/index/notification_level.lua	Tue Jun 19 21:20:46 2012 +0200
    10.3 @@ -0,0 +1,19 @@
    10.4 +ui.heading{ level = 2, content = _"Notification level not set yet" }
    10.5 +
    10.6 +slot.put("<br />")
    10.7 +
    10.8 +ui.tag{
    10.9 +  tag = "div",
   10.10 +  content = _"You didn't set the level of notifications you like to receive"
   10.11 +}
   10.12 +
   10.13 +slot.put("<br />")
   10.14 +
   10.15 +ui.link{
   10.16 +  text = _"Configure notifications now",
   10.17 +  module = "member",
   10.18 +  view = "settings_notification",
   10.19 +}
   10.20 +slot.put("<br />")
   10.21 +slot.put("<br />")
   10.22 +
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/app/main/index/updated_drafts.lua	Tue Jun 19 21:20:46 2012 +0200
    11.3 @@ -0,0 +1,14 @@
    11.4 +local initiatives_selector = Initiative:selector_for_updated_drafts(app.session.member_id)
    11.5 +
    11.6 +ui.container{
    11.7 +  attr = { class = "heading" },
    11.8 +  content = _"Open initiatives you are supporting which has been updated their draft:"
    11.9 +}
   11.10 +
   11.11 +slot.put("<br />")
   11.12 +
   11.13 +execute.view{
   11.14 +  module = "initiative",
   11.15 +  view = "_list",
   11.16 +  params = { initiatives_selector = initiatives_selector }
   11.17 +}
    12.1 --- a/app/main/initiative/_list.lua	Tue Jun 19 18:45:45 2012 +0200
    12.2 +++ b/app/main/initiative/_list.lua	Tue Jun 19 21:20:46 2012 +0200
    12.3 @@ -1,6 +1,13 @@
    12.4  local issue = param.get("issue", "table")
    12.5 +local initiatives_selector = param.get("initiatives_selector", "table")
    12.6  
    12.7 -local initiatives = issue.initiatives
    12.8 +local initiatives
    12.9 +if issue then
   12.10 +  initiatives = issue.initiatives
   12.11 +else
   12.12 +  initiatives = initiatives_selector:exec()
   12.13 +  initiatives:load_everything_for_member_id(app.session.member_id)
   12.14 +end
   12.15  
   12.16  local highlight_initiative = param.get("highlight_initiative", "table")
   12.17  
   12.18 @@ -21,8 +28,6 @@
   12.19    end
   12.20  end
   12.21  
   12.22 -local issue = param.get("issue", "table")
   12.23 -
   12.24  local name = "initiative_list"
   12.25  if issue then
   12.26    name = "issue_" .. tostring(issue.id) ..  "_initiative_list"
    13.1 --- a/app/main/member/_area_list.lua	Tue Jun 19 18:45:45 2012 +0200
    13.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.3 @@ -1,107 +0,0 @@
    13.4 -local member = param.get("member", "table")
    13.5 -local for_member = param.get("for_member", atom.boolean)
    13.6 -local filter_unit = param.get_all_cgi()["filter_unit"] or "personal"
    13.7 -
    13.8 -ui.container{ attr = { class = "ui_filter_head" }, content = function()
    13.9 -
   13.10 -  ui.link{
   13.11 -    attr = { class = filter_unit == "personal" and "ui_tabs_link active" or nil },
   13.12 -    text = _"With voting right",
   13.13 -    module = "index", view = "index", params = { filter_unit = "personal" }
   13.14 -  }
   13.15 -  
   13.16 -  slot.put(" ")
   13.17 -
   13.18 -  ui.link{
   13.19 -    attr = { class = filter_unit == "global" and "active" or nil },
   13.20 -    text = _"All units",
   13.21 -    module = "index", view = "index", params = { filter_unit = "global" }
   13.22 -  }
   13.23 -end }
   13.24 -
   13.25 -slot.put("<br />")
   13.26 -
   13.27 -
   13.28 -if filter_unit == "global" then
   13.29 -  execute.view{ module = "unit", view = "_list" }
   13.30 -  return
   13.31 -end
   13.32 -
   13.33 -local units = Unit:new_selector():exec()
   13.34 -
   13.35 -for i, unit in ipairs(units) do
   13.36 -  local trustee_member = Member:new_selector()
   13.37 -    :join("delegation", nil, { "delegation.scope = 'unit' AND delegation.unit_id = ? AND delegation.trustee_id = member.id AND delegation.truster_id = ?", unit.id, member.id })
   13.38 -    :optional_object_mode()
   13.39 -    :exec()
   13.40 -  
   13.41 -  local areas_selector = Area:new_selector()
   13.42 -    :join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id })
   13.43 -    :add_where{ "area.unit_id = ?", unit.id }
   13.44 -    :add_where{ "area.active" }
   13.45 -    :add_order_by("area.member_weight DESC")
   13.46 -  
   13.47 -  local area_count = areas_selector:count()
   13.48 -  
   13.49 -  ui.container{ attr = { class = "member_area_list" }, content = function()
   13.50 -    ui.container{ attr = { class = "xunit_head" }, content = function()
   13.51 -      ui.link{
   13.52 -        text = unit.name,
   13.53 -        module = "unit", view = "show", id = unit.id
   13.54 -      }
   13.55 -
   13.56 -      if trustee_member then
   13.57 -        local text = _("Unit delegated to '#{name}'", { name = trustee_member.name })
   13.58 -        ui.image{
   13.59 -          attr = { class = "delegation_arrow", alt = text, title = text },
   13.60 -          static = "delegation_arrow_24_horizontal.png"
   13.61 -        }
   13.62 -        execute.view{
   13.63 -          module = "member_image",
   13.64 -          view = "_show",
   13.65 -          params = {
   13.66 -            member = trustee_member,
   13.67 -            image_type = "avatar",
   13.68 -            show_dummy = true,
   13.69 -            class = "micro_avatar",
   13.70 -            popup_text = text
   13.71 -          }
   13.72 -        }
   13.73 -      end
   13.74 -    end }
   13.75 -
   13.76 -    if area_count > 0 then
   13.77 -      execute.view{
   13.78 -        module = "area", view = "_list",
   13.79 -        params = { areas_selector = areas_selector, hide_membership = true }
   13.80 -      }
   13.81 -    elseif member:has_voting_right_for_unit_id(unit.id) then
   13.82 -      if for_member then
   13.83 -        ui.container{ attr = { class = "voting_priv_info" }, content = _"This member has voting privileges for this unit, but you ist not member of any of its areas." }
   13.84 -      else
   13.85 -        ui.container{ attr = { class = "voting_priv_info" }, content = _"You have voting privileges for this unit, but you are not member of any of its areas." }
   13.86 -      end
   13.87 -    end
   13.88 -    local max_area_count = Area:new_selector()
   13.89 -      :add_where{ "area.unit_id = ?", unit.id }
   13.90 -      :add_where{ "area.active" }
   13.91 -      :count()
   13.92 -    local more_area_count = max_area_count - area_count
   13.93 -    local delegated_count = Area:new_selector()
   13.94 -      :add_where{ "area.unit_id = ?", unit.id }
   13.95 -      :add_where{ "area.active" }
   13.96 -      :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id } )
   13.97 -      :add_where{ "membership.member_id ISNULL" }
   13.98 -      :join("delegation", nil, { "delegation.area_id = area.id AND delegation.truster_id = ?", member.id } )
   13.99 -      :count()
  13.100 -    if more_area_count > 0 then
  13.101 -      slot.put("<br />")
  13.102 -      ui.container{ attr = { class = "more_areas" }, content = function()
  13.103 -        ui.link{ content = _("#{count} more areas in this unit, #{delegated_count} of them are delegated", { count = more_area_count, delegated_count = delegated_count }), module = "unit", view = "show", id = unit.id }
  13.104 -      end }
  13.105 -    end
  13.106 -  end }
  13.107 -
  13.108 -end
  13.109 -
  13.110 -
    14.1 --- a/app/main/member/_email_unconfirmed.lua	Tue Jun 19 18:45:45 2012 +0200
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,49 +0,0 @@
    14.4 -if app.session.member.notify_email_unconfirmed then
    14.5 -
    14.6 -  local current = Member:new_selector()
    14.7 -    :add_where{ "id = ?", app.session.member_id }
    14.8 -    :add_where("notify_email_unconfirmed NOTNULL")
    14.9 -    :add_where("notify_email_secret_expiry > now()")
   14.10 -    :optional_object_mode()
   14.11 -    :exec()
   14.12 -
   14.13 -  ui.heading{ level = 2, content = _"Notification address unconfirmed" }
   14.14 -
   14.15 -  if current then
   14.16 -    ui.tag{
   14.17 -      tag = "div",
   14.18 -      content = _("You didn't confirm your email address '#{email}'. You have received an email with an activation link.", { email = app.session.member.notify_email_unconfirmed })
   14.19 -    }
   14.20 -  else
   14.21 -    ui.tag{
   14.22 -      tag = "div",
   14.23 -      content = _("You didn't confirm your email address '#{email}' within 7 days.", { email = app.session.member.notify_email_unconfirmed })
   14.24 -    }
   14.25 -  end
   14.26 -  slot.put("<br />")
   14.27 -
   14.28 -  ui.link{
   14.29 -    text = _"Change email address",
   14.30 -    module = "member",
   14.31 -    view = "settings_email",
   14.32 -  }
   14.33 -  slot.put("<br />")
   14.34 -  slot.put("<br />")
   14.35 -
   14.36 -  ui.link{
   14.37 -    text = _("Resend activation email to '#{email}'", { email = app.session.member.notify_email_unconfirmed }),
   14.38 -    module = "member",
   14.39 -    action = "update_email",
   14.40 -    params = {
   14.41 -      resend = true
   14.42 -    },
   14.43 -    routing = {
   14.44 -      default = {
   14.45 -        mode = "redirect",
   14.46 -        module = "index",
   14.47 -        view = "index"
   14.48 -      }
   14.49 -    }
   14.50 -  }
   14.51 -
   14.52 -end
    15.1 --- a/app/main/member/_notify_level_not_set.lua	Tue Jun 19 18:45:45 2012 +0200
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,19 +0,0 @@
    15.4 -ui.heading{ level = 2, content = _"Notification level not set yet" }
    15.5 -
    15.6 -slot.put("<br />")
    15.7 -
    15.8 -ui.tag{
    15.9 -  tag = "div",
   15.10 -  content = _"You didn't set the level of notifications you like to receive"
   15.11 -}
   15.12 -
   15.13 -slot.put("<br />")
   15.14 -
   15.15 -ui.link{
   15.16 -  text = _"Configure notifications now",
   15.17 -  module = "member",
   15.18 -  view = "settings_notification",
   15.19 -}
   15.20 -slot.put("<br />")
   15.21 -slot.put("<br />")
   15.22 -
    16.1 --- a/app/main/member/_show.lua	Tue Jun 19 18:45:45 2012 +0200
    16.2 +++ b/app/main/member/_show.lua	Tue Jun 19 21:20:46 2012 +0200
    16.3 @@ -1,136 +1,21 @@
    16.4 -local show_as_homepage = param.get("show_as_homepage", atom.boolean)
    16.5 -
    16.6  local member = param.get("member", "table")
    16.7  
    16.8  local tabs = {
    16.9    module = "member",
   16.10    view = "show_tab",
   16.11    static_params = {
   16.12 -    member_id = member.id,
   16.13 -    show_as_homepage = show_as_homepage
   16.14 +    member_id = member.id
   16.15    }
   16.16  }
   16.17  
   16.18 -if show_as_homepage and app.session.member_id == member.id then
   16.19 -
   16.20 -  if app.session.member.notify_email_unconfirmed then
   16.21 -    tabs[#tabs+1] = {
   16.22 -      class = "yellow",
   16.23 -      name = "email_unconfirmed",
   16.24 -      label = _"Email unconfirmed",
   16.25 -      module = "member",
   16.26 -      view = "_email_unconfirmed",
   16.27 -      params = {}
   16.28 -    }
   16.29 -  end
   16.30 -
   16.31 -  if app.session.member.notify_level == nil then
   16.32 -    tabs[#tabs+1] = {
   16.33 -      class = "yellow",
   16.34 -      name = "notify_level_not_set",
   16.35 -      label = _"Notifications",
   16.36 -      module = "member",
   16.37 -      view = "_notify_level_not_set"
   16.38 -    }
   16.39 -  end
   16.40 -  
   16.41 -  local broken_delegations = Delegation:new_selector()
   16.42 -    :join("issue", nil, "issue.id = delegation.issue_id AND issue.closed ISNULL")
   16.43 -    :join("member", nil, "delegation.trustee_id = member.id")
   16.44 -    :add_where{"delegation.truster_id = ?", member.id}
   16.45 -    :add_where{"member.active = 'f' OR (member.last_activity IS NULL OR age(member.last_activity) > ?::interval)", config.delegation_warning_time }
   16.46 -
   16.47 -  if broken_delegations:count() > 0 then
   16.48 -    tabs[#tabs+1] = {
   16.49 -      class = "red",
   16.50 -      name = "broken_delegations",
   16.51 -      label = _"Delegation problems" .. " (" .. tostring(broken_delegations:count()) .. ")",
   16.52 -      icon = { static = "icons/16/table_go.png" },
   16.53 -      module = "delegation",
   16.54 -      view = "_list",
   16.55 -      params = { delegations_selector = broken_delegations, outgoing = true },
   16.56 -    }
   16.57 -  end
   16.58 -
   16.59 -  local selector = Issue:new_selector()
   16.60 -    :join("area", nil, "area.id = issue.area_id")
   16.61 -    :join("privilege", nil, { "privilege.unit_id = area.unit_id AND privilege.member_id = ? AND privilege.voting_right", app.session.member_id })
   16.62 -    :left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id })
   16.63 -    :left_join("interest", nil, { "interest.issue_id = issue.id AND interest.member_id = ?", app.session.member.id })
   16.64 -    :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ? ", app.session.member.id })
   16.65 -    :add_where{ "direct_voter.member_id ISNULL" }
   16.66 -    :add_where{ "interest.member_id NOTNULL OR membership.member_id NOTNULL" }
   16.67 -    :add_where{ "issue.fully_frozen NOTNULL" }
   16.68 -    :add_where{ "issue.closed ISNULL" }
   16.69 -    :add_order_by{ "issue.fully_frozen + issue.voting_time ASC" }
   16.70 -    
   16.71 -  local count = selector:count()
   16.72 -  if count > 0 then
   16.73 -    tabs[#tabs+1] = {
   16.74 -      class = "yellow",
   16.75 -      name = "not_voted_issues",
   16.76 -      label = _"Now in voting" .. " (" .. tostring(count) .. ")",
   16.77 -      icon = { static = "icons/16/email_open.png" },
   16.78 -      module = "issue",
   16.79 -      view = "_list",
   16.80 -      params = {
   16.81 -        issues_selector = selector,
   16.82 -        no_filter = true
   16.83 -      }
   16.84 -    }
   16.85 -  end
   16.86 -
   16.87 -  local initiator_invites_selector = Initiative:new_selector()
   16.88 -    :join("issue", "_issue_state", "_issue_state.id = initiative.issue_id")
   16.89 -    :join("initiator", nil, { "initiator.initiative_id = initiative.id AND initiator.member_id = ? AND initiator.accepted ISNULL", app.session.member.id })
   16.90 -    :add_where("_issue_state.closed ISNULL AND _issue_state.half_frozen ISNULL")
   16.91 -
   16.92 -  if initiator_invites_selector:count() > 0 then
   16.93 -    tabs[#tabs+1] = {
   16.94 -      class = "yellow",
   16.95 -      name = "initiator_invites",
   16.96 -      label = _"Initiator invites" .. " (" .. tostring(initiator_invites_selector:count()) .. ")",
   16.97 -      icon = { static = "icons/16/user_add.png" },
   16.98 -      module = "index",
   16.99 -      view = "_initiator_invites",
  16.100 -      params = {
  16.101 -        initiatives_selector = initiator_invites_selector
  16.102 -      }
  16.103 -    }
  16.104 -  end
  16.105 -
  16.106 -  local updated_drafts_selector = Initiative:new_selector()
  16.107 -    :join("issue", "_issue_state", "_issue_state.id = initiative.issue_id AND _issue_state.closed ISNULL AND _issue_state.fully_frozen ISNULL")
  16.108 -    :join("current_draft", "_current_draft", "_current_draft.initiative_id = initiative.id")
  16.109 -    :join("supporter", "supporter", { "supporter.member_id = ? AND supporter.initiative_id = initiative.id AND supporter.draft_id < _current_draft.id", app.session.member_id })
  16.110 -    :add_where("initiative.revoked ISNULL")
  16.111 -
  16.112 -  if updated_drafts_selector:count() > 0 then
  16.113 -    tabs[#tabs+1] = {
  16.114 -      class = "yellow",
  16.115 -      name = "updated_drafts",
  16.116 -      label = _"Updated drafts" .. " (" .. tostring(updated_drafts_selector:count()) .. ")",
  16.117 -      icon = { static = "icons/16/script.png" },
  16.118 -      module = "index",
  16.119 -      view = "_updated_drafts",
  16.120 -      params = {
  16.121 -        initiatives_selector = updated_drafts_selector
  16.122 -      }
  16.123 -    }
  16.124 -  end
  16.125 -end
  16.126 -
  16.127 -if not show_as_homepage then
  16.128 -  tabs[#tabs+1] = {
  16.129 -    name = "profile",
  16.130 -    label = _"Profile",
  16.131 -    icon = { static = "icons/16/application_form.png" },
  16.132 -    module = "member",
  16.133 -    view = "_profile",
  16.134 -    params = { member = member },
  16.135 -  }
  16.136 -end
  16.137 -
  16.138 +tabs[#tabs+1] = {
  16.139 +  name = "profile",
  16.140 +  label = _"Profile",
  16.141 +  icon = { static = "icons/16/application_form.png" },
  16.142 +  module = "member",
  16.143 +  view = "_profile",
  16.144 +  params = { member = member },
  16.145 +}
  16.146  
  16.147  local areas_selector = member:get_reference_selector("areas")
  16.148  tabs[#tabs+1] = {
  16.149 @@ -139,26 +24,16 @@
  16.150    icon = { static = "icons/16/package.png" },
  16.151    module = "member",
  16.152    view = "_area_list",
  16.153 -  params = { areas_selector = areas_selector, member = member, for_member = not show_as_homepage },
  16.154 +  params = { areas_selector = areas_selector, member = member, for_member = true },
  16.155  }
  16.156    
  16.157 -if show_as_homepage then
  16.158 -  tabs[#tabs+1] = {
  16.159 -    name = "timeline",
  16.160 -    label = _"Latest events",
  16.161 -    module = "member",
  16.162 -    view = "_event_list",
  16.163 -    params = { }
  16.164 -  }
  16.165 -else
  16.166 -  tabs[#tabs+1] = {
  16.167 -    name = "timeline",
  16.168 -    label = _"Events",
  16.169 -    module = "event",
  16.170 -    view = "_list",
  16.171 -    params = { for_member = member }
  16.172 -  }
  16.173 -end
  16.174 +tabs[#tabs+1] = {
  16.175 +  name = "timeline",
  16.176 +  label = _"Events",
  16.177 +  module = "event",
  16.178 +  view = "_list",
  16.179 +  params = { for_member = member }
  16.180 +}
  16.181  
  16.182  tabs[#tabs+1] = {
  16.183    name = "open",
  16.184 @@ -166,11 +41,11 @@
  16.185    module = "issue",
  16.186    view = "_list",
  16.187    link_params = { 
  16.188 -    filter_interest = not show_as_homepage and "issue" or nil,
  16.189 +    filter_interest = "issue",
  16.190    },
  16.191    params = {
  16.192      for_state = "open",
  16.193 -    for_member = not show_as_homepage and member or nil,
  16.194 +    for_member = member,
  16.195      issues_selector = Issue:new_selector()
  16.196        :add_where("issue.closed ISNULL")
  16.197        :add_order_by("coalesce(issue.fully_frozen + issue.voting_time, issue.half_frozen + issue.verification_time, issue.accepted + issue.discussion_time, issue.created + issue.admission_time) - now()")
  16.198 @@ -183,11 +58,11 @@
  16.199    module = "issue",
  16.200    view = "_list",
  16.201    link_params = { 
  16.202 -    filter_interest = not show_as_homepage and "issue" or nil,
  16.203 +    filter_interest = "issue",
  16.204    },
  16.205    params = {
  16.206      for_state = "closed",
  16.207 -    for_member = not show_as_homepage and member or nil,
  16.208 +    for_member = member,
  16.209      issues_selector = Issue:new_selector()
  16.210        :add_where("issue.closed NOTNULL")
  16.211        :add_order_by("issue.closed DESC")
  16.212 @@ -195,52 +70,39 @@
  16.213    }
  16.214  }
  16.215  
  16.216 -if show_as_homepage then
  16.217 -  tabs[#tabs+1] = {
  16.218 -    name = "members",
  16.219 -    label = _"Members",
  16.220 -    module = 'member',
  16.221 -    view   = '_list',
  16.222 -    params = { members_selector = Member:new_selector() }
  16.223 -  }
  16.224 -end
  16.225  
  16.226 -
  16.227 +local outgoing_delegations_selector = member:get_reference_selector("outgoing_delegations")
  16.228 +  :left_join("issue", "_member_showtab_issue", "_member_showtab_issue.id = delegation.issue_id")
  16.229 +  :add_where("_member_showtab_issue.closed ISNULL")
  16.230 +tabs[#tabs+1] = {
  16.231 +  name = "outgoing_delegations",
  16.232 +  label = _"Outgoing delegations" .. " (" .. tostring(outgoing_delegations_selector:count()) .. ")",
  16.233 +  icon = { static = "icons/16/table_go.png" },
  16.234 +  module = "delegation",
  16.235 +  view = "_list",
  16.236 +  params = { delegations_selector = outgoing_delegations_selector, outgoing = true },
  16.237 +}
  16.238  
  16.239 -if not show_as_homepage then
  16.240 -  local outgoing_delegations_selector = member:get_reference_selector("outgoing_delegations")
  16.241 -    :left_join("issue", "_member_showtab_issue", "_member_showtab_issue.id = delegation.issue_id")
  16.242 -    :add_where("_member_showtab_issue.closed ISNULL")
  16.243 -  tabs[#tabs+1] = {
  16.244 -    name = "outgoing_delegations",
  16.245 -    label = _"Outgoing delegations" .. " (" .. tostring(outgoing_delegations_selector:count()) .. ")",
  16.246 -    icon = { static = "icons/16/table_go.png" },
  16.247 -    module = "delegation",
  16.248 -    view = "_list",
  16.249 -    params = { delegations_selector = outgoing_delegations_selector, outgoing = true },
  16.250 -  }
  16.251 +local incoming_delegations_selector = member:get_reference_selector("incoming_delegations")
  16.252 +  :left_join("issue", "_member_showtab_issue", "_member_showtab_issue.id = delegation.issue_id")
  16.253 +  :add_where("_member_showtab_issue.closed ISNULL")
  16.254 +tabs[#tabs+1] = {
  16.255 +  name = "incoming_delegations",
  16.256 +  label = _"Incoming delegations" .. " (" .. tostring(incoming_delegations_selector:count()) .. ")",
  16.257 +  icon = { static = "icons/16/table_go.png" },
  16.258 +  module = "delegation",
  16.259 +  view = "_list",
  16.260 +  params = { delegations_selector = incoming_delegations_selector, incoming = true },
  16.261 +}
  16.262  
  16.263 -  local incoming_delegations_selector = member:get_reference_selector("incoming_delegations")
  16.264 -    :left_join("issue", "_member_showtab_issue", "_member_showtab_issue.id = delegation.issue_id")
  16.265 -    :add_where("_member_showtab_issue.closed ISNULL")
  16.266 -  tabs[#tabs+1] = {
  16.267 -    name = "incoming_delegations",
  16.268 -    label = _"Incoming delegations" .. " (" .. tostring(incoming_delegations_selector:count()) .. ")",
  16.269 -    icon = { static = "icons/16/table_go.png" },
  16.270 -    module = "delegation",
  16.271 -    view = "_list",
  16.272 -    params = { delegations_selector = incoming_delegations_selector, incoming = true },
  16.273 -  }
  16.274 -
  16.275 -  local contacts_selector = member:get_reference_selector("saved_members"):add_where("public")
  16.276 -  tabs[#tabs+1] = {
  16.277 -    name = "contacts",
  16.278 -    label = _"Contacts" .. " (" .. tostring(contacts_selector:count()) .. ")",
  16.279 -    icon = { static = "icons/16/book_edit.png" },
  16.280 -    module = "member",
  16.281 -    view = "_list",
  16.282 -    params = { members_selector = contacts_selector },
  16.283 -  }
  16.284 -end
  16.285 +local contacts_selector = member:get_reference_selector("saved_members"):add_where("public")
  16.286 +tabs[#tabs+1] = {
  16.287 +  name = "contacts",
  16.288 +  label = _"Contacts" .. " (" .. tostring(contacts_selector:count()) .. ")",
  16.289 +  icon = { static = "icons/16/book_edit.png" },
  16.290 +  module = "member",
  16.291 +  view = "_list",
  16.292 +  params = { members_selector = contacts_selector },
  16.293 +}
  16.294  
  16.295  ui.tabs(tabs)
    17.1 --- a/model/delegation.lua	Tue Jun 19 18:45:45 2012 +0200
    17.2 +++ b/model/delegation.lua	Tue Jun 19 21:20:46 2012 +0200
    17.3 @@ -53,3 +53,12 @@
    17.4    end
    17.5    return selector:exec()
    17.6  end
    17.7 +
    17.8 +function Delegation:selector_for_broken(member_id)
    17.9 +  return Delegation:new_selector()
   17.10 +    :join("issue", nil, "issue.id = delegation.issue_id AND issue.closed ISNULL")
   17.11 +    :join("member", nil, "delegation.trustee_id = member.id")
   17.12 +    :add_where{"delegation.truster_id = ?", member_id}
   17.13 +    :add_where{"member.active = 'f' OR (member.last_activity IS NULL OR age(member.last_activity) > ?::interval)", config.delegation_warning_time }
   17.14 +end
   17.15 +  
   17.16 \ No newline at end of file
    18.1 --- a/model/initiative.lua	Tue Jun 19 18:45:45 2012 +0200
    18.2 +++ b/model/initiative.lua	Tue Jun 19 21:20:46 2012 +0200
    18.3 @@ -215,10 +215,13 @@
    18.4      :add_group_by("_direct_supporter_snapshot.member_id")
    18.5  end
    18.6  
    18.7 ---function Member:get_search_selector(search_string)
    18.8 ---  return self:new_selector()
    18.9 ---    :add_where("active")
   18.10 ---end
   18.11 +function Initiative:selector_for_updated_drafts(member_id)
   18.12 +  return Initiative:new_selector()
   18.13 +    :join("issue", "_issue_state", "_issue_state.id = initiative.issue_id AND _issue_state.closed ISNULL AND _issue_state.fully_frozen ISNULL")
   18.14 +    :join("current_draft", "_current_draft", "_current_draft.initiative_id = initiative.id")
   18.15 +    :join("supporter", "supporter", { "supporter.member_id = ? AND supporter.initiative_id = initiative.id AND supporter.draft_id < _current_draft.id", member_id })
   18.16 +    :add_where("initiative.revoked ISNULL")
   18.17 +end
   18.18  
   18.19  
   18.20  function Initiative.object_get:current_draft()
    19.1 --- a/model/initiator.lua	Tue Jun 19 18:45:45 2012 +0200
    19.2 +++ b/model/initiator.lua	Tue Jun 19 21:20:46 2012 +0200
    19.3 @@ -25,3 +25,10 @@
    19.4      :optional_object_mode()
    19.5      :exec()
    19.6  end
    19.7 +
    19.8 +function Initiator:selector_for_invites(member_id)
    19.9 +  return Initiative:new_selector()
   19.10 +    :join("issue", "_issue_state", "_issue_state.id = initiative.issue_id")
   19.11 +    :join("initiator", nil, { "initiator.initiative_id = initiative.id AND initiator.member_id = ? AND initiator.accepted ISNULL", member_id })
   19.12 +    :add_where("_issue_state.closed ISNULL AND _issue_state.half_frozen ISNULL")
   19.13 +end  
    20.1 --- a/static/style.css	Tue Jun 19 18:45:45 2012 +0200
    20.2 +++ b/static/style.css	Tue Jun 19 21:20:46 2012 +0200
    20.3 @@ -514,6 +514,25 @@
    20.4  }
    20.5  
    20.6  /*************************************************************************
    20.7 + * notifications
    20.8 + */
    20.9 +
   20.10 +.notifications {
   20.11 +  margin-top: 1ex;
   20.12 +  background-color: #fdd;
   20.13 +  padding: 1ex;
   20.14 +}
   20.15 +
   20.16 +.notifications ul {
   20.17 +  margin: 0;
   20.18 +  margin-left: 2em;
   20.19 +}
   20.20 +  
   20.21 +.notifications li {
   20.22 +  margin-top: 0.5ex;
   20.23 +}
   20.24 +
   20.25 +/*************************************************************************
   20.26   * vertical ui.form
   20.27   */
   20.28  

Impressum / About Us