diff --git a/.gitignore b/.gitignore
index b0a831463..deb7e02ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,5 @@ spec/dummy/tmp/
/spec/dummy/public/system/*
/spec/dummy/webdav/*
-/.idea/
\ No newline at end of file
+/.idea/
+.ruby-version
diff --git a/app/assets/javascripts/droom.js.coffee b/app/assets/javascripts/droom.js.coffee
index 01c52f921..ace2e572a 100644
--- a/app/assets/javascripts/droom.js.coffee
+++ b/app/assets/javascripts/droom.js.coffee
@@ -8,6 +8,7 @@
#= require sortablejs/Sortable
#= require jquery.cookie/jquery.cookie
#= require ep-jquery-tokeninput/src/jquery.tokeninput
+#= require imagesloaded/imagesloaded.pkgd
#= require droom/lib/jquery.datepicker
#= require droom/lib/jquery.animate-colors
@@ -67,8 +68,10 @@ jQuery ($) ->
@find_including_self('[data-action="toggle"]').toggle()
@find_including_self('[data-action="alternate"]').alternator()
@find_including_self('[data-action="replace"]').replace_with_remote_content()
+ @find_including_self('[data-action="update_content"]').update_main_content()
@find_including_self('[data-action="autofetch"]').replace_with_remote_content ".holder", {force: true}
@find_including_self('[data-action="slide"]').sliding_link()
+ @find_including_self('[data-action="proxy"]').click_proxy()
@find_including_self('[data-action="fit"]').self_sizes()
@find_including_self('form[data-action="filter"]').filter_form()
@find_including_self('form[data-action="quick_search"]').quick_search_form()
@@ -116,8 +119,11 @@ jQuery ($) ->
@find_including_self('.sortable_files').sortable_files()
@find_including_self('[data-draggable]').draggable()
@find_including_self('.gridbox:not(.notice)').gridBox()
- @find_including_self('.notice').notice()
@find_including_self('.tagger').tagger()
@find_including_self('form.search.quick').quick_search_form()
+ @find_including_self('#noticeboard').imagesLoaded =>
+ console.log "imagesLoaded ", @
+ $('.notice').notice()
+
@
diff --git a/app/assets/javascripts/droom/actions.js.coffee b/app/assets/javascripts/droom/actions.js.coffee
index 744817ecf..62e064613 100644
--- a/app/assets/javascripts/droom/actions.js.coffee
+++ b/app/assets/javascripts/droom/actions.js.coffee
@@ -4,18 +4,18 @@
# The basic approach here is PJAX: our remote calls always return chunks of html. There are no client-side templates
# or model classes. Instead we have defined a set of actions, by which page elements can interact with the server in
# structured ways. A tag will declare that it triggers an action, and may also declare that the action will replace or
-# affect other elements.
+# affect other elements.
#
# Many elements are refreshable, and an action that affects them will trigger their refreshment. Editing an event is
# a simple remote form operation with several consequences for the page:
#
# link_to t(:edit_event), edit_event_url(event), :class => 'edit minimal', :data => {
-# :action => "popup",
-# :replaced => "#event_#{event.id}",
+# :action => "popup",
+# :replaced => "#event_#{event.id}",
# :affected => ".minimonth"
# }
#
-# When the `popup` action is complete - that is, the server returns a response with no form in it - the final response
+# When the `popup` action is complete - that is, the server returns a response with no form in it - the final response
# will replace the original event container and the small calendar display will be refreshed to show the possibly revised
# date of the event.
#
@@ -50,7 +50,7 @@ jQuery ($) ->
if @_url
@_container.addClass('working')
- params =
+ params =
dataType: "html"
beforeSend: @prep
success: @replace
@@ -87,7 +87,7 @@ jQuery ($) ->
# attribute. The delete link next to an event, for example:
#
# link_to t(:remove), event_url(event), :method => 'delete', :data => {
- # :confirm => t(:confirm_delete_event, :name => event.name),
+ # :confirm => t(:confirm_delete_event, :name => event.name),
# :removes => ".holder",
# :affected => ".minimonth"}
#
@@ -99,9 +99,13 @@ jQuery ($) ->
affected = $(@).attr('data-affected')
$(@).remote
on_success: (response) =>
- $(@).parents(removed).first().fadeOut 'fast', () ->
- $(@).remove()
- $(affected).trigger "refresh"
+ if $(@).parents('.menu').first().length == 1
+ $(@).parents('.menu').first().fadeOut 'fast', () ->
+ $(removed).remove()
+ else
+ $(@).parents(removed).first().fadeOut 'fast', () ->
+ $(@).remove()
+ $(affected).trigger "refresh"
# The 'remove_all' action takes out on success anything matching the given selector:
@@ -114,7 +118,7 @@ jQuery ($) ->
@each ->
removed = $(@).attr('data-removed')
affected = $(@).attr('data-affected')
-
+
$(@).remote
on_success: (response) =>
$(removed).fadeOut 'fast', () ->
@@ -156,6 +160,9 @@ jQuery ($) ->
response ?= e
replaced = if container is "self" then $el else $el.self_or_ancestor(container).last()
replacement = $(response).insertAfter(replaced)
+ console.log 'response', response
+ console.log 'replaced', replaced
+ console.log 'replacement', replacement
replaced?.remove()
replacement.activate()
replacement.hide().slideDown() if options.slide
@@ -166,6 +173,24 @@ jQuery ($) ->
$.rails.handleRemote($el)
+ # *update_main_content* is used in services page, action from popup and effected on main content
+ $.fn.update_main_content = (selector, opts) ->
+ selector ?= '.holder'
+ options = $.extend { force: false, confirm: false, slide: false, pjax: true, credentials: false }, opts
+ @each ->
+ $el = $(@)
+ container = selector
+ $el.remote
+ credentials: options.credentials
+ pjax: options.pjax
+ on_success: (e, response) =>
+ response ?= e
+ className = $(response).attr "class"
+ id = $(response).attr "id"
+ @_selector = $("[data-menu=\"#{id}\"]")
+ $(@_selector).removeClass().addClass(className)
+ $(".menu").hide()
+
# The reinviter is an ordinary remote link.
#
@@ -258,7 +283,7 @@ jQuery ($) ->
$.fn.setter = () ->
@each ->
new Setter(@)
-
+
class Setter
constructor: (element, @_root, @_property) ->
@_link = $(element)
@@ -273,7 +298,7 @@ jQuery ($) ->
data = {}
value = if @_link.hasClass('yes') then @_negative else @_positive
data[@_root] = {}
- data[@_root][@_property] = value
+ data[@_root][@_property] = value
data['_method'] = "PUT"
data
@@ -328,7 +353,7 @@ jQuery ($) ->
else
@_showing = $(@_selector).is(":visible")
@store()
-
+
apply: (e) =>
e.preventDefault() if e
if @_showing then @show() else @hide()
@@ -349,19 +374,19 @@ jQuery ($) ->
@_container.text(@_showing_text)
@_showing = true
@store()
-
+
slideUp: =>
$(@_selector).slideUp () =>
@hide()
@_container.trigger('toggled')
-
+
hide: =>
$(@_selector).hide()
@_container.removeClass('showing')
@_container.text(@_hiding_text)
@_showing = false
@store()
-
+
store: () =>
if @_remembered
value = if @_showing then "showing" else "hidden"
@@ -390,10 +415,10 @@ jQuery ($) ->
@preview = @container.find(@options.preview)
@switch.click @toggle
@set()
-
+
set: () =>
if @container.hasClass("open") then @show() else @hide()
-
+
toggle: (e) =>
e.preventDefault() if e
if @container.hasClass("open") then @hide() else @show()
@@ -401,7 +426,7 @@ jQuery ($) ->
hide: () =>
@container.removeClass('open')
@preview.show().css('position', 'relative')
- @body.stop().slideUp
+ @body.stop().slideUp
duration: 'slow'
easing: 'glide'
complete: =>
@@ -448,7 +473,7 @@ jQuery ($) ->
- # The *alternator* action is a more extreme toggle. It allows an element to declare its alternate:
+ # The *alternator* action is a more extreme toggle. It allows an element to declare its alternate:
# when a link within the element is clicked, it will be removed from the DOM and its alternate
# inserted. Usually the relation is reciprocal, so that another link in the alternate will bring
# the original element back.
@@ -468,13 +493,13 @@ jQuery ($) ->
@_container.after(@_alternate)
@_container.remove()
@_alternate.find('a').click @revert
-
+
revert: (e) =>
e.preventDefault() if e
@_alternate.before(@_container)
@_alternate.remove()
@_container.find('a').click @flip
-
+
$.fn.alternator = ->
@each ->
new Alternator(@)
@@ -484,7 +509,7 @@ jQuery ($) ->
$.fn.hover_table = ->
@each ->
$(@).find('td').hover_cell()
-
+
$.fn.hover_cell = ->
@each ->
classes = @className.split(' ').join('.')
@@ -554,14 +579,14 @@ jQuery ($) ->
@_defilter.show()
else
@_defilter.hide()
-
+
setQuery: (e) =>
kc = e.which
# delete, backspace, alphanumerics, number pad, punctuation
if (kc is 8) or (kc is 46) or (47 < kc < 91) or (96 < kc < 112) or (kc > 145)
@setDefilter()
@filter()
-
+
clearQuery: (e) =>
e.preventDefault() if e
@_container.val("")
@@ -576,7 +601,7 @@ jQuery ($) ->
# A text input that sizes to fit will adjust its font size to suit the length of its content.
- # At the moment this is done just by fitting a curve, but it ought really to be based on a
+ # At the moment this is done just by fitting a curve, but it ought really to be based on a
# calculation of area occupied.
$.size_to_fit = (e) ->
@@ -587,10 +612,10 @@ jQuery ($) ->
'font-size': "#{size}em"
width: 532
height: 290
- ,
+ ,
queue: false
duration: 100
-
+
$.fn.self_sizes = () ->
@each ->
$(@).bind "keyup", $.size_to_fit
diff --git a/app/assets/javascripts/droom/ajax.js.coffee b/app/assets/javascripts/droom/ajax.js.coffee
index cb62f6c1e..3f2795789 100644
--- a/app/assets/javascripts/droom/ajax.js.coffee
+++ b/app/assets/javascripts/droom/ajax.js.coffee
@@ -58,6 +58,8 @@ jQuery ($) ->
@_control.trigger "remote:progress", prog
fail: (event, xhr, status) =>
+ if xhr.status == 401
+ window.location.reload()
event.stopPropagation()
@_control.removeClass('waiting').addClass('erratic')
@_control.find('input[type=submit]').removeClass('waiting')
diff --git a/app/assets/javascripts/droom/editors.js.coffee b/app/assets/javascripts/droom/editors.js.coffee
index 34cf7de22..5f4548006 100644
--- a/app/assets/javascripts/droom/editors.js.coffee
+++ b/app/assets/javascripts/droom/editors.js.coffee
@@ -25,27 +25,45 @@ jQuery ($) ->
allowMultiParagraphSelection: true
buttons: [
name: 'bold'
- contentDefault: ''
+ contentDefault: ''
,
name: 'italic'
- contentDefault: ''
+ contentDefault: ''
,
name: 'anchor'
- contentDefault: ''
+ contentDefault: ''
,
name: 'orderedlist'
- contentDefault: ''
+ contentDefault: ''
,
name: 'unorderedlist'
- contentDefault: ''
+ contentDefault: ''
,
name: 'h2'
- contentDefault: ''
+ contentDefault: ''
,
name: 'h3'
- contentDefault: ''
+ contentDefault: ''
,
name: 'removeFormat'
- contentDefault: ''
+ contentDefault: ''
]
-
diff --git a/app/assets/javascripts/droom/grid.js.coffee b/app/assets/javascripts/droom/grid.js.coffee
index e6feecc9d..0dc918109 100644
--- a/app/assets/javascripts/droom/grid.js.coffee
+++ b/app/assets/javascripts/droom/grid.js.coffee
@@ -5,12 +5,19 @@ $.fn.autoGrid = ->
$.fn.gridBox = ->
@each ->
$el = $(@)
- contents = $el.find('.content')
row = 20
space = 20
- rows_touched = Math.ceil(contents.outerHeight() / (row + space))
- $el.css "grid-row-end", "span #{rows_touched}"
- $el.addClass('ready')
+ sizer = ->
+ contents = $el.find('.content')
+ console.log "sizer!", contents, contents.outerHeight()
+ rows_touched = Math.ceil(contents.outerHeight() / (row + space)) + 2
+ $el.css "grid-row-end", "span #{rows_touched}"
+ $el.addClass('ready')
+ sizer()
+ $el.find('img').on 'load', =>
+ console.log "resizer!"
+ sizer()
+
$.fn.highlight = ->
if @offset()
diff --git a/app/assets/javascripts/droom/lib/assets.js.coffee.erb b/app/assets/javascripts/droom/lib/assets.js.coffee.erb
index ee00b9855..189c946ed 100644
--- a/app/assets/javascripts/droom/lib/assets.js.coffee.erb
+++ b/app/assets/javascripts/droom/lib/assets.js.coffee.erb
@@ -1,5 +1,7 @@
jQuery ($) ->
$.withZxcbvn = (fn) ->
- url = "<%= URI.join(Settings.url, asset_path('droom/zxcvbn.js')) %>"
- $.getScript(url).done (response) => fn?(response)
+ based_url = 'https://data.croucher.org.hk/'
+ asset_path = based_url + "<%=asset_path('droom/zxcvbn.js')%>"
+
+ $.getScript(asset_path).done (response) => fn?(response)
diff --git a/app/assets/javascripts/droom/popups.js.coffee b/app/assets/javascripts/droom/popups.js.coffee
index 02db7d93a..901f6af30 100644
--- a/app/assets/javascripts/droom/popups.js.coffee
+++ b/app/assets/javascripts/droom/popups.js.coffee
@@ -26,6 +26,7 @@ jQuery ($) ->
@_iteration = 0
@_affected = @_link.data('affected')
@_replaced = @_link.data('replaced')
+ @_removed = @_link.data('removed')
@_aftered = @_link.data('appended')
@_befored = @_link.data('prepended')
@_reporter = @_link.data('reporter')
@@ -104,6 +105,8 @@ jQuery ($) ->
addition = $(data)
$(@_befored).before(addition)
addition.activate().signal_confirmation()
+ if @_removed?
+ $(@_removed).remove()
if @_replaced?
replacement = $(data)
$(@_replaced).after(replacement)
diff --git a/app/assets/javascripts/droom/widgets.js.coffee b/app/assets/javascripts/droom/widgets.js.coffee
index c0ef0b8e2..f90b56089 100644
--- a/app/assets/javascripts/droom/widgets.js.coffee
+++ b/app/assets/javascripts/droom/widgets.js.coffee
@@ -4,20 +4,32 @@
jQuery ($) ->
+ # The date_picker uses a lightly customised version of jquery.datepicker to present a nice year/month/day interface
+ # below a date field that can also be filled in manually. The format is always dd-mm-yyyy.
+ #
$.fn.date_picker = ()->
@each ->
new DatePicker(@)
class DatePicker
@month_names: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
constructor: (element) ->
@_container = $(element)
+ view = @_container.attr("data-view") ? 'days'
if @_container.is("input")
@_field = @_container
- event = 'focus'
+ @_container = @_field.clone().attr('name', 'display-date').insertAfter(@_field)
+ if iso_date = @_field.val()
+ [y,m,d] = iso_date.split('-')
+ @_container.val [d, DatePicker.month_names[m-1], y].join(' ')
+ @_field.hide()
+ @_event = 'focus'
+ @_simple = true
else
@_field = @_container.find('input')
- event = 'click'
+ @_event = 'click'
+ @_simple = false
@_mon = @_container.find('span.mon')
@_dom = @_container.find('span.dom')
@_year = @_container.find('span.year')
@@ -26,9 +38,9 @@ jQuery ($) ->
calendars: 1
date: initial_date
current: initial_date
- view: 'days'
+ view: view
position: 'bottom'
- showOn: event
+ showOn: @_event
onChange: @setDate
getDate: () =>
@@ -36,16 +48,19 @@ jQuery ($) ->
new Date(Date.parse(value))
setDate: (date) =>
+ $('.datepicker').hide()
if date
- $('.datepicker').hide()
d = $.zeroPad(date.getDate())
- m = DatePicker.month_names[date.getMonth()]
+ m = date.getMonth()
y = date.getFullYear()
- dateString = [y, m, d].join('-')
- @_field.val(dateString)
- @_dom?.text(d)
- @_mon?.text(m)
- @_year?.text(y)
+ realDateString = [y, $.zeroPad(m+1), $.zeroPad(d)].join('-')
+ @_field.val(realDateString)
+ if @_simple
+ @_container.val [d, DatePicker.month_names[m], y].join(' ')
+ else
+ @_dom.text(d)
+ @_mon.text(DatePicker.month_names[m])
+ @_year.text(y)
class TimePicker
@@ -83,15 +98,16 @@ jQuery ($) ->
-
$.fn.file_picker = () ->
@each ->
new FilePicker @
- $.fn.click_proxy = (target_selector) ->
- this.bind "click", (e) ->
- e.preventDefault()
- $(target_selector).click()
+ $.fn.click_proxy = () ->
+ @each ->
+ target = $(@).attr('data-affected')
+ $(@).bind "click", (e) ->
+ e.preventDefault() if e
+ $(target).trigger "click"
class FilePicker
constructor: (element) ->
@@ -320,17 +336,17 @@ jQuery ($) ->
new PasswordMeter(@)
@
-
class PasswordMeter
constructor: (element) ->
@_container = $(element)
@_warnings = @_container.find('[data-role="warnings"]')
+ @_suggestions = @_container.find('[data-role="suggestions"]')
@_gauge = @_container.find('[data-role="gauge"]')
@_score = @_container.find('[data-role="score"]')
@_notes = @_container.find('[data-role="notes"]')
@_original_warning = @_warnings.html()
@_original_notes = @_notes.html()
- @_ready = false
+ @_zxcvbn_ready = false
$.withZxcbvn =>
@_ready = true
@@ -343,7 +359,7 @@ jQuery ($) ->
tooShort: () =>
@clear()
@_container.addClass('s0')
- @_warnings.text("Password too short. Please continue.")
+ @_warnings.text("Password too short.")
check: (value) =>
if @_ready
@@ -352,13 +368,11 @@ jQuery ($) ->
result.score
display: (result) =>
- @_container.removeClass('s0 s1 s2 s3 s4 acceptable').addClass('s' + result.score)
- @_warnings.text("")
- if result.score < 3
- @_warnings.text("Warning: " + result.feedback.warning) if result.feedback?.warning
+ if result.score < 2
+ @_warnings.text(result.feedback.warning) if result.feedback?.warning
+ @_suggestions.text(result.feedback?.suggestions)
else
- @_container.addClass('acceptable')
-
+ @_container.removeClass('s0 s1 s2 s3 s4 acceptable')
diff --git a/app/assets/javascripts/ed/views/_helpers.coffee b/app/assets/javascripts/ed/views/_helpers.coffee
index 976bd0adf..e2785178c 100644
--- a/app/assets/javascripts/ed/views/_helpers.coffee
+++ b/app/assets/javascripts/ed/views/_helpers.coffee
@@ -104,7 +104,7 @@ class Ed.Views.AssetInserter extends Ed.View
# Wrap around an embedded asset to present controls for editing or replacing it.
# The editor is responsible for adding pickers, stylers, importers and so on.
# We also catch some events here and pass them on, so as to present a broad target,
-# including click to select and drop to upload.
+# including click to select and drop to upload.
#
class Ed.Views.AssetEditor extends Ed.View
defaultSize: "full"
@@ -608,28 +608,47 @@ class Ed.Views.Toolbar extends Ed.View
allowMultiParagraphSelection: true
buttons: [
name: 'bold'
- contentDefault: ''
+ contentDefault: ''
,
name: 'italic'
- contentDefault: ''
+ contentDefault: ''
,
name: 'anchor'
- contentDefault: ''
+ contentDefault: ''
,
name: 'orderedlist'
- contentDefault: ''
+ contentDefault: ''
,
name: 'unorderedlist'
- contentDefault: ''
+ contentDefault: ''
,
name: 'h2'
- contentDefault: ''
+ contentDefault: ''
,
name: 'h3'
- contentDefault: ''
+ contentDefault: ''
,
name: 'removeFormat'
- contentDefault: ''
+ contentDefault: ''
]
@@ -679,7 +698,7 @@ class Ed.Views.ImageWeighter extends Ed.Views.MenuView
events:
"click @ui.head": "toggleMenu"
- bindings:
+ bindings:
"input.weight": "main_image_weighting"
@@ -695,7 +714,7 @@ class Ed.Views.ProgressBar extends Ed.View
template: _.noop
tagName: "span"
className: "ed-progress"
-
+
bindings:
":el":
observe: "progressing"
@@ -757,9 +776,3 @@ class Ed.Views.Helper extends Ed.View
@$el.removeClass 'showing'
else
@$el.addClass 'showing'
-
-
-
-
-
-
diff --git a/app/assets/stylesheets/droom.css.sass b/app/assets/stylesheets/droom.css.sass
index ca54aa265..f501235cb 100644
--- a/app/assets/stylesheets/droom.css.sass
+++ b/app/assets/stylesheets/droom.css.sass
@@ -731,9 +731,13 @@ ul.downloads
padding: 0
list-style: none
+a.no-bg-image
+ background-image: none !important
+ padding: 6px 0 0 0 !important
+
a.document
display: inline-block
- padding: 6px 0 0 28px
+ padding: 6px 0 0 34px
min-height: 20px
line-height: 22px
color: $mid
diff --git a/app/assets/stylesheets/droom/_definitions.css.sass b/app/assets/stylesheets/droom/_definitions.css.sass
index 13d7b732c..c3ea012ee 100644
--- a/app/assets/stylesheets/droom/_definitions.css.sass
+++ b/app/assets/stylesheets/droom/_definitions.css.sass
@@ -12,6 +12,7 @@ $coolgrey9: #747679
$coolgrey10: #616265
$coolgrey11: #4d4e53
$black6: #52555f
+$pearl_black: #101820
$rubine: #d1005d
$blue: #2c91c9
$darkblue: #226c95
@@ -21,6 +22,7 @@ $reddish: #d34a4a
$darkred: #b92a23
$purple: #a27ec3
$red: #ed1c24
+$yellow: #ffa500
$palered: lighten($red, 30)
$lightred: #C0504D
diff --git a/app/assets/stylesheets/droom/_events.sass b/app/assets/stylesheets/droom/_events.sass
index 57158925c..3b487cbd3 100644
--- a/app/assets/stylesheets/droom/_events.sass
+++ b/app/assets/stylesheets/droom/_events.sass
@@ -44,9 +44,16 @@ div.event, form.event, div.project
p.practicalities
margin: 0
margin-top: 0
- color: $mid
+ color: $pearl_black
span.admin
margin-left: 20px
+ span.location
+ display: block
+ padding-bottom: 5px
+ div.detail
+ display: flex
+ flex-wrap: nowrap
+ padding-top: 5px
p.visibility
float: right
margin: 10px 16px 0 10px
@@ -128,5 +135,3 @@ div.timepicker
@media (max-width: 700px)
.datemark
margin-left: 0
-
-
\ No newline at end of file
diff --git a/app/assets/stylesheets/droom/_forms.css.sass b/app/assets/stylesheets/droom/_forms.css.sass
index b92af2a91..81fa2e809 100644
--- a/app/assets/stylesheets/droom/_forms.css.sass
+++ b/app/assets/stylesheets/droom/_forms.css.sass
@@ -1,5 +1,4 @@
div.popup
-
input[type="text"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"], input[type="date"], input[type="time"], textarea, [contenteditable]:not([contenteditable=false]),
padding: 8px
border: 1px solid $pale
@@ -30,6 +29,7 @@ input[type="text"], input[type="password"], input[type="email"], input[type="tel
&[disabled]
color: $pale
background-color: #fff
+ background-image: none
border-color: $verypale
opacity: 0.75
&.valid, &.invalid
@@ -40,6 +40,11 @@ input[type="text"], input[type="password"], input[type="email"], input[type="tel
background-position: right 3px
&.invalid
background-position: right -97px
+ &.waiting
+ background:
+ image: image-url('droom/spinner.gif')
+ position: right 3px top 3px
+ repeat: no-repeat
&::-webkit-input-placeholder
color: $pale
font-weight: lighter
@@ -76,6 +81,8 @@ input[type="text"], input[type="password"], input[type="email"], input[type="tel
select
position: relative
-webkit-appearance: none
+ -webkit-border-radius: 0
+ -webkit-padding: 8px
-webkit-padding-end: 24px
font-size: 1.2em
border: 1px solid $pale
@@ -90,13 +97,11 @@ select
image: image-url('droom/symbols/select.svg')
size: 10px
repeat: no-repeat
- &:hover
- color: $hover
&.small
font-size: 0.9em
-webkit-padding-end: 16px
-input[type="submit"]
+input[type="submit"], button.confirm
+button($green)
display: inline-block
transition: width 0.5s ease-out, height 0.5s ease-out, font-size 0.5s ease-out, padding 0.5s ease-out
@@ -114,10 +119,28 @@ input[type="submit"]
image: image-url('droom/spinner.gif')
&.positive
+button($green, white)
- &.negative
+ &.negative, &.dangerous
+button($red, white)
+ &.edit
+ +button($blue, white)
+ &.publish
+ +button($purple, white)
+ &.reset
+ +button($pale, white)
+ padding: 9px 14px
+ &.new
+ +button($green, white)
+ &.back
+ +button($mid, white)
+ &.shortcut
+ +button($reddish, white)
+ &.minor
+ +button($pale, white)
+ &.cancel
+ +button($pale, white)
&.large
font-size: 1.5em
+ padding: 20px
&.next
float: right
&.disabled
@@ -125,6 +148,9 @@ input[type="submit"]
color: $mid
box-shadow: none
+input:-webkit-autofill, input:-webkit-autofill:hover, input:-webkit-autofill:focus, textarea:-webkit-autofill, textarea:-webkit-autofill:hover, textarea:-webkit-autofill:focus, select:-webkit-autofill, select:-webkit-autofill:hover, select:-webkit-autofill:focus
+ transition: background-color 5000s ease-in-out 0s
+
#margin
input[type="text"], input[type="email"], input[type="password"]
box-sizing: border-box
@@ -144,9 +170,15 @@ input[type="submit"]
min-height: 4em
fieldset.disabled
+ label
+ color: $pale
input[type="submit"]
+button(white, $pale)
+span.disabled, p.disabled
+ label
+ color: $pale
+
label
&.required
font-weight: bold
@@ -158,6 +190,8 @@ label
display: inline-block
vertical-align: top
white-space: nowrap
+ margin-right: 6px
+ overflow: hidden
.error
color: $error
@@ -206,6 +240,13 @@ form.waiting
textarea
line-height: 1.5
+ &.small
+ font-size: 85%
+ &.medium
+ height: 142px
+ &.long
+ height: 360px
+
// checkbox cosmetics
diff --git a/app/assets/stylesheets/droom/_menus.css.sass b/app/assets/stylesheets/droom/_menus.css.sass
index b4bda3ddf..3c051d7ab 100644
--- a/app/assets/stylesheets/droom/_menus.css.sass
+++ b/app/assets/stylesheets/droom/_menus.css.sass
@@ -1,28 +1,33 @@
-div.menu
- position: absolute
- z-index: 50
- display: none
- box-shadow: 0 4px 6px $shadow
- background-color: $admin
- ul.actions
- padding: 0
- margin: 8px 10px 3px 5px
- list-style: none
- font-size: 14px
- li
- a
- color: white !important
- min-height: 16px
- &.add
- +menu_link("droom/menu/smalladd.png")
- &.edit
- +menu_link("droom/menu/smalledit.png")
- &.delete
- +menu_link("droom/menu/smalldelete.png")
- &.download
- +menu_link("droom/menu/smalldownload.png")
- &.send
- +menu_link("droom/menu/smalledit.png")
+// div.menu
+// position: absolute
+// z-index: 50
+// display: none
+// box-shadow: 0 4px 6px $shadow
+// background-color: $admin
+// ul.actions
+// padding: 0
+// margin: 8px 10px 3px 5px
+// list-style: none
+// font-size: 14px
+// text-align: justify
+// li
+// a
+// color: white !important
+// min-height: 16px
+// &.add
+// +menu_link("droom/menu/smalladd.png")
+// &.edit
+// +menu_link("droom/menu/smalledit.png")
+// &.delete
+// +menu_link("droom/menu/smalldelete.png")
+// &.download
+// +menu_link("droom/menu/smalldownload.png")
+// &.send
+// +menu_link("droom/menu/smalledit.png")
+// &.all
+// +menu_link("droom/menu/smalladd.png")
+// &.read_only
+// +menu_link("droom/menu/smalledit.png")
a.menu
display: inline-block
diff --git a/app/assets/stylesheets/droom/_pages.css.sass b/app/assets/stylesheets/droom/_pages.css.sass
index 3944b816f..506b4d442 100644
--- a/app/assets/stylesheets/droom/_pages.css.sass
+++ b/app/assets/stylesheets/droom/_pages.css.sass
@@ -2,6 +2,15 @@ $page_width: 86%
$page_max_width: 65rem
h1.pagetitle
+ div.button-group
+ display: inline-block
+ width: 54%
+ float: right
+ span.action
+ font-weight: lighter
+ font-size: 16px
+ letter-spacing: 0.2px
+ padding-left: 20px
a.breadhead:before
content: "< "
@@ -410,4 +419,4 @@ aside#page_controls
padding-top: 3em
margin-top: 3em
border-top: 2px solid $dark
- background-color: $coolgrey00
\ No newline at end of file
+ background-color: $coolgrey00
diff --git a/app/assets/stylesheets/droom/_popups.css.sass b/app/assets/stylesheets/droom/_popups.css.sass
index 3fc661ad1..87d7827b6 100644
--- a/app/assets/stylesheets/droom/_popups.css.sass
+++ b/app/assets/stylesheets/droom/_popups.css.sass
@@ -29,7 +29,6 @@ div.popup
+pale
margin-top: 0
-div.popup
#event, #document, #user, #group, #event_type, #membership, #folder
max-width: 580px
> p
@@ -38,6 +37,7 @@ div.popup
div.popup
position: absolute
overflow-x: hidden
+ overflow-y: auto
top: 10px
left: 55px
min-height: 100px
@@ -45,6 +45,7 @@ div.popup
max-width: 80vw
width: 580px
height: auto
+ padding: 0
z-index: 6000
background-color: $coolgrey00
box-shadow: 1px 3px 8px $shadow
@@ -55,6 +56,7 @@ div.popup
opacity: 0.8
&.help
width: 360px
+
.header
position: relative
box-sizing: border-box
@@ -64,16 +66,21 @@ div.popup
background-color: $text
padding: 8px 10px 4px 20px
cursor: move
+ z-index: 6010
h2
position: relative
height: 24px
line-height: 27px
+ box-sizing: border-box
margin: 0
margin-right: 40px
font-weight: normal
font-size: 0.9em
color: white
white-space: nowrap
+ span.context
+ font-weight: normal
+ color: $verypale
a.closer
display: block
float: right
@@ -114,10 +121,6 @@ div.popup
margin-top: 0.5em
.header
- position: relative
- top: 0
- left: 0
- width: 100%
a.next, a.prev, span.next, span.prev
position: relative
display: inline-block
diff --git a/app/assets/stylesheets/droom/_symbols.sass b/app/assets/stylesheets/droom/_symbols.sass
index 83da468da..fcab8898a 100644
--- a/app/assets/stylesheets/droom/_symbols.sass
+++ b/app/assets/stylesheets/droom/_symbols.sass
@@ -16,6 +16,22 @@ svg.icon
margin: 0
&+span
display: inline-block
+ &.pint-sized
+ width: 20px
+ height: 18px
+ &.pint-sized2
+ width: 20px
+ height: 20px
+ margin-top: -2px
+ margin-left: 2px
+ @media not all and (min-resolution:.001dpcm)
+ @supports (-webkit-appearance:none) and (stroke-color:transparent)
+ height: 16px
+ margin-left: 0
+ @-moz-document url-prefix()
+ height: 20px
+ margin: 2px
+
&.small
width: 20px
height: 20px
@@ -23,7 +39,12 @@ svg.icon
&+span
min-height: 20px
width: calc(100% - 32px)
-
+ &.medium
+ width: 24px
+ height: 30px
+ &+span
+ min-height: 30px
+ width: calc(100% - 22px)
svg.prefix, svg.suffix
width: 1.2em
height: 1.2em
@@ -71,4 +92,4 @@ svg.red
h1
a.edit
svg.icon
- vertical-align: bottom
\ No newline at end of file
+ vertical-align: bottom
diff --git a/app/assets/stylesheets/droom/_toggles.sass b/app/assets/stylesheets/droom/_toggles.sass
index 4cad7934d..d4297cfbd 100644
--- a/app/assets/stylesheets/droom/_toggles.sass
+++ b/app/assets/stylesheets/droom/_toggles.sass
@@ -70,11 +70,13 @@ table.toggles
td.toggle
min-height: 32px
text-align: center
- span.no, span.yes, a.yes, a.no
+ span.no, span.yes, a.yes, a.no, a.read
+icon("droom/minisymbols.png")
margin: 5px 0 0 3px
&.yes
background-position: 0 -384px
+ &.read
+ background-position: 0 -455px
&.no
background-position: 0 -528px
opacity: 0.3
@@ -89,7 +91,7 @@ table.toggles
opacity: 0.3
a.no:hover
background-position: 0 -648px
- a.yes:hover
+ a.yes:hover, a.read:hover
background-position: 0 -480px
tr.unconfirmed
td, th, td.name
@@ -97,4 +99,3 @@ table.toggles
a
color: $pale
+hover
-
diff --git a/app/assets/stylesheets/droom/_uploader.css.sass b/app/assets/stylesheets/droom/_uploader.css.sass
index 6f83ee42c..69ff14988 100644
--- a/app/assets/stylesheets/droom/_uploader.css.sass
+++ b/app/assets/stylesheets/droom/_uploader.css.sass
@@ -46,10 +46,10 @@ ul.uploads
height: 26px
width: 28px
margin-top: -2px
- background:
- repeat: no-repeat
- position: 0 0
- image: image-url('droom/icons.png')
+ // background:
+ // repeat: no-repeat
+ // position: 0 0
+ // image: image-url('droom/icons.png')
span.filename
color: $pale
display: inline-block
@@ -184,6 +184,3 @@ ul.uploads
width: 120px
padding: 5px
margin: 20px 0 0 0
-
-
-
diff --git a/app/channels/droom/changes_channel.rb b/app/channels/droom/changes_channel.rb
new file mode 100644
index 000000000..491184740
--- /dev/null
+++ b/app/channels/droom/changes_channel.rb
@@ -0,0 +1,13 @@
+module Droom
+ class ChangesChannel < Channel
+
+ def subscribed
+ stream_from "changes"
+ end
+
+ def unsubscribed
+ # No cleanup
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/app/channels/droom/channel.rb b/app/channels/droom/channel.rb
new file mode 100644
index 000000000..b60d74e15
--- /dev/null
+++ b/app/channels/droom/channel.rb
@@ -0,0 +1,5 @@
+module Droom
+ class Channel < ActionCable::Channel::Base
+
+ end
+end
diff --git a/app/channels/droom/connection.rb b/app/channels/droom/connection.rb
new file mode 100644
index 000000000..adeb99085
--- /dev/null
+++ b/app/channels/droom/connection.rb
@@ -0,0 +1,20 @@
+module Droom
+ class Connection < ActionCable::Connection::Base
+ identified_by :current_user
+
+ def connect
+ self.current_user = find_verified_user
+ end
+
+ protected
+
+ def find_verified_user
+ if user = env['warden'].user
+ user
+ else
+ reject_unauthorized_connection
+ end
+ end
+
+ end
+end
diff --git a/app/controllers/droom/address_types_controller.rb b/app/controllers/droom/address_types_controller.rb
index f4eb7662e..c67bd0e82 100644
--- a/app/controllers/droom/address_types_controller.rb
+++ b/app/controllers/droom/address_types_controller.rb
@@ -17,7 +17,7 @@ def new
end
def create
- @address_type.update_attributes(address_type_params)
+ @address_type.update(address_type_params)
respond_with @address_type
end
@@ -26,7 +26,7 @@ def edit
end
def update
- @address_type.update_attributes(address_type_params)
+ @address_type.update(address_type_params)
respond_with @address_type
end
diff --git a/app/controllers/droom/api/api_controller.rb b/app/controllers/droom/api/api_controller.rb
index ad54340a4..9d1a8f07e 100644
--- a/app/controllers/droom/api/api_controller.rb
+++ b/app/controllers/droom/api/api_controller.rb
@@ -27,19 +27,6 @@ def blew_up(exception)
render json: { errors: exception.message }.to_json, status: :internal_server_error
end
- def echo_auth
- Rails.logger.warn "??? token_and_options: #{ActionController::HttpAuthentication::Token.token_and_options(request).inspect}"
- Rails.logger.warn " token auth header is #{request.headers["HTTP_AUTHORIZATION"]}"
- end
-
- def echo_user_status
- Rails.logger.warn ">>> user_signed_in? is #{user_signed_in?.inspect}"
- if user_signed_in?
- Rails.logger.warn " current_user is #{current_user.inspect}"
- Rails.logger.warn " permissions: is #{current_user.permission_codes.inspect}"
- end
- end
-
def name_from_controller
params[:controller].sub("Controller", "").underscore.split('/').last
end
diff --git a/app/controllers/droom/api/events_controller.rb b/app/controllers/droom/api/events_controller.rb
index 686004def..4d5c705f2 100644
--- a/app/controllers/droom/api/events_controller.rb
+++ b/app/controllers/droom/api/events_controller.rb
@@ -14,7 +14,7 @@ def show
end
def update
- @event.update_attributes(event_params)
+ @event.update(event_params)
render json: @event
end
diff --git a/app/controllers/droom/api/images_controller.rb b/app/controllers/droom/api/images_controller.rb
index 6e817ea8b..e1f5a862c 100644
--- a/app/controllers/droom/api/images_controller.rb
+++ b/app/controllers/droom/api/images_controller.rb
@@ -15,7 +15,7 @@ def show
end
def update
- if @image.update_attributes(image_params)
+ if @image.update(image_params)
return_image
else
return_errors
diff --git a/app/controllers/droom/api/organisations_controller.rb b/app/controllers/droom/api/organisations_controller.rb
index c2ed3f901..a49ce9210 100644
--- a/app/controllers/droom/api/organisations_controller.rb
+++ b/app/controllers/droom/api/organisations_controller.rb
@@ -12,7 +12,7 @@ def show
end
def update
- if @organisation.update_attributes(organisation_params)
+ if @organisation.update(organisation_params)
return_organisation
else
return_errors
diff --git a/app/controllers/droom/api/sessions_controller.rb b/app/controllers/droom/api/sessions_controller.rb
index 1f1770007..f2223f8e3 100644
--- a/app/controllers/droom/api/sessions_controller.rb
+++ b/app/controllers/droom/api/sessions_controller.rb
@@ -3,6 +3,7 @@ class SessionsController < Devise::SessionsController
include Droom::Concerns::LocalApi
respond_to :json
+ # skip_before_action :authenticate_user!, raise: false
skip_before_action :verify_authenticity_token, raise: false
before_action :set_access_control_headers
@@ -22,9 +23,9 @@ def create
end
# This is called on every request by a remote service.
- # Lots of care has to be taken here, to respond quickly but lapse correctly,
+ # Care has to be taken here, to respond quickly but lapse correctly,
# and never to set up a cascade of mutual enquiry.
- # also must make sure that we check, not sign in. Signing in will create a new session id...
+ # also must make sure that we check, *not sign in*, as signing in would create a new session id.
#
def authenticate
token = params[:tok]
@@ -38,6 +39,7 @@ def authenticate
render json: { errors: "Session timed out" }, status: :unauthorized
else
bypass_sign_in @user
+ @user.set_last_request_at!
render json: @user, serializer: Droom::UserAuthSerializer
end
else
@@ -62,10 +64,12 @@ def deauthenticate
end
end
-
def api_controller?
true
end
+ def devise_controller?
+ true
+ end
end
end
\ No newline at end of file
diff --git a/app/controllers/droom/api/users_controller.rb b/app/controllers/droom/api/users_controller.rb
index 181f10cfb..93465028f 100644
--- a/app/controllers/droom/api/users_controller.rb
+++ b/app/controllers/droom/api/users_controller.rb
@@ -19,8 +19,17 @@ def whoami
render json: current_user
end
+ # This is a background call to request the user information necessary for session creation.
+ # It usually happens on acceptable of an invitation, or some other situation where
+ # a remote object is triggering user confirmation or automatic login.
+ #
+ def authenticable
+ @user.ensure_unique_session_id!
+ render json: @user, serializer: Droom::UserAuthSerializer
+ end
+
def update
- @user.update_attributes(user_params)
+ @user.update(user_params)
render json: @user
end
@@ -51,6 +60,9 @@ def find_or_create_user
end
if params[:user][:email].present?
@user ||= Droom::User.where(email: params[:user][:email]).first
+ unless @user
+ @user ||= Droom::Email.where(email: params[:user][:email]).first.try(:user)
+ end
end
end
params = user_params
@@ -71,7 +83,7 @@ def get_users
end
def user_params
- params.require(:user).permit(:uid, :person_uid, :title, :family_name, :given_name, :chinese_name, :honours, :affiliation, :email, :phone, :mobile, :description, :address, :post_code, :correspondence_address, :country_code, :organisation_id, :female, :defer_confirmation, :send_confirmation, :password, :password_confirmation, :confirmed, :confirmed_at, :image_data, :image_name)
+ params.require(:user).permit(:uid, :person_uid, :title, :family_name, :given_name, :chinese_name, :honours, :affiliation, :email, :phone, :mobile, :description, :address, :post_code, :correspondence_address, :country_code, :organisation_id, :female, :defer_confirmation, :send_confirmation, :password, :password_confirmation, :confirmed, :confirmed_at, :image_data, :image_name, :last_request_at)
end
end
diff --git a/app/controllers/droom/api/venues_controller.rb b/app/controllers/droom/api/venues_controller.rb
index a41b4f348..4fc3dcbd9 100644
--- a/app/controllers/droom/api/venues_controller.rb
+++ b/app/controllers/droom/api/venues_controller.rb
@@ -14,7 +14,7 @@ def show
end
def update
- @venue.update_attributes(venue_params)
+ @venue.update(venue_params)
render json: @venue
end
diff --git a/app/controllers/droom/api/videos_controller.rb b/app/controllers/droom/api/videos_controller.rb
index 73e1ad53a..f482d8190 100644
--- a/app/controllers/droom/api/videos_controller.rb
+++ b/app/controllers/droom/api/videos_controller.rb
@@ -14,7 +14,7 @@ def show
end
def update
- if @video.update_attributes(video_params)
+ if @video.update(video_params)
return_video
else
return_errors
diff --git a/app/controllers/droom/concerns/controller_helpers.rb b/app/controllers/droom/concerns/controller_helpers.rb
index b3a0f36e7..0b92e121e 100644
--- a/app/controllers/droom/concerns/controller_helpers.rb
+++ b/app/controllers/droom/concerns/controller_helpers.rb
@@ -20,18 +20,19 @@ module Droom::Concerns::ControllerHelpers
before_action :check_user_is_confirmed, except: [:cors_check, :setup], unless: :devise_controller?
before_action :check_user_setup, except: [:cors_check, :setup], unless: :devise_controller?
before_action :check_user_has_organisation, except: [:cors_check, :setup_organisation], unless: :devise_controller?
- before_action :check_data_room_permission, except: [:cors_check, :set_password]
+ before_action :check_data_room_permission, except: [:cors_check, :set_password, :setup], unless: :devise_controller?
before_action :note_current_user, except: [:cors_check]
before_action :set_section, except: [:cors_check]
before_action :set_access_control_headers
skip_before_action :verify_authenticity_token, only: [:cors_check], raise: false
- after_action :update_auth_cookie, unless: :api_controller?
+ after_action :update_auth_cookie, except: [:cors_check], unless: :api_controller?
layout :no_layout_if_pjax
end
+
# Usually overridden in a base ApiController
#
def api_controller?
@@ -47,7 +48,7 @@ def html_request?
def cors_check
head :ok
end
-
+
def set_access_control_headers
if request.env["HTTP_ORIGIN"].present? && Droom.cors_domains.empty? || Droom.cors_domains.include?(request.env["HTTP_ORIGIN"])
headers['Access-Control-Allow-Origin'] = request.env["HTTP_ORIGIN"]
@@ -121,8 +122,10 @@ def check_data_room_permission
#
def update_auth_cookie
if user_signed_in? && current_user.unique_session_id?
+ Rails.logger.warn "✅ setting auth_cookie after #{request.fullpath}"
Droom::AuthCookie.new(warden.cookies).set(current_user)
else
+ Rails.logger.warn "⚠️ unsetting auth_cookie after #{request.fullpath}"
Droom::AuthCookie.new(warden.cookies).unset
end
end
@@ -146,14 +149,16 @@ def set_exception_context
## Error responses
#
def not_authorized(exception)
+ Rails.logger.warn "⚠️ not_authorized"
respond_to do |format|
- format.html { render :file => "#{Rails.root}/public/401.html", :status => :forbidden, :layout => false }
+ format.html { render :file => "#{Rails.root}/public/403.html", :status => :forbidden, :layout => false }
format.js { head :unauthorized }
format.json { head :unauthorized }
end
end
def not_allowed(exception)
+ Rails.logger.warn "⚠️ not_allowed"
respond_to do |format|
format.html { render :file => "#{Rails.root}/public/403.html", :status => :forbidden, :layout => false }
format.js { head :forbidden }
@@ -162,6 +167,7 @@ def not_allowed(exception)
end
def not_found(exception)
+ Rails.logger.warn "⚠️ not_found"
@error = exception.message
Honeybadger.notify(exception)
respond_to do |format|
@@ -183,6 +189,7 @@ def check_user_is_confirmed
end
def prompt_for_confirmation
+ Rails.logger.warn "⚠️ prompt_for_confirmation"
render template: "/devise/registrations/confirm", locals: {resource: current_user}
end
@@ -194,6 +201,7 @@ def check_user_setup
end
def prompt_for_setup
+ Rails.logger.warn "⚠️ prompt_for_setup"
render template: "/droom/users/setup", locals: {user: current_user}
end
@@ -210,11 +218,13 @@ def check_user_has_organisation
end
def prompt_for_organisation
+ Rails.logger.warn "⚠️ prompt_for_organisation"
@organisations = Droom::Organisation.matching_email(current_user.email)
render template: "/droom/users/setup_organisation"
end
def await_organisation_approval
+ Rails.logger.warn "⚠️ await_organisation_approval"
@organisations = Droom::Organisation.matching_email(current_user.email)
render template: "/droom/users/await_organisation_approval"
end
@@ -285,4 +295,4 @@ def default_layout
Droom.config.layout
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/droom/concerns/searchable.rb b/app/controllers/droom/concerns/searchable.rb
index 1729e065a..a6c3ebcdb 100644
--- a/app/controllers/droom/concerns/searchable.rb
+++ b/app/controllers/droom/concerns/searchable.rb
@@ -1,3 +1,5 @@
+# This is used in the organisations controller and made available to non-droom classes.
+
module Droom::Concerns::Searchable
extend ActiveSupport::Concern
diff --git a/app/controllers/droom/confirmations_controller.rb b/app/controllers/droom/confirmations_controller.rb
index ed42fb49b..77327458a 100644
--- a/app/controllers/droom/confirmations_controller.rb
+++ b/app/controllers/droom/confirmations_controller.rb
@@ -26,7 +26,7 @@ def show
#
def update
if self.resource = resource_class.where(id: params[:id], confirmation_token: params[resource_name][:confirmation_token]).first
- result = resource.update_attributes(permitted_params)
+ result = resource.update(permitted_params)
if result && resource.password_match?
set_flash_message :notice, :confirmed
resource.confirm!
diff --git a/app/controllers/droom/documents_controller.rb b/app/controllers/droom/documents_controller.rb
index 29fc3f6ce..b9bc58cb3 100644
--- a/app/controllers/droom/documents_controller.rb
+++ b/app/controllers/droom/documents_controller.rb
@@ -36,23 +36,24 @@ def create
render :partial => 'listing'
end
end
-
+
def edit
render
end
-
+
def update
- @document.update_attributes(document_params)
+ @document.update(document_params)
render :partial => 'listing', :object => @document
end
def reposition
- @document.update_attributes(reposition_params)
+ @document.update(reposition_params)
head :ok
end
def destroy
@document.destroy
+ # @document.enqueue_for_croucher_deindexing # calling search_client method
head :ok
end
@@ -92,7 +93,7 @@ def select_documents
# ui can check for @searching if the default list is not a useful browser.
@documents = Document.search terms, fields: fields, where: criteria, order: order, per_page: @show, page: @page, highlight: highlight, aggs: aggregations
end
-
+
def document_params
if params[:document]
params.require(:document).permit(:name, :file, :description, :folder_id, :position)
@@ -112,6 +113,6 @@ def reposition_params
def get_folder
@folder = Droom::Folder.find(params[:folder_id])
end
-
+
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/droom/enquiries_controller.rb b/app/controllers/droom/enquiries_controller.rb
index 05de6d8ae..393344e55 100644
--- a/app/controllers/droom/enquiries_controller.rb
+++ b/app/controllers/droom/enquiries_controller.rb
@@ -32,13 +32,13 @@ def edit
end
def update
- @enquiry.update_attributes(enquiry_params)
+ @enquiry.update(enquiry_params)
respond_with @enquiry
end
def create
@enquiry.request = request
- if @enquiry.update_attributes(enquiry_params)
+ if @enquiry.update(enquiry_params)
render
else
render template: 'edit'
diff --git a/app/controllers/droom/event_types_controller.rb b/app/controllers/droom/event_types_controller.rb
index ca44b43cf..c491be320 100644
--- a/app/controllers/droom/event_types_controller.rb
+++ b/app/controllers/droom/event_types_controller.rb
@@ -24,12 +24,12 @@ def edit
end
def update
- @event_type.update_attributes(event_type_params)
+ @event_type.update(event_type_params)
render :partial => 'event_type'
end
def create
- if @event_type.update_attributes(event_type_params)
+ if @event_type.update(event_type_params)
render :partial => "created"
else
respond_with @event_type
diff --git a/app/controllers/droom/events_controller.rb b/app/controllers/droom/events_controller.rb
index fb63c4d97..3721ee853 100644
--- a/app/controllers/droom/events_controller.rb
+++ b/app/controllers/droom/events_controller.rb
@@ -71,7 +71,7 @@ def create
end
def update
- if @event.update_attributes(event_params)
+ if @event.update(event_params)
render :partial => "event"
else
respond_with @event
diff --git a/app/controllers/droom/folders_controller.rb b/app/controllers/droom/folders_controller.rb
index 1de47ff38..b8028b89c 100644
--- a/app/controllers/droom/folders_controller.rb
+++ b/app/controllers/droom/folders_controller.rb
@@ -28,7 +28,7 @@ def new
end
def create
- @folder.update_attributes(folder_params)
+ @folder.update(folder_params)
respond_with @folder do |format|
format.js { render :partial => "droom/folders/folder" }
end
@@ -39,7 +39,7 @@ def edit
end
def update
- @folder.update_attributes(folder_params)
+ @folder.update(folder_params)
respond_with @folder do |format|
format.js { render :partial => "droom/folders/folder" }
end
@@ -50,8 +50,34 @@ def destroy
head :ok
end
- def with_parent
-
+ def move_folder
+ respond_with @folder
+ end
+
+ def moved
+ if params.include?('new_parent_id') && params.include?('id')
+ folder = Droom::Folder.find(params[:id])
+ folder.parent_id = params[:new_parent_id]
+ folder.save
+ end
+ head :ok
+ end
+
+ def child_folders
+ if params.include?('target_parent_id')
+ target_parent_id = params[:target_parent_id]
+ mapped_children = ''
+ if target_parent_id != '' && folder = Droom::Folder.find(target_parent_id)
+ child_folders = folder.children
+ if child_folders.any?
+ mapped_children = {}
+ child_folders.map{|child|
+ mapped_children[child.id] = child.name
+ }
+ end
+ end
+ end
+ render json: mapped_children
end
protected
diff --git a/app/controllers/droom/group_invitations_controller.rb b/app/controllers/droom/group_invitations_controller.rb
index 5882a071b..62b23b4d6 100644
--- a/app/controllers/droom/group_invitations_controller.rb
+++ b/app/controllers/droom/group_invitations_controller.rb
@@ -20,7 +20,7 @@ def new
end
def create
- if @group_invitation.update_attributes(group_invitation_params)
+ if @group_invitation.update(group_invitation_params)
render :partial => "created"
else
respond_with @group_invitation
diff --git a/app/controllers/droom/group_permissions_controller.rb b/app/controllers/droom/group_permissions_controller.rb
index 196b1e3eb..82d4cf92f 100644
--- a/app/controllers/droom/group_permissions_controller.rb
+++ b/app/controllers/droom/group_permissions_controller.rb
@@ -1,25 +1,57 @@
module Droom
class GroupPermissionsController < Droom::DroomController
respond_to :js, :html
-
+
load_and_authorize_resource :group, :class => Droom::Group
load_and_authorize_resource :group_permission, :through => :group, :class => Droom::GroupPermission
-
+
def create
@group_permission.save
render :partial => 'droom/group_permissions/toggle'
end
+ def upsert
+ @group_permission = Droom::GroupPermission.find_or_initialize_by(group_permission_params)
+ @group_permission.delete_permissions(params[:read_only])
+ @group_permission.save
+
+ html_tag = ""
+ render html: html_tag.html_safe
+ end
+
+ def delete_by_ids
+ get_gp_permissions(group_permission_params)
+ @group_permission.try(:destroy_all)
+ @read_group_permission.try(:destroy_all)
+
+ html_tag = ""
+ render html: html_tag.html_safe
+ end
+
def destroy
@group_permission.destroy
render :partial => 'droom/group_permissions/toggle'
end
protected
-
+
def group_permission_params
params.require(:group_permission).permit(:permission_id, :group_id)
end
+ def get_gp_permissions(params)
+ gp_klass = Droom::GroupPermission
+ perm_klass = Droom::Permission
+
+ permission = perm_klass.find(params[:permission_id])
+ read_permission = permission.get_read_permission
+
+ @group_permission = gp_klass.where(params)
+ unless @group_permission.present?
+ params[:permission_id] = read_permission.id
+ @read_group_permission = gp_klass.where(params)
+ end
+ end
+
end
end
diff --git a/app/controllers/droom/groups_controller.rb b/app/controllers/droom/groups_controller.rb
index d71158ecf..7e7ac603b 100644
--- a/app/controllers/droom/groups_controller.rb
+++ b/app/controllers/droom/groups_controller.rb
@@ -31,12 +31,12 @@ def edit
end
def update
- @group.update_attributes(group_params)
+ @group.update(group_params)
render :partial => 'group'
end
def create
- if @group.update_attributes(group_params)
+ if @group.update(group_params)
render :partial => "created"
else
respond_with @group
diff --git a/app/controllers/droom/helps_controller.rb b/app/controllers/droom/helps_controller.rb
index 25e1f2902..42c875bf1 100644
--- a/app/controllers/droom/helps_controller.rb
+++ b/app/controllers/droom/helps_controller.rb
@@ -11,7 +11,7 @@ def new
end
def create
- if @help.update_attributes(help_params)
+ if @help.update(help_params)
redirect_to droom.help_url(@help.slug)
else
render action: :new
@@ -19,7 +19,7 @@ def create
end
def update
- if @help.update_attributes(help_params)
+ if @help.update(help_params)
redirect_to droom.help_url(@help.slug)
else
render action: :edit
diff --git a/app/controllers/droom/invitations_controller.rb b/app/controllers/droom/invitations_controller.rb
index da8e3d719..46e52cb81 100644
--- a/app/controllers/droom/invitations_controller.rb
+++ b/app/controllers/droom/invitations_controller.rb
@@ -20,7 +20,7 @@ def new
end
def create
- if @invitation.update_attributes(invitation_params)
+ if @invitation.update(invitation_params)
render :partial => "created"
else
respond_with @invitation
diff --git a/app/controllers/droom/organisation_types_controller.rb b/app/controllers/droom/organisation_types_controller.rb
index a378bb90f..a2c0ff88b 100644
--- a/app/controllers/droom/organisation_types_controller.rb
+++ b/app/controllers/droom/organisation_types_controller.rb
@@ -24,12 +24,12 @@ def edit
end
def update
- @organisation_type.update_attributes(organisation_type_params)
+ @organisation_type.update(organisation_type_params)
render :partial => 'organisation_type'
end
def create
- if @organisation_type.update_attributes(organisation_type_params)
+ if @organisation_type.update(organisation_type_params)
render :partial => "created"
else
respond_with @organisation_type
diff --git a/app/controllers/droom/organisations_controller.rb b/app/controllers/droom/organisations_controller.rb
index 8f6d5dc92..f1f831153 100644
--- a/app/controllers/droom/organisations_controller.rb
+++ b/app/controllers/droom/organisations_controller.rb
@@ -2,6 +2,7 @@ module Droom
class OrganisationsController < Droom::DroomController
include Droom::Concerns::Searchable
helper Droom::DroomHelper
+ respond_to :html, :js
load_and_authorize_resource
before_action :set_view, only: [:show, :edit, :update, :create]
@@ -22,13 +23,13 @@ def pending
end
def create
- @organisation.update_attributes(organisation_params)
+ @organisation.update(organisation_params)
@organisation.approve!(current_user)
respond_with @organisation
end
def update
- @organisation.update_attributes(organisation_params)
+ @organisation.update(organisation_params)
respond_with @organisation
end
@@ -42,8 +43,9 @@ def disapprove
redirect_to organisation_url
end
+ # always an ajax call so for now we only confirm.
def merge
- @other_org = Droom::Organisation.find(params[:other_id])
+ @other_org = Droom::Organisation.find(merge_params[:other_id])
@other_org.subsume(@organisation)
head :no_content
end
@@ -63,6 +65,10 @@ def organisation_params
end
end
+ def merge_params
+ params.require(:organisation).permit(:other_id)
+ end
+
def set_view
@view = params[:view] if %w{page listed gridded quick full status users pending subsume}.include?(params[:view])
end
diff --git a/app/controllers/droom/pages_controller.rb b/app/controllers/droom/pages_controller.rb
index 81b1879d6..b0646d4eb 100644
--- a/app/controllers/droom/pages_controller.rb
+++ b/app/controllers/droom/pages_controller.rb
@@ -6,7 +6,7 @@ class PagesController < Droom::DroomController
def new
@page = Droom::Page.new(slug: params[:slug])
- render layout: Droom.pages_layout
+ render layout: Droom.page_layout
end
def show
@@ -18,7 +18,7 @@ def edit
end
def create
- if @page.update_attributes(page_params)
+ if @page.update(page_params)
redirect_to droom.page_url(@page)
else
render action: :new
@@ -26,7 +26,7 @@ def create
end
def update
- if @page.update_attributes(page_params)
+ if @page.update(page_params)
redirect_to droom.page_url(@page)
else
render action: :edit
@@ -68,4 +68,4 @@ def page_params
end
end
-end
\ No newline at end of file
+end
diff --git a/app/controllers/droom/permissions_controller.rb b/app/controllers/droom/permissions_controller.rb
index cb3f83b29..398a99e1f 100644
--- a/app/controllers/droom/permissions_controller.rb
+++ b/app/controllers/droom/permissions_controller.rb
@@ -28,7 +28,7 @@ def edit
end
def update
- @permission.update_attributes(permission_params)
+ @permission.update(permission_params)
respond_with @service, @permission
end
diff --git a/app/controllers/droom/preferences_controller.rb b/app/controllers/droom/preferences_controller.rb
index 9a2628bc2..0c488eae4 100644
--- a/app/controllers/droom/preferences_controller.rb
+++ b/app/controllers/droom/preferences_controller.rb
@@ -6,13 +6,13 @@ class PreferencesController < Droom::DroomController
load_and_authorize_resource :through => :current_user
def create
- @preference.update_attributes(params[:preference])
+ @preference.update(params[:preference])
@preference.save
render :partial => "preference"
end
def update
- @preference.update_attributes(params[:preference])
+ @preference.update(params[:preference])
@preference.save
render :partial => "preference"
end
diff --git a/app/controllers/droom/scraps_controller.rb b/app/controllers/droom/scraps_controller.rb
index 7cee7f3f3..6434fcc5b 100644
--- a/app/controllers/droom/scraps_controller.rb
+++ b/app/controllers/droom/scraps_controller.rb
@@ -25,12 +25,12 @@ def edit
end
def update
- @scrap.update_attributes(scrap_params(@scraptype))
+ @scrap.update(scrap_params(@scraptype))
respond_with(@scrap)
end
def create
- @scrap.update_attributes(scrap_params(@scraptype))
+ @scrap.update(scrap_params(@scraptype))
respond_with(@scrap)
end
diff --git a/app/controllers/droom/services_controller.rb b/app/controllers/droom/services_controller.rb
index ada2af5eb..9c990efbc 100644
--- a/app/controllers/droom/services_controller.rb
+++ b/app/controllers/droom/services_controller.rb
@@ -21,7 +21,7 @@ def new
end
def create
- @service.update_attributes(service_params)
+ @service.update(service_params)
respond_with @service
end
@@ -30,7 +30,7 @@ def edit
end
def update
- @service.update_attributes(service_params)
+ @service.update(service_params)
respond_with @service
end
diff --git a/app/controllers/droom/users/passwords_controller.rb b/app/controllers/droom/users/passwords_controller.rb
index ada75be25..3d012a3a7 100644
--- a/app/controllers/droom/users/passwords_controller.rb
+++ b/app/controllers/droom/users/passwords_controller.rb
@@ -2,8 +2,9 @@ module Droom::Users
class PasswordsController < Devise::PasswordsController
respond_to :html, :json
before_action :set_access_control_headers
- skip_before_action :require_no_authentication, only: [:completed]
+ skip_before_action :require_no_authentication, only: [:completed, :edit]
before_action :remember_original_destination, only: [:new]
+ before_action :clear_session, only: [:edit]
def show
render
@@ -13,6 +14,19 @@ def completed
render
end
+ def clear_session
+ original_token = params[:reset_password_token]
+ reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
+ sign_out(resource_name)
+ unless Droom::User.find_by_reset_password_token(reset_password_token)
+ redirect_to droom.expired_reset_password_token_url
+ end
+ end
+
+ def expired_reset_password_token
+
+ end
+
def after_resetting_password_path_for(resource)
droom.complete_confirmation_url
end
diff --git a/app/controllers/droom/users/registrations_controller.rb b/app/controllers/droom/users/registrations_controller.rb
index f5874103f..87ade3ba0 100644
--- a/app/controllers/droom/users/registrations_controller.rb
+++ b/app/controllers/droom/users/registrations_controller.rb
@@ -1,3 +1,5 @@
+require 'net/https'
+
module Droom::Users
class RegistrationsController < Devise::RegistrationsController
before_action :set_access_control_headers
@@ -16,6 +18,24 @@ def new
end
end
+ def create
+ if helpers.check_recaptcha?
+ min_score = 0.5
+ secret_key = ENV['RECAPTCHA_SECRET_KEY']
+ token = params[:recaptcha_token]
+
+ uri = URI.parse("https://www.google.com/recaptcha/api/siteverify?secret=#{secret_key}&response=#{token}")
+ response = Net::HTTP.get_response(uri)
+ json = JSON.parse(response.body)
+ result = json['success'] && json['score'] > min_score && json['action'] == 'submit'
+
+ unless result
+ return redirect_to signup_url
+ end
+ end
+ super
+ end
+
def after_sign_up_path_for(resource)
root_url
end
diff --git a/app/controllers/droom/users_controller.rb b/app/controllers/droom/users_controller.rb
index db47385ad..de4f4459d 100644
--- a/app/controllers/droom/users_controller.rb
+++ b/app/controllers/droom/users_controller.rb
@@ -18,10 +18,15 @@ def index
@users = paginated(@users, params[:pp].presence || 24)
respond_with @users do |format|
format.js { render :partial => 'droom/users/users' }
- format.vcf { render :vcf => @users.map(&:to_vcf) }
end
end
+ def download
+ @users = @users.internal.in_name_order.includes(:emails, :phones, :addresses)
+ @users = @users.matching(params[:q]) unless params[:q].blank?
+ render :vcf => @users.map(&:to_vcf)
+ end
+
def show
respond_with @user
end
@@ -60,7 +65,8 @@ def edit
# This has to handle small preference updates over js and large account-management forms over html.
#
def update
- if @user.update_attributes(user_params)
+ @user.delete_user_permissions(user_params[:group_ids]) unless user_params[:group_ids].blank?
+ if @user.update(user_params)
respond_with @user, location: user_url(view: @view) do |format|
format.js { head :no_content }
end
@@ -85,7 +91,7 @@ def setup
flash[:notice] = t(:password_set)
redirect_to params[:destination].presence || droom.dashboard_url
else
- render
+ raise Droom::AccessDenied
end
else
render template: "/droom/users/request_password"
@@ -93,7 +99,7 @@ def setup
end
def set_organisation
- if current_user.update_attributes(set_organisation_params)
+ if current_user.update(set_organisation_params)
redirect_to params[:destination].presence || droom.dashboard_url
else
render template: "/droom/users/setup_organisation"
@@ -124,14 +130,14 @@ def search_users
filters[:groups] = params[:account_group] if params[:account_group].present?
filters[:account_confirmation] = params[:account_confirmed] if params[:account_confirmed].present?
filters[:organisation] = params[:organisation] if params[:organisation].present?
-
+
query = params[:q].presence || '*'
arguments = {
where: filters,
aggs: [:groups, :account_confirmation, :organisation],
order: {name: :asc}
}
-
+
if params[:show] == "all"
arguments[:limit] = 1000
else
@@ -166,9 +172,10 @@ def user_params
:country_code,
:mobile,
:female,
- :image
+ :image,
+ group_ids: []
]
-
+
if current_user.organisation_admin?
permitted_params += [
:organisation_admin,
@@ -181,8 +188,7 @@ def user_params
:organisation_id,
:organisation_admin,
:send_confirmation,
- :defer_confirmation,
- group_ids: []
+ :defer_confirmation
]
end
@@ -205,7 +211,7 @@ def setup_params
end
def set_organisation_params
- params.require(:user).permit(:organisation_id, organisation_attributes: [:name, :chinese_name, :url, :organisation_type_id, :description, :tags])
+ params.require(:user).permit(:organisation_id, organisation_attributes: [:name, :chinese_name, :url, :organisation_type_id, :description, :tags, :owner_id])
end
def set_view
diff --git a/app/controllers/droom/venues_controller.rb b/app/controllers/droom/venues_controller.rb
index 3a4159765..ed9d3f343 100644
--- a/app/controllers/droom/venues_controller.rb
+++ b/app/controllers/droom/venues_controller.rb
@@ -17,7 +17,7 @@ def show
end
def update
- @venue.update_attributes(params[:venue])
+ @venue.update(params[:venue])
respond_with @venue
end
diff --git a/app/helpers/droom/droom_helper.rb b/app/helpers/droom/droom_helper.rb
index 4a31db162..1a9f1e118 100644
--- a/app/helpers/droom/droom_helper.rb
+++ b/app/helpers/droom/droom_helper.rb
@@ -75,12 +75,12 @@ def allowed?(permission_code)
current_user.admin? || current_user.permitted?(permission_code)
end
- def action_menulink(thing, html_options={})
+ def action_menulink(thing, html_options={}, group=nil)
if can?(:edit, thing)
classname = thing.class.to_s.underscore.split('/').last
html_options.reverse_merge!({
:class => "",
- :data => {:menu => "#{classname}_#{thing.id}"}
+ :data => {:menu => "#{classname}_#{thing.id}#{group.try(:id)}"}
})
html_options[:class] << ' menu'
link_to t(:edit), "#", html_options if can?(:edit, thing)
@@ -210,5 +210,28 @@ def url_for_date(date)
def day_names
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
end
+
+ def check_recaptcha?
+ if ENV['RECAPTCHA_CHECK'] && ENV['RECAPTCHA_CHECK'] == 'true'
+ true
+ else
+ false
+ end
+ end
+
+ def recaptcha_execute(action)
+ id = "recaptcha_token_#{SecureRandom.hex(10)}"
+
+ raw %Q{
+
+
+ }
+ end
end
end
diff --git a/app/jobs/droom/index_document_job.rb b/app/jobs/droom/index_document_job.rb
index 481e0f5f5..5ccfc0a9f 100644
--- a/app/jobs/droom/index_document_job.rb
+++ b/app/jobs/droom/index_document_job.rb
@@ -1,3 +1,5 @@
+require 'droom'
+
module Droom
class IndexDocumentJob < ActiveJob::Base
diff --git a/app/models/droom/ability.rb b/app/models/droom/ability.rb
index 0d2d26c6d..53bc485c6 100644
--- a/app/models/droom/ability.rb
+++ b/app/models/droom/ability.rb
@@ -25,46 +25,59 @@ def initialize(user)
if !Droom.require_internal_organisation? || user.internal?
- if !Droom.require_login_permission? || user.permitted?('droom.login')
+ if user.data_room_user?
can :read, :dashboard
- can :read, Droom::Event
- can :read, Droom::Scrap
- can :read, Droom::Venue
- can :read, Droom::User
- can :read, Droom::Group
- can :read, Droom::Organisation
can :index, :suggestions
# If someone has been allowed to create something, they are generally allowed to edit or remove it.
# This rule must sit after the user rules because users have no created_by_id column.
#
- can :manage, [Droom::Event, Droom::Document, Droom::Scrap], :created_by_id => user.id
+ # can :manage, [Droom::Event, Droom::Document, Droom::Scrap], :created_by_id => user.id
# Then other abilities are determined by permissions. Our permissions are relatively abstract and
# not closely coupled to Cancan abilities. Here we map them onto more concrete operations.
#
if user.permitted?('droom.calendar')
can :manage, Droom::Event
+ can :manage, Droom::EventType
can :manage, Droom::EventSet
can :manage, Droom::Venue
can :manage, Droom::Invitation
can :manage, Droom::GroupInvitation
can :manage, Droom::AgendaCategory
+ elsif user.permitted?('droom.calendar.read')
+ can :read, Droom::Event
+ can :read, Droom::EventType
+ can :read, Droom::EventSet
+ can :read, Droom::Venue
+ can :read, Droom::Invitation
+ can :read, Droom::GroupInvitation
+ can :read, Droom::AgendaCategory
end
if user.permitted?('droom.directory')
can :manage, Droom::Group
can :manage, Droom::Organisation
can :manage, Droom::User
+ elsif user.permitted?('droom.directory.read')
+ can :read, Droom::Group
+ can :read, Droom::Organisation
+ can :read, Droom::User
end
if user.permitted?('droom.library')
can :manage, Droom::Folder
can :manage, Droom::Document
+ elsif user.permitted?('droom.library.read')
+ can :read, Droom::Folder
+ can :read, Droom::Document
end
if user.permitted?('droom.stream')
can :create, Droom::Scrap
+ can :read, Droom::Scrap
+ elsif user.permitted?('droom.stream.read')
+ can :read, Droom::Scrap
end
if user.permitted?('droom.enquiry')
@@ -83,8 +96,6 @@ def initialize(user)
end
end
- can :read, Droom::Scrap
-
else
# What can an external user do? Nothing, by default, but the main app can add permissions.
@@ -96,4 +107,4 @@ def initialize(user)
end
end
end
-end
\ No newline at end of file
+end
diff --git a/app/models/droom/calendar.rb b/app/models/droom/calendar.rb
index 6a7c0d706..be774d10c 100644
--- a/app/models/droom/calendar.rb
+++ b/app/models/droom/calendar.rb
@@ -1,7 +1,7 @@
module Droom
class Calendar < Droom::DroomRecord
include Droom::Concerns::Slugged
-
+
belongs_to :created_by, :class_name => "Droom::User"
has_many :events
diff --git a/app/models/droom/concerns/address_book_property.rb b/app/models/droom/concerns/address_book_property.rb
index fb97bf4ed..b710a1405 100644
--- a/app/models/droom/concerns/address_book_property.rb
+++ b/app/models/droom/concerns/address_book_property.rb
@@ -34,4 +34,8 @@ def undefault_others
end
end
+ def address_type_name
+ address_type.name if address_type
+ end
+
end
diff --git a/app/models/droom/concerns/changes_notified.rb b/app/models/droom/concerns/changes_notified.rb
new file mode 100644
index 000000000..e35d45875
--- /dev/null
+++ b/app/models/droom/concerns/changes_notified.rb
@@ -0,0 +1,39 @@
+module Droom::Concerns::ChangesNotified
+ extend ActiveSupport::Concern
+
+ included do
+ after_create :notify_of_creation
+ after_update :notify_of_update
+ after_destroy :notify_of_destruction
+ end
+
+ def timestamp
+ (try(:updated_at) || try(:created_at) || Time.now).to_f
+ end
+
+ def notify_of_change(event, additional_data={})
+ if Droom.config.enable_pubsub?
+ time = event == 'destroyed' ? Time.now.to_i : timestamp
+ change_data = {
+ event: event,
+ timestamp: time,
+ object_class: self.class.to_s.underscore,
+ object_id: id,
+ }
+ Droom::ChangesChannel.broadcast_to 'changes', change_data.merge(additional_data)
+ end
+ end
+
+ def notify_of_creation(additional_data={})
+ notify_of_change "created", additional_data
+ end
+
+ def notify_of_update(additional_data={})
+ notify_of_change "updated", additional_data
+ end
+
+ def notify_of_destruction(additional_data={})
+ notify_of_change "destroyed", additional_data
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/droom/concerns/published.rb b/app/models/droom/concerns/published.rb
index 762a8a2f6..2cb08dba2 100644
--- a/app/models/droom/concerns/published.rb
+++ b/app/models/droom/concerns/published.rb
@@ -23,7 +23,7 @@ def illustrated?
def publish!
unless publishing?
- update_attributes({
+ update({
published_title: title,
published_subtitle: subtitle,
published_intro: intro,
diff --git a/app/models/droom/document.rb b/app/models/droom/document.rb
index d18ed352e..283301d11 100644
--- a/app/models/droom/document.rb
+++ b/app/models/droom/document.rb
@@ -45,6 +45,8 @@ class Document < Droom::DroomRecord
scope :latest, -> limit { order("droom_documents.updated_at DESC, droom_documents.created_at DESC").limit(limit) }
+ scope :unindexed, -> { where(indexed_at: nil) }
+
def attach_to(holder)
self.folder = holder.folder
end
@@ -106,9 +108,8 @@ def file_bucket
## Search
#
- searchkick _all: false, callbacks: false, default_fields: [:name, :content], highlight: [:name, :content]
- attr_accessor :updating_index
- after_save :enqueue_for_indexing, unless: :updating_index?
+ searchkick callbacks: false, default_fields: [:name, :content], highlight: [:name, :content]
+ after_save :enqueue_for_indexing
def search_data
{
@@ -157,29 +158,28 @@ def inherit_confidentiality
end
def enqueue_for_indexing!
+ Rails.logger.debug "⚠️ enqueue_for_indexing Droom::Document #{id}"
Droom::IndexDocumentJob.perform_later(id, Time.now.to_i)
end
def enqueue_for_indexing
- if name_changed? || file_file_name_changed? || file_fingerprint_changed?
- Droom::IndexDocumentJob.perform_later(id, Time.now.to_i)
+ if saved_change_to_name? || saved_change_to_file_file_name? || saved_change_to_file_fingerprint?
+ enqueue_for_indexing!
end
end
def update_index!
- unless self.updating_index
- self.updating_index = true
- with_local_file do |path|
- @file_content = Yomu.new(path).text
- self.reindex
- end
- self.update_column(:indexed_at, Time.now)
- self.updating_index = false
+ with_local_file do |path|
+ @file_content = Yomu.new(path).text
+ self.reindex
+ self.secondary_reindex
end
+ self.update_column(:indexed_at, Time.now)
+ true
end
- def updating_index?
- !!updating_index
+ def secondary_reindex
+ # noop here
end
# Pass block to perform operations with a local file, which will be
diff --git a/app/models/droom/droom_record.rb b/app/models/droom/droom_record.rb
index 2622190ea..23fd73250 100644
--- a/app/models/droom/droom_record.rb
+++ b/app/models/droom/droom_record.rb
@@ -1,6 +1,7 @@
module Droom
class DroomRecord < ActiveRecord::Base
- include Droom::Folders
+ include Droom::Concerns::ChangesNotified
+ include Droom::Folders # TODO please can we get rid of this now?
self.abstract_class = true
end
end
\ No newline at end of file
diff --git a/app/models/droom/event.rb b/app/models/droom/event.rb
index c61da3582..8744e519b 100644
--- a/app/models/droom/event.rb
+++ b/app/models/droom/event.rb
@@ -27,6 +27,9 @@ class Event < Droom::DroomRecord
has_folder :within => :event_type #... and subfolders via agenda_categories
after_destroy :destroy_related_folder
+ around_update :update_folder_name
+
+ after_save :set_parent_folder_id
validates :start, :presence => true, :date => true
validates :finish, :date => {:after => :start, :allow_nil => true}
@@ -57,7 +60,7 @@ class Event < Droom::DroomRecord
scope :future_and_current, -> { where(['(finish > :now) OR (finish IS NULL AND start > :now)', :now => Time.zone.now]) }
scope :finished, -> { where(['(finish < :now) OR (finish IS NULL AND start < :now)', :now => Time.zone.now]) }
-
+
scope :unbegun, -> { where(['start > :now', :now => Time.zone.now])}
scope :by_finish, -> { order("finish ASC") }
@@ -96,7 +99,7 @@ class Event < Droom::DroomRecord
.group("droom_events.id")
}
- scope :matching, -> fragment {
+ scope :matching, -> fragment {
fragment = "%#{fragment}%"
where('droom_events.name like :f OR droom_events.description like :f', :f => fragment)
}
@@ -122,19 +125,19 @@ def self.in_month(year, month) # numbers. eg calendar.occurrences.in_mo
finish = start + 1.month
between(start, finish)
end
-
+
def self.in_week(year, week) # numbers, with a commercial week: eg calendar.occurrences.in_week(2010, 35)
start = DateTime.commercial(year, week)
finish = start + 1.week
between(start, finish)
end
-
+
def self.on_day (year, month, day) # numbers: eg calendar.occurrences.on_day(2010, 12, 12)
start = DateTime.civil(year, month, day)
finish = start + 1.day
between(start, finish)
end
-
+
def self.in_span(span) # Chronic::Span
between(span.begin, span.end)
end
@@ -160,7 +163,7 @@ def invite(user)
def attach(doc)
self.documents << doc
end
-
+
# We store the start and end points of the event as a single DateTime value to make comparison simple.
# The setters for date and time are overridden to pass strings through chronic's natural language parser
# and to treat numbers as epoch seconds. These should all work as you'd expect:
@@ -178,19 +181,19 @@ def finish=(value)
write_attribute :finish, parse_date(value)
end
- # For interface purposes we often want to separate date and time parts. These getters will return the
+ # For interface purposes we often want to separate date and time parts. These getters will return the
# corresponding Date or Time object.
#
# The `tod` gem makes time handling a bit more intuitive by concealing the date part of a Time object.
#
-
+
def start
tz = timezone || Time.zone
if start = read_attribute(:start)
start.in_time_zone(tz)
end
end
-
+
def start_time
Tod::TimeOfDay(start) if start
end
@@ -198,7 +201,7 @@ def start_time
def start_date
start.to_date if start
end
-
+
def finish
tz = timezone || Time.zone
if finish = read_attribute(:finish)
@@ -233,7 +236,7 @@ def duration
0
end
end
-
+
def venue_name
venue.name if venue
end
@@ -245,7 +248,7 @@ def venue_name=(name)
def find_or_create_agenda_category(category)
agenda_categories.where(category_id: category.id).first_or_create
end
-
+
def categories_for_selection
cats = categories.map{|c| [c.name, c.id] }
cats.unshift(['', ''])
@@ -266,7 +269,7 @@ def visible_to?(user)
return false if self.confidential?# || Droom.events_private_by_default?
return true
end
-
+
def detail_visible_to?(user)
return true if self.public?
return false unless user
@@ -275,7 +278,7 @@ def detail_visible_to?(user)
return false if self.private?
return true
end
-
+
def has_anyone?
invitations.any?
end
@@ -299,7 +302,7 @@ def continuing?
def finished?
start < Time.zone.now && (!finish || finish < Time.zone.now)
end
-
+
def url_with_protocol
if url? && url !~ /^https?:\/\//
"http://#{url}"
@@ -362,13 +365,21 @@ def as_search_result
:id => id
}
end
-
+
def folder_name
"#{name} (#{month_name} #{year})"
end
protected
+ # Set event_type.folder.id to event.folder.parent_id if event.event_type changed
+ #
+ def set_parent_folder_id
+ if event_type && event_type.folder
+ folder.update(parent_id: event_type.folder.id)
+ end
+ end
+
# This is mostly for ical/webcal distributions but we also use it in the API.
#
def set_uuid
@@ -398,5 +409,16 @@ def destroy_related_folder
end
end
+ def update_folder_name
+ is_changed = self.name_changed?
+ yield
+ if is_changed
+ if event_folder = self.folder
+ event_folder.name = self.folder_name
+ event_folder.save
+ end
+ end
+ end
+
end
-end
\ No newline at end of file
+end
diff --git a/app/models/droom/folder.rb b/app/models/droom/folder.rb
index f746190db..16ec663ae 100644
--- a/app/models/droom/folder.rb
+++ b/app/models/droom/folder.rb
@@ -1,7 +1,7 @@
module Droom
class Folder < Droom::DroomRecord
include ActsAsTree
- include Droom::Concerns::Slugged
+ # don't use Slugged: we need to apply a dynamic parent scope.
belongs_to :created_by, :class_name => "Droom::User"
belongs_to :holder, :polymorphic => true
@@ -9,11 +9,9 @@ class Folder < Droom::DroomRecord
has_many :personal_folders, :dependent => :destroy
acts_as_tree :order => "droom_folders.name ASC"
+ before_validation :set_properties
validates :slug, :presence => true, :uniqueness => { :scope => :parent_id }
- before_validation :set_properties
- before_validation :slug_from_name
-
default_scope -> { includes(:documents) }
scope :all_private, -> { where("#{table_name}.private = 1") }
@@ -21,7 +19,10 @@ class Folder < Droom::DroomRecord
scope :all_public, -> { where("#{table_name}.public = 1 AND #{table_name}.private <> 1 OR #{table_name}.private IS NULL") }
scope :not_public, -> { where("#{table_name}.public <> 1 OR #{table_name}.private = 1)") }
scope :by_name, -> { order("#{table_name}.name ASC") }
- scope :other_than, -> folders {where.not(id: folders.map(&:id))}
+ scope :other_than, -> folders {
+ folders = [folders].flatten
+ where.not(id: folders.map(&:id))
+ }
scope :visible_to, -> user {
if user
select('droom_folders.*')
@@ -134,7 +135,7 @@ def distribute_confidentiality
children.each {|folder| folder.set_confidentiality!(confidential?) }
end
- protected
+ protected
def set_properties
if holder
@@ -145,10 +146,26 @@ def set_properties
end
self.slug ||= holder.slug
end
- # folders originally only had slugs, so this happens from time to time
+
+ # pass new or existing slug through uniqueness check as it may have come from user or holder
+ base = slug.presence || name || "Folder"
+ self.slug = unique_slug(base)
+
+ # folders originally only had slugs, so this could happen too
self.name ||= self.slug
- self.public = !holder && (!parent || parent.public?)
- true
+ end
+
+ # Protect against slug-collision within parent folder scope.
+ #
+ def unique_slug(base)
+ slug = base
+ addendum = 0
+ skope = parent ? parent.children : Folder.loose
+ while skope.other_than(self).find_by(slug: slug)
+ addendum += 1
+ slug = "#{base}_#{addendum}"
+ end
+ slug
end
end
diff --git a/app/models/droom/group_permission.rb b/app/models/droom/group_permission.rb
index 7735e76ec..5f379fa2b 100644
--- a/app/models/droom/group_permission.rb
+++ b/app/models/droom/group_permission.rb
@@ -4,7 +4,7 @@ class GroupPermission < Droom::DroomRecord
belongs_to :permission
has_many :user_permissions, :dependent => :destroy
after_save :create_user_permissions
-
+
# This is set up such that a personal permission created by group membership can be deleted
# while still leaving in place a personal permission that was granted separately.
#
@@ -14,16 +14,26 @@ def create_user_permissions
end
end
+ def delete_permissions(read_only = false)
+ read_permission = self.permission.get_read_permission
+ read_gp_permissions = self.class.find_by(group_id: self.group_id, permission_id: read_permission.try(:id))
+ self.permission_id = read_permission.id if read_only == 'true'
+
+ # delete users permissions
+ self.user_permissions.destroy_all
+ read_gp_permissions.user_permissions.destroy_all if read_gp_permissions.present?
+ end
+
def create_permission_for(user)
user_permissions.where(:user_id => user.id, :permission_id => permission.id).first_or_create
end
-
+
def self.by_group_id
all.each_with_object({}) do |gp, hash|
hash[gp.group_id] ||= {}
hash[gp.group_id][gp.permission.id] = gp
end
end
-
+
end
-end
\ No newline at end of file
+end
diff --git a/app/models/droom/mailing_list_membership.rb b/app/models/droom/mailing_list_membership.rb
index 5bb736193..dbdc13bd0 100644
--- a/app/models/droom/mailing_list_membership.rb
+++ b/app/models/droom/mailing_list_membership.rb
@@ -71,12 +71,12 @@ class MailingListMembership < Droom::DroomRecord
#
# If no such connection is defined, we will use the local `droom_mailing_list_memberships` table.
#
- begin
- establish_connection :"mailman_#{Rails.env}"
- set_table_name Droom.mailman_table_name
- rescue ActiveRecord::AdapterNotSpecified
- Rails.logger.warn "Droom: No mailman connection configured. Using #{Rails.env} database."
- end
+ # begin
+ # establish_connection :"mailman_#{Rails.env}"
+ # set_table_name Droom.mailman_table_name
+ # rescue ActiveRecord::AdapterNotSpecified
+ # Rails.logger.warn "Droom: No mailman connection configured. Using #{Rails.env} database."
+ # end
## Associations
#
@@ -111,8 +111,8 @@ class MailingListMembership < Droom::DroomRecord
private
def set_defaults
- self.bi_lastnotice = 0
- self.bi_date = 0
+ self.bi_lastnotice = '0000-00-00'
+ self.bi_date = '0000-00-00'
self.ack = true
self.nomail = !Droom.mailing_lists_active_by_default?
self.digest = Droom.mailing_lists_digest_by_default?
diff --git a/app/models/droom/organisation.rb b/app/models/droom/organisation.rb
index 11078b6a3..b26d04e87 100644
--- a/app/models/droom/organisation.rb
+++ b/app/models/droom/organisation.rb
@@ -35,11 +35,17 @@ class Organisation < Droom::DroomRecord
attr_accessor :other_id
def self.for_selection(with_external=false)
- organisations = order("name asc")
+ organisations = approved.order("name asc")
organisations = organisations.where(external: false) unless with_external
organisations.map{|f| [f.name, f.id] }.unshift(['', ''])
end
+ def self.for_selection_with_owner(with_external=false)
+ organisations = approved.order("name asc").includes(:owner)
+ organisations = organisations.where(external: false) unless with_external
+ organisations.map{|o| ["#{o.name} (#{o.owner_name || 'No owner'})", o.id] }.unshift(['', ''])
+ end
+
def self.matching_email(email)
domain = email.split('@').last
where(joinable: true, email_domain: domain)
@@ -63,7 +69,7 @@ def self.disapprove_all
#
def approve!(approving_user=nil)
unless approved?
- self.update_attributes({
+ self.update({
approved_at: Time.now,
approved_by: approving_user,
disapproved_at: nil,
@@ -77,7 +83,7 @@ def approve!(approving_user=nil)
#
def approve
unless approved?
- self.update_attributes({
+ self.update({
approved_at: Time.now,
disapproved_at: nil,
disapproved_by: nil
@@ -91,7 +97,7 @@ def approved?
def disapprove!(user)
unless disapproved?
- self.update_attributes({
+ self.update({
disapproved_at: Time.now,
disapproved_by: user,
approved_at: nil,
@@ -216,11 +222,18 @@ def subsume(org)
self.tags << org.tags
self.chinese_name = org.chinese_name unless chinese_name?
self.description = org.description unless description?
+ self.logo = org.logo unless logo?
+ self.owner = org.owner unless owner
+ subsume_other_resources(org)
self.save
- chinese_name.destroy
+ org.destroy
end
end
+ def subsume_other_resources(org)
+ # noop here
+ end
+
def capture_owner
unless owner
if user = users.first
@@ -238,6 +251,7 @@ def search_data
{
name: name || "",
chinese_name: chinese_name || "",
+ owner_name: owner_name,
description: description,
tags: tag_names,
all_tags: tags_with_synonyms,
@@ -248,5 +262,9 @@ def search_data
}
end
+ def owner_name
+ owner.name if owner
+ end
+
end
end
\ No newline at end of file
diff --git a/app/models/droom/permission.rb b/app/models/droom/permission.rb
index 29be0b062..ed59b0cc7 100644
--- a/app/models/droom/permission.rb
+++ b/app/models/droom/permission.rb
@@ -5,13 +5,26 @@ class Permission < Droom::DroomRecord
has_many :user_permissions, :dependent => :destroy
acts_as_list :scope => :service_id
before_save :set_slug
-
+
validates :slug, :uniqueness => true
-
+
+ def get_read_permission
+ self.class.find_by(name: "#{self.name}.read")
+ end
+
+ def define_permission_color(group_permission, group, read_permission)
+ group_read_permission = Droom::GroupPermission.find_by(group_id: group.id, permission_id: read_permission.id)
+ color = 'no'
+ color = 'read' if group_read_permission && !group_read_permission.destroyed?
+ color = 'yes' if group_permission && !group_permission.destroyed?
+
+ color
+ end
+
protected
-
+
def set_slug
self.slug = [service.slug, self.name].join('.')
end
end
-end
\ No newline at end of file
+end
diff --git a/app/models/droom/user.rb b/app/models/droom/user.rb
index 26db3400e..f53a7eedd 100644
--- a/app/models/droom/user.rb
+++ b/app/models/droom/user.rb
@@ -1,3 +1,5 @@
+require 'vcard'
+
module Droom
class User < Droom::DroomRecord
include Droom::Concerns::Imaged
@@ -30,7 +32,7 @@ class User < Droom::DroomRecord
accepts_nested_attributes_for :organisation
before_validation :ensure_uid!
- before_save :ensure_authentication_token
+ before_save :ensure_authentication_token!
before_save :org_admin_if_alone
after_save :send_confirmation_if_directed
@@ -79,6 +81,12 @@ def defer_confirmation?
defer_confirmation && defer_confirmation != "false"
end
+ # For users of peripheral services we can leave it up to them to require or offer confirmation.
+ #
+ def confirmation_required?
+ !confirmed? && data_room_user?
+ end
+
# send_confirmation? is called after save by our own later confirmation mechanism.
# If the send_confirmation flag has been set, we confirm.
#
@@ -111,7 +119,7 @@ def names?
# Our old user accounts store passwords as salted sha512 digests. Current standard uses BCrypt
# so we migrate user accounts across in this rescue block whenever we hear BCrypt grumbling about the old hash.
-
+
def valid_password?(password)
begin
super(password)
@@ -129,7 +137,7 @@ def valid_password?(password)
return false
end
end
- end
+ end
# This is called from a warden hook during every authenticated request, either to the data room or its API.
# The value of last_request_at is used to invalidate stale sessions.
@@ -152,6 +160,7 @@ def reset_session_ids!
end
def clear_session_ids!
+ Rails.logger.warn "⚠️ clear_session_ids!"
self.update_columns({
session_id: "",
unique_session_id: ""
@@ -175,7 +184,7 @@ def authenticate_safely(attribute, token)
Devise.secure_compare(send(attribute), token)
end
- def ensure_authentication_token
+ def ensure_authentication_token!
if authentication_token.blank?
self.authentication_token = generate_authentication_token
end
@@ -184,6 +193,7 @@ def ensure_authentication_token
def ensure_unique_session_id!
unless unique_session_id.present?
+ Rails.logger.warn "✅ calling update_unique_session_id!"
update_unique_session_id!(Devise.friendly_token)
end
unique_session_id
@@ -229,7 +239,7 @@ def external?
end
def internal?
- !organisation && !organisation.external?
+ organisation && !organisation.external?
end
@@ -358,7 +368,7 @@ def documents
# Can hold multiple emails, phones and addresses for each user.
# Address book data is simple and always nested.
#
- has_many :emails
+ has_many :emails, :dependent => :destroy
accepts_nested_attributes_for :emails, :allow_destroy => true
has_many :phones
accepts_nested_attributes_for :phones, :allow_destroy => true
@@ -584,7 +594,7 @@ def self.for_selection
## Names
#
def title_ordinary?
- ['Mr', 'Ms', 'Mrs', '', nil].include?(title)
+ title.nil? || ['mr', 'ms', 'mrs', 'mr.', 'ms.', 'mrs.', ''].include?(title.downcase.strip)
end
def title_if_it_matters
@@ -615,19 +625,34 @@ def full_name
def to_vcf
@vcard ||= Vcard::Vcard::Maker.make2 do |maker|
maker.add_name do |n|
- n.given = name || ""
+ n.family = family_name || ""
+ n.given = given_name || ""
+ n.prefix = title unless title_ordinary?
+ end
+ emails.each do |email|
+ if email.email?
+ location = email.address_type_name || 'home'
+ maker.add_email email.email { |e| t.location = location.downcase }
+ end
+ end
+ phones.each do |phone|
+ if phone.phone?
+ location = phone.address_type_name || 'cell'
+ location = 'cell' if location == 'Mobile'
+ maker.add_tel phone.phone { |e| t.location = location.downcase }
+ end
+ end
+ addresses.each do |address|
+ location = address.address_type_name || 'home'
+ maker.add_addr {|a|
+ a.location = location.downcase
+ a.country = address.country_code || ""
+ a.region = address.region || ""
+ a.locality = address.city || ""
+ a.street = "#{address.line_1}, #{address.line_2}"
+ a.postalcode = address.postal_code || ""
+ }
end
- maker.add_addr {|a|
- a.location = 'home' # until we do this properly with multiple contact sets
- a.country = post_country || ""
- a.region = post_region || ""
- a.locality = post_city || ""
- a.street = "#{post_line1}, #{post_line2}"
- a.postalcode = post_code || ""
- }
- maker.add_tel phone { |t| t.location = 'home' } unless phone.blank?
- maker.add_tel mobile { |t| t.location = 'cell' } unless mobile.blank?
- maker.add_email email { |e| t.location = 'home' }
end
@vcard.to_s
end
@@ -854,7 +879,7 @@ def subsume(other_user)
def subsume!(other_user)
Droom::User.transaction do
- %w{emails phones addresses memberships organisations scraps documents invitations memberships user_permissions dropbox_tokens dropbox_documents personal_folders}.each do |association|
+ %w{emails phones addresses memberships scraps documents invitations memberships user_permissions dropbox_tokens dropbox_documents personal_folders}.each do |association|
self.send(association.to_sym) << other_user.send(association.to_sym)
end
%w{encrypted_password password_salt family_name given_name chinese_name title gender organisation_id description image}.each do |property|
@@ -869,6 +894,12 @@ def subsume!(other_user)
end
end
+ def delete_user_permissions(group_ids = [])
+ self.user_permissions.each do |perm|
+ group_id = perm.group_permission.group_id
+ perm.delete unless group_ids.map(&:to_i).include?(group_id)
+ end
+ end
protected
diff --git a/app/serializers/droom/user_auth_serializer.rb b/app/serializers/droom/user_auth_serializer.rb
index 9b91dcd3a..b74a25377 100644
--- a/app/serializers/droom/user_auth_serializer.rb
+++ b/app/serializers/droom/user_auth_serializer.rb
@@ -17,7 +17,6 @@ class Droom::UserAuthSerializer < ActiveModel::Serializer
:password_set,
:images
-
def name
object.colloquial_name
end
diff --git a/app/serializers/droom/user_serializer.rb b/app/serializers/droom/user_serializer.rb
index 5c7130d1d..d2cf7933c 100644
--- a/app/serializers/droom/user_serializer.rb
+++ b/app/serializers/droom/user_serializer.rb
@@ -1,6 +1,12 @@
+# This is user as data object: all the fields we need to manage or present the user.
+# It does not include authentication information.
+#
+# Also note btw that the address book is squashed down to one email, one phone and one mobile.
+# We don't yet support remote management of all that detail, but users can update their listing,
+# which has the effect of adding a new preferred address but not deleting the old.
+#
class Droom::UserSerializer < ActiveModel::Serializer
attributes :uid,
- :authentication_token,
:status,
:title,
:given_name,
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index 2787f3fd8..8935c6459 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -1,6 +1,12 @@
+- if check_recaptcha?
+ %script{src: "https://www.google.com/recaptcha/api.js?render=#{ENV['RECAPTCHA_SITE_KEY']}"}
+
= form_for(resource, as: resource_name, url: droom.register_path, method: "post") do |f|
= render "devise/shared/error_messages", resource: resource
= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: t("placeholders.registration.email"), class: "half"
- = f.submit t('registration.submit')
+ = f.submit t('registration.submit'), id: 'submitWithRecaptcha'
+
+ - if check_recaptcha?
+ = recaptcha_execute('submit')
diff --git a/app/views/droom/dashboard/_events.html.haml b/app/views/droom/dashboard/_events.html.haml
index 8d1b7f43c..ea362c881 100644
--- a/app/views/droom/dashboard/_events.html.haml
+++ b/app/views/droom/dashboard/_events.html.haml
@@ -10,9 +10,10 @@
- if calendar.events.future_and_current.count > 5
%span.addendum
=t(:click_for_more, count: calendar.events.future_and_current.count - 5)
- - if can?(:create, Droom::Event)
- %span.action
+ %span.action
+ - if can?(:create, Droom::Event)
= link_to t(:add_event), droom.new_event_url, :class => "add", :data => {:action => "popup", :affected => "#events"}
+ = link_to t(:subscribe_to_calendar), subscribe_events_url(current_user.authentication_token, protocol: "webcal", format: "ics"), class: "subscribe"
- if events.any?
= render :partial => "droom/events/event", :collection => events, :locals => {:brief => true}
@@ -24,4 +25,4 @@
- else
%p.nothing
- = t :no_events
\ No newline at end of file
+ = t :no_events
diff --git a/app/views/droom/dashboard/_past_events.haml b/app/views/droom/dashboard/_past_events.haml
index 29c0a30b1..2d612a2ce 100644
--- a/app/views/droom/dashboard/_past_events.haml
+++ b/app/views/droom/dashboard/_past_events.haml
@@ -6,7 +6,7 @@
%section
#past_events
%h2.section
- = link_to main_app.calendar_url do
+ = link_to droom.past_events_url do
= t(:recent_events)
- if calendar.events.past.count > 5
%span.addendum
diff --git a/app/views/droom/dashboard/_stream_events.html.haml b/app/views/droom/dashboard/_stream_events.html.haml
new file mode 100644
index 000000000..394cc9b01
--- /dev/null
+++ b/app/views/droom/dashboard/_stream_events.html.haml
@@ -0,0 +1,11 @@
+#suggestion_box
+
+- if calendar = Droom::Calendar.where(name: 'stream').first
+ #other
+ %h2.section
+ = t(:external_events)
+ - if can?(:create, Droom::Event)
+ %span.action
+ = link_to t(:add_external_event), droom.new_event_url(event: {calendar_id: calendar.id}), :class => "add minimal", :data => {:action => "popup", :affected => "#minor_events"}
+
+ = render "droom/calendars/event_list", calendar: calendar, events: calendar.events.by_date_descending.limit(10)
diff --git a/app/views/droom/documents/_listing.html.haml b/app/views/droom/documents/_listing.html.haml
index 4c31f72a4..b485002f1 100644
--- a/app/views/droom/documents/_listing.html.haml
+++ b/app/views/droom/documents/_listing.html.haml
@@ -5,7 +5,13 @@
- character_count = narrow ? 28 : 64
%li.document{:id => "document_#{document.id}", data: {narrow: narrow, doc_id: document.id}}
- = link_to shorten(document.name, character_count, ''), droom.folder_document_url(document.folder, document), class: "document #{document.file_extension.downcase}", target: "_blank"
+ - extension = document.file_extension.downcase
+ = link_to droom.folder_document_url(document.folder, document), class: "document #{extension == 'pdf' ? 'no-bg-image' : ''} #{extension}", target: "_blank" do
+ - if extension == 'pdf'
+ %svg.icon.medium
+ %use{"xlink:href" => "#pdf_symbol"}
+ %span
+ = shorten(document.name, character_count, '')
- unless omit_menu
= action_menulink(document)
= action_menu(document)
diff --git a/app/views/droom/events/_event.html.haml b/app/views/droom/events/_event.html.haml
index 23a94b65f..cf5b2e4b3 100644
--- a/app/views/droom/events/_event.html.haml
+++ b/app/views/droom/events/_event.html.haml
@@ -6,7 +6,7 @@
- event_master_id = event.master_id || event.id
- if @seen_events[event_master_id]
- - repeating = true
+ - repeating = true
- brief = true
- @seen_events[event_master_id] = true;
@@ -16,7 +16,7 @@
- cssclasses << 'invited' if event.attended_by?(current_user)
- div_data = {refreshing: event_path(event, format: :js)}
-- if show_folder && !full_attachments
+- if show_folder
- div_data[:droppable] = droom.folder_documents_path(event.folder)
- div_data[:queue] = "#queue_to_#{event.folder.id}"
- div_data[:refreshes] = "#event_#{event.id}"
@@ -37,27 +37,33 @@
= action_menu(event)
%p.practicalities
- %time
- = l(event.start_time, :format => :natural)
-
- - if event.finish?
- = t :to
- %time
- = l(event.finish_time, :format => :natural)
- - if event.timezone == "London"
- = "(#{t(:uk_time)})"
-
- if event.venue
%span.location
- = t(:at)
+ %svg.icon.pint-sized
+ %use{"xlink:href" => "#location_symbol"}
- if event.venue.url
- = link_to event.venue.definite_name + ".", event.venue.url
+ = link_to event.venue.definite_name, event.venue.url
- elsif Droom.show_venue_map?
- = link_to event.venue.definite_name + ".", venues_url(:id => event.venue.id)
+ = link_to event.venue.definite_name, venues_url(:id => event.venue.id)
- else
- = event.venue.definite_name + "."
+ = event.venue.definite_name
+ %span.time
+ %svg.icon.pint-sized2
+ %use{"xlink:href" => "#clock_symbol"}
+ %time
+ = l(event.start_time, :format => :natural)
+
+ - if event.finish?
+ = t :to
+ %time
+ = l(event.finish_time, :format => :natural)
+ - if event.timezone == "London"
+ = "(#{t(:uk_time)})"
.detail
+ - if event.description?
+ .content
+ = sanitize(event.description)
- if show_folder && !full_attachments
- if event.folder.populated? || can?(:read, event.folder)
.attachments
@@ -65,8 +71,6 @@
%ul.uploads{id: "queue_to_#{event.folder.id}"}
= render :partial => "droom/folders/contents", :locals => {:folder => event.folder, :open => true, :flat => true, :narrow => true, :limit => 3, :omit_menu => true, :for_more => event_url(event)}
- - if event.description?
- = sanitize(event.description)
- if show_folder && full_attachments
.addenda
diff --git a/app/views/droom/events/_shortcuts.html.haml b/app/views/droom/events/_shortcuts.html.haml
new file mode 100644
index 000000000..30f0bd024
--- /dev/null
+++ b/app/views/droom/events/_shortcuts.html.haml
@@ -0,0 +1,9 @@
+- years = Droom::Event.pluck(:start).compact.map(&:year).uniq.sort.reverse
+
+%p.navigation
+ = link_to_unless_current "Future events", droom.events_url
+ \|
+ = link_to_unless_current "Past events", droom.past_events_url
+ - years.each do |y|
+ \|
+ = link_to_unless_current y, events_url(year: y)
diff --git a/app/views/droom/events/index.html.haml b/app/views/droom/events/index.html.haml
index 545164d20..704ad87b9 100644
--- a/app/views/droom/events/index.html.haml
+++ b/app/views/droom/events/index.html.haml
@@ -1,15 +1,24 @@
-#calendar
- = render :partial => 'events'
+- if @year
+ - title = t(:events_in, period: @year)
+- elsif @direction == "past"
+ - title = t(:past_events)
+- else
+ - title = t(:calendar)
- content_for :title do
- - if @year
- = t(:events_in, period: @year)
- - elsif @direction == "past"
- = t(:past_events)
- - else
- = t(:calendar)
-
+ = "#{title}"
+ .button-group
+ %span.action
+ - if can?(:create, Droom::Event)
+ = link_to t(:add_event), droom.new_event_url, :class => "add", :data => {:action => "popup", :affected => "#events"}
+ = link_to t(:subscribe_to_calendar), subscribe_events_url(current_user.authentication_token, protocol: "webcal", format: "ics"), class: "subscribe"
+
- content_for :margin do
= render :partial => "droom/shared/calendar_holder"
= render :partial => "droom/invitations/invitations"
- = render :partial => "droom/events/scrap_events"
+ = render :partial => "droom/dashboard/stream_events"
+
+
+#calendar
+ = render :partial => 'shortcuts'
+ = render :partial => 'events'
diff --git a/app/views/droom/folders/_action_menu.html.haml b/app/views/droom/folders/_action_menu.html.haml
index 188cc68ed..e8f85a2c2 100644
--- a/app/views/droom/folders/_action_menu.html.haml
+++ b/app/views/droom/folders/_action_menu.html.haml
@@ -7,6 +7,8 @@
= link_to t(:add_document), '#', class: "add", id: "upload_to_folder_#{folder.id}", data: {role: "upload-file"}
%li
= link_to t(:add_subfolder), droom.new_folder_folder_url(folder), :class => "add", :data => {:action => "popup", :type => "html", :affected => "#folder_#{folder.id}"}
+ %li
+ = link_to t(:move_folder), droom.move_folder_folder_url(folder), :class => "edit", :id => "move_folder_#{folder.id}", :data => {:action => "popup", :type => "html", :affected => "#folders"}
%li
= link_to t(:delete_folder), droom.folder_url(folder), :method => 'delete', :class => 'delete', :data => {:action => "remove", :removed => "#folder_#{folder.id}", :confirm => t(:confirm_delete_folder, :path => folder.path)}
- if folder.populated?
diff --git a/app/views/droom/folders/_contents.html.haml b/app/views/droom/folders/_contents.html.haml
index 67fdfcece..1f08a1042 100644
--- a/app/views/droom/folders/_contents.html.haml
+++ b/app/views/droom/folders/_contents.html.haml
@@ -16,7 +16,7 @@
- if limit && document_count > 0 && limit < document_count
%li.files{data: lidata}
%ul
- = render :partial => 'droom/documents/listing', :collection => folder.documents.limit(limit)
+ = render :partial => 'droom/documents/listing', :collection => folder.documents.limit(limit), locals: {narrow: narrow}
- if for_more
%li.more
= link_to t(:more_documents_count, :count => document_count - limit), for_more
diff --git a/app/views/droom/folders/_move_folder.html.haml b/app/views/droom/folders/_move_folder.html.haml
new file mode 100644
index 000000000..94c6cf947
--- /dev/null
+++ b/app/views/droom/folders/_move_folder.html.haml
@@ -0,0 +1,72 @@
+%form.edit.folder{action: "/folders/#{@folder.id}/moved", :method => 'put'}
+ = hidden_field_tag :new_parent_id, ''
+ = select_tag 'folders', options_from_collection_for_select(Droom::Folder.roots, "id", "name"), id: 'folders_list_1', class: 'folders_list', prompt: "Please Select a Root Folder"
+ #child_folders
+ .buttons
+ %input{name: "submit", type: "submit", value: t(:save_folder)}
+ = t :or
+ = link_to t(:cancel), '#', :class => 'cancel'
+
+:javascript
+ $(document).ready( function() {
+
+ var fetch_child_folder = function(current_element){
+ current_element_id = current_element.attr('id');
+ var new_parent_id = current_element.children("option:selected").val();
+ var ids = $('.folders_list').map(function() {
+ return $(this).attr('id');
+ }).get();
+
+ var current_element_index = $.inArray(current_element_id, ids);
+ var array_size = ids.size;
+ elements_to_remove = ids.slice(current_element_index+1,array_size);
+
+ for ( var index = 0, length = elements_to_remove.length; index <= length; index++ ) {
+ remove = elements_to_remove[index];
+ var id_to_remove = ids.indexOf(remove);
+ if (id_to_remove !== -1) {
+ ids.splice(id_to_remove, 1);
+ }
+ $('#'+remove).remove();
+ if(index == length){
+ elements_to_remove = [];
+ }
+ }
+
+ $("#new_parent_id").val(new_parent_id);
+
+ selected_parent_id = current_element.children("option:selected").val();
+ $.ajax({url: "/child_folders?target_parent_id="+selected_parent_id, success: function(result){
+ var $option = $("", {
+ "value": '',
+ "html": "Please Select a Sub-Folder"
+ });
+ if(result != ''){
+ options = [$option];
+ $.each(result, function (key, value) {
+ options.push($option.clone().val(key).html(value));
+ });
+
+ new_length = $(".folders_list").length + 1;
+ new_select_id = "folders_list_" + new_length;
+
+ $('', {
+ name: 'child_folders_list',
+ id: new_select_id,
+ class: 'folders_list',
+ append: options
+ }).appendTo('#child_folders');
+
+ $('#'+ new_select_id).on('change', function(e){
+ fetch_child_folder($(this));
+ });
+ }
+ }});
+ };
+
+ $('#folders_list_1').on('change', function(e){
+ fetch_child_folder($(this));
+ });
+ });
+
+
diff --git a/app/views/droom/folders/move_folder.html.haml b/app/views/droom/folders/move_folder.html.haml
new file mode 100644
index 000000000..03ad2ec3d
--- /dev/null
+++ b/app/views/droom/folders/move_folder.html.haml
@@ -0,0 +1,3 @@
+#folder
+ = render 'droom/shared/popup_header', title: t(:move_folder)
+ = render :partial => 'move_folder'
\ No newline at end of file
diff --git a/app/views/droom/group_permissions/_action_menu.html.haml b/app/views/droom/group_permissions/_action_menu.html.haml
new file mode 100644
index 000000000..8601edb99
--- /dev/null
+++ b/app/views/droom/group_permissions/_action_menu.html.haml
@@ -0,0 +1,16 @@
+- group_permission ||= @group_permission
+- if group_permission.present?
+ - permission ||= group_permission.permission
+ - group ||= group_permission.group
+ - linkid ||= "#{group.slug}_#{permission.slug.gsub('.', '_')}"
+
+- params = { :group_permission => {:permission_id => permission.id, group_id: group.id}, :linkid => linkid }
+
+.menu{:data => {:for => "#{linkid}"}}
+ %ul.actions
+ %li
+ = link_to t(:all_permissions), upsert_group_group_permissions_url(group, params.merge(:classname => 'yes')), :id => linkid, :class => 'all', :method => :post, :data => {:action => "update_content", :replaced => "##{linkid}"}
+ %li
+ = link_to t(:read_permission), upsert_group_group_permissions_url(group, params.merge(:read_only => true, :classname => 'read')), :id => linkid, :class => 'read_only', :method => :post, :data => {:action => "update_content", :replaced => "##{linkid}"}
+ %li
+ = link_to t(:no_permission), delete_group_group_permissions_url(group, params.merge(:classname => 'no')), :id => linkid, :class => 'delete', :method => :delete, :data => {:action => "update_content", :replaced => "##{linkid}"}
diff --git a/app/views/droom/group_permissions/_toggle.html.haml b/app/views/droom/group_permissions/_toggle.html.haml
index 32f0dcdc0..c6ebadcc1 100644
--- a/app/views/droom/group_permissions/_toggle.html.haml
+++ b/app/views/droom/group_permissions/_toggle.html.haml
@@ -1,6 +1,7 @@
- group_permission ||= @group_permission
- group ||= @group
- permission ||= @permission
+- read_permission ||= @read_permission
- unless group_permission
- if @group_permissions
@@ -12,7 +13,13 @@
- permission ||= group_permission.permission
- linkid = "#{group.slug}_#{permission.slug.gsub('.', '_')}"
-- if group_permission && !group_permission.destroyed?
- = link_to t(:disallow), group_group_permission_url(group, group_permission), :id => linkid, :class => "yes", :method => :delete, :data => {:action => "replace", :replaced => "##{linkid}"}
-- else
- = link_to t(:allow), group_group_permissions_url(group, :group_permission => {:permission_id => permission.id}), :id => linkid, :class => "no", :method => :post, :data => {:action => "replace", :replaced => "##{linkid}"}
+-if read_permission.present?
+ - classname = permission.define_permission_color(group_permission, group, read_permission)
+ = link_to t(:disallow), "#", :class => "#{classname}", :data => { :menu => "#{linkid}" }
+ = render :partial => "droom/group_permissions/action_menu", :locals => { :group_permission => group_permission, :group => group, :permission => permission, :read_permission => read_permission, :linkid => linkid }
+
+-else
+ - if group_permission && !group_permission.destroyed?
+ = link_to t(:disallow), group_group_permission_url(group, group_permission), :id => linkid, :class => "yes", :method => :delete, :data => {:action => "replace", :replaced => "##{linkid}"}
+ - else
+ = link_to t(:allow), group_group_permissions_url(group, :group_permission => {:permission_id => permission.id}), :id => linkid, :class => "no", :method => :post, :data => {:action => "replace", :replaced => "##{linkid}"}
diff --git a/app/views/droom/groups/_users.html.haml b/app/views/droom/groups/_users.html.haml
index 03eff5b9d..d0fb8f3d9 100644
--- a/app/views/droom/groups/_users.html.haml
+++ b/app/views/droom/groups/_users.html.haml
@@ -3,6 +3,6 @@
.paginated
%ul.people
- = render partial: "droom/users/show/listed", collection: users
+ = render partial: "droom/users/show/listed", collection: users, locals: {group: group}
.pagination.sliding<
= paginate(users, params: {controller: 'groups', action: "show", id: group.id, format: :js})
\ No newline at end of file
diff --git a/app/views/droom/organisations/_action_menu.html.haml b/app/views/droom/organisations/_action_menu.html.haml
index bf14b6a07..a7798c676 100644
--- a/app/views/droom/organisations/_action_menu.html.haml
+++ b/app/views/droom/organisations/_action_menu.html.haml
@@ -8,6 +8,6 @@
%li
= link_to t(:add_user), droom.new_organisation_user_url(organisation), :class => 'add', :data => {:action => "popup", :affected => "#organisation_#{organisation.id}"}
%li
- = link_to t(:subsume_organisation), droom.edit_organisation_url(organisation, view: "subsume"), :class => 'delete', :data => {:action => "popup", :removed => "#organisation_#{organisation.id}"}
+ = link_to t(:subsume_organisation), droom.edit_organisation_url(organisation, view: "subsume"), :class => 'delete', :data => {:action => "popup", :affected => "#organisations"}
%li
= link_to t(:delete_organisation), droom.organisation_url(organisation), :method => 'delete', :class => 'delete', :data => {:action => "remove", :removed => "#organisation_#{organisation.id}", :confirm => t(:confirm_delete_organisation, :name => organisation.name)}
diff --git a/app/views/droom/organisations/_organisations.html.haml b/app/views/droom/organisations/_organisations.html.haml
index b84f12073..d50d78b2f 100644
--- a/app/views/droom/organisations/_organisations.html.haml
+++ b/app/views/droom/organisations/_organisations.html.haml
@@ -4,7 +4,7 @@
- if @organisations.any?
- %div{class: cssclass, data: {refreshing: true, url: "#{droom.organisations_path}.js"}}
+ %div#organisations{class: cssclass, data: {refreshing: true, url: "#{droom.organisations_path(page: @page, per_page: @show)}.js"}}
= render partial: "droom/organisations/show/#{view}", collection: @organisations
.pagination.lower
diff --git a/app/views/droom/organisations/edit/_subsume.html.haml b/app/views/droom/organisations/edit/_subsume.html.haml
index 9c9f1e42e..650804481 100644
--- a/app/views/droom/organisations/edit/_subsume.html.haml
+++ b/app/views/droom/organisations/edit/_subsume.html.haml
@@ -4,17 +4,19 @@
#edit_organisation
= render 'droom/shared/popup_header', title: title
- = form_for [droom, organisation], url: merge_organisation_url(organisation), :html => {:class => 'edit organisation'} do |f|
+ = form_for [droom, organisation], url: merge_organisation_url(organisation), method: 'put', html: {:class => 'edit organisation'} do |f|
%p
- You are about to reassign all the users, assets and history of
+ You are about to transfer all the users, assets and history of
%strong
= organisation.name
- to the organisation you choose below. Please be very careful: the subsumed organisation will vanish completely.
+ into the organisation you choose below. Please be careful: the
+ = organisation.name
+ organisation will vanish completely and its users will have a new affiliation.
%p.organisation
= f.label :other_id
- = f.select :other_id, Droom::Organisation.for_selection(true)
+ = f.select :other_id, Droom::Organisation.for_selection_with_owner(true)
.buttons
= f.submit t(:subsume_organisation)
diff --git a/app/views/droom/pages/edit/_form.html.haml b/app/views/droom/pages/edit/_form.html.haml
index 84874f975..80c274503 100644
--- a/app/views/droom/pages/edit/_form.html.haml
+++ b/app/views/droom/pages/edit/_form.html.haml
@@ -86,3 +86,6 @@
%svg.prefix
%use{"xlink:href" => "#reuse_symbol"}
recycle button to choose a previously uploaded asset.
+
+
+= render "droom/shared/ed_symbols"
diff --git a/app/views/droom/scraps/index.html.haml b/app/views/droom/scraps/index.html.haml
index 595db1f6f..49cc14ccd 100644
--- a/app/views/droom/scraps/index.html.haml
+++ b/app/views/droom/scraps/index.html.haml
@@ -4,5 +4,8 @@
- content_for :standfirst do
= render :partial => "noticeboard_introduction"
-= render :partial => "noticeboard"
+ - if can? :create, Droom::Scrap
+ %p.admin
+ = link_to t(:add_notice), droom.new_scrap_url, :class => "add", :remote => true, :data => {:action => "popup", :type => "html", :affected => "#noticeboard"}
+= render :partial => "noticeboard"
diff --git a/app/views/droom/scraps/notices/_image.html.haml b/app/views/droom/scraps/notices/_image.html.haml
index e16048e8f..1a527a9bf 100644
--- a/app/views/droom/scraps/notices/_image.html.haml
+++ b/app/views/droom/scraps/notices/_image.html.haml
@@ -1,8 +1,7 @@
-- if notice.url?
- = link_to notice.url_with_protocol do
- %img{src: notice.image.url(:notice)}
-- else
- %img{src: notice.image.url(:notice)}
-
.content
+ - if notice.url?
+ = link_to notice.url_with_protocol do
+ %img{src: notice.image.url(:notice)}
+ - else
+ %img{src: notice.image.url(:notice)}
= render "droom/scraps/notices/body", notice: notice, caption_limit: 300
\ No newline at end of file
diff --git a/app/views/droom/services/_services.html.haml b/app/views/droom/services/_services.html.haml
index 548481304..24bf6cfc5 100644
--- a/app/views/droom/services/_services.html.haml
+++ b/app/views/droom/services/_services.html.haml
@@ -6,20 +6,23 @@
= link_to t(:add_service), new_service_url, :class => 'add', :data => {:action => 'popup', :affected => '#services'}
- if can?(:create, Droom::Group)
= link_to t(:add_group), new_group_url, :class => 'add', :data => {:action => 'popup', :affected => '#services'}
-
+
- @services.each do |service|
- - width = service.permissions.count
+ - read_permissions = service.permissions.select{ |p| p.name.include?('.read')}
+ - width = service.permissions.count - read_permissions.length
%th.d{:colspan => width > 1 ? width : 1, :class => "s_#{service.id}"}
%h4
= service.name
= action_menulink(service, :class => "small")
= action_menu(service)
-
+
%tr.rotated
%th.spacer
- @services.each do |service|
- - width = service.permissions.count
+ - read_permissions = service.permissions.select{ |p| p.name.include?('.read')}
+ - width = service.permissions.count - read_permissions.length
- service.permissions.each_with_index do |perm, i|
+ - next if perm.name.include?('.read')
%td{:class => "s_#{service.id} p_#{perm.id} #{'d' if i == width-1}".strip}
= action_menulink(perm, :class => "small")
%span.rotated
@@ -32,12 +35,17 @@
%td.name
= truncate(group.name, :length => 16)
- @services.each do |service|
- - width = service.permissions.count
+ - read_permissions = service.permissions.select{ |p| p.name.include?('.read') }
+ - width = service.permissions.count - read_permissions.length
- service.permissions.each_with_index do |perm, i|
+
+ - next if perm.name.include?('.read')
+ - read_permission = perm.get_read_permission
+
%td.toggle{:class => "s_#{service.id} p_#{perm.id} #{'d' if i == width-1}".strip}
- = render :partial => 'droom/group_permissions/toggle', :locals => {:group => group, :permission => perm}
+ = render :partial => 'droom/group_permissions/toggle', :locals => {:group => group, :permission => perm, :read_permission => read_permission}
- if @services.empty?
.everyone
%p
- No services are defined.
\ No newline at end of file
+ No services are defined.
diff --git a/app/views/droom/shared/_ed_symbols.html.haml b/app/views/droom/shared/_ed_symbols.html.haml
new file mode 100644
index 000000000..8dea34107
--- /dev/null
+++ b/app/views/droom/shared/_ed_symbols.html.haml
@@ -0,0 +1,125 @@
+#svg_holder
+ %svg{xmlns: "http://www.w3.org/2000/svg", style: "display: none;"}
+ // Ed
+ %symbol#save_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M13,28.4c-0.6-0.6-0.6-1.6,0-2.2l9.9-9.9c0.5-0.6,1.6-0.6,2.2,0l10.1,9.9c0.6,0.6,0.6,1.6,0,2.2c-0.3,0.3-0.8,0.4-1.2,0.4 c-0.5,0-0.9-0.1-1.2-0.4l-7.2-7.2l0,14.8c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6l0-14.8l-7.2,7.2C14.6,29,13.6,29,13,28.4z M0.3,23.9c0-6.3,2.5-12.3,7-16.8c4.5-4.3,10.5-6.8,16.8-6.8c6.5,0,12.5,2.4,17,7c4.3,4.5,6.8,10.5,6.8,16.9 c-0.2,6.3-2.6,12.3-7.2,16.8c-4.4,4.3-10.4,6.8-16.7,6.8c-6.4,0-12.4-2.5-16.9-7C2.7,36.2,0.3,30.2,0.3,23.9z M10.6,8.5l26.9,0 c-3.8-3.2-8.5-5-13.5-5S14.3,5.3,10.6,8.5z M3.4,24.1c0,5.5,2.2,10.7,6,14.6c3.8,4,9,6.1,14.6,6.1c5.4,0,10.6-2.2,14.4-6 c7.5-7.4,8.1-19,1.9-27.1c-0.1,0.1-0.3,0.1-0.5,0.1l-31.6,0c-0.2,0-0.4-0.1-0.5-0.1C5,15.2,3.5,19.5,3.4,24.1z"}
+ %symbol#save_button_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M3.6,11.8c-2.2,3.6-3.3,7.8-3.3,12.1c0,6.3,2.4,12.3,6.8,16.9c4.5,4.5,10.5,7,16.9,7c6.3,0,12.3-2.5,16.7-6.8 c4.6-4.5,7-10.5,7.2-16.8c0-4.5-1.2-8.7-3.4-12.4L3.6,11.8z M35.2,28.4c-0.3,0.3-0.8,0.4-1.2,0.4c-0.5,0-0.9-0.1-1.2-0.4l-7.2-7.2 l0,14.8c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6l0-14.8l-7.2,7.2c-0.6,0.6-1.6,0.6-2.2,0c-0.6-0.6-0.6-1.6,0-2.2l9.9-9.9 c0.5-0.6,1.6-0.6,2.2,0l10.1,9.9C35.8,26.8,35.8,27.8,35.2,28.4z M6,8.5C6.4,8,6.8,7.6,7.3,7.1c4.5-4.3,10.5-6.8,16.8-6.8 c6.5,0,12.5,2.4,17,7c0.4,0.4,0.7,0.8,1.1,1.2L6,8.5z"}
+ %symbol#revert_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M39.9,8.5C35.6,4.3,30,1.9,24,1.9c-5.9,0-11.5,2.3-15.6,6.4C4.1,12.5,1.8,18.1,1.7,24c0,6,2.3,11.6,6.5,15.8 c4.2,4.3,9.8,6.6,15.9,6.6c5.9,0,11.5-2.3,15.6-6.4C48.4,31.4,48.5,17.3,39.9,8.5z M37.5,37.9c-3.6,3.6-8.4,5.6-13.5,5.6 c-5.2,0-10.1-2-13.7-5.7c-3.6-3.7-5.6-8.5-5.6-13.7c0-5.1,2.1-10,5.7-13.6c3.6-3.6,8.4-5.6,13.5-5.6c5.2,0,10.1,2,13.7,5.7 C45.2,18.2,45.1,30.4,37.5,37.9z M37.4,24.2c0,0.8-0.7,1.5-1.5,1.5H12.2c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5h23.6 C36.7,22.7,37.3,23.3,37.4,24.2z"}
+ %symbol#revert_button_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M39.9,8.5C35.6,4.3,30,1.9,24,1.9c-5.9,0-11.5,2.3-15.6,6.4C4.1,12.5,1.8,18.1,1.7,24c0,6,2.3,11.6,6.5,15.8 c4.2,4.3,9.8,6.6,15.9,6.6c5.9,0,11.5-2.3,15.6-6.4C48.4,31.4,48.5,17.3,39.9,8.5z M37.4,24.2c0,0.8-0.7,1.5-1.5,1.5H12.2 c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5h23.6C36.7,22.7,37.3,23.3,37.4,24.2z"}
+
+ %symbol#download_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M35.2,19.7c0.6,0.6,0.6,1.6,0,2.2l-9.9,9.9c-0.5,0.6-1.6,0.6-2.2,0L13,21.9c-0.6-0.6-0.6-1.6,0-2.2c0.3-0.3,0.8-0.4,1.2-0.4 c0.5,0,0.9,0.1,1.2,0.4l7.2,7.2V12.1c0-0.9,0.7-1.6,1.6-1.6c0.9,0,1.6,0.7,1.6,1.6v14.8l7.2-7.2C33.6,19.1,34.6,19.1,35.2,19.7z M47.9,24.2c0,6.3-2.5,12.3-7,16.8c-4.5,4.3-10.5,6.8-16.8,6.8c-6.5,0-12.5-2.4-17-7c-4.3-4.5-6.8-10.5-6.8-16.9 c0.2-6.3,2.6-12.3,7.2-16.8c4.4-4.3,10.4-6.8,16.7-6.8c6.4,0,12.4,2.5,16.9,7C45.5,11.9,47.9,17.9,47.9,24.2z M37.6,39.6H10.7 c3.8,3.2,8.5,5,13.5,5S33.9,42.8,37.6,39.6z M44.8,24c0-5.5-2.2-10.7-6-14.6c-3.8-4-9-6.1-14.6-6.1c-5.4,0-10.6,2.2-14.4,6 c-7.5,7.4-8.1,19-1.9,27.1c0.1-0.1,0.3-0.1,0.5-0.1H40c0.2,0,0.4,0.1,0.5,0.1C43.2,32.9,44.7,28.6,44.8,24z"}
+ %symbol#download_button_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M44.6,36.3c2.2-3.6,3.3-7.8,3.3-12.1c0-6.3-2.4-12.3-6.8-16.9c-4.5-4.5-10.5-7-16.9-7c-6.3,0-12.3,2.5-16.7,6.8 c-4.6,4.5-7,10.5-7.2,16.8c0,4.5,1.2,8.7,3.4,12.4H44.6z M13,19.7c0.3-0.3,0.8-0.4,1.2-0.4c0.5,0,0.9,0.1,1.2,0.4l7.2,7.2V12.1 c0-0.9,0.7-1.6,1.6-1.6c0.9,0,1.6,0.7,1.6,1.6v14.8l7.2-7.2c0.6-0.6,1.6-0.6,2.2,0c0.6,0.6,0.6,1.6,0,2.2l-9.9,9.9 c-0.5,0.6-1.6,0.6-2.2,0L13,21.9C12.4,21.3,12.4,20.3,13,19.7z M42.2,39.6c-0.4,0.5-0.8,0.9-1.3,1.4c-4.5,4.3-10.5,6.8-16.8,6.8 c-6.5,0-12.5-2.4-17-7C6.7,40.4,6.4,40,6,39.6H42.2z"}
+
+ %symbol#insert_symbol{viewBox: "0 0 48 48"}
+ %polygon.fg{points: "39.7,22.7 25.5,22.7 25.5,8.5 22.9,8.5 22.9,22.7 8.7,22.7 8.7,25.3 22.9,25.3 22.9,39.5 25.5,39.5 25.5,25.3 39.7,25.3"}
+
+ %symbol#image_symbol{viewBox: "0 0 100 100"}
+ %path{d: "M50,0.8C22.5,1,0.6,23.2,0.8,50.3c0,5,0.9,10,2.3,14.5c0,0.7,0.2,1.3,0.7,1.9c6.9,18.9,25,32.5,46.3,32.5h0.3 C63.5,99.1,75.8,94,85,84.6s14.3-21.8,14.2-34.9C99,22.8,77,0.8,50,0.8z M69.6,16c0,2.8-2.3,5.1-5.1,5.1c-2.8,0-5.1-2.3-5.1-5.1 s2.3-5.1,5.1-5.1C67.3,10.8,69.6,13.1,69.6,16z M50.2,92.6H50c-17.9,0-33.4-11.3-39.6-27l26.1-22.6l31.7,45.6 C62.7,91.1,56.6,92.6,50.2,92.6z M80.3,80c-2.1,2.1-4.3,3.8-6.6,5.5l-15.2-22l14.1-12.1l14.1,20.3C84.9,74.6,82.8,77.3,80.3,80z M89.8,65.1l-14-20.3c-0.5-0.8-1.3-1.2-2.2-1.3c-0.9-0.1-1.8,0.1-2.5,0.8l-16.3,14L39.7,36.3c-0.5-0.8-1.3-1.2-2.2-1.3 c-0.9-0.1-1.8,0.1-2.5,0.8L8.3,58.9c-0.5-2.7-0.9-5.7-1-8.6C7.2,26.7,26.2,7.6,49.7,7.3c2.3,0,4.5,0.2,6.6,0.5 C54.2,10,53,12.8,53,16c0,6.3,5.1,11.5,11.5,11.5c6.2,0,11.3-4.9,11.5-11C86,24.2,92.4,36.2,92.6,49.8C92.7,55,91.7,60.2,89.8,65.1z"}
+ %symbol#image_button_symbol{viewBox: "0 0 100 100"}
+ %path.fg{d: "M93.9,71.8c3.4-6.7,5.3-14.2,5.3-22.1C99,23.3,76.5,0.8,49.9,0.8c-7.3,0-14.2,1.6-20.5,4.5c-0.8,0.3-1.4,0.7-2.2,1.1 c-15.9,8.2-26.7,24.8-26.6,44c0,5.4,1,10.5,2.5,15.3c0,0.2,0.1,0.4,0.2,0.7c0.1,0.4,0.3,0.8,0.4,1.2C11,86,28.9,99.2,49.8,99.2h0.3 C69.1,99.2,85.8,87.8,93.9,71.8z M64.4,8.5c4.1,0,7.4,3.3,7.4,7.4s-3.3,7.4-7.4,7.4c-4.1,0-7.4-3.3-7.4-7.4S60.3,8.5,64.4,8.5z M10.1,65.9l25.8-22.3c0.2-0.2,0.5-0.2,0.7-0.2c0.1,0,0.4,0.1,0.7,0.3l31.2,45c-5.6,2.7-11.8,4.2-18.3,4.2h-0.2 C32,92.9,16.5,81.7,10.1,65.9z M80.5,80.1c-2,2-4.2,3.8-6.5,5.4L58.6,63.3l13.2-11.5c0.2-0.2,0.5-0.2,0.7-0.2c0.1,0,0.4,0.1,0.7,0.3 l14,19.3C85.4,74.5,83.1,77.5,80.5,80.1z"}
+
+ %symbol#video_symbol{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M38.4,38.8c-3.8,3.8-8.9,6-14.4,6 c-5.5,0-10.7-2.1-14.6-6.1s-6-9-6-14.6c0-5.4,2.2-10.6,6.1-14.5s8.9-6,14.4-6c5.5,0,10.7,2.1,14.6,6.1 C46.6,17.8,46.5,30.8,38.4,38.8z M38.1,16.4c0-0.7-0.6-1.3-1.3-1.3c-8.6-0.6-17-0.6-25.6,0c-0.7,0-1.3,0.6-1.3,1.3 c-0.3,5.2-0.3,10.5,0,15.7c0,0.7,0.6,1.3,1.3,1.3c8.6,0.6,17,0.6,25.6,0c0.7,0,1.3-0.6,1.3-1.3C38.4,26.8,38.4,21.6,38.1,16.4z M29,24.6L21.3,29c-0.3,0.2-0.6,0-0.6-0.4v-8.9c0-0.3,0.3-0.6,0.6-0.4l7.7,4.4C29.3,24,29.3,24.4,29,24.6z"}
+ %symbol#video_button_symbol{viewBox: "0 0 48 48"}
+ %path.fg{d: "M41,7.4c-4.6-4.5-10.6-7-17-7c-6.3,0-12.3,2.5-16.7,6.8c-4.6,4.5-7,10.5-7.2,16.8c0,6.4,2.5,12.4,6.9,16.9 c4.5,4.6,10.5,7,17,7c6.3,0,12.3-2.5,16.7-6.8C50.1,31.9,50.2,16.8,41,7.4z M38.1,32.1c0,0.7-0.6,1.3-1.3,1.3 c-8.6,0.6-17.1,0.6-25.7,0c-0.7,0-1.3-0.6-1.3-1.3c-0.3-5.2-0.3-10.5,0-15.8c0-0.7,0.6-1.3,1.3-1.3c8.6-0.6,17.1-0.6,25.7,0 c0.7,0,1.3,0.6,1.3,1.3C38.5,21.6,38.5,26.8,38.1,32.1z M29,24.6L21.3,29c-0.3,0.2-0.6,0-0.6-0.4v-8.9c0-0.3,0.3-0.6,0.6-0.4 l7.7,4.4C29.3,24,29.3,24.4,29,24.6z"}
+
+ %symbol#document_symbol{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M38.4,38.8c-3.8,3.8-8.9,6-14.4,6s-10.7-2.1-14.6-6.1 c-3.9-4-6-9-6-14.6c0-5.4,2.2-10.6,6.1-14.5c3.9-3.9,8.9-6,14.4-6s10.7,2.1,14.6,6.1C46.6,17.8,46.5,30.8,38.4,38.8z M36.8,26.7 c-0.8-1.5-3.3-2.3-7.1-2.3H29c-1-1.2-2.1-2.4-2.8-3.3L26,20.8c-0.3-0.4-0.6-0.7-0.8-1.1c0.5-1.5,0.8-2.8,0.9-3.7 c0.2-2.5-0.1-4.1-0.9-4.9c-0.6-0.6-1.4-0.8-2.2-0.6c-0.5,0.1-1.3,0.6-1.7,1.9c-0.6,1.7-0.4,4.8,1.3,7.6c-0.7,1.8-1.7,3.9-2.8,5.9 c-2.1,0.8-3.8,1.7-5.1,2.9c-1.7,1.6-2.3,3.3-1.8,4.5c0.3,0.8,1.1,1.3,2,1.3c0.6,0,1.3-0.2,1.9-0.6c1.4-0.9,3.3-3.8,4.6-6.2 c1.5-0.5,3.3-0.8,5.3-1l1.4-0.1c1.9,2,3.5,3.1,5,3.4l0.1,0c0.3,0,0.5,0.1,0.8,0.1c1.3,0,2.3-0.5,2.8-1.4 C37.1,28.1,37.1,27.4,36.8,26.7z M34.7,27.7c-0.1,0.1-0.3,0.1-0.7,0.1c-0.1,0-0.3,0-0.5,0c-0.6-0.1-1.2-0.5-2-1 C33.5,27,34.4,27.5,34.7,27.7z M15.1,32.3c0.1-0.3,0.4-0.9,1.2-1.8c0.4-0.4,0.9-0.7,1.3-1c-0.9,1.4-1.7,2.3-2.1,2.6 C15.3,32.2,15.2,32.3,15.1,32.3z M24.2,22.2c0.1,0.1,0.2,0.2,0.3,0.3c0.5,0.6,1.1,1.3,1.7,2c-0.9,0.1-2.1,0.3-3.3,0.5 C23.3,24,23.8,23.1,24.2,22.2z M23.7,15.8c0,0.2-0.1,0.5-0.1,0.8c-0.4-1.3-0.4-2.6-0.1-3.5c0-0.1,0.1-0.2,0.1-0.3 C23.7,13.3,23.9,14.1,23.7,15.8z"}
+ %symbol#document_button_symbol{viewBox: "0 0 48 48"}
+ %path.fg{d: "M24.6,22.3c0.7,0.9,1.5,1.9,2.4,2.9h-0.1c-1.2,0.1-2.9,0.4-4.9,0.8c0.8-1.5,1.5-3.1,2.1-4.5C24.2,21.8,24.3,22.1,24.6,22.3z M23.8,14.5c0.2-3.1-0.3-3.9-0.4-4c-0.1,0-0.3,0.3-0.5,0.8c-0.5,1.4-0.2,3.4,0.5,5.3C23.6,15.7,23.8,15,23.8,14.5z M12.8,34.6 C12.8,34.6,12.8,34.6,12.8,34.6c0.4,0,0.7-0.1,1-0.4c0.7-0.5,2-2,3.3-4.2c-1.1,0.5-2,1.2-2.7,1.9C12.9,33.5,12.7,34.4,12.8,34.6z M40.7,41c-4.4,4.4-10.3,6.8-16.6,6.8c-6.5,0-12.4-2.4-16.9-7C2.7,36.3,0.3,30.4,0.3,24c0.1-6.3,2.5-12.2,7.1-16.7 C11.8,2.9,17.7,0.5,24,0.5c6.4,0,12.3,2.5,16.9,7C50.1,16.9,50,31.9,40.7,41z M38.9,27.7c-1-1.7-3.9-2.6-8.2-2.6c-0.3,0-0.6,0-1,0 c-1.2-1.3-2.4-2.8-3.4-4.1c-0.5-0.6-1-1.2-1.4-1.8v0c0.6-1.9,1-3.4,1.1-4.5c0.2-2.9-0.1-4.8-1-5.7c-0.6-0.6-1.5-0.9-2.3-0.6 c-0.6,0.2-1.4,0.7-1.9,2.1c-0.7,2.1-0.4,5.9,1.6,8.9c-0.9,2.3-2.1,4.9-3.4,7.4c-2.5,0.9-4.6,2.1-6.1,3.5c-1.9,1.9-2.7,3.7-2.1,5.1 c0.4,0.9,1.1,1.4,2.1,1.4c0.7,0,1.4-0.2,2.1-0.7c1.8-1.2,4.2-5.1,5.4-7.4c2.6-0.8,5.2-1.1,6.5-1.3c0.6,0,1.2-0.1,1.8-0.1 c2.3,2.4,4.2,3.7,5.9,4c0.4,0,0.7,0.1,1,0.1c1.4,0,2.6-0.5,3.1-1.5C39.3,29.2,39.3,28.4,38.9,27.7z M31.9,27.3 c1.3,1.1,2.3,1.7,3.2,1.9c0.2,0,0.5,0,0.6,0c0.7,0,1.1-0.2,1.2-0.4c0,0,0,0,0-0.1C36.7,28.2,35.3,27.4,31.9,27.3z"}
+
+ %symbol#quote_symbol{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M38.4,38.8c-3.8,3.8-8.9,6-14.4,6 c-5.5,0-10.7-2.1-14.6-6.1s-6-9-6-14.6c0-5.4,2.2-10.6,6.1-14.5s8.9-6,14.4-6c5.5,0,10.7,2.1,14.6,6.1 C46.6,17.8,46.5,30.8,38.4,38.8z M17.8,15.5c3.2,0,5.7,3,5.7,7c0,5.2-4.8,10.4-9.8,10.4c-0.7,0-1.4-0.6-1.4-1.2 c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6C12.7,17.9,15,15.5,17.8,15.5z M30.3,15.5c3.2,0,5.7,3,5.7,7 c0,5.2-4.8,10.4-9.8,10.4c-0.7,0-1.4-0.6-1.4-1.2c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6C25.1,17.9,27.5,15.5,30.3,15.5z"}
+ %symbol#quote_button_symbol{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M17.8,15.5c3.2,0,5.7,3,5.7,7c0,5.2-4.8,10.4-9.8,10.4 c-0.7,0-1.4-0.6-1.4-1.2c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6C12.7,17.9,15,15.5,17.8,15.5z M30.3,15.5 c3.2,0,5.7,3,5.7,7c0,5.2-4.8,10.4-9.8,10.4c-0.7,0-1.4-0.6-1.4-1.2c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6 C25.1,17.9,27.5,15.5,30.3,15.5z"}
+
+ %symbol#close_symbol{viewBox: "0 0 48 48"}
+ %path{d: "M41.2,7.1c-0.6-0.6-1.7-0.6-2.3,0L24.1,21.9L9.3,6.9C8.7,6.3,7.7,6.3,7,6.9C6.4,7.5,6.4,8.6,7,9.2l14.8,15l-15,14.8 c-0.6,0.6-0.6,1.7,0,2.3c0.4,0.4,0.8,0.4,1.3,0.4c0.4,0,0.8,0,1.3-0.4l14.8-14.8l14.8,14.8c0.4,0.4,0.8,0.4,1.3,0.4s0.8,0,1.3-0.4 c0.6-0.6,0.6-1.7,0-2.3l-15-14.8L41.2,9.4C41.8,8.8,41.8,7.7,41.2,7.1z"}
+
+
+ %symbol#right_arrow_symbol{viewBox: "0 0 100 100"}
+ %path{d: "M76.7,47.6c1.2,1.2,1.2,3.6,0,4.8L55.9,73.1c-1.3,1.3-3.4,1.3-4.8,0c-0.7-0.6-1-1.4-1-2.3c0-0.8,0.3-1.7,1-2.3l15-15.1H25 c-1.8,0-3.3-1.6-3.3-3.3c0-1.8,1.4-3.3,3.3-3.3h41.1L51,31.6c-1.3-1.3-1.3-3.4,0-4.8s3.4-1.3,4.8,0L76.7,47.6z M100,49.7V50 c0,27.4-22.3,49.8-49.7,50C22.8,100.2,0.2,77.9,0,50C0,22.6,22.3,0.2,49.7,0c13.3-0.1,26,5,35.4,14.4l0,0 C94.7,23.8,99.9,36.3,100,49.7z M93.2,49.7c-0.1-11.6-4.7-22.3-12.9-30.6c-8.1-8-19-12.4-30.6-12.3c-23.7,0.1-43,19.6-43,43.6 c0.1,23.9,19.7,43.1,43.6,43C74,93.1,93.2,73.7,93.2,50L93.2,49.7z"}
+ %symbol#left_arrow_symbol{viewBox: "0 0 100 100"}
+ %path{d: "M23.3,52.4c-1.2-1.2-1.2-3.6,0-4.8l20.8-20.8c1.3-1.3,3.4-1.3,4.8,0c0.7,0.6,1,1.4,1,2.3c0,0.8-0.3,1.7-1,2.3l-15,15.1H75 c1.8,0,3.3,1.6,3.3,3.3c0,1.8-1.4,3.3-3.3,3.3H33.9L49,68.4c1.3,1.3,1.3,3.4,0,4.8s-3.4,1.3-4.8,0L23.3,52.4z M0,50.3V50 C0,22.6,22.3,0.2,49.7,0C77.2-0.2,99.8,22.1,100,50c0,27.4-22.3,49.8-49.7,50c-13.3,0.1-26-5-35.4-14.4l0,0 C5.3,76.2,0.1,63.7,0,50.3z M6.8,50.3c0.1,11.6,4.7,22.3,12.9,30.6c8.1,8,19,12.4,30.6,12.3c23.7-0.1,43-19.6,43-43.6 c-0.1-23.9-19.7-43.1-43.6-43C26,6.9,6.8,26.3,6.8,50L6.8,50.3z"}
+ %symbol#popout_symbol{viewBox: "0 0 100 100"}
+ %path{d: "M67.1,29.4c1.7,0,3.4,1.6,3.4,3.4l0,29.4c0,1.9-1.5,3.4-3.4,3.4c-0.9,0.1-1.7-0.3-2.4-0.9c-0.6-0.6-0.9-1.4-0.9-2.4L63.7,41 L34.7,70c-1.3,1.3-3.5,1.3-4.7,0c-1.3-1.3-1.3-3.4,0-4.7L59,36.3l-21.4,0c-1.9,0-3.4-1.5-3.4-3.4c0-1.9,1.5-3.4,3.4-3.4L67.1,29.4z M85.1,14.4l0.2,0.2c19.4,19.4,19.4,51,0.2,70.5c-19.3,19.6-51.1,19.8-70.9,0.2c-19.4-19.4-19.4-51-0.2-70.5C23.8,5.4,36.3,0,49.7,0 l0,0C63-0.1,75.6,5.1,85.1,14.4z M80.3,19.2C72.1,11.1,61.2,6.7,49.6,6.7c-11.4,0.1-22.2,4.6-30.3,12.9C2.6,36.4,2.7,63.8,19.7,80.8 c17,16.8,44.4,16.6,61.2-0.4c16.6-16.9,16.4-44.2-0.3-61L80.3,19.2z"}
+
+ %symbol#first_symbol{viewBox: "0 0 48 64"}
+ %path{d: "M29.1,9.62a1.61,1.61,0,0,1,2.27,2.27L19.26,24,31.37,36.11a1.61,1.61,0,1,1-2.27,2.27L15.85,25.14a1.61,1.61,0,0,1,0-2.27Z"}
+ %symbol#last_symbol{viewBox: "0 0 48 64"}
+ %path{d: "M20.13,9.7A1.59,1.59,0,0,0,17.88,12l12,12-12,12a1.59,1.59,0,1,0,2.25,2.25L33.25,25.07a1.59,1.59,0,0,0,0-2.25Z"}
+ %symbol#prev_symbol{viewBox: "0 0 48 64"}
+ %path{d: "M27.91,9.62a1.61,1.61,0,1,1,2.27,2.27L18.07,24,30.18,36.11a1.61,1.61,0,1,1-2.27,2.27L14.66,25.14a1.61,1.61,0,0,1,0-2.27Z"}
+ %symbol#next_symbol{viewBox: "0 0 48 64"}
+ %path{d: "M20.13,9.7A1.59,1.59,0,0,0,17.88,12l12,12-12,12a1.59,1.59,0,1,0,2.25,2.25L33.25,25.07a1.59,1.59,0,0,0,0-2.25Z"}
+
+ %symbol#edit_symbol{viewBox: "0 0 48 64"}
+ %path{d: "M11.5,32l4.4,4.4l-6.3,1.8L11.5,32z M33.4,9.1c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.2,0.1,0.3,0.1l4.9,4.9 c0.1,0.1,0.1,0.3,0.1,0.3c0,0.1,0,0.2-0.1,0.3l-3.6,3.6l-5.6-5.6L33.4,9.1z M27.7,14.7l5.6,5.6L18.6,35L13,29.4L27.7,14.7z M7.5,42 c0.1,0,0.3,0,0.4-0.1l10.3-3c0.5-0.1,1.1-0.4,1.5-0.9L41,16.8c0.6-0.6,1-1.5,1-2.5c0-0.9-0.3-1.8-1-2.4L36.1,7 c-1.3-1.3-3.6-1.3-4.9,0L10,28.3c-0.4,0.4-0.7,0.9-0.9,1.5l-3,10.3c-0.1,0.5,0,1.1,0.4,1.5C6.7,41.9,7.1,42,7.5,42L7.5,42z"}
+
+
+ // positioning controls for inserted assets
+ %symbol#left_symbol{viewBox: "0 0 48 48"}
+ %rect.fg{x:"13.3", y: "13.9", width: "26.6", height: "2.3"}
+ %rect.fg{x:"24.1", y: "18.4", width: "15.8", height: "2.3"}
+ %rect.fg{x:"24.1", y: "23", width: "15.8", height: "2.3"}
+ %rect.fg{x:"24.1", y: "27.6" , width: "15.8", height: "2.3"}
+ %rect.fg{x:"13.3", y: "32.2", width: "26.6", height: "2.3"}
+ %rect.fg{x:"8.9", y: "18.4", width: "13.4", height: "11.4"}
+
+ %symbol#right_symbol{viewBox: "0 0 48 48"}
+ %rect.fg{x: "8.9", y: "13.9", width: "26.6", height: "2.3"}
+ %rect.fg{x: "8.9", y: "18.4", width: "15.8", height: "2.3"}
+ %rect.fg{x: "8.9", y: "23", width: "15.8", height: "2.3"}
+ %rect.fg{x: "8.9", y: "27.6", width: "15.8", height: "2.3"}
+ %rect.fg{x: "8.9", y: "32.2", width: "26.6", height: "2.3"}
+ %rect.fg{x: "26.5", y: "18.4", width: "13.4", height: "11.4"}
+
+ %symbol#full_symbol{viewBox: "0 0 48 48"}
+ %rect.fg{x: "10.7", y: "13.7", width: "26.6", height: "2.3"}
+ %rect.fg{x: "10.7", y: "32", width: "26.6", height: "2.3"}
+ %rect.fg{x: "10.7", y: "18.2", width: "26.6", height: "11.4"}
+
+ %symbol#wide_symbol{viewBox: "0 0 48 48"}
+ %rect.fg{x: "10.7", y: "13.7", width: "26.6", height: "2.3"}
+ %rect.fg{x: "10.7", y: "32", width: "26.6", height: "2.3"}
+ %rect.fg{x: "6.5", y: "18.2", width: "34.9", height: "11.4"}
+
+ %symbol#hero_button{viewBox: "0 0 48 48"}
+ %path.bg{d: "M40.3,7.6c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.6,40.3,7.6z"}
+ %path.fg{d: "M4.7,36.5C8.8,42.7,15.9,46.9,24,46.9s15.2-4.2,19.3-10.4H4.7z" }
+ %path.fg{d: "M43.6,11.9c-4-6.6-11.3-11-19.6-11S8.4,5.3,4.4,11.9H43.6z" }
+
+ // Toolbar buttons
+ %symbol#bold_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M26.5,18.4c0-1.6-1.3-2.4-2.8-2.4H21v5.1h2.7C25.4,21.1,26.5,20.1,26.5,18.4z"}
+ %path.fg{d: "M24.7,25.7H21v5.7h4c1.9,0,3.2-1.1,3.2-3C28.3,26.8,27.1,25.7,24.7,25.7z"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M25.7,35.9h-7.1 c-1.6,0-2.7-1-2.7-2.6V14c0-1.6,1.1-2.6,2.7-2.6h6.6c3.6,0,6.5,2.4,6.5,6c0,2.4-1,4-3.2,5v0.1c2.9,0.4,5.2,2.8,5.2,5.8 C33.7,33.3,30.4,35.9,25.7,35.9z"}
+
+ %symbol#italic_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M32.1,13.9l-9.4,19.5 c-0.8,1.7-2.7,2.7-4.7,2.7c-1.9,0-2.9-1-2.1-2.7l9.4-19.5c0.8-1.7,2.7-2.7,4.7-2.7C31.9,11.2,32.9,12.2,32.1,13.9z"}
+
+ %symbol#link_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M32.1,13.9l-9.4,19.5 c-0.8,1.7-2.7,2.7-4.7,2.7c-1.9,0-2.9-1-2.1-2.7l9.4-19.5c0.8-1.7,2.7-2.7,4.7-2.7C31.9,11.2,32.9,12.2,32.1,13.9z"}
+
+ %symbol#ol_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M12.4,11.8h1.2c0.4,0,0.6,0.3,0.6,0.6v4.7 c0,0.4-0.3,0.6-0.6,0.6s-0.6-0.2-0.6-0.6v-4.2h-0.5c-0.4,0-0.6-0.3-0.6-0.6C11.8,12.1,12,11.8,12.4,11.8z M13.3,36.2 c-1.2,0-2-0.9-2-1.6c0-0.3,0.3-0.6,0.6-0.6c0.6,0,0.4,1,1.4,1c0.4,0,0.8-0.3,0.8-0.8c0-1.2-1.4-0.3-1.4-1.3c0-0.9,1.2-0.3,1.2-1.2 c0-0.3-0.2-0.6-0.6-0.6c-0.8,0-0.7,0.8-1.2,0.8c-0.3,0-0.5-0.3-0.5-0.6c0-0.6,0.9-1.3,1.8-1.3c1.2,0,1.8,0.9,1.8,1.5 c0,0.5-0.2,1-0.7,1.3c0.6,0.3,1,0.8,1,1.5C15.4,35.4,14.5,36.2,13.3,36.2z M14.9,26.9h-2.8c-0.4,0-0.6-0.2-0.6-0.5 c0-0.2,0.1-0.3,0.2-0.4c0.7-0.8,1.4-1.6,2.1-2.5c0.1-0.2,0.3-0.5,0.3-0.8c0-0.3-0.3-0.6-0.6-0.6c-1,0-0.5,1.3-1.3,1.3 c-0.4,0-0.6-0.3-0.6-0.6c0-1,0.9-1.9,1.9-1.9s1.8,0.7,1.8,1.7c0,1.2-1.3,2.3-2,3.2h1.5c0.4,0,0.6,0.2,0.6,0.5 C15.5,26.7,15.2,26.9,14.9,26.9z M33.9,36.1H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 S35.5,36.1,33.9,36.1z M33.9,26.9H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9S35.5,26.9,33.9,26.9z M33.9,17.7H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9S35.5,17.7,33.9,17.7z"}
+
+ %symbol#ul_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M14.2,36.1c-1.6,0-2.9-1.3-2.9-2.9 c0-1.6,1.3-2.9,2.9-2.9c1.6,0,2.9,1.3,2.9,2.9C17.1,34.8,15.8,36.1,14.2,36.1z M14.2,26.9c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9 c1.6,0,2.9,1.3,2.9,2.9S15.8,26.9,14.2,26.9z M14.2,17.7c-1.6,0-2.9-1.3-2.9-2.9c0-1.6,1.3-2.9,2.9-2.9c1.6,0,2.9,1.3,2.9,2.9 C17.1,16.4,15.8,17.7,14.2,17.7z M33.9,36.1H21.8c-1.6,0-2.9-1.3-2.9-2.9c0-1.6,1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 C36.8,34.8,35.5,36.1,33.9,36.1z M33.9,26.9H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 S35.5,26.9,33.9,26.9z M33.9,17.7H21.8c-1.6,0-2.9-1.3-2.9-2.9c0-1.6,1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 C36.8,16.4,35.5,17.7,33.9,17.7z"}
+
+ %symbol#h1_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M34.6,33.7 c0,1.7-1.1,2.7-2.6,2.7s-2.6-1-2.6-2.7v-7.5H18.7v7.5c0,1.7-1.1,2.7-2.6,2.7s-2.6-1-2.6-2.7V14.3c0-1.7,1.1-2.7,2.6-2.7 s2.6,1,2.6,2.7v7.3h10.8v-7.3c0-1.7,1.1-2.7,2.6-2.7s2.6,1,2.6,2.7V33.7z"}
+
+ %symbol#h2_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M33.2,33.3 c0,1.6-1,2.7-2.5,2.7s-2.5-1.1-2.5-2.7v-8.6c0-2.3-1.4-3.4-3.2-3.4c-2,0-3.2,1.5-3.2,3.4v8.6c0,1.6-1,2.7-2.5,2.7s-2.5-1.1-2.5-2.7 V12.2c0-1.6,1-2.7,2.5-2.7s2.5,1.1,2.5,2.7v6.7h0.1c1-1.3,3-1.7,4.5-1.7c3.9,0,6.8,2.5,6.8,6.7V33.3z"}
+
+ %symbol#anchor_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M23.2,36.1 c-1.6,1.6-3.7,2.6-6,2.5c-2.2-0.1-4.1-0.9-5.6-2.5c-3-3.2-2.8-8.2,0.3-11.3l4.9-4.9c0.1-0.1,0.2-0.2,0.3-0.2c0.4-0.4,1-0.8,1.6-1.1 c1.5-0.8,3.2-1.1,4.9-0.9c0.4,0,0.8,0.1,1.2,0.2c0.4,0.1,0.8,0.2,1.1,0.4c0.8,0.4,1.5,0.9,2.2,1.5c0.8,0.8,0.7,2.2-0.1,3l-1,1 c-0.1,0.2-0.3,0.3-0.5,0.4c-0.2-0.7-0.6-1.4-1.1-1.9c-0.6-0.6-1.4-1-2.2-1.2c-0.3-0.1-0.6-0.1-0.9-0.1h-0.2 c-0.4,0.1-0.7,0.1-1.1,0.2c-0.7,0.2-1.4,0.6-1.9,1.1l-4.9,4.9c-0.7,0.7-1.3,1.7-1.4,2.8c-0.1,1.2,0.3,2.5,1.2,3.4 c0.9,0.9,2.1,1.4,3.4,1.2c1.1-0.1,2-0.6,2.8-1.4l0.9-0.9c0.5-0.5,1.2-0.6,1.8-0.4c0.4,0.1,0.8,0.2,1.2,0.2c0.4,0.1,0.7,0.1,1.1,0.1 c0.4,0,0.8-0.1,1.2-0.1L23.2,36.1z M36.1,23.3l-4.9,4.9c-0.1,0.1-0.2,0.2-0.3,0.2c-0.4,0.4-1,0.9-1.6,1.2c-1.2,0.6-2.5,0.9-3.8,0.9 c-0.4,0-0.7,0-1.1-0.1c-0.4,0-0.8-0.1-1.2-0.2c-0.4-0.1-0.8-0.2-1.1-0.4c-0.8-0.4-1.5-0.9-2.2-1.5c-0.8-0.8-0.8-2.2,0.1-3l1-1 c0.2-0.2,0.4-0.3,0.6-0.4c0.2,0.7,0.6,1.4,1.1,1.9c0.6,0.6,1.4,1,2.2,1.2c0.3,0.1,0.6,0.1,0.9,0.1h0.1c0.4-0.1,0.7-0.1,1.1-0.2 c0.7-0.2,1.4-0.6,1.9-1.1l4.9-4.9c1.6-1.6,1.9-4.2,0.4-5.9c-0.9-1.1-2.2-1.7-3.7-1.5c-1.1,0.1-2.1,0.6-2.8,1.4l-1,1 c-0.4,0.4-0.9,0.5-1.4,0.3c-0.4-0.1-0.8-0.2-1.2-0.2c-0.9-0.1-1.8-0.1-2.7,0.1l3.7-3.7c1.6-1.6,3.7-2.6,6-2.5 c2.2,0.1,4.1,0.9,5.6,2.5C39.5,15,39.3,20.1,36.1,23.3z"}
+
+ %symbol#clear_button{viewBox: "0 0 48 48"}
+ %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M26.8,24.1c2.8,2.8,5.7,5.7,8.5,8.5 c0.7,0.7,0.8,1.8,0.1,2.5c-0.7,0.8-1.8,0.8-2.6,0.1c-0.1-0.1-0.1-0.1-0.2-0.2c-2.8-2.8-5.5-5.5-8.4-8.4c-0.1-0.1-0.1-0.1-0.2-0.2 c-0.1,0.1-0.1,0.1-0.2,0.2c-2.8,2.8-5.7,5.7-8.5,8.5c-0.8,0.8-2,0.8-2.7-0.1c-0.6-0.7-0.5-1.7,0.1-2.4c0.5-0.5,1-1,1.5-1.5 c2.4-2.4,4.8-4.8,7.1-7.1c0-0.1,0.1-0.1,0.2-0.1c-0.1-0.1-0.1-0.2-0.2-0.2c-2.8-2.8-5.7-5.7-8.5-8.5c-0.7-0.7-0.8-1.8-0.1-2.5 c0.7-0.8,1.8-0.8,2.6-0.1c0.1,0.1,0.1,0.1,0.2,0.2c2.8,2.8,5.5,5.5,8.4,8.4c0.1,0.1,0.1,0.1,0.2,0.3c0.1-0.1,0.1-0.2,0.2-0.2 c2.8-2.8,5.7-5.7,8.5-8.5c0.7-0.9,2-0.8,2.7,0c0.6,0.7,0.5,1.7-0.1,2.4c-0.2,0.2-0.6,0.6-0.8,0.8c-2.6,2.6-5.2,5.2-7.8,7.8 c-0.1,0-0.1,0.1-0.2,0.1C26.7,24,26.8,24.1,26.8,24.1z"}
diff --git a/app/views/droom/shared/_symbols.html.haml b/app/views/droom/shared/_symbols.html.haml
index c2b79fcb2..dfcc25d14 100644
--- a/app/views/droom/shared/_symbols.html.haml
+++ b/app/views/droom/shared/_symbols.html.haml
@@ -39,8 +39,7 @@
%path{d: "M24.1,1.9c-12.3,0-22.2,9.9-22.2,22.2c0,12.3,9.9,22.2,22.2,22.2c12.3,0,22.2-9.9,22.2-22.2C46.3,11.8,36.3,1.9,24.1,1.9z M35,19.3L21.2,33.1c-0.3,0.3-0.7,0.4-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4l-5.9-5.9c-0.1-0.2-0.4-0.6-0.4-1.1c0-0.6,0.4-1.1,0.9-1.3 c0.6-0.2,1.3,0,1.7,0.4l4.9,4.9L33,17.2c0.4-0.4,1-0.6,1.6-0.4c0.6,0.2,0.9,0.7,0.9,1.4C35.5,18.7,35.1,19.1,35,19.3z"}
%symbol#cross_symbol{viewBox: "0 0 48 48"}
- %path{d: "M24.1,44.8c-11.4,0-20.7-9.3-20.7-20.7S12.7,3.4,24.1,3.4s20.7,9.3,20.7,20.7S35.5,44.8,24.1,44.8z M24.1,1.9 c-12.3,0-22.2,9.9-22.2,22.2s9.9,22.2,22.2,22.2s22.2-9.9,22.2-22.2S36.3,1.9,24.1,1.9z"}
- %path{d: "M32.2,16c-0.3-0.3-0.8-0.3-1.1,0L24.1,23l-7-7.1c-0.3-0.3-0.8-0.3-1.1,0c-0.3,0.3-0.3,0.8,0,1.1l7,7.1l-7.1,7 c-0.3,0.3-0.3,0.8,0,1.1c0.2,0.2,0.4,0.2,0.6,0.2s0.4,0,0.6-0.2l7-7l7,7c0.2,0.2,0.4,0.2,0.6,0.2s0.4,0,0.6-0.2 c0.3-0.3,0.3-0.8,0-1.1l-7.1-7l7-7C32.5,16.8,32.5,16.3,32.2,16z"}
+ %path{d: "M7.5,7C16.8-2,31.8-1.9,41,7.5c9,9.3,9,24.1-0.2,33.3c-0.1,0.1-0.1,0.1-0.2,0.2 c-9.1,9-24.1,9-33.3-0.2c-0.1-0.1-0.1-0.1-0.3-0.3C-1.9,31.3-1.9,16.2,7.5,7z M43.7,18.3c-1-3.5-2.8-6.6-5.5-9.2 C30.3,1.7,18,1.6,10.1,8.9c-7.8,7.3-8.8,19.5-1.9,27.9c3.2,3.9,7.2,6.4,12.2,7.2c6.9,1.1,12.9-0.8,18.1-5.5c0,0,0,0,0.1-0.1 c0,0,0,0,0.1-0.1c2.7-2.7,4.4-5.8,5.3-9.4C44.7,25.5,44.6,21.9,43.7,18.3z M34.1,14c0.5,0.6,0.4,1.5-0.1,2.1 c-0.2,0.2-0.5,0.5-0.7,0.7c-2.3,2.3-4.6,4.6-6.9,6.9c-0.1,0-0.1,0.1-0.2,0.1c0.1,0.1,0.2,0.2,0.2,0.2c2.5,2.5,5,5,7.5,7.5 c0.6,0.6,0.7,1.6,0.1,2.2c-0.6,0.7-1.6,0.7-2.3,0.1c-0.1-0.1-0.1-0.1-0.2-0.2c-2.5-2.5-4.9-4.9-7.4-7.4c-0.1-0.1-0.1-0.1-0.2-0.2 c-0.1,0.1-0.1,0.1-0.2,0.2c-2.5,2.5-5,5-7.5,7.5c-0.7,0.7-1.8,0.7-2.4-0.1c-0.5-0.6-0.4-1.5,0.1-2.1c0.4-0.4,0.9-0.9,1.3-1.3 c2.1-2.1,4.2-4.2,6.3-6.3c0-0.1,0.1-0.1,0.2-0.1c-0.1-0.1-0.1-0.2-0.2-0.2c-2.5-2.5-5-5-7.5-7.5c-0.6-0.6-0.7-1.6-0.1-2.2 c0.6-0.7,1.6-0.7,2.3-0.1c0.1,0.1,0.1,0.1,0.2,0.2c2.5,2.5,4.9,4.9,7.4,7.4c0.1,0.1,0.1,0.1,0.2,0.4c0.1-0.1,0.1-0.2,0.2-0.2 c2.5-2.5,5-5,7.5-7.5C32.3,13.3,33.5,13.4,34.1,14z"}
%symbol#cross_button_symbol{viewBox: "0 0 48 48"}
%path{d: "M24.1,1.9c-12.3,0-22.2,9.9-22.2,22.2c0,12.3,9.9,22.2,22.2,22.2c12.3,0,22.2-9.9,22.2-22.2C46.3,11.8,36.3,1.9,24.1,1.9z M32.5,32.4c-0.3,0.3-0.6,0.3-0.8,0.3c-0.2,0-0.5,0-0.8-0.3l-6.8-6.8l-6.8,6.8c-0.3,0.3-0.6,0.3-0.8,0.3s-0.5,0-0.8-0.3 c-0.4-0.4-0.4-1.1,0-1.5l6.9-6.8l-6.8-6.9c-0.4-0.4-0.4-1.1,0-1.5c0.4-0.4,1.1-0.4,1.5,0l6.8,6.9l6.8-6.8c0.4-0.4,1.1-0.4,1.5,0 s0.4,1.1,0,1.5l-6.8,6.8l6.9,6.8C32.9,31.3,32.9,32,32.5,32.4z"}
@@ -108,129 +107,12 @@
%path{d: "M50,6.3C25.3,6.3,5.2,26.4,5,51C4.9,63,9.5,74.3,18,82.8c8.4,8.6,19.7,13.3,31.7,13.4H50c24.7,0,44.8-20.1,45-44.7 C95.2,26.8,75.1,6.5,50,6.3z M49.8,90.2c-9.5-0.1-18.4-3.5-25.4-9.7v-0.1v-0.5c0-4.7,1.4-9.1,4-13c2.4-3.4,5.6-6.1,9.3-7.8 c3.3,2.8,7.6,4.5,12.3,4.5s9-1.7,12.3-4.5c3.7,1.7,6.9,4.4,9.3,7.8c2.6,3.8,4,8.3,4,13v0.5C68.7,86.4,59.7,90.1,49.8,90.2L49.8,90.2 L49.8,90.2z M50,58c-7.3,0-13.3-6-13.3-13.3s6-13.4,13.3-13.4s13.3,6,13.3,13.3S57.3,58,50,58z M81,74.8c-0.7-4-2.3-7.7-4.6-11.1 c-2.6-3.8-6.1-6.9-10.2-9c1.8-2.9,2.9-6.4,2.9-10c0-10.5-8.6-19.1-19.1-19.1s-19.1,8.6-19.1,19.1c0,3.7,1,7.1,2.9,10 c-4,2.1-7.5,5.2-10.2,9c-2.3,3.4-3.9,7.1-4.6,11.1c-5.2-6.8-8-15.1-7.9-23.8c0.1-21.3,17.6-38.7,39.2-38.7 C71.8,12.4,89.1,30,89,51.5C88.9,60.2,85.9,68.2,81,74.8z"}
%symbol#email_symbol{viewBox: "0 0 100 100"}
%path{d: "M50.5,6.2c24.8,0.4,44.7,20.6,44.7,45.4C95,76.1,74.7,96.3,49.9,96.3c-0.2,0-0.4,0-0.6,0c-24.6-0.2-44.5-20.2-44.5-45 c0-0.2,0-0.4,0-0.8C5,26.1,25.5,5.8,50.5,6.2z M85.4,67.5c4.8-10.9,4.8-21.8,0-32.6c-6.1,5.5-12,10.9-17.9,16.2 C73.4,56.8,79.3,62.1,85.4,67.5z M82.5,72.8c-6.5-5.9-13-11.6-19.3-17.6l-0.2,0.2c-3.6,3.2-7.3,6.5-10.9,9.9 c-1.3,1.1-2.9,1.1-4.2,0c-2.9-2.5-5.7-5.2-8.6-7.6c-1-0.8-1.7-1.5-2.7-2.3c-6.5,5.9-13,11.6-19.5,17.6c8.4,11.6,19.5,17.8,34,17.4 C64.4,89.8,74.7,83.5,82.5,72.8z M49.9,59.3c10.9-9.9,21.8-19.7,32.4-29.6C66.7,6.2,32.7,6.6,17.5,29.9 C28.1,39.6,39,49.5,49.9,59.3z M32.3,51.3C26.4,45.7,20.5,40.4,14.4,35c-4.8,10.1-4.8,22.9,0,32.4C20.5,62.1,26.4,56.6,32.3,51.3z"}
-
- // Ed
- %symbol#save_symbol{viewBox: "0 0 48 48"}
- %path{d: "M13,28.4c-0.6-0.6-0.6-1.6,0-2.2l9.9-9.9c0.5-0.6,1.6-0.6,2.2,0l10.1,9.9c0.6,0.6,0.6,1.6,0,2.2c-0.3,0.3-0.8,0.4-1.2,0.4 c-0.5,0-0.9-0.1-1.2-0.4l-7.2-7.2l0,14.8c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6l0-14.8l-7.2,7.2C14.6,29,13.6,29,13,28.4z M0.3,23.9c0-6.3,2.5-12.3,7-16.8c4.5-4.3,10.5-6.8,16.8-6.8c6.5,0,12.5,2.4,17,7c4.3,4.5,6.8,10.5,6.8,16.9 c-0.2,6.3-2.6,12.3-7.2,16.8c-4.4,4.3-10.4,6.8-16.7,6.8c-6.4,0-12.4-2.5-16.9-7C2.7,36.2,0.3,30.2,0.3,23.9z M10.6,8.5l26.9,0 c-3.8-3.2-8.5-5-13.5-5S14.3,5.3,10.6,8.5z M3.4,24.1c0,5.5,2.2,10.7,6,14.6c3.8,4,9,6.1,14.6,6.1c5.4,0,10.6-2.2,14.4-6 c7.5-7.4,8.1-19,1.9-27.1c-0.1,0.1-0.3,0.1-0.5,0.1l-31.6,0c-0.2,0-0.4-0.1-0.5-0.1C5,15.2,3.5,19.5,3.4,24.1z"}
- %symbol#save_button_symbol{viewBox: "0 0 48 48"}
- %path{d: "M3.6,11.8c-2.2,3.6-3.3,7.8-3.3,12.1c0,6.3,2.4,12.3,6.8,16.9c4.5,4.5,10.5,7,16.9,7c6.3,0,12.3-2.5,16.7-6.8 c4.6-4.5,7-10.5,7.2-16.8c0-4.5-1.2-8.7-3.4-12.4L3.6,11.8z M35.2,28.4c-0.3,0.3-0.8,0.4-1.2,0.4c-0.5,0-0.9-0.1-1.2-0.4l-7.2-7.2 l0,14.8c0,0.9-0.7,1.6-1.6,1.6c-0.9,0-1.6-0.7-1.6-1.6l0-14.8l-7.2,7.2c-0.6,0.6-1.6,0.6-2.2,0c-0.6-0.6-0.6-1.6,0-2.2l9.9-9.9 c0.5-0.6,1.6-0.6,2.2,0l10.1,9.9C35.8,26.8,35.8,27.8,35.2,28.4z M6,8.5C6.4,8,6.8,7.6,7.3,7.1c4.5-4.3,10.5-6.8,16.8-6.8 c6.5,0,12.5,2.4,17,7c0.4,0.4,0.7,0.8,1.1,1.2L6,8.5z"}
- %symbol#revert_symbol{viewBox: "0 0 48 48"}
- %path{d: "M39.9,8.5C35.6,4.3,30,1.9,24,1.9c-5.9,0-11.5,2.3-15.6,6.4C4.1,12.5,1.8,18.1,1.7,24c0,6,2.3,11.6,6.5,15.8 c4.2,4.3,9.8,6.6,15.9,6.6c5.9,0,11.5-2.3,15.6-6.4C48.4,31.4,48.5,17.3,39.9,8.5z M37.5,37.9c-3.6,3.6-8.4,5.6-13.5,5.6 c-5.2,0-10.1-2-13.7-5.7c-3.6-3.7-5.6-8.5-5.6-13.7c0-5.1,2.1-10,5.7-13.6c3.6-3.6,8.4-5.6,13.5-5.6c5.2,0,10.1,2,13.7,5.7 C45.2,18.2,45.1,30.4,37.5,37.9z M37.4,24.2c0,0.8-0.7,1.5-1.5,1.5H12.2c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5h23.6 C36.7,22.7,37.3,23.3,37.4,24.2z"}
- %symbol#revert_button_symbol{viewBox: "0 0 48 48"}
- %path{d: "M39.9,8.5C35.6,4.3,30,1.9,24,1.9c-5.9,0-11.5,2.3-15.6,6.4C4.1,12.5,1.8,18.1,1.7,24c0,6,2.3,11.6,6.5,15.8 c4.2,4.3,9.8,6.6,15.9,6.6c5.9,0,11.5-2.3,15.6-6.4C48.4,31.4,48.5,17.3,39.9,8.5z M37.4,24.2c0,0.8-0.7,1.5-1.5,1.5H12.2 c-0.8,0-1.5-0.7-1.5-1.5s0.7-1.5,1.5-1.5h23.6C36.7,22.7,37.3,23.3,37.4,24.2z"}
-
- %symbol#download_symbol{viewBox: "0 0 48 48"}
- %path{d: "M35.2,19.7c0.6,0.6,0.6,1.6,0,2.2l-9.9,9.9c-0.5,0.6-1.6,0.6-2.2,0L13,21.9c-0.6-0.6-0.6-1.6,0-2.2c0.3-0.3,0.8-0.4,1.2-0.4 c0.5,0,0.9,0.1,1.2,0.4l7.2,7.2V12.1c0-0.9,0.7-1.6,1.6-1.6c0.9,0,1.6,0.7,1.6,1.6v14.8l7.2-7.2C33.6,19.1,34.6,19.1,35.2,19.7z M47.9,24.2c0,6.3-2.5,12.3-7,16.8c-4.5,4.3-10.5,6.8-16.8,6.8c-6.5,0-12.5-2.4-17-7c-4.3-4.5-6.8-10.5-6.8-16.9 c0.2-6.3,2.6-12.3,7.2-16.8c4.4-4.3,10.4-6.8,16.7-6.8c6.4,0,12.4,2.5,16.9,7C45.5,11.9,47.9,17.9,47.9,24.2z M37.6,39.6H10.7 c3.8,3.2,8.5,5,13.5,5S33.9,42.8,37.6,39.6z M44.8,24c0-5.5-2.2-10.7-6-14.6c-3.8-4-9-6.1-14.6-6.1c-5.4,0-10.6,2.2-14.4,6 c-7.5,7.4-8.1,19-1.9,27.1c0.1-0.1,0.3-0.1,0.5-0.1H40c0.2,0,0.4,0.1,0.5,0.1C43.2,32.9,44.7,28.6,44.8,24z"}
- %symbol#download_button_symbol{viewBox: "0 0 48 48"}
- %path{d: "M44.6,36.3c2.2-3.6,3.3-7.8,3.3-12.1c0-6.3-2.4-12.3-6.8-16.9c-4.5-4.5-10.5-7-16.9-7c-6.3,0-12.3,2.5-16.7,6.8 c-4.6,4.5-7,10.5-7.2,16.8c0,4.5,1.2,8.7,3.4,12.4H44.6z M13,19.7c0.3-0.3,0.8-0.4,1.2-0.4c0.5,0,0.9,0.1,1.2,0.4l7.2,7.2V12.1 c0-0.9,0.7-1.6,1.6-1.6c0.9,0,1.6,0.7,1.6,1.6v14.8l7.2-7.2c0.6-0.6,1.6-0.6,2.2,0c0.6,0.6,0.6,1.6,0,2.2l-9.9,9.9 c-0.5,0.6-1.6,0.6-2.2,0L13,21.9C12.4,21.3,12.4,20.3,13,19.7z M42.2,39.6c-0.4,0.5-0.8,0.9-1.3,1.4c-4.5,4.3-10.5,6.8-16.8,6.8 c-6.5,0-12.5-2.4-17-7C6.7,40.4,6.4,40,6,39.6H42.2z"}
-
- %symbol#insert_symbol{viewBox: "0 0 48 48"}
- %polygon.fg{points: "39.7,22.7 25.5,22.7 25.5,8.5 22.9,8.5 22.9,22.7 8.7,22.7 8.7,25.3 22.9,25.3 22.9,39.5 25.5,39.5 25.5,25.3 39.7,25.3"}
-
- %symbol#image_symbol{viewBox: "0 0 100 100"}
- %path{d: "M50,0.8C22.5,1,0.6,23.2,0.8,50.3c0,5,0.9,10,2.3,14.5c0,0.7,0.2,1.3,0.7,1.9c6.9,18.9,25,32.5,46.3,32.5h0.3 C63.5,99.1,75.8,94,85,84.6s14.3-21.8,14.2-34.9C99,22.8,77,0.8,50,0.8z M69.6,16c0,2.8-2.3,5.1-5.1,5.1c-2.8,0-5.1-2.3-5.1-5.1 s2.3-5.1,5.1-5.1C67.3,10.8,69.6,13.1,69.6,16z M50.2,92.6H50c-17.9,0-33.4-11.3-39.6-27l26.1-22.6l31.7,45.6 C62.7,91.1,56.6,92.6,50.2,92.6z M80.3,80c-2.1,2.1-4.3,3.8-6.6,5.5l-15.2-22l14.1-12.1l14.1,20.3C84.9,74.6,82.8,77.3,80.3,80z M89.8,65.1l-14-20.3c-0.5-0.8-1.3-1.2-2.2-1.3c-0.9-0.1-1.8,0.1-2.5,0.8l-16.3,14L39.7,36.3c-0.5-0.8-1.3-1.2-2.2-1.3 c-0.9-0.1-1.8,0.1-2.5,0.8L8.3,58.9c-0.5-2.7-0.9-5.7-1-8.6C7.2,26.7,26.2,7.6,49.7,7.3c2.3,0,4.5,0.2,6.6,0.5 C54.2,10,53,12.8,53,16c0,6.3,5.1,11.5,11.5,11.5c6.2,0,11.3-4.9,11.5-11C86,24.2,92.4,36.2,92.6,49.8C92.7,55,91.7,60.2,89.8,65.1z"}
- %symbol#image_button_symbol{viewBox: "0 0 100 100"}
- %path.fg{d: "M93.9,71.8c3.4-6.7,5.3-14.2,5.3-22.1C99,23.3,76.5,0.8,49.9,0.8c-7.3,0-14.2,1.6-20.5,4.5c-0.8,0.3-1.4,0.7-2.2,1.1 c-15.9,8.2-26.7,24.8-26.6,44c0,5.4,1,10.5,2.5,15.3c0,0.2,0.1,0.4,0.2,0.7c0.1,0.4,0.3,0.8,0.4,1.2C11,86,28.9,99.2,49.8,99.2h0.3 C69.1,99.2,85.8,87.8,93.9,71.8z M64.4,8.5c4.1,0,7.4,3.3,7.4,7.4s-3.3,7.4-7.4,7.4c-4.1,0-7.4-3.3-7.4-7.4S60.3,8.5,64.4,8.5z M10.1,65.9l25.8-22.3c0.2-0.2,0.5-0.2,0.7-0.2c0.1,0,0.4,0.1,0.7,0.3l31.2,45c-5.6,2.7-11.8,4.2-18.3,4.2h-0.2 C32,92.9,16.5,81.7,10.1,65.9z M80.5,80.1c-2,2-4.2,3.8-6.5,5.4L58.6,63.3l13.2-11.5c0.2-0.2,0.5-0.2,0.7-0.2c0.1,0,0.4,0.1,0.7,0.3 l14,19.3C85.4,74.5,83.1,77.5,80.5,80.1z"}
-
- %symbol#video_symbol{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M38.4,38.8c-3.8,3.8-8.9,6-14.4,6 c-5.5,0-10.7-2.1-14.6-6.1s-6-9-6-14.6c0-5.4,2.2-10.6,6.1-14.5s8.9-6,14.4-6c5.5,0,10.7,2.1,14.6,6.1 C46.6,17.8,46.5,30.8,38.4,38.8z M38.1,16.4c0-0.7-0.6-1.3-1.3-1.3c-8.6-0.6-17-0.6-25.6,0c-0.7,0-1.3,0.6-1.3,1.3 c-0.3,5.2-0.3,10.5,0,15.7c0,0.7,0.6,1.3,1.3,1.3c8.6,0.6,17,0.6,25.6,0c0.7,0,1.3-0.6,1.3-1.3C38.4,26.8,38.4,21.6,38.1,16.4z M29,24.6L21.3,29c-0.3,0.2-0.6,0-0.6-0.4v-8.9c0-0.3,0.3-0.6,0.6-0.4l7.7,4.4C29.3,24,29.3,24.4,29,24.6z"}
- %symbol#video_button_symbol{viewBox: "0 0 48 48"}
- %path.fg{d: "M41,7.4c-4.6-4.5-10.6-7-17-7c-6.3,0-12.3,2.5-16.7,6.8c-4.6,4.5-7,10.5-7.2,16.8c0,6.4,2.5,12.4,6.9,16.9 c4.5,4.6,10.5,7,17,7c6.3,0,12.3-2.5,16.7-6.8C50.1,31.9,50.2,16.8,41,7.4z M38.1,32.1c0,0.7-0.6,1.3-1.3,1.3 c-8.6,0.6-17.1,0.6-25.7,0c-0.7,0-1.3-0.6-1.3-1.3c-0.3-5.2-0.3-10.5,0-15.8c0-0.7,0.6-1.3,1.3-1.3c8.6-0.6,17.1-0.6,25.7,0 c0.7,0,1.3,0.6,1.3,1.3C38.5,21.6,38.5,26.8,38.1,32.1z M29,24.6L21.3,29c-0.3,0.2-0.6,0-0.6-0.4v-8.9c0-0.3,0.3-0.6,0.6-0.4 l7.7,4.4C29.3,24,29.3,24.4,29,24.6z"}
-
- %symbol#document_symbol{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M38.4,38.8c-3.8,3.8-8.9,6-14.4,6s-10.7-2.1-14.6-6.1 c-3.9-4-6-9-6-14.6c0-5.4,2.2-10.6,6.1-14.5c3.9-3.9,8.9-6,14.4-6s10.7,2.1,14.6,6.1C46.6,17.8,46.5,30.8,38.4,38.8z M36.8,26.7 c-0.8-1.5-3.3-2.3-7.1-2.3H29c-1-1.2-2.1-2.4-2.8-3.3L26,20.8c-0.3-0.4-0.6-0.7-0.8-1.1c0.5-1.5,0.8-2.8,0.9-3.7 c0.2-2.5-0.1-4.1-0.9-4.9c-0.6-0.6-1.4-0.8-2.2-0.6c-0.5,0.1-1.3,0.6-1.7,1.9c-0.6,1.7-0.4,4.8,1.3,7.6c-0.7,1.8-1.7,3.9-2.8,5.9 c-2.1,0.8-3.8,1.7-5.1,2.9c-1.7,1.6-2.3,3.3-1.8,4.5c0.3,0.8,1.1,1.3,2,1.3c0.6,0,1.3-0.2,1.9-0.6c1.4-0.9,3.3-3.8,4.6-6.2 c1.5-0.5,3.3-0.8,5.3-1l1.4-0.1c1.9,2,3.5,3.1,5,3.4l0.1,0c0.3,0,0.5,0.1,0.8,0.1c1.3,0,2.3-0.5,2.8-1.4 C37.1,28.1,37.1,27.4,36.8,26.7z M34.7,27.7c-0.1,0.1-0.3,0.1-0.7,0.1c-0.1,0-0.3,0-0.5,0c-0.6-0.1-1.2-0.5-2-1 C33.5,27,34.4,27.5,34.7,27.7z M15.1,32.3c0.1-0.3,0.4-0.9,1.2-1.8c0.4-0.4,0.9-0.7,1.3-1c-0.9,1.4-1.7,2.3-2.1,2.6 C15.3,32.2,15.2,32.3,15.1,32.3z M24.2,22.2c0.1,0.1,0.2,0.2,0.3,0.3c0.5,0.6,1.1,1.3,1.7,2c-0.9,0.1-2.1,0.3-3.3,0.5 C23.3,24,23.8,23.1,24.2,22.2z M23.7,15.8c0,0.2-0.1,0.5-0.1,0.8c-0.4-1.3-0.4-2.6-0.1-3.5c0-0.1,0.1-0.2,0.1-0.3 C23.7,13.3,23.9,14.1,23.7,15.8z"}
- %symbol#document_button_symbol{viewBox: "0 0 48 48"}
- %path.fg{d: "M24.6,22.3c0.7,0.9,1.5,1.9,2.4,2.9h-0.1c-1.2,0.1-2.9,0.4-4.9,0.8c0.8-1.5,1.5-3.1,2.1-4.5C24.2,21.8,24.3,22.1,24.6,22.3z M23.8,14.5c0.2-3.1-0.3-3.9-0.4-4c-0.1,0-0.3,0.3-0.5,0.8c-0.5,1.4-0.2,3.4,0.5,5.3C23.6,15.7,23.8,15,23.8,14.5z M12.8,34.6 C12.8,34.6,12.8,34.6,12.8,34.6c0.4,0,0.7-0.1,1-0.4c0.7-0.5,2-2,3.3-4.2c-1.1,0.5-2,1.2-2.7,1.9C12.9,33.5,12.7,34.4,12.8,34.6z M40.7,41c-4.4,4.4-10.3,6.8-16.6,6.8c-6.5,0-12.4-2.4-16.9-7C2.7,36.3,0.3,30.4,0.3,24c0.1-6.3,2.5-12.2,7.1-16.7 C11.8,2.9,17.7,0.5,24,0.5c6.4,0,12.3,2.5,16.9,7C50.1,16.9,50,31.9,40.7,41z M38.9,27.7c-1-1.7-3.9-2.6-8.2-2.6c-0.3,0-0.6,0-1,0 c-1.2-1.3-2.4-2.8-3.4-4.1c-0.5-0.6-1-1.2-1.4-1.8v0c0.6-1.9,1-3.4,1.1-4.5c0.2-2.9-0.1-4.8-1-5.7c-0.6-0.6-1.5-0.9-2.3-0.6 c-0.6,0.2-1.4,0.7-1.9,2.1c-0.7,2.1-0.4,5.9,1.6,8.9c-0.9,2.3-2.1,4.9-3.4,7.4c-2.5,0.9-4.6,2.1-6.1,3.5c-1.9,1.9-2.7,3.7-2.1,5.1 c0.4,0.9,1.1,1.4,2.1,1.4c0.7,0,1.4-0.2,2.1-0.7c1.8-1.2,4.2-5.1,5.4-7.4c2.6-0.8,5.2-1.1,6.5-1.3c0.6,0,1.2-0.1,1.8-0.1 c2.3,2.4,4.2,3.7,5.9,4c0.4,0,0.7,0.1,1,0.1c1.4,0,2.6-0.5,3.1-1.5C39.3,29.2,39.3,28.4,38.9,27.7z M31.9,27.3 c1.3,1.1,2.3,1.7,3.2,1.9c0.2,0,0.5,0,0.6,0c0.7,0,1.1-0.2,1.2-0.4c0,0,0,0,0-0.1C36.7,28.2,35.3,27.4,31.9,27.3z"}
-
- %symbol#quote_symbol{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M38.4,38.8c-3.8,3.8-8.9,6-14.4,6 c-5.5,0-10.7-2.1-14.6-6.1s-6-9-6-14.6c0-5.4,2.2-10.6,6.1-14.5s8.9-6,14.4-6c5.5,0,10.7,2.1,14.6,6.1 C46.6,17.8,46.5,30.8,38.4,38.8z M17.8,15.5c3.2,0,5.7,3,5.7,7c0,5.2-4.8,10.4-9.8,10.4c-0.7,0-1.4-0.6-1.4-1.2 c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6C12.7,17.9,15,15.5,17.8,15.5z M30.3,15.5c3.2,0,5.7,3,5.7,7 c0,5.2-4.8,10.4-9.8,10.4c-0.7,0-1.4-0.6-1.4-1.2c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6C25.1,17.9,27.5,15.5,30.3,15.5z"}
- %symbol#quote_button_symbol{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.9,7.5c-4.6-4.5-10.5-7-16.9-7c-6.3,0-12.2,2.4-16.6,6.8c-4.6,4.5-7,10.4-7.1,16.7c0,6.4,2.4,12.3,6.9,16.8 c4.5,4.6,10.4,7,16.9,7c6.3,0,12.2-2.4,16.6-6.8C50,31.9,50.1,16.9,40.9,7.5z M17.8,15.5c3.2,0,5.7,3,5.7,7c0,5.2-4.8,10.4-9.8,10.4 c-0.7,0-1.4-0.6-1.4-1.2c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6C12.7,17.9,15,15.5,17.8,15.5z M30.3,15.5 c3.2,0,5.7,3,5.7,7c0,5.2-4.8,10.4-9.8,10.4c-0.7,0-1.4-0.6-1.4-1.2c0-2.6,3.7-0.8,4.6-6.5c-2.7-0.7-4.3-2.4-4.3-4.6 C25.1,17.9,27.5,15.5,30.3,15.5z"}
-
- %symbol#close_symbol{viewBox: "0 0 48 48"}
- %path{d: "M41.2,7.1c-0.6-0.6-1.7-0.6-2.3,0L24.1,21.9L9.3,6.9C8.7,6.3,7.7,6.3,7,6.9C6.4,7.5,6.4,8.6,7,9.2l14.8,15l-15,14.8 c-0.6,0.6-0.6,1.7,0,2.3c0.4,0.4,0.8,0.4,1.3,0.4c0.4,0,0.8,0,1.3-0.4l14.8-14.8l14.8,14.8c0.4,0.4,0.8,0.4,1.3,0.4s0.8,0,1.3-0.4 c0.6-0.6,0.6-1.7,0-2.3l-15-14.8L41.2,9.4C41.8,8.8,41.8,7.7,41.2,7.1z"}
-
-
- %symbol#right_arrow_symbol{viewBox: "0 0 100 100"}
- %path{d: "M76.7,47.6c1.2,1.2,1.2,3.6,0,4.8L55.9,73.1c-1.3,1.3-3.4,1.3-4.8,0c-0.7-0.6-1-1.4-1-2.3c0-0.8,0.3-1.7,1-2.3l15-15.1H25 c-1.8,0-3.3-1.6-3.3-3.3c0-1.8,1.4-3.3,3.3-3.3h41.1L51,31.6c-1.3-1.3-1.3-3.4,0-4.8s3.4-1.3,4.8,0L76.7,47.6z M100,49.7V50 c0,27.4-22.3,49.8-49.7,50C22.8,100.2,0.2,77.9,0,50C0,22.6,22.3,0.2,49.7,0c13.3-0.1,26,5,35.4,14.4l0,0 C94.7,23.8,99.9,36.3,100,49.7z M93.2,49.7c-0.1-11.6-4.7-22.3-12.9-30.6c-8.1-8-19-12.4-30.6-12.3c-23.7,0.1-43,19.6-43,43.6 c0.1,23.9,19.7,43.1,43.6,43C74,93.1,93.2,73.7,93.2,50L93.2,49.7z"}
- %symbol#left_arrow_symbol{viewBox: "0 0 100 100"}
- %path{d: "M23.3,52.4c-1.2-1.2-1.2-3.6,0-4.8l20.8-20.8c1.3-1.3,3.4-1.3,4.8,0c0.7,0.6,1,1.4,1,2.3c0,0.8-0.3,1.7-1,2.3l-15,15.1H75 c1.8,0,3.3,1.6,3.3,3.3c0,1.8-1.4,3.3-3.3,3.3H33.9L49,68.4c1.3,1.3,1.3,3.4,0,4.8s-3.4,1.3-4.8,0L23.3,52.4z M0,50.3V50 C0,22.6,22.3,0.2,49.7,0C77.2-0.2,99.8,22.1,100,50c0,27.4-22.3,49.8-49.7,50c-13.3,0.1-26-5-35.4-14.4l0,0 C5.3,76.2,0.1,63.7,0,50.3z M6.8,50.3c0.1,11.6,4.7,22.3,12.9,30.6c8.1,8,19,12.4,30.6,12.3c23.7-0.1,43-19.6,43-43.6 c-0.1-23.9-19.7-43.1-43.6-43C26,6.9,6.8,26.3,6.8,50L6.8,50.3z"}
- %symbol#popout_symbol{viewBox: "0 0 100 100"}
- %path{d: "M67.1,29.4c1.7,0,3.4,1.6,3.4,3.4l0,29.4c0,1.9-1.5,3.4-3.4,3.4c-0.9,0.1-1.7-0.3-2.4-0.9c-0.6-0.6-0.9-1.4-0.9-2.4L63.7,41 L34.7,70c-1.3,1.3-3.5,1.3-4.7,0c-1.3-1.3-1.3-3.4,0-4.7L59,36.3l-21.4,0c-1.9,0-3.4-1.5-3.4-3.4c0-1.9,1.5-3.4,3.4-3.4L67.1,29.4z M85.1,14.4l0.2,0.2c19.4,19.4,19.4,51,0.2,70.5c-19.3,19.6-51.1,19.8-70.9,0.2c-19.4-19.4-19.4-51-0.2-70.5C23.8,5.4,36.3,0,49.7,0 l0,0C63-0.1,75.6,5.1,85.1,14.4z M80.3,19.2C72.1,11.1,61.2,6.7,49.6,6.7c-11.4,0.1-22.2,4.6-30.3,12.9C2.6,36.4,2.7,63.8,19.7,80.8 c17,16.8,44.4,16.6,61.2-0.4c16.6-16.9,16.4-44.2-0.3-61L80.3,19.2z"}
-
- %symbol#first_symbol{viewBox: "0 0 48 64"}
- %path{d: "M29.1,9.62a1.61,1.61,0,0,1,2.27,2.27L19.26,24,31.37,36.11a1.61,1.61,0,1,1-2.27,2.27L15.85,25.14a1.61,1.61,0,0,1,0-2.27Z"}
- %symbol#last_symbol{viewBox: "0 0 48 64"}
- %path{d: "M20.13,9.7A1.59,1.59,0,0,0,17.88,12l12,12-12,12a1.59,1.59,0,1,0,2.25,2.25L33.25,25.07a1.59,1.59,0,0,0,0-2.25Z"}
- %symbol#prev_symbol{viewBox: "0 0 48 64"}
- %path{d: "M27.91,9.62a1.61,1.61,0,1,1,2.27,2.27L18.07,24,30.18,36.11a1.61,1.61,0,1,1-2.27,2.27L14.66,25.14a1.61,1.61,0,0,1,0-2.27Z"}
- %symbol#next_symbol{viewBox: "0 0 48 64"}
- %path{d: "M20.13,9.7A1.59,1.59,0,0,0,17.88,12l12,12-12,12a1.59,1.59,0,1,0,2.25,2.25L33.25,25.07a1.59,1.59,0,0,0,0-2.25Z"}
-
- %symbol#edit_symbol{viewBox: "0 0 48 64"}
- %path{d: "M11.5,32l4.4,4.4l-6.3,1.8L11.5,32z M33.4,9.1c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.2,0.1,0.3,0.1l4.9,4.9 c0.1,0.1,0.1,0.3,0.1,0.3c0,0.1,0,0.2-0.1,0.3l-3.6,3.6l-5.6-5.6L33.4,9.1z M27.7,14.7l5.6,5.6L18.6,35L13,29.4L27.7,14.7z M7.5,42 c0.1,0,0.3,0,0.4-0.1l10.3-3c0.5-0.1,1.1-0.4,1.5-0.9L41,16.8c0.6-0.6,1-1.5,1-2.5c0-0.9-0.3-1.8-1-2.4L36.1,7 c-1.3-1.3-3.6-1.3-4.9,0L10,28.3c-0.4,0.4-0.7,0.9-0.9,1.5l-3,10.3c-0.1,0.5,0,1.1,0.4,1.5C6.7,41.9,7.1,42,7.5,42L7.5,42z"}
-
-
- // positioning controls for inserted assets
- %symbol#left_symbol{viewBox: "0 0 48 48"}
- %rect.fg{x:"13.3", y: "13.9", width: "26.6", height: "2.3"}
- %rect.fg{x:"24.1", y: "18.4", width: "15.8", height: "2.3"}
- %rect.fg{x:"24.1", y: "23", width: "15.8", height: "2.3"}
- %rect.fg{x:"24.1", y: "27.6" , width: "15.8", height: "2.3"}
- %rect.fg{x:"13.3", y: "32.2", width: "26.6", height: "2.3"}
- %rect.fg{x:"8.9", y: "18.4", width: "13.4", height: "11.4"}
-
- %symbol#right_symbol{viewBox: "0 0 48 48"}
- %rect.fg{x: "8.9", y: "13.9", width: "26.6", height: "2.3"}
- %rect.fg{x: "8.9", y: "18.4", width: "15.8", height: "2.3"}
- %rect.fg{x: "8.9", y: "23", width: "15.8", height: "2.3"}
- %rect.fg{x: "8.9", y: "27.6", width: "15.8", height: "2.3"}
- %rect.fg{x: "8.9", y: "32.2", width: "26.6", height: "2.3"}
- %rect.fg{x: "26.5", y: "18.4", width: "13.4", height: "11.4"}
-
- %symbol#full_symbol{viewBox: "0 0 48 48"}
- %rect.fg{x: "10.7", y: "13.7", width: "26.6", height: "2.3"}
- %rect.fg{x: "10.7", y: "32", width: "26.6", height: "2.3"}
- %rect.fg{x: "10.7", y: "18.2", width: "26.6", height: "11.4"}
-
- %symbol#wide_symbol{viewBox: "0 0 48 48"}
- %rect.fg{x: "10.7", y: "13.7", width: "26.6", height: "2.3"}
- %rect.fg{x: "10.7", y: "32", width: "26.6", height: "2.3"}
- %rect.fg{x: "6.5", y: "18.2", width: "34.9", height: "11.4"}
-
- %symbol#hero_button{viewBox: "0 0 48 48"}
- %path.bg{d: "M40.3,7.6c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.6,40.3,7.6z"}
- %path.fg{d: "M4.7,36.5C8.8,42.7,15.9,46.9,24,46.9s15.2-4.2,19.3-10.4H4.7z" }
- %path.fg{d: "M43.6,11.9c-4-6.6-11.3-11-19.6-11S8.4,5.3,4.4,11.9H43.6z" }
-
- // Toolbar buttons
- %symbol#bold_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M26.5,18.4c0-1.6-1.3-2.4-2.8-2.4H21v5.1h2.7C25.4,21.1,26.5,20.1,26.5,18.4z"}
- %path.fg{d: "M24.7,25.7H21v5.7h4c1.9,0,3.2-1.1,3.2-3C28.3,26.8,27.1,25.7,24.7,25.7z"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M25.7,35.9h-7.1 c-1.6,0-2.7-1-2.7-2.6V14c0-1.6,1.1-2.6,2.7-2.6h6.6c3.6,0,6.5,2.4,6.5,6c0,2.4-1,4-3.2,5v0.1c2.9,0.4,5.2,2.8,5.2,5.8 C33.7,33.3,30.4,35.9,25.7,35.9z"}
-
- %symbol#italic_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M32.1,13.9l-9.4,19.5 c-0.8,1.7-2.7,2.7-4.7,2.7c-1.9,0-2.9-1-2.1-2.7l9.4-19.5c0.8-1.7,2.7-2.7,4.7-2.7C31.9,11.2,32.9,12.2,32.1,13.9z"}
-
- %symbol#link_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M32.1,13.9l-9.4,19.5 c-0.8,1.7-2.7,2.7-4.7,2.7c-1.9,0-2.9-1-2.1-2.7l9.4-19.5c0.8-1.7,2.7-2.7,4.7-2.7C31.9,11.2,32.9,12.2,32.1,13.9z"}
-
- %symbol#ol_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M12.4,11.8h1.2c0.4,0,0.6,0.3,0.6,0.6v4.7 c0,0.4-0.3,0.6-0.6,0.6s-0.6-0.2-0.6-0.6v-4.2h-0.5c-0.4,0-0.6-0.3-0.6-0.6C11.8,12.1,12,11.8,12.4,11.8z M13.3,36.2 c-1.2,0-2-0.9-2-1.6c0-0.3,0.3-0.6,0.6-0.6c0.6,0,0.4,1,1.4,1c0.4,0,0.8-0.3,0.8-0.8c0-1.2-1.4-0.3-1.4-1.3c0-0.9,1.2-0.3,1.2-1.2 c0-0.3-0.2-0.6-0.6-0.6c-0.8,0-0.7,0.8-1.2,0.8c-0.3,0-0.5-0.3-0.5-0.6c0-0.6,0.9-1.3,1.8-1.3c1.2,0,1.8,0.9,1.8,1.5 c0,0.5-0.2,1-0.7,1.3c0.6,0.3,1,0.8,1,1.5C15.4,35.4,14.5,36.2,13.3,36.2z M14.9,26.9h-2.8c-0.4,0-0.6-0.2-0.6-0.5 c0-0.2,0.1-0.3,0.2-0.4c0.7-0.8,1.4-1.6,2.1-2.5c0.1-0.2,0.3-0.5,0.3-0.8c0-0.3-0.3-0.6-0.6-0.6c-1,0-0.5,1.3-1.3,1.3 c-0.4,0-0.6-0.3-0.6-0.6c0-1,0.9-1.9,1.9-1.9s1.8,0.7,1.8,1.7c0,1.2-1.3,2.3-2,3.2h1.5c0.4,0,0.6,0.2,0.6,0.5 C15.5,26.7,15.2,26.9,14.9,26.9z M33.9,36.1H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 S35.5,36.1,33.9,36.1z M33.9,26.9H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9S35.5,26.9,33.9,26.9z M33.9,17.7H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9S35.5,17.7,33.9,17.7z"}
-
- %symbol#ul_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M14.2,36.1c-1.6,0-2.9-1.3-2.9-2.9 c0-1.6,1.3-2.9,2.9-2.9c1.6,0,2.9,1.3,2.9,2.9C17.1,34.8,15.8,36.1,14.2,36.1z M14.2,26.9c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9 c1.6,0,2.9,1.3,2.9,2.9S15.8,26.9,14.2,26.9z M14.2,17.7c-1.6,0-2.9-1.3-2.9-2.9c0-1.6,1.3-2.9,2.9-2.9c1.6,0,2.9,1.3,2.9,2.9 C17.1,16.4,15.8,17.7,14.2,17.7z M33.9,36.1H21.8c-1.6,0-2.9-1.3-2.9-2.9c0-1.6,1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 C36.8,34.8,35.5,36.1,33.9,36.1z M33.9,26.9H21.8c-1.6,0-2.9-1.3-2.9-2.9s1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 S35.5,26.9,33.9,26.9z M33.9,17.7H21.8c-1.6,0-2.9-1.3-2.9-2.9c0-1.6,1.3-2.9,2.9-2.9h12.2c1.6,0,2.9,1.3,2.9,2.9 C36.8,16.4,35.5,17.7,33.9,17.7z"}
-
- %symbol#h1_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M34.6,33.7 c0,1.7-1.1,2.7-2.6,2.7s-2.6-1-2.6-2.7v-7.5H18.7v7.5c0,1.7-1.1,2.7-2.6,2.7s-2.6-1-2.6-2.7V14.3c0-1.7,1.1-2.7,2.6-2.7 s2.6,1,2.6,2.7v7.3h10.8v-7.3c0-1.7,1.1-2.7,2.6-2.7s2.6,1,2.6,2.7V33.7z"}
-
- %symbol#h2_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M33.2,33.3 c0,1.6-1,2.7-2.5,2.7s-2.5-1.1-2.5-2.7v-8.6c0-2.3-1.4-3.4-3.2-3.4c-2,0-3.2,1.5-3.2,3.4v8.6c0,1.6-1,2.7-2.5,2.7s-2.5-1.1-2.5-2.7 V12.2c0-1.6,1-2.7,2.5-2.7s2.5,1.1,2.5,2.7v6.7h0.1c1-1.3,3-1.7,4.5-1.7c3.9,0,6.8,2.5,6.8,6.7V33.3z"}
-
- %symbol#anchor_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M23.2,36.1 c-1.6,1.6-3.7,2.6-6,2.5c-2.2-0.1-4.1-0.9-5.6-2.5c-3-3.2-2.8-8.2,0.3-11.3l4.9-4.9c0.1-0.1,0.2-0.2,0.3-0.2c0.4-0.4,1-0.8,1.6-1.1 c1.5-0.8,3.2-1.1,4.9-0.9c0.4,0,0.8,0.1,1.2,0.2c0.4,0.1,0.8,0.2,1.1,0.4c0.8,0.4,1.5,0.9,2.2,1.5c0.8,0.8,0.7,2.2-0.1,3l-1,1 c-0.1,0.2-0.3,0.3-0.5,0.4c-0.2-0.7-0.6-1.4-1.1-1.9c-0.6-0.6-1.4-1-2.2-1.2c-0.3-0.1-0.6-0.1-0.9-0.1h-0.2 c-0.4,0.1-0.7,0.1-1.1,0.2c-0.7,0.2-1.4,0.6-1.9,1.1l-4.9,4.9c-0.7,0.7-1.3,1.7-1.4,2.8c-0.1,1.2,0.3,2.5,1.2,3.4 c0.9,0.9,2.1,1.4,3.4,1.2c1.1-0.1,2-0.6,2.8-1.4l0.9-0.9c0.5-0.5,1.2-0.6,1.8-0.4c0.4,0.1,0.8,0.2,1.2,0.2c0.4,0.1,0.7,0.1,1.1,0.1 c0.4,0,0.8-0.1,1.2-0.1L23.2,36.1z M36.1,23.3l-4.9,4.9c-0.1,0.1-0.2,0.2-0.3,0.2c-0.4,0.4-1,0.9-1.6,1.2c-1.2,0.6-2.5,0.9-3.8,0.9 c-0.4,0-0.7,0-1.1-0.1c-0.4,0-0.8-0.1-1.2-0.2c-0.4-0.1-0.8-0.2-1.1-0.4c-0.8-0.4-1.5-0.9-2.2-1.5c-0.8-0.8-0.8-2.2,0.1-3l1-1 c0.2-0.2,0.4-0.3,0.6-0.4c0.2,0.7,0.6,1.4,1.1,1.9c0.6,0.6,1.4,1,2.2,1.2c0.3,0.1,0.6,0.1,0.9,0.1h0.1c0.4-0.1,0.7-0.1,1.1-0.2 c0.7-0.2,1.4-0.6,1.9-1.1l4.9-4.9c1.6-1.6,1.9-4.2,0.4-5.9c-0.9-1.1-2.2-1.7-3.7-1.5c-1.1,0.1-2.1,0.6-2.8,1.4l-1,1 c-0.4,0.4-0.9,0.5-1.4,0.3c-0.4-0.1-0.8-0.2-1.2-0.2c-0.9-0.1-1.8-0.1-2.7,0.1l3.7-3.7c1.6-1.6,3.7-2.6,6-2.5 c2.2,0.1,4.1,0.9,5.6,2.5C39.5,15,39.3,20.1,36.1,23.3z"}
-
- %symbol#clear_button{viewBox: "0 0 48 48"}
- %path.fg{d: "M40.3,7.8c-9-9-23.5-9-32.5,0s-9,23.5,0,32.5s23.5,9,32.5,0S49.2,16.8,40.3,7.8z M26.8,24.1c2.8,2.8,5.7,5.7,8.5,8.5 c0.7,0.7,0.8,1.8,0.1,2.5c-0.7,0.8-1.8,0.8-2.6,0.1c-0.1-0.1-0.1-0.1-0.2-0.2c-2.8-2.8-5.5-5.5-8.4-8.4c-0.1-0.1-0.1-0.1-0.2-0.2 c-0.1,0.1-0.1,0.1-0.2,0.2c-2.8,2.8-5.7,5.7-8.5,8.5c-0.8,0.8-2,0.8-2.7-0.1c-0.6-0.7-0.5-1.7,0.1-2.4c0.5-0.5,1-1,1.5-1.5 c2.4-2.4,4.8-4.8,7.1-7.1c0-0.1,0.1-0.1,0.2-0.1c-0.1-0.1-0.1-0.2-0.2-0.2c-2.8-2.8-5.7-5.7-8.5-8.5c-0.7-0.7-0.8-1.8-0.1-2.5 c0.7-0.8,1.8-0.8,2.6-0.1c0.1,0.1,0.1,0.1,0.2,0.2c2.8,2.8,5.5,5.5,8.4,8.4c0.1,0.1,0.1,0.1,0.2,0.3c0.1-0.1,0.1-0.2,0.2-0.2 c2.8-2.8,5.7-5.7,8.5-8.5c0.7-0.9,2-0.8,2.7,0c0.6,0.7,0.5,1.7-0.1,2.4c-0.2,0.2-0.6,0.6-0.8,0.8c-2.6,2.6-5.2,5.2-7.8,7.8 c-0.1,0-0.1,0.1-0.2,0.1C26.7,24,26.8,24.1,26.8,24.1z"}
-
-
+ %symbol#location_symbol{viewBox: "0 0 24 24",fill: "#101820", stroke: "#101820", "stroke-width": ".1"}
+ %g{transform: "translate(4 1)"}
+ %circle{cx: "8", cy: "8.14", r: "2"}
+ %path{d: "M8 .14a8 8 0 0 0-8 8c0 3.77 5 14 8 14s8-10.23 8-14a8 8 0 0 0-8-8zm0 20c-1.49-.56-6-8.51-6-12a6 6 0 1 1 12 0c0 3.46-4.51 11.41-6 11.99v.01z"}
+ %symbol#clock_symbol{viewBox: "-1 -1 18 18", fill: "#101820", width: "16", height: "16", stroke: "#101820", "stroke-width": "0.7"}
+ -# %path{d: "M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z", stroke: "#101820", "stroke-width": "1"}
+ -# %path{d: "M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z", stroke: "#101820", "stroke-width": "1"}
+ %path{d: "M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"}
+ %path{d: "M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"}
diff --git a/app/views/droom/users/_action_menu.html.haml b/app/views/droom/users/_action_menu.html.haml
index 7a4f39645..7ce846a00 100644
--- a/app/views/droom/users/_action_menu.html.haml
+++ b/app/views/droom/users/_action_menu.html.haml
@@ -1,17 +1,20 @@
- event ||= false
- group ||= false
+- data_for = "user_#{user.id}"
+- if group
+ - data_for = "user_#{user.id}#{group.id}"
- if can?(:edit, user)
- .menu{:data => {:for => "user_#{user.id}"}}
+ .menu{:data => {:for => data_for}}
%ul.actions
%li
- = link_to t(:edit_profile), droom.edit_user_url(user), :class => 'edit', :data => {:action => "popup", :affected => "#user_#{user.id}"}
+ = link_to t(:edit_profile), droom.edit_user_url(user), :class => 'edit', :data => {:action => "popup", :affected => "#user_#{user.id}#{group.try(:id)}"}
%li
- = link_to t(:edit_account), droom.edit_user_url(user, view: 'preferences'), :class => 'edit', :data => {:action => "popup", :affected => "#user_#{user.id}"}
+ = link_to t(:edit_account), droom.edit_user_url(user, view: 'preferences'), :class => 'edit', :data => {:action => "popup", :affected => "#user_#{user.id}#{group.try(:id)}"}
- unless user == current_user
%li
- = link_to t(:delete_user), droom.user_url(user), :method => 'delete', :class => 'delete', :data => {:action => "remove", :removed => "#user_#{user.id}", :confirm => t(:confirm_delete_user, :name => user.full_name)}
+ = link_to t(:delete_user), droom.user_url(user), :method => 'delete', :class => 'delete', :data => {:action => "remove", :removed => "#user_#{user.id}#{group.try(:id)}", :confirm => t(:confirm_delete_user, :name => user.full_name)}
- if !user.confirmed? && can?(:reinvite, user)
%li
@@ -19,5 +22,4 @@
- if group && membership = user.membership_of(group)
%li
- = link_to t(:expel, name: group.name), droom.membership_url(membership), :method => 'delete', :class => 'delete', :data => {:action => "remove", :removed => "#user_#{user.id}"}
-
+ = link_to t(:expel, name: group.name), droom.membership_url(membership), :method => 'delete', :class => 'delete', :data => {:action => "remove", :removed => "#user_#{user.id}#{group.try(:id)}"}
\ No newline at end of file
diff --git a/app/views/droom/users/edit/_groups.html.haml b/app/views/droom/users/edit/_groups.html.haml
index d82b403c5..a9f4d84ae 100644
--- a/app/views/droom/users/edit/_groups.html.haml
+++ b/app/views/droom/users/edit/_groups.html.haml
@@ -5,6 +5,7 @@
= form_for user, :html => {class: 'poppedup'}, data: {remote: true, type: :html} do |f|
= hidden_field_tag :view, @view
+ = hidden_field_tag "user[group_ids][]", nil
- groups = Droom::Group.all
- if groups.any?
@@ -15,4 +16,4 @@
= check_box_tag "user[group_ids][]", group.id, user.group_ids.include?(group.id), id: "user_group_ids_#{group.id}"
= group.name
- = render partial: 'droom/users/edit/buttons', locals: {f: f}
\ No newline at end of file
+ = render partial: 'droom/users/edit/buttons', locals: {f: f}
diff --git a/app/views/droom/users/edit/_password_fields.html.haml b/app/views/droom/users/edit/_password_fields.html.haml
index 1b5226d94..ef0b23828 100644
--- a/app/views/droom/users/edit/_password_fields.html.haml
+++ b/app/views/droom/users/edit/_password_fields.html.haml
@@ -5,11 +5,6 @@
- fclass << " required" unless user.password_set?
%fieldset{data: {role: 'password'}}
- %p.password_quality.floating{data: {role: "meter"}}
- %span.instructions{data: {role: "warnings"}}
- %span.meter{data: {role: "gauge"}}
- %svg
- %use{"xlink:href" => "#warning_symbol"}
%p.manage_password
%span.col.password>
@@ -21,6 +16,12 @@
%br
= f.password_field :password_confirmation, placeholder: "", required: true
+ %p.password_quality.floating{data: {role: "meter"}}
+ %span.instructions{data: {role: "warnings"}}
+ %span.meter{data: {role: "gauge"}}
+ %svg
+ %use{"xlink:href" => "#warning_symbol"}
+
- if with_submit
%p.buttons{:data => {:role => "confirmation"}}>
= f.submit t(:set_password)
diff --git a/app/views/droom/users/edit/_simple.html.haml b/app/views/droom/users/edit/_simple.html.haml
index c437e32f3..7ef9cb7d8 100644
--- a/app/views/droom/users/edit/_simple.html.haml
+++ b/app/views/droom/users/edit/_simple.html.haml
@@ -15,6 +15,17 @@
= form_for user, :html => {:class => 'edit user', :autocomplete => false} do |f|
= render partial: 'droom/users/edit/user_fields', locals: {f: f, user: user, minimal: true}
+ -# = render partial: 'droom/users/edit/groups', locals: {f: f, user: user, minimal: true}
+ = hidden_field_tag "user[group_ids][]", nil
+
+ - groups = Droom::Group.all
+ - if groups.any?
+ %div{style: 'width: 100%;'}
+ = f.label(:group_ids, class: "important") + ":"
+ - groups.each do |group|
+ %label.inline{for: "user_group_ids_#{group.id}"}
+ = check_box_tag "user[group_ids][]", group.id, user.group_ids.include?(group.id), id: "user_group_ids_#{group.id}"
+ = group.name
- unless @user.confirmed?
= f.hidden_field :send_confirmation, value: true
diff --git a/app/views/droom/users/passwords/expired_reset_password_token.html.haml b/app/views/droom/users/passwords/expired_reset_password_token.html.haml
new file mode 100644
index 000000000..97dfd0b77
--- /dev/null
+++ b/app/views/droom/users/passwords/expired_reset_password_token.html.haml
@@ -0,0 +1,10 @@
+%h1.pagetitle
+ =t :expired_token
+%br/
+
+%h4
+ = t(:want_to_reset_again)
+
+%br
+= link_to 'Yes', new_password_path(resource_name), style: "background-color: #74b87a; border: none; color: white; padding: 9px 27px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer;"
+= link_to 'No', root_url, style: "background-color: #f44336; border: none; color: white; padding: 9px 27px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer;"
\ No newline at end of file
diff --git a/app/views/droom/users/passwords/show.html.haml b/app/views/droom/users/passwords/show.html.haml
index 8088340d4..91b1a1e5e 100644
--- a/app/views/droom/users/passwords/show.html.haml
+++ b/app/views/droom/users/passwords/show.html.haml
@@ -3,4 +3,4 @@
=t :message_sent
%p
- = t :password_reset_message_sent, sender: Settings.email.address
+ = t :password_reset_message_sent_html, sender: Settings.email.address
diff --git a/app/views/droom/users/show/_account_info.html.haml b/app/views/droom/users/show/_account_info.html.haml
index bb4dd7987..10ca22a44 100644
--- a/app/views/droom/users/show/_account_info.html.haml
+++ b/app/views/droom/users/show/_account_info.html.haml
@@ -4,7 +4,7 @@
%section{class: cssclass, data: {refreshing: true, url: droom.user_url(@user, view: 'account_info', format: :js)}}
- if @user && can?(:edit, @user)
%span.user_controls
- = link_to t(:edit_user_profile_symbol), droom.edit_user_url(@user, view: 'account_info'), class: "edit", data: {action: 'popup', affected: "section.account_info"}
+ = link_to t(:edit_user_profile_symbol), droom.edit_user_url(@user, view: 'preferences'), class: "edit", data: {action: 'popup', affected: "section.account_info"}
%h2.section
= t :user_profile_account
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 731623ae8..34294e61f 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -256,7 +256,7 @@ def user_deserialize(key)
end
# We used to set shared domain cookie on sign in here
- # but it proved impossible, or anyway very unreliable, to try and do that after session_limitable had set its unique_session_id.
+ # but it proved impossible, or anyway very unreliable, to make sure that happened after session_limitable had set its unique_session_id.
# so now the cookie is set or updated on every request, in an after_action.
# Unset session id and shared domain cookie on sign out.
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 1797c629e..1eb7bcc8c 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -35,6 +35,7 @@ en:
add_root_folder: "Add root folder"
add_service: "Add service"
add_subfolder: Add sub-folder
+ move_folder: "Move folder"
add_to_stream: "add to stream"
add_user: "Add User"
added_to: "has been added to '%{group}'."
@@ -42,11 +43,12 @@ en:
advanced_search: "Search terms"
advanced_search_link: "...advanced search"
allow: "Allow"
+ all_permissions: 'All Permissions'
and_invited: "and invited to log in."
apps:
data_room: "Data
Room"
at: "at"
- attachment_count:
+ attachment_count:
zero: "No documents attached."
one: "One document attached."
other: "%{count} documents attached."
@@ -118,7 +120,7 @@ en:
devices: "Devices"
directory: "Directory"
directory_introduction: "The directory organises people by group. To find someone quickly, start typing their name on the right."
- document_count:
+ document_count:
zero: "no documents"
one: "one document"
other: "%{count} documents"
@@ -183,6 +185,7 @@ en:
events_in: "Events in %{period}"
events_on: "Events on %{day}"
expel: "remove from %{name} group"
+ expired_token: "Your password reset token has expired"
file_under: "File under"
filter_account_confirmed: "Status..."
filter_content_type: "File type..."
@@ -383,6 +386,7 @@ en:
no_past_events: "There are no past events to display."
no_past_events: No past events to show
no_people: "Sorry: there are no directory entries accessible by your account."
+ no_permission: 'No Permission'
no_person_description: "No biography available."
no_results: "Sorry: nothing to show here."
no_stream: "Nothing yet to show here."
@@ -438,9 +442,10 @@ en:
password_requirements: "Ideally your password should include numbers and punctuation and should not resemble a dictionary word."
password_reset_complete: "Thank you for persevering with the account administration. Your password has been reset and you have been signed in automatically."
password_reset_instructions: "Please enter your email address below. A message will be sent containing reset instructions."
- password_reset_message_sent: "A message has been sent to your email address containing a link that will let you reset your password. Please allow a few minutes for it to arrive, and keep an eye on your spam folder. The address it will seem to come from is %{sender}."
+ password_reset_message_sent_html: "A message has been sent to your email address containing a link that will let you reset your password. Please allow a few minutes for it to arrive, and keep an eye on your spam folder. The address it will seem to come from is %{sender}."
password_reset_welcome: "Please use the form below to set a new password for your account. A confirmation box will appear when your password meets the requirements, and the submit button will go green and become clickable when the passwords match."
password_set: "Thank you. Your password has been set and your account is fully active."
+ past_events: "Past Events"
pending_organisations_introduction:
zero: "No applications"
one: "Review one application"
@@ -520,6 +525,7 @@ en:
quick_search: "Quick search"
quick_search: "Quick search"
quick_search_prompt: "Type anything here"
+ read_permission: "Read Permission"
recent_events: "Past events"
register_organisation: "Apply to register your organisation"
reject: "reject"
@@ -619,6 +625,7 @@ en:
pagination:
previous: "«"
next: "»"
+ want_to_reset_again: "Do you want to generate new reset password token ?"
welcome_preamble: "Welcome to the Croucher Foundation. We notice that this is the first time you have used your Croucher Foundation account so before we go any further, please choose a password."
would_you_like_to_return: "You probably want to return to"
year: "YEAR"
diff --git a/config/routes.rb b/config/routes.rb
index e77adc8ab..cc130afd7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -13,6 +13,7 @@
resources :users do
post 'reindex', on: :member, as: :reindex
get "whoami" , on: :collection, as: :whoami
+ get "authenticable", on: :member, as: :authenticable
end
resources :events
resources :venues
@@ -38,6 +39,7 @@
devise_scope :user do
+ get "/users/expired_reset_password_token" => "users/passwords#expired_reset_password_token", as: :expired_reset_password_token
get "/signup" => "users/registrations#new", as: :signup
post '/register' => 'users/registrations#create', as: :register
get "/users/registrations/confirm" => "users/registrations#confirm", as: :confirm_registration
@@ -51,6 +53,7 @@
delete '/api/users/sign_out' => 'api/sessions#destroy', as: :api_sign_out
get '/api/authenticate/:tok' => 'api/sessions#authenticate', as: 'authenticate'
get '/api/deauthenticate/:tok' => 'api/sessions#deauthenticate', as: 'deauthenticate'
+ get '/api/users/authenticable/:id' => 'api/users#authenticable', as: 'authenticable'
end
resources :helps
@@ -97,10 +100,14 @@
resources :folders do
get "dropbox", on: :member, as: :dropbox
+ get "move_folder", on: :member
+ put "moved", on: :member
resources :documents
resources :folders
end
+ get "child_folders" => "folders#child_folders"
+
resources :organisations do
resources :users
collection do
@@ -117,6 +124,7 @@
get "activity" => "users#activity", as: :activity
get :preferences, on: :member, as: :preferences
put :preference, on: :member, as: :set_preference
+ get :download, on: :collection
get :admin, on: :collection
put :setup, on: :collection
put :reinvite, on: :member
@@ -129,7 +137,10 @@
resources :groups do
resources :memberships
- resources :group_permissions
+ resources :group_permissions do
+ post :upsert, on: :collection
+ delete :delete_by_ids, on: :collection, as: :delete
+ end
end
resources :event_types
diff --git a/droom.gemspec b/droom.gemspec
index f66db296d..d2fc2bf55 100644
--- a/droom.gemspec
+++ b/droom.gemspec
@@ -17,9 +17,10 @@ Gem::Specification.new do |s|
s.test_files = Dir["spec/**/*"]
- s.add_dependency "rails", "~> 5.2"
+ s.add_dependency "rails", "~> 6.1"
s.add_dependency "responders"
s.add_dependency "acts_as_tree"
+ s.add_dependency "acts_as_list"
s.add_dependency "active_model_serializers"
s.add_dependency "api-pagination"
@@ -27,8 +28,8 @@ Gem::Specification.new do |s|
s.add_dependency "settingslogic"
s.add_dependency "request_store"
- s.add_dependency "devise"
- s.add_dependency "devise-security"
+ s.add_dependency "devise", "4.7.3"
+ s.add_dependency "devise-security", "0.15.0"
s.add_dependency "devise_zxcvbn"
s.add_dependency "cancancan"
@@ -53,6 +54,7 @@ Gem::Specification.new do |s|
s.add_dependency "rdiscount"
s.add_dependency "searchkick"
+ s.add_dependency "elasticsearch"
s.add_dependency "yomu"
s.add_dependency "typhoeus"
diff --git a/lib/droom.rb b/lib/droom.rb
index 9f10eb087..c8276f24e 100644
--- a/lib/droom.rb
+++ b/lib/droom.rb
@@ -10,9 +10,6 @@
module Droom
- # Droom configuration is handled by accessors on the Droom base module.
- # Boolean items also offer the interrogative form.
-
mattr_accessor :config
@@config = Droom::Config.new
diff --git a/lib/droom/config.rb b/lib/droom/config.rb
index 4a3360d41..726f5dc11 100644
--- a/lib/droom/config.rb
+++ b/lib/droom/config.rb
@@ -57,7 +57,8 @@ class Config
:mc_api_key,
:mc_news_template,
:mc_news_list,
- :session_timeout
+ :session_timeout,
+ :enable_pubsub
def home_url
@home_url ||= "http://example.com"
@@ -287,7 +288,11 @@ def default_permissions
end
def session_timeout
- @@session_timeout ||= 15.minutes
+ @@session_timeout ||= 1.hour
+ end
+
+ def enable_pubsub?
+ !!@enable_pubsub
end
diff --git a/lib/droom/version.rb b/lib/droom/version.rb
index a332e5aa5..97dde8131 100644
--- a/lib/droom/version.rb
+++ b/lib/droom/version.rb
@@ -1,3 +1,3 @@
module Droom
- VERSION = "0.12.3"
+ VERSION = "0.12.4"
end
diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity
index 8901bd844..fac457aa4 100644
--- a/node_modules/.yarn-integrity
+++ b/node_modules/.yarn-integrity
@@ -1,5 +1,5 @@
{
- "systemParams": "darwin-x64-72",
+ "systemParams": "darwin-x64-64",
"modulesFolders": [
"node_modules"
],
@@ -13,7 +13,7 @@
"clipboard@^2.0.0",
"ep-jquery-tokeninput@^1.8.10",
"honeybadger@^1.3.0",
- "imagesloaded@^4.1.3",
+ "imagesloaded@^4.1.4",
"jquery-circle-progress@^1.2.2",
"jquery-deserialize@^2.0.0-rc1",
"jquery.cookie@^1.4.1",
@@ -68,7 +68,7 @@
"har-validator@~5.0.3": "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd",
"honeybadger@^1.3.0": "https://registry.yarnpkg.com/honeybadger/-/honeybadger-1.3.0.tgz#e9f01314d52a26a6d615d65485a0a4d4e50ec8f8",
"http-signature@~1.2.0": "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1",
- "imagesloaded@^4.1.3": "https://registry.yarnpkg.com/imagesloaded/-/imagesloaded-4.1.4.tgz#1376efcd162bb768c34c3727ac89cc04051f3cc7",
+ "imagesloaded@^4.1.4": "https://registry.yarnpkg.com/imagesloaded/-/imagesloaded-4.1.4.tgz#1376efcd162bb768c34c3727ac89cc04051f3cc7",
"is-typedarray@~1.0.0": "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a",
"isstream@~0.1.2": "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a",
"jquery-circle-progress@^1.2.2": "https://registry.yarnpkg.com/jquery-circle-progress/-/jquery-circle-progress-1.2.2.tgz#260e9130ac8e2b5572eaa7a93b9e8a6b27bc8eea",
diff --git a/package.json b/package.json
index ea422b054..ca250355c 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"clipboard": "^2.0.0",
"ep-jquery-tokeninput": "^1.8.10",
"honeybadger": "^1.3.0",
- "imagesloaded": "^4.1.3",
+ "imagesloaded": "^4.1.4",
"jquery": "^3.2.1",
"jquery-circle-progress": "^1.2.2",
"jquery-deserialize": "^2.0.0-rc1",
diff --git a/spec/dummy/db/migrate/20130724124775_user_properties.droom.rb b/spec/dummy/db/migrate/20130724124775_user_properties.droom.rb
index 40e31c929..544ee193c 100644
--- a/spec/dummy/db/migrate/20130724124775_user_properties.droom.rb
+++ b/spec/dummy/db/migrate/20130724124775_user_properties.droom.rb
@@ -25,7 +25,7 @@ def change
Droom::User.reset_column_information
Droom::Person.all.each do |p|
if u = Droom::User.find(p.user_id)
- u.update_attributes p.attributes.slice(*%w{organisation_id phone description description post_line1 post_line2 post_city post_region post_country post_code mobile image})
+ u.update p.attributes.slice(*%w{organisation_id phone description description post_line1 post_line2 post_city post_region post_country post_code mobile image})
end
end
diff --git a/yarn.lock b/yarn.lock
index 080f21d3d..851e6d47e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -227,7 +227,7 @@ http-signature@~1.2.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
-imagesloaded@^4.1.3:
+imagesloaded@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/imagesloaded/-/imagesloaded-4.1.4.tgz#1376efcd162bb768c34c3727ac89cc04051f3cc7"
integrity sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==