decidim-gallery 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +30 -0
  4. data/Rakefile +9 -0
  5. data/app/cells/decidim/gallery/main/image.erb +51 -0
  6. data/app/cells/decidim/gallery/main/image_collection.erb +13 -0
  7. data/app/cells/decidim/gallery/main/image_list.erb +9 -0
  8. data/app/cells/decidim/gallery/main/video.erb +20 -0
  9. data/app/cells/decidim/gallery/main/video_collection.erb +3 -0
  10. data/app/cells/decidim/gallery/main_cell.rb +88 -0
  11. data/app/cells/decidim/gallery/video/show.erb +6 -0
  12. data/app/cells/decidim/gallery/video_cell.rb +96 -0
  13. data/app/commands/decidim/gallery/admin/create_gallery_item.rb +50 -0
  14. data/app/commands/decidim/gallery/admin/publish_gallery_item.rb +38 -0
  15. data/app/commands/decidim/gallery/admin/unpublish_gallery_item.rb +38 -0
  16. data/app/commands/decidim/gallery/admin/update_gallery_item.rb +44 -0
  17. data/app/controllers/decidim/gallery/admin/application_controller.rb +24 -0
  18. data/app/controllers/decidim/gallery/admin/gallery_item_controller.rb +104 -0
  19. data/app/controllers/decidim/gallery/application_controller.rb +8 -0
  20. data/app/controllers/decidim/gallery/gallery_controller.rb +9 -0
  21. data/app/forms/decidim/gallery/admin/gallery_item_form.rb +17 -0
  22. data/app/forms/decidim/gallery/admin/gallery_item_image_form.rb +14 -0
  23. data/app/forms/decidim/gallery/admin/gallery_item_video_form.rb +12 -0
  24. data/app/helpers/decidim/gallery/admin/application_helper.rb +18 -0
  25. data/app/model/decidim/gallery/application_record.rb +10 -0
  26. data/app/model/decidim/gallery/gallery_item.rb +38 -0
  27. data/app/overrides/decidim/admin/static_pages/edit/add_gallery.html.erb.deface +11 -0
  28. data/app/overrides/decidim/pages/_standalone/add_content_blocks.html.erb.deface +7 -0
  29. data/app/overrides/decidim/pages/_tabbed/add_content_blocks.html.erb.deface +5 -0
  30. data/app/packs/entrypoints/decidim_gallery.js +3 -0
  31. data/app/packs/images/decidim/gallery/icon.svg +1 -0
  32. data/app/packs/src/decidim/gallery/gallery.js +42 -0
  33. data/app/packs/src/decidim/gallery/masonry/EvEmitter.js +85 -0
  34. data/app/packs/src/decidim/gallery/masonry/getSize.js +181 -0
  35. data/app/packs/src/decidim/gallery/masonry/jQueryBridget.js +109 -0
  36. data/app/packs/src/decidim/gallery/masonry/masonry.js +202 -0
  37. data/app/packs/src/decidim/gallery/masonry/matchesSelector.js +25 -0
  38. data/app/packs/src/decidim/gallery/masonry/outlayer.js +885 -0
  39. data/app/packs/src/decidim/gallery/masonry/outlayerItem.js +522 -0
  40. data/app/packs/src/decidim/gallery/masonry/utils.js +203 -0
  41. data/app/packs/stylesheets/decidim/gallery/_gallery.scss +3 -0
  42. data/app/permissions/decidim/gallery/admin/permissions.rb +18 -0
  43. data/app/permissions/decidim/gallery/permissions.rb +17 -0
  44. data/app/views/decidim/gallery/admin/gallery_item/_form_image.html.erb +16 -0
  45. data/app/views/decidim/gallery/admin/gallery_item/_form_video.html.erb +16 -0
  46. data/app/views/decidim/gallery/admin/gallery_item/edit.html.erb +9 -0
  47. data/app/views/decidim/gallery/admin/gallery_item/index.html.erb +52 -0
  48. data/app/views/decidim/gallery/admin/gallery_item/new.html.erb +9 -0
  49. data/app/views/decidim/gallery/gallery/index.html.erb +11 -0
  50. data/config/assets.rb +9 -0
  51. data/config/i18n-tasks.yml +22 -0
  52. data/config/locales/en.yml +79 -0
  53. data/config/locales/fr.yml +79 -0
  54. data/config/locales/ro.yml +79 -0
  55. data/lib/decidim/gallery/admin/static_pages/command.rb +15 -0
  56. data/lib/decidim/gallery/admin/static_pages/form.rb +21 -0
  57. data/lib/decidim/gallery/admin.rb +14 -0
  58. data/lib/decidim/gallery/admin_engine.rb +27 -0
  59. data/lib/decidim/gallery/component.rb +47 -0
  60. data/lib/decidim/gallery/engine.rb +60 -0
  61. data/lib/decidim/gallery/test/factories.rb +58 -0
  62. data/lib/decidim/gallery/version.rb +14 -0
  63. data/lib/decidim/gallery.rb +14 -0
  64. data/lib/tasks/decidim_gallery.rake +16 -0
  65. metadata +163 -0
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Gallery
5
+ module Admin
6
+ class GalleryItemImageForm < GalleryItemForm
7
+ include Decidim::HasUploadValidations
8
+
9
+ attribute :photo
10
+ attribute :remove_photo, Boolean, default: false
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Gallery
5
+ module Admin
6
+ class GalleryItemVideoForm < GalleryItemForm
7
+ translatable_attribute :video_url, String
8
+ validates :video_url, translatable_presence: true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Gallery
5
+ module Admin
6
+ module ApplicationHelper
7
+ def formatted_gallery_name(gallery)
8
+ text = I18n.t("formatted_text",
9
+ component_name: translated_attribute(gallery.name),
10
+ space_name: translated_attribute(gallery.participatory_space.title),
11
+ scope: "decidim.gallery.admin")
12
+
13
+ [text, gallery.id]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Gallery
5
+ # Abstract class from which all models in this engine inherit.
6
+ class ApplicationRecord < ActiveRecord::Base
7
+ self.abstract_class = true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Gallery
5
+ class GalleryItem < ApplicationRecord
6
+ include Decidim::Resourceable
7
+ include Decidim::HasComponent
8
+ include Decidim::Authorable
9
+ include Decidim::TranslatableResource
10
+ include Decidim::Publicable
11
+ include Decidim::HasUploadValidations
12
+
13
+ include Traceable
14
+ include Loggable
15
+
16
+ translatable_fields :title, :video_url
17
+
18
+ component_manifest_name "gallery"
19
+
20
+ has_one_attached :photo
21
+ validates_upload :photo, uploader: Decidim::ImageUploader
22
+
23
+ scope :created_at_desc, -> { order(arel_table[:created_at].desc) }
24
+
25
+ def visible?
26
+ participatory_space.try(:visible?) && component.try(:published?)
27
+ end
28
+
29
+ def video_url=(value)
30
+ data[:video_url] = value
31
+ end
32
+
33
+ def video_url
34
+ data.fetch("video_url", {})
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ <!-- insert_before 'div.form-general-submit' -->
2
+ <div class="card">
3
+ <div class="card-divider">
4
+ <h2 class="card-title"><%= t("decidim.gallery.settings")%></h2>
5
+ </div>
6
+ <div class="card-section">
7
+ <div class="row column">
8
+ <%= f.select :gallery_id, f.object.galleries.map { |gallery| formatted_gallery_name(gallery) }, include_blank: t("decidim.gallery.none") %>
9
+ </div>
10
+ </div>
11
+ </div>
@@ -0,0 +1,7 @@
1
+ <!-- insert_after ".card__content" -->
2
+
3
+ <div class="card__content">
4
+ <% if page.gallery_id.present? %>
5
+ <%= cell("decidim/gallery/main", OpenStruct.new(gallery_id: page.gallery_id), context: { view_context: self }) %>
6
+ <% end %>
7
+ </div>
@@ -0,0 +1,5 @@
1
+ <!-- insert_after ".static__content" -->
2
+
3
+ <% if page.gallery_id.present? %>
4
+ <%= cell("decidim/gallery/main", OpenStruct.new(gallery_id: page.gallery_id), context: { view_context: self }) %>
5
+ <% end %>
@@ -0,0 +1,3 @@
1
+ import "src/decidim/gallery/gallery"
2
+
3
+ require.context("../images", true)
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,42 @@
1
+ import Masonry from "./masonry/masonry";
2
+
3
+ function addGalleryHandlers() {
4
+ $(".gallery-item > a").on("click", function(event){
5
+ event.preventDefault();
6
+ $(".gallery-item > a.gallery-item-active").removeClass("gallery-item-active");
7
+ $(event.target).parent("a").addClass("gallery-item-active");
8
+
9
+ $(".reveal").on("open.zf.reveal", function(){
10
+ $(window).trigger('resize');
11
+ });
12
+ $(".reveal").on("closeme.zf.reveal", function(){
13
+ let link = $(".gallery-item > a.gallery-item-active");
14
+ let id = link.data("id");
15
+ let chosenSlide = $("[data-target="+id+"]");
16
+ let index = chosenSlide.index(".orbit-container > li");
17
+ $('.orbit').foundation('changeSlide', true, chosenSlide, index);
18
+ $(document).on("slidechange.zf.orbit", function (){
19
+ $(window).trigger('resize');
20
+ });
21
+ });
22
+ });
23
+ }
24
+
25
+ $(() => {
26
+ $(".gallery img").one("load", function() {
27
+ $('.gallery.image').masonry({
28
+ itemSelector: '.gallery-item',
29
+ });
30
+ }).each(function() {
31
+ if(this.complete) {
32
+ $(this).trigger('load'); // For jQuery >= 3.0
33
+ }
34
+ });
35
+
36
+ $(document).on('open.zf.reveal', '[data-reveal]', function () {
37
+ $(window).trigger('resize');
38
+ });
39
+
40
+ });
41
+
42
+ document.addEventListener("DOMContentLoaded", function(event) { addGalleryHandlers(); });
@@ -0,0 +1,85 @@
1
+
2
+ function EvEmitter() {}
3
+
4
+ var proto = EvEmitter.prototype;
5
+
6
+ proto.on = function( eventName, listener ) {
7
+ if ( !eventName || !listener ) {
8
+ return;
9
+ }
10
+ // set events hash
11
+ var events = this._events = this._events || {};
12
+ // set listeners array
13
+ var listeners = events[ eventName ] = events[ eventName ] || [];
14
+ // only add once
15
+ if ( listeners.indexOf( listener ) == -1 ) {
16
+ listeners.push( listener );
17
+ }
18
+
19
+ return this;
20
+ };
21
+
22
+ proto.once = function( eventName, listener ) {
23
+ if ( !eventName || !listener ) {
24
+ return;
25
+ }
26
+ // add event
27
+ this.on( eventName, listener );
28
+ // set once flag
29
+ // set onceEvents hash
30
+ var onceEvents = this._onceEvents = this._onceEvents || {};
31
+ // set onceListeners object
32
+ var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
33
+ // set flag
34
+ onceListeners[ listener ] = true;
35
+
36
+ return this;
37
+ };
38
+
39
+ proto.off = function( eventName, listener ) {
40
+ var listeners = this._events && this._events[ eventName ];
41
+ if ( !listeners || !listeners.length ) {
42
+ return;
43
+ }
44
+ var index = listeners.indexOf( listener );
45
+ if ( index != -1 ) {
46
+ listeners.splice( index, 1 );
47
+ }
48
+
49
+ return this;
50
+ };
51
+
52
+ proto.emitEvent = function( eventName, args ) {
53
+ var listeners = this._events && this._events[ eventName ];
54
+ if ( !listeners || !listeners.length ) {
55
+ return;
56
+ }
57
+ // copy over to avoid interference if .off() in listener
58
+ listeners = listeners.slice(0);
59
+ args = args || [];
60
+ // once stuff
61
+ var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
62
+
63
+ for ( var i=0; i < listeners.length; i++ ) {
64
+ var listener = listeners[i]
65
+ var isOnce = onceListeners && onceListeners[ listener ];
66
+ if ( isOnce ) {
67
+ // remove listener
68
+ // remove before trigger to prevent recursion
69
+ this.off( eventName, listener );
70
+ // unset once flag
71
+ delete onceListeners[ listener ];
72
+ }
73
+ // trigger listener
74
+ listener.apply( this, args );
75
+ }
76
+
77
+ return this;
78
+ };
79
+
80
+ proto.allOff = function() {
81
+ delete this._events;
82
+ delete this._onceEvents;
83
+ };
84
+
85
+ export default EvEmitter;
@@ -0,0 +1,181 @@
1
+
2
+ // -------------------------- helpers -------------------------- //
3
+
4
+ // get a number from a string, not a percentage
5
+ function getStyleSize( value ) {
6
+ var num = parseFloat( value );
7
+ // not a percent like '100%', and a number
8
+ var isValid = value.indexOf('%') == -1 && !isNaN( num );
9
+ return isValid && num;
10
+ }
11
+
12
+ function noop() {}
13
+
14
+ var logError = typeof console == 'undefined' ? noop :
15
+ function( message ) {
16
+ console.error( message );
17
+ };
18
+
19
+ // -------------------------- measurements -------------------------- //
20
+
21
+ var measurements = [
22
+ 'paddingLeft',
23
+ 'paddingRight',
24
+ 'paddingTop',
25
+ 'paddingBottom',
26
+ 'marginLeft',
27
+ 'marginRight',
28
+ 'marginTop',
29
+ 'marginBottom',
30
+ 'borderLeftWidth',
31
+ 'borderRightWidth',
32
+ 'borderTopWidth',
33
+ 'borderBottomWidth'
34
+ ];
35
+
36
+ var measurementsLength = measurements.length;
37
+
38
+ function getZeroSize() {
39
+ var size = {
40
+ width: 0,
41
+ height: 0,
42
+ innerWidth: 0,
43
+ innerHeight: 0,
44
+ outerWidth: 0,
45
+ outerHeight: 0
46
+ };
47
+ for ( var i=0; i < measurementsLength; i++ ) {
48
+ var measurement = measurements[i];
49
+ size[ measurement ] = 0;
50
+ }
51
+ return size;
52
+ }
53
+
54
+ // -------------------------- getStyle -------------------------- //
55
+
56
+ /**
57
+ * getStyle, get style of element, check for Firefox bug
58
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
59
+ */
60
+ function getStyle( elem ) {
61
+ var style = getComputedStyle( elem );
62
+ if ( !style ) {
63
+ logError( 'Style returned ' + style +
64
+ '. Are you running this code in a hidden iframe on Firefox? ' +
65
+ 'See https://bit.ly/getsizebug1' );
66
+ }
67
+ return style;
68
+ }
69
+
70
+ // -------------------------- setup -------------------------- //
71
+
72
+ var isSetup = false;
73
+
74
+ var isBoxSizeOuter;
75
+
76
+ /**
77
+ * setup
78
+ * check isBoxSizerOuter
79
+ * do on first getSize() rather than on page load for Firefox bug
80
+ */
81
+ function setup() {
82
+ // setup once
83
+ if ( isSetup ) {
84
+ return;
85
+ }
86
+ isSetup = true;
87
+
88
+ // -------------------------- box sizing -------------------------- //
89
+
90
+ /**
91
+ * Chrome & Safari measure the outer-width on style.width on border-box elems
92
+ * IE11 & Firefox<29 measures the inner-width
93
+ */
94
+ var div = document.createElement('div');
95
+ div.style.width = '200px';
96
+ div.style.padding = '1px 2px 3px 4px';
97
+ div.style.borderStyle = 'solid';
98
+ div.style.borderWidth = '1px 2px 3px 4px';
99
+ div.style.boxSizing = 'border-box';
100
+
101
+ var body = document.body || document.documentElement;
102
+ body.appendChild( div );
103
+ var style = getStyle( div );
104
+ // round value for browser zoom. desandro/masonry#928
105
+ isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
106
+ getSize.isBoxSizeOuter = isBoxSizeOuter;
107
+
108
+ body.removeChild( div );
109
+ }
110
+
111
+ // -------------------------- getSize -------------------------- //
112
+
113
+ function getSize( elem ) {
114
+ setup();
115
+
116
+ // use querySeletor if elem is string
117
+ if ( typeof elem == 'string' ) {
118
+ elem = document.querySelector( elem );
119
+ }
120
+
121
+ // do not proceed on non-objects
122
+ if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
123
+ return;
124
+ }
125
+
126
+ var style = getStyle( elem );
127
+
128
+ // if hidden, everything is 0
129
+ if ( style.display == 'none' ) {
130
+ return getZeroSize();
131
+ }
132
+
133
+ var size = {};
134
+ size.width = elem.offsetWidth;
135
+ size.height = elem.offsetHeight;
136
+
137
+ var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
138
+
139
+ // get all measurements
140
+ for ( var i=0; i < measurementsLength; i++ ) {
141
+ var measurement = measurements[i];
142
+ var value = style[ measurement ];
143
+ var num = parseFloat( value );
144
+ // any 'auto', 'medium' value will be 0
145
+ size[ measurement ] = !isNaN( num ) ? num : 0;
146
+ }
147
+
148
+ var paddingWidth = size.paddingLeft + size.paddingRight;
149
+ var paddingHeight = size.paddingTop + size.paddingBottom;
150
+ var marginWidth = size.marginLeft + size.marginRight;
151
+ var marginHeight = size.marginTop + size.marginBottom;
152
+ var borderWidth = size.borderLeftWidth + size.borderRightWidth;
153
+ var borderHeight = size.borderTopWidth + size.borderBottomWidth;
154
+
155
+ var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
156
+
157
+ // overwrite width and height if we can get it from style
158
+ var styleWidth = getStyleSize( style.width );
159
+ if ( styleWidth !== false ) {
160
+ size.width = styleWidth +
161
+ // add padding and border unless it's already including it
162
+ ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
163
+ }
164
+
165
+ var styleHeight = getStyleSize( style.height );
166
+ if ( styleHeight !== false ) {
167
+ size.height = styleHeight +
168
+ // add padding and border unless it's already including it
169
+ ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
170
+ }
171
+
172
+ size.innerWidth = size.width - ( paddingWidth + borderWidth );
173
+ size.innerHeight = size.height - ( paddingHeight + borderHeight );
174
+
175
+ size.outerWidth = size.width + marginWidth;
176
+ size.outerHeight = size.height + marginHeight;
177
+
178
+ return size;
179
+ }
180
+
181
+ export default getSize;
@@ -0,0 +1,109 @@
1
+
2
+ // ----- utils ----- //
3
+
4
+ var arraySlice = Array.prototype.slice;
5
+
6
+ // helper function for logging errors
7
+ // $.error breaks jQuery chaining
8
+ var console = window.console;
9
+ var logError = typeof console == 'undefined' ? function() {} :
10
+ function( message ) {
11
+ console.error( message );
12
+ };
13
+
14
+ // ----- jQueryBridget ----- //
15
+
16
+ function jQueryBridget( namespace, PluginClass, $ ) {
17
+ $ = $ || jQuery || window.jQuery;
18
+ if ( !$ ) {
19
+ return;
20
+ }
21
+
22
+ // add option method -> $().plugin('option', {...})
23
+ if ( !PluginClass.prototype.option ) {
24
+ // option setter
25
+ PluginClass.prototype.option = function( opts ) {
26
+ // bail out if not an object
27
+ if ( !$.isPlainObject( opts ) ){
28
+ return;
29
+ }
30
+ this.options = $.extend( true, this.options, opts );
31
+ };
32
+ }
33
+
34
+ // make jQuery plugin
35
+ $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
36
+ if ( typeof arg0 == 'string' ) {
37
+ // method call $().plugin( 'methodName', { options } )
38
+ // shift arguments by 1
39
+ var args = arraySlice.call( arguments, 1 );
40
+ return methodCall( this, arg0, args );
41
+ }
42
+ // just $().plugin({ options })
43
+ plainCall( this, arg0 );
44
+ return this;
45
+ };
46
+
47
+ // $().plugin('methodName')
48
+ function methodCall( $elems, methodName, args ) {
49
+ var returnValue;
50
+ var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
51
+
52
+ $elems.each( function( i, elem ) {
53
+ // get instance
54
+ var instance = $.data( elem, namespace );
55
+ if ( !instance ) {
56
+ logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
57
+ pluginMethodStr );
58
+ return;
59
+ }
60
+
61
+ var method = instance[ methodName ];
62
+ if ( !method || methodName.charAt(0) == '_' ) {
63
+ logError( pluginMethodStr + ' is not a valid method' );
64
+ return;
65
+ }
66
+
67
+ // apply method, get return value
68
+ var value = method.apply( instance, args );
69
+ // set return value if value is returned, use only first value
70
+ returnValue = returnValue === undefined ? value : returnValue;
71
+ });
72
+
73
+ return returnValue !== undefined ? returnValue : $elems;
74
+ }
75
+
76
+ function plainCall( $elems, options ) {
77
+ $elems.each( function( i, elem ) {
78
+ var instance = $.data( elem, namespace );
79
+ if ( instance ) {
80
+ // set options & init
81
+ instance.option( options );
82
+ instance._init();
83
+ } else {
84
+ // initialize new instance
85
+ instance = new PluginClass( elem, options );
86
+ $.data( elem, namespace, instance );
87
+ }
88
+ });
89
+ }
90
+
91
+ updateJQuery( $ );
92
+
93
+ }
94
+
95
+ // ----- updateJQuery ----- //
96
+
97
+ // set $.bridget for v1 backwards compatibility
98
+ function updateJQuery( $ ) {
99
+ if ( !$ || ( $ && $.bridget ) ) {
100
+ return;
101
+ }
102
+ $.bridget = jQueryBridget;
103
+ }
104
+
105
+ updateJQuery( jQuery || window.jQuery );
106
+
107
+ // ----- ----- //
108
+
109
+ export default jQueryBridget;