liquid_feedback_frontend

diff app/main/draft/new.lua @ 1496:ed3c40911ae1

Completed image attachments feature
author bsw
date Tue Feb 11 15:24:36 2020 +0100 (2020-02-11)
parents 17e7082c377a
children a59dd2ae9cd8
line diff
     1.1 --- a/app/main/draft/new.lua	Mon Feb 10 21:10:49 2020 +0100
     1.2 +++ b/app/main/draft/new.lua	Tue Feb 11 15:24:36 2020 +0100
     1.3 @@ -1,24 +1,68 @@
     1.4 -local initiative = Initiative:by_id(param.get("initiative_id"))
     1.5 -initiative:load_everything_for_member_id(app.session.member_id)
     1.6 -initiative.issue:load_everything_for_member_id(app.session.member_id)
     1.7 +local issue
     1.8 +local area
     1.9 +local area_id
    1.10  
    1.11 -if initiative.issue.closed then
    1.12 -  slot.put_into("error", _"This issue is already closed.")
    1.13 -  return
    1.14 -elseif initiative.issue.half_frozen then 
    1.15 -  slot.put_into("error", _"This issue is already frozen.")
    1.16 -  return
    1.17 -elseif initiative.issue.phase_finished then
    1.18 -  slot.put_into("error", _"Current phase is already closed.")
    1.19 -  return
    1.20 +local issue_id = param.get("issue_id", atom.integer)
    1.21 +if issue_id then
    1.22 +  issue = Issue:new_selector():add_where{"id=?",issue_id}:single_object_mode():exec()
    1.23 +  issue:load_everything_for_member_id(app.session.member_id)
    1.24 +  area = issue.area
    1.25 +else
    1.26 +  area_id = param.get("area_id", atom.integer)
    1.27 +  if area_id then
    1.28 +    area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec()
    1.29 +    area:load_delegation_info_once_for_member_id(app.session.member_id)
    1.30 +  end
    1.31 +end
    1.32 +
    1.33 +local polling = param.get("polling", atom.boolean)
    1.34 +
    1.35 +local policy_id = param.get("policy_id", atom.integer)
    1.36 +local policy
    1.37 +
    1.38 +local preview = param.get("preview")
    1.39 +
    1.40 +if #(slot.get_content("error")) > 0 then
    1.41 +  preview = false
    1.42 +end
    1.43 +
    1.44 +if policy_id then
    1.45 +  policy = Policy:by_id(policy_id)
    1.46  end
    1.47  
    1.48 -local draft = initiative.current_draft
    1.49 -if config.initiative_abstract then
    1.50 -  draft.abstract = string.match(draft.content, "(.+)<!%--END_OF_ABSTRACT%-->")
    1.51 -  if draft.abstract then
    1.52 -    draft.content = string.match(draft.content, "<!%--END_OF_ABSTRACT%-->(.*)")
    1.53 +
    1.54 +
    1.55 +
    1.56 +local initiative_id = param.get("initiative_id")
    1.57 +local initiative = Initiative:by_id(initiative_id)
    1.58 +local draft
    1.59 +if initiative then
    1.60 +  initiative:load_everything_for_member_id(app.session.member_id)
    1.61 +  initiative.issue:load_everything_for_member_id(app.session.member_id)
    1.62 +
    1.63 +  if initiative.issue.closed then
    1.64 +    slot.put_into("error", _"This issue is already closed.")
    1.65 +    return
    1.66 +  elseif initiative.issue.half_frozen then 
    1.67 +    slot.put_into("error", _"This issue is already frozen.")
    1.68 +    return
    1.69 +  elseif initiative.issue.phase_finished then
    1.70 +    slot.put_into("error", _"Current phase is already closed.")
    1.71 +    return
    1.72    end
    1.73 +
    1.74 +  draft = initiative.current_draft
    1.75 +  if config.initiative_abstract then
    1.76 +    draft.abstract = string.match(draft.content, "(.+)<!%--END_OF_ABSTRACT%-->")
    1.77 +    if draft.abstract then
    1.78 +      draft.content = string.match(draft.content, "<!%--END_OF_ABSTRACT%-->(.*)")
    1.79 +    end
    1.80 +  end
    1.81 +end
    1.82 +
    1.83 +if not initiative and not issue and not area then
    1.84 +  ui.heading{ content = _"Missing parameter" }
    1.85 +  return false
    1.86  end
    1.87  
    1.88  ui.form{
    1.89 @@ -26,13 +70,17 @@
    1.90    attr = { class = "vertical section", enctype = 'multipart/form-data' },
    1.91    module = "draft",
    1.92    action = "add",
    1.93 -  params = { initiative_id = initiative.id },
    1.94 +  params = {
    1.95 +    area_id = area and area.id,
    1.96 +    issue_id = issue and issue.id or nil,
    1.97 +    initiative_id = initiative_id
    1.98 +  },
    1.99    routing = {
   1.100      ok = {
   1.101        mode = "redirect",
   1.102        module = "initiative",
   1.103        view = "show",
   1.104 -      id = initiative.id
   1.105 +      id = initiative_id
   1.106      }
   1.107    },
   1.108    content = function()
   1.109 @@ -41,11 +89,30 @@
   1.110        ui.cell_main{ content = function()
   1.111          ui.container{ attr = { class = "mdl-card mdl-shadow--2dp mdl-card__fullwidth" }, content = function()
   1.112            ui.container{ attr = { class = "mdl-card__title mdl-card--border" }, content = function()
   1.113 -            ui.heading { attr = { class = "mdl-card__title-text" }, level = 2, content = initiative.display_name }
   1.114 +            if initiative then
   1.115 +              ui.heading { attr = { class = "mdl-card__title-text" }, level = 2, content = initiative.display_name }
   1.116 +            elseif param.get("name") then
   1.117 +              ui.heading { attr = { class = "mdl-card__title-text" }, level = 2, content = param.get("name") }
   1.118 +            elseif issue then
   1.119 +              ui.heading { attr = { class = "mdl-card__title-text" }, level = 2, content = _("New competing initiative in issue '#{issue}'", { issue = issue.name }) }
   1.120 +            elseif area then
   1.121 +              ui.heading { attr = { class = "mdl-card__title-text" }, level = 2, content = _("New issue in area '#{area}'", { area = area.name }) }
   1.122 +            end
   1.123            end }
   1.124            ui.container{ attr = { class = "mdl-card__content mdl-card--border" }, content = function()
   1.125 +
   1.126 +-- -------- PREVIEW
   1.127              if param.get("preview") then
   1.128                ui.sectionRow( function()
   1.129 +                if not issue and not initiative then
   1.130 +                  ui.container { content = policy.name }
   1.131 +                end
   1.132 +                if param.get("free_timing") then
   1.133 +                  ui.container { content = param.get("free_timing") }
   1.134 +                end
   1.135 +                slot.put("<br />")
   1.136 +                ui.field.hidden{ name = "policy_id", value = param.get("policy_id") }
   1.137 +                ui.field.hidden{ name = "name", value = param.get("name") }
   1.138                  if config.initiative_abstract then
   1.139                    ui.field.hidden{ name = "abstract", value = param.get("abstract") }
   1.140                    ui.container{
   1.141 @@ -72,31 +139,35 @@
   1.142                    )
   1.143                    file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
   1.144                    ui.field.hidden{ name = "file_upload_session", value = file_upload_session }
   1.145 -                   local files = File:new_selector()
   1.146 -                    :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
   1.147 -                    :add_where{ "draft_attachment.draft_id = ?", initiative.current_draft.id }
   1.148 -                    :reset_fields()
   1.149 -                    :add_field("file.id")
   1.150 -                    :add_field("draft_attachment.title")
   1.151 -                    :add_field("draft_attachment.description")
   1.152 -                    :add_order_by("draft_attachment.id")
   1.153 -                    :exec()
   1.154 +                  if initiative then
   1.155 +                     local files = File:new_selector()
   1.156 +                      :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
   1.157 +                      :add_where{ "draft_attachment.draft_id = ?", initiative.current_draft.id }
   1.158 +                      :reset_fields()
   1.159 +                      :add_field("file.id")
   1.160 +                      :add_field("draft_attachment.title")
   1.161 +                      :add_field("draft_attachment.description")
   1.162 +                      :add_order_by("draft_attachment.id")
   1.163 +                      :exec()
   1.164  
   1.165 -                  if #files > 0 then
   1.166 -                    ui.container {
   1.167 -                      content = function()
   1.168 -                        for i, file in ipairs(files) do
   1.169 -                          if param.get("file_delete_" .. file.id, atom.boolean) then
   1.170 -                            ui.field.hidden{ name = "file_delete_" .. file.id, value = "1" }
   1.171 -                          else
   1.172 -                            ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
   1.173 -                            ui.container{ content = file.title or "" }
   1.174 -                            ui.container{ content = file.description or "" }
   1.175 -                            slot.put("<br /><br />")
   1.176 +                    if #files > 0 then
   1.177 +                      ui.container {
   1.178 +                        content = function()
   1.179 +                          for i, file in ipairs(files) do
   1.180 +                            if param.get("file_delete_" .. file.id, atom.boolean) then
   1.181 +                              ui.field.hidden{ name = "file_delete_" .. file.id, value = "1" }
   1.182 +                            else
   1.183 +                              ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
   1.184 +                              ui.container{ content = function()
   1.185 +                                ui.tag{ tag = "strong", content = file.title or "" }
   1.186 +                              end }
   1.187 +                              ui.container{ content = file.description or "" }
   1.188 +                              slot.put("<br /><br />")
   1.189 +                            end
   1.190                            end
   1.191                          end
   1.192 -                      end
   1.193 -                    }
   1.194 +                      }
   1.195 +                    end
   1.196                    end
   1.197                    local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
   1.198                    local fh = io.open(filename, "r")
   1.199 @@ -106,7 +177,9 @@
   1.200                        ui.image{ module = "draft", view = "show_file_upload", params = {
   1.201                          file_upload_session = file_upload_session, file_id = file_upload.id, preview = true
   1.202                        } }
   1.203 -                      ui.container{ content = file_upload.title or "" }
   1.204 +                      ui.container{ content = function()
   1.205 +                        ui.tag{ tag = "strong", content = file_upload.title or "" }
   1.206 +                      end }
   1.207                        ui.container{ content = file_upload.description or "" }
   1.208                        slot.put("<br />")
   1.209                      end
   1.210 @@ -139,47 +212,108 @@
   1.211                  ui.link{
   1.212                    attr = { class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" },
   1.213                    content = _"Cancel",
   1.214 -                  module = "initiative",
   1.215 +                  module = initiative and "initiative" or "area",
   1.216                    view = "show",
   1.217 -                  id = initiative.id
   1.218 +                  id = initiative_id or area_id
   1.219                  }
   1.220                end )
   1.221  
   1.222 +-- -------- EDIT
   1.223              else
   1.224 -              ui.sectionRow( function()
   1.225 -                if config.initiative_abstract then
   1.226 -                  ui.container { content = _"Enter abstract:" }
   1.227 -                  ui.container{ attr = { class = "mdl-textfield mdl-js-textfield mdl-textfield--expandable mdl-textfield__fullwidth" }, content = function()
   1.228 +
   1.229 +              if not issue_id and not initiative_id then
   1.230 +                local tmp = { { id = -1, name = "" } }
   1.231 +                for i, allowed_policy in ipairs(area.allowed_policies) do
   1.232 +                  if not allowed_policy.polling or app.session.member:has_polling_right_for_unit_id(area.unit_id) then
   1.233 +                    tmp[#tmp+1] = allowed_policy
   1.234 +                  end
   1.235 +                end
   1.236 +                ui.container{ content = _"Please choose a policy for the new issue:" }
   1.237 +                ui.field.select{
   1.238 +                  name = "policy_id",
   1.239 +                  foreign_records = tmp,
   1.240 +                  foreign_id = "id",
   1.241 +                  foreign_name = "name",
   1.242 +                  value = param.get("policy_id", atom.integer) or area.default_policy and area.default_policy.id
   1.243 +                }
   1.244 +                if policy and policy.free_timeable then
   1.245 +                  local available_timings
   1.246 +                  if config.free_timing and config.free_timing.available_func then
   1.247 +                    available_timings = config.free_timing.available_func(policy)
   1.248 +                    if available_timings == false then
   1.249 +                      slot.put_into("error", "error in free timing config")
   1.250 +                      return false
   1.251 +                    end
   1.252 +                  end
   1.253 +                  ui.heading{ level = 4, content = _"Free timing:" }
   1.254 +                  if available_timings then
   1.255 +                    ui.field.select{
   1.256 +                      name = "free_timing",
   1.257 +                      foreign_records = available_timings,
   1.258 +                      foreign_id = "id",
   1.259 +                      foreign_name = "name",
   1.260 +                      value = param.get("free_timing")
   1.261 +                    }
   1.262 +                  else
   1.263                      ui.field.text{
   1.264 -                      name = "abstract",
   1.265 -                      multiline = true, 
   1.266 -                      attr = { id = "abstract", style = "height: 20ex; width: 100%;" },
   1.267 -                      value = param.get("abstract")
   1.268 +                      name = "free_timing",
   1.269 +                      value = param.get("free_timing")
   1.270                      }
   1.271 -                  end }
   1.272 +                  end
   1.273                  end
   1.274 -                
   1.275 -                ui.container { content = _"Enter your proposal and/or reasons:" }
   1.276 -                ui.field.wysihtml{
   1.277 -                  name = "content",
   1.278 -                  multiline = true, 
   1.279 -                  attr = { id = "draft", style = "height: 50ex; width: 100%;" },
   1.280 -                  value = param.get("content")
   1.281 -                }
   1.282 -                if not issue or issue.state == "admission" or issue.state == "discussion" then
   1.283 -                  ui.container { content = _"You can change your text again anytime during admission and discussion phase" }
   1.284 -                else
   1.285 -                  ui.container { content = _"You cannot change your text again later, because this issue is already in verfication phase!" }
   1.286 -                end
   1.287 +              end
   1.288 +
   1.289 +              if issue and issue.policy.polling and app.session.member:has_polling_right_for_unit_id(area.unit_id) then
   1.290 +                slot.put("<br />")
   1.291 +                ui.field.boolean{ name = "polling", label = _"No admission needed", value = polling }
   1.292 +              end
   1.293 +
   1.294 +              if not initiative then
   1.295 +                ui.container{ attr = { class = "mdl-textfield mdl-js-textfield mdl-textfield--floating-label mdl-card__fullwidth" }, content = function ()
   1.296 +                  ui.field.text{
   1.297 +                    attr = { id = "lf-initiative__name", class = "mdl-textfield__input" },
   1.298 +                    label_attr = { class = "mdl-textfield__label", ["for"] = "lf-initiative__name" },
   1.299 +                    label = _"Title",
   1.300 +                    name  = "name",
   1.301 +                    value = param.get("name")
   1.302 +                  }
   1.303 +                end }
   1.304 +              end
   1.305  
   1.306 -                slot.put("<br />")
   1.307 -                if config.attachments then
   1.308 -                  local file_upload_session = param.get("file_upload_session") or multirand.string(
   1.309 -                    32,
   1.310 -                    '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
   1.311 -                  )
   1.312 -                  file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
   1.313 -                  ui.field.hidden{ name = "file_upload_session", value = file_upload_session }
   1.314 +              if config.initiative_abstract then
   1.315 +                ui.container { content = _"Enter abstract:" }
   1.316 +                ui.container{ attr = { class = "mdl-textfield mdl-js-textfield mdl-textfield--expandable mdl-textfield__fullwidth" }, content = function()
   1.317 +                  ui.field.text{
   1.318 +                    name = "abstract",
   1.319 +                    multiline = true, 
   1.320 +                    attr = { id = "abstract", style = "height: 20ex; width: 100%;" },
   1.321 +                    value = param.get("abstract")
   1.322 +                  }
   1.323 +                end }
   1.324 +              end
   1.325 +              
   1.326 +              ui.container { content = _"Enter your proposal and/or reasons:" }
   1.327 +              ui.field.wysihtml{
   1.328 +                name = "content",
   1.329 +                multiline = true, 
   1.330 +                attr = { id = "draft", style = "height: 50ex; width: 100%;" },
   1.331 +                value = param.get("content")
   1.332 +              }
   1.333 +              if not issue or issue.state == "admission" or issue.state == "discussion" then
   1.334 +                ui.container { content = _"You can change your text again anytime during admission and discussion phase" }
   1.335 +              else
   1.336 +                ui.container { content = _"You cannot change your text again later, because this issue is already in verfication phase!" }
   1.337 +              end
   1.338 +
   1.339 +              slot.put("<br />")
   1.340 +              if config.attachments then
   1.341 +                local file_upload_session = param.get("file_upload_session") or multirand.string(
   1.342 +                  32,
   1.343 +                  '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
   1.344 +                )
   1.345 +                file_upload_session = string.gsub(file_upload_session, "[^A-Za-z0-9]", "")
   1.346 +                ui.field.hidden{ name = "file_upload_session", value = file_upload_session }
   1.347 +                if initiative then
   1.348                    local files = File:new_selector()
   1.349                      :left_join("draft_attachment", nil, "draft_attachment.file_id = file.id")
   1.350                      :add_where{ "draft_attachment.draft_id = ?", initiative.current_draft.id }
   1.351 @@ -195,7 +329,9 @@
   1.352                        content = function()
   1.353                          for i, file in ipairs(files) do
   1.354                            ui.image{ module = "file", view = "show.jpg", id = file.id, params = { preview = true } }
   1.355 -                          ui.container{ content = file.title or "" }
   1.356 +                          ui.container{ content = function()
   1.357 +                            ui.tag{ tag = "strong", content = file.title or "" }
   1.358 +                          end }
   1.359                            ui.container{ content = file.description or "" }
   1.360                            ui.field.boolean{ label = _"delete", name = "file_delete_" .. file.id, value = param.get("file_delete_" .. file.id) and true or false }
   1.361                            slot.put("<br /><br />")
   1.362 @@ -203,61 +339,63 @@
   1.363                        end
   1.364                      }
   1.365                    end
   1.366 -
   1.367 -                  local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
   1.368 -                  local fh = io.open(filename, "r")
   1.369 -                  if fh then
   1.370 -                    local file_uploads = json.import(fh:read("*a"))
   1.371 -                    for i, file_upload in ipairs(file_uploads) do
   1.372 -                      ui.image{ module = "draft", view = "show_file_upload", params = {
   1.373 -                        file_upload_session = file_upload_session, file_id = file_upload.id, preview = true
   1.374 -                      } }
   1.375 -                      ui.container{ content = file_upload.title or "" }
   1.376 -                      ui.container{ content = file_upload.description or "" }
   1.377 -                      ui.field.boolean{ label = _"delete", name = "file_upload_delete_" .. file_upload.id }
   1.378 -                      slot.put("<br />")
   1.379 -                    end
   1.380 +                end
   1.381 +                local filename = encode.file_path(WEBMCP_BASE_PATH, 'tmp', "file_upload-" .. file_upload_session .. ".json")
   1.382 +                local fh = io.open(filename, "r")
   1.383 +                if fh then
   1.384 +                  local file_uploads = json.import(fh:read("*a"))
   1.385 +                  for i, file_upload in ipairs(file_uploads) do
   1.386 +                    ui.image{ module = "draft", view = "show_file_upload", params = {
   1.387 +                      file_upload_session = file_upload_session, file_id = file_upload.id, preview = true
   1.388 +                    } }
   1.389 +                    ui.container{ content = function()
   1.390 +                      ui.tag{ tag = "strong", content = file_upload.title or "" }
   1.391 +                    end }
   1.392 +                    ui.container{ content = file_upload.description or "" }
   1.393 +                    ui.field.boolean{ label = _"delete", name = "file_upload_delete_" .. file_upload.id }
   1.394 +                    slot.put("<br />")
   1.395                    end
   1.396 -                  ui.container{ attr = { id = "file_upload_template", style = "display: none;" }, content = function()
   1.397 -                    ui.field.text{ label = _"Title", name = "__ID_title__" }
   1.398 -                    ui.field.text{ label = _"Description", name = "__ID_description__" }
   1.399 -                    ui.field.image{ field_name = "__ID_file__" }
   1.400 -                  end }
   1.401 -                  ui.container{ attr = { id = "file_upload" }, content = function()
   1.402 -                  end }
   1.403 -                  ui.field.hidden{ attr = { id = "file_upload_last_id" }, name = "file_upload_last_id" }
   1.404 -                  ui.script{ script = [[ var file_upload_id = 1; ]] }
   1.405 -                  ui.tag{ tag = "a", content = _"Attach image", attr = { 
   1.406 -                    href = "#",
   1.407 -                    onclick = "var html = document.getElementById('file_upload_template').innerHTML; html = html.replace('__ID_file__', 'file_' + file_upload_id); html = html.replace('__ID_title__', 'title_' + file_upload_id); html = html.replace('__ID_description__', 'description_' + file_upload_id); var el = document.createElement('div'); el.innerHTML = html; document.getElementById('file_upload').appendChild(el); document.getElementById('file_upload_last_id').value = file_upload_id; file_upload_id++; return false;"
   1.408 -                  } }
   1.409 -                  slot.put("<br />")
   1.410 -                  
   1.411 -                  slot.put("<br />")
   1.412 +                end
   1.413 +                ui.container{ attr = { id = "file_upload_template", style = "display: none;" }, content = function()
   1.414 +                  ui.field.text{ label = _"Title", name = "__ID_title__" }
   1.415 +                  ui.field.text{ label = _"Description", name = "__ID_description__" }
   1.416 +                  ui.field.image{ field_name = "__ID_file__" }
   1.417 +                end }
   1.418 +                ui.container{ attr = { id = "file_upload" }, content = function()
   1.419 +                end }
   1.420 +                ui.field.hidden{ attr = { id = "file_upload_last_id" }, name = "file_upload_last_id" }
   1.421 +                ui.script{ script = [[ var file_upload_id = 1; ]] }
   1.422 +                ui.tag{ tag = "a", content = _"Attach image", attr = { 
   1.423 +                  href = "#",
   1.424 +                  onclick = "var html = document.getElementById('file_upload_template').innerHTML; html = html.replace('__ID_file__', 'file_' + file_upload_id); html = html.replace('__ID_title__', 'title_' + file_upload_id); html = html.replace('__ID_description__', 'description_' + file_upload_id); var el = document.createElement('div'); el.innerHTML = html; document.getElementById('file_upload').appendChild(el); document.getElementById('file_upload_last_id').value = file_upload_id; file_upload_id++; return false;"
   1.425 +                } }
   1.426 +                slot.put("<br />")
   1.427 +                
   1.428 +                slot.put("<br />")
   1.429  
   1.430 -                end
   1.431 +              end
   1.432  
   1.433 -                ui.tag{
   1.434 -                  tag = "input",
   1.435 -                  attr = {
   1.436 -                    type = "submit",
   1.437 -                    name = "preview",
   1.438 -                    class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored",
   1.439 -                    value = _'Preview'
   1.440 -                  },
   1.441 -                  content = ""
   1.442 -                }
   1.443 -                slot.put(" &nbsp; ")
   1.444 -                
   1.445 -                ui.link{
   1.446 -                  content = _"Cancel",
   1.447 -                  module = "initiative",
   1.448 -                  view = "show",
   1.449 -                  id = initiative.id,
   1.450 -                  attr = { class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" }
   1.451 -                }
   1.452 -                
   1.453 -              end )
   1.454 +              ui.tag{
   1.455 +                tag = "input",
   1.456 +                attr = {
   1.457 +                  type = "submit",
   1.458 +                  name = "preview",
   1.459 +                  class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored",
   1.460 +                  value = _'Preview'
   1.461 +                },
   1.462 +                content = ""
   1.463 +              }
   1.464 +              slot.put(" &nbsp; ")
   1.465 +              
   1.466 +              ui.link{
   1.467 +                content = _"Cancel",
   1.468 +                module = initiative and "initiative" or issue and "issue" or "index",
   1.469 +                view = area and "index" or "show",
   1.470 +                id = initiative_id or issue_id,
   1.471 +                params = { area = area_id, unit = area and area.unit_id or nil },
   1.472 +                attr = { class = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" }
   1.473 +              }
   1.474 +              
   1.475              end
   1.476            end }
   1.477          end }

Impressum / About Us