intranet-pictures 1.0.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/intranet/pictures/json_db_provider.rb +41 -71
  3. data/lib/intranet/pictures/responder.rb +127 -89
  4. data/lib/intranet/pictures/version.rb +1 -1
  5. data/lib/intranet/resources/haml/pictures_browse.haml +8 -8
  6. data/lib/intranet/resources/haml/pictures_home.haml +18 -19
  7. data/lib/intranet/resources/locales/en.yml +0 -1
  8. data/lib/intranet/resources/locales/fr.yml +0 -1
  9. data/lib/intranet/resources/www/jpictures.js +32 -14
  10. data/lib/intranet/resources/www/photoswipe/photoswipe-dynamic-caption-plugin.css +47 -0
  11. data/lib/intranet/resources/www/photoswipe/photoswipe-dynamic-caption-plugin.esm.js +400 -0
  12. data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js +1382 -0
  13. data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.js.map +1 -0
  14. data/lib/intranet/resources/www/photoswipe/photoswipe-lightbox.esm.min.js +5 -0
  15. data/lib/intranet/resources/www/photoswipe/photoswipe.css +383 -142
  16. data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js +5279 -0
  17. data/lib/intranet/resources/www/photoswipe/photoswipe.esm.js.map +1 -0
  18. data/lib/intranet/resources/www/photoswipe/photoswipe.esm.min.js +5 -0
  19. data/lib/intranet/resources/www/style.css +13 -0
  20. data/spec/intranet/pictures/json_db_provider_spec.rb +52 -147
  21. data/spec/intranet/pictures/responder_spec.rb +176 -191
  22. data/spec/intranet/pictures/sample-db.json +28 -38
  23. data/spec/spec_helper.rb +6 -8
  24. metadata +26 -28
  25. data/lib/intranet/resources/haml/pictures_photoswipe.haml +0 -23
  26. data/lib/intranet/resources/www/photoswipe/LICENSE +0 -21
  27. data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.css +0 -484
  28. data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.png +0 -0
  29. data/lib/intranet/resources/www/photoswipe/default-skin/default-skin.svg +0 -1
  30. data/lib/intranet/resources/www/photoswipe/default-skin/preloader.gif +0 -0
  31. data/lib/intranet/resources/www/photoswipe/photoswipe-ui-default.js +0 -861
  32. data/lib/intranet/resources/www/photoswipe/photoswipe-ui-default.min.js +0 -4
  33. data/lib/intranet/resources/www/photoswipe/photoswipe.js +0 -3734
  34. data/lib/intranet/resources/www/photoswipe/photoswipe.min.js +0 -4
@@ -48,54 +48,11 @@ RSpec.describe Intranet::Pictures::Responder do
48
48
  end
49
49
  end
50
50
 
51
- describe '#css_dependencies' do
52
- it 'should return the list of CSS dependencies' do
53
- expect(@responder.css_dependencies).to include(
54
- 'design/style.css',
55
- 'design/photoswipe/photoswipe.css',
56
- 'design/photoswipe/default-skin/default-skin.css'
57
- )
58
- end
59
- end
60
-
61
- describe '#js_dependencies' do
62
- it 'should return the list of JavaScript dependencies' do
63
- expect(@responder.js_dependencies).to include(
64
- 'design/jpictures.js',
65
- 'design/photoswipe/photoswipe.min.js',
66
- 'design/photoswipe/photoswipe-ui-default.min.js'
67
- )
68
- end
69
- end
70
-
71
51
  describe '#generate_page' do
72
- def photoswipe_markup # rubocop:disable Metrics/MethodLength
73
- "<div aria-hidden class='pswp' role='dialog' tabindex='-1'>\n" \
74
- "<div class='pswp__bg'></div>\n" \
75
- "<div class='pswp__scroll-wrap'>\n" \
76
- "<div class='pswp__container'>\n" \
77
- "<div class='pswp__item'></div>\n" \
78
- "<div class='pswp__item'></div>\n" \
79
- "<div class='pswp__item'></div>\n" \
80
- "</div>\n" \
81
- "<div class='pswp__ui pswp__ui--hidden'>\n" \
82
- "<div class='pswp__top-bar'>\n" \
83
- "<div class='pswp__counter'></div>\n" \
84
- "<button class='pswp__button pswp__button--close' title='#{I18n.t('pictures.viewer.close')} (Esc)'></button>\n" \
85
- "<button class='pswp__button pswp__button--fs' title='#{I18n.t('pictures.viewer.fullscreen')}'></button>\n" \
86
- "<button class='pswp__button pswp__button--zoom' title='#{I18n.t('pictures.viewer.zoom')}'></button>\n" \
87
- "<div class='pswp__preloader'>\n<div class='pswp__preloader__icn'>\n<div class='pswp__preloader__cut'>\n<div class='pswp__preloader__donut'></div>\n</div>\n</div>\n</div>\n</div>\n" \
88
- "<div class='pswp__share-modal pswp__share-modal--hidden pswp__single-tap'>\n<div class='pswp__share-tooltip'></div>\n</div>\n" \
89
- "<button class='pswp__button pswp__button--arrow--left' title='#{I18n.t('pictures.viewer.previous')}'></button>\n" \
90
- "<button class='pswp__button pswp__button--arrow--right' title='#{I18n.t('pictures.viewer.next')}'></button>\n" \
91
- "<div class='pswp__caption'>\n<div class='pswp__caption__center'></div>\n</div>\n" \
92
- "</div>\n</div>\n</div>\n\n"
93
- end
94
-
95
52
  context 'when asked for \'/index.html\'' do
96
53
  it 'should return a partial HTML content showing recent groups according to configuration' do
97
54
  # Nominal case with limit
98
- recents = [{ group_type: 'group2', sort_by: 'value', asc: false, limit: 2 }]
55
+ recents = [{ 'group_by' => 'location', 'sort_by' => 'datetime', 'sort_order' => 'desc', 'limit' => 2 }]
99
56
  @responder.instance_variable_set(:@recents, recents)
100
57
  code, mime, content = @responder.generate_page('/index.html', {})
101
58
  expect(code).to eql(206)
@@ -108,19 +65,25 @@ RSpec.describe Intranet::Pictures::Responder do
108
65
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
109
66
  "<li>My Gallery</li>\n" \
110
67
  "</ul>\n\n" \
111
- "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
68
+ "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
112
69
  "<ul class='groups'>\n" \
113
- "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
114
- "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
70
+ "<li title='New York, USA'>\n<a onclick='openImagesGallery(&quot;location=New York, USA&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=New York, USA&quot;)'></div>\n<figcaption>\nNew York, USA\n</figcaption>\n</figure>\n</a>\n</li>\n" \
71
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;location=Paris, France&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Paris, France&quot;)'></div>\n<figcaption>\nParis, France\n<br>\n<em>The City of Light</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
115
72
  "</ul>\n" \
116
- "<p class='see_more'>\n<a href='browse_group2.html?sort_by=value&amp;sort_order=desc'>#{I18n.t('pictures.see_more')}</a>\n</p>\n" \
117
- "</section>\n" + photoswipe_markup,
118
- title: 'My Gallery'
73
+ "<p class='see_more'>\n<a href='browse.html?group_by=location&amp;sort_by=datetime&amp;sort_order=desc'>#{I18n.t('pictures.see_more')}</a>\n</p>\n" \
74
+ "</section>\n",
75
+ title: 'My Gallery',
76
+ stylesheets: [
77
+ 'design/style.css',
78
+ 'design/photoswipe/photoswipe.css',
79
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
80
+ ],
81
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
119
82
  }
120
83
  )
121
84
 
122
85
  # Nominal case with limit
123
- recents = [{ group_type: 'group2', sort_by: 'value', asc: true, limit: 2 }]
86
+ recents = [{ 'group_by' => 'location', 'sort_by' => 'datetime', 'limit' => 2 }]
124
87
  @responder.instance_variable_set(:@recents, recents)
125
88
  code, mime, content = @responder.generate_page('/index.html', {})
126
89
  expect(code).to eql(206)
@@ -133,19 +96,25 @@ RSpec.describe Intranet::Pictures::Responder do
133
96
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
134
97
  "<li>My Gallery</li>\n" \
135
98
  "</ul>\n\n" \
136
- "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
99
+ "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
137
100
  "<ul class='groups'>\n" \
138
- "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
139
- "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
101
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;location=Paris, France&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Paris, France&quot;)'></div>\n<figcaption>\nParis, France\n<br>\n<em>The City of Light</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
102
+ "<li title='Tokyo, Japan'>\n<a onclick='openImagesGallery(&quot;location=Tokyo, Japan&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Tokyo, Japan&quot;)'></div>\n<figcaption>\nTokyo, Japan\n</figcaption>\n</figure>\n</a>\n</li>\n" \
140
103
  "</ul>\n" \
141
- "<p class='see_more'>\n<a href='browse_group2.html?sort_by=value'>#{I18n.t('pictures.see_more')}</a>\n</p>\n" \
142
- "</section>\n" + photoswipe_markup,
143
- title: 'My Gallery'
104
+ "<p class='see_more'>\n<a href='browse.html?group_by=location&amp;sort_by=datetime'>#{I18n.t('pictures.see_more')}</a>\n</p>\n" \
105
+ "</section>\n",
106
+ title: 'My Gallery',
107
+ stylesheets: [
108
+ 'design/style.css',
109
+ 'design/photoswipe/photoswipe.css',
110
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
111
+ ],
112
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
144
113
  }
145
114
  )
146
115
 
147
116
  # Nominal case without limit
148
- recents = [{ group_type: 'group2' }]
117
+ recents = [{ 'group_by' => 'location', 'sort_by' => 'location', 'sort_order' => 'desc' }]
149
118
  @responder.instance_variable_set(:@recents, recents)
150
119
  code, mime, content = @responder.generate_page('/index.html', {})
151
120
  expect(code).to eql(206)
@@ -158,19 +127,25 @@ RSpec.describe Intranet::Pictures::Responder do
158
127
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
159
128
  "<li>My Gallery</li>\n" \
160
129
  "</ul>\n\n" \
161
- "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
130
+ "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
162
131
  "<ul class='groups'>\n" \
163
- "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
164
- "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
165
- "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
132
+ "<li title='Tokyo, Japan'>\n<a onclick='openImagesGallery(&quot;location=Tokyo, Japan&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Tokyo, Japan&quot;)'></div>\n<figcaption>\nTokyo, Japan\n</figcaption>\n</figure>\n</a>\n</li>\n" \
133
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;location=Paris, France&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Paris, France&quot;)'></div>\n<figcaption>\nParis, France\n<br>\n<em>The City of Light</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
134
+ "<li title='New York, USA'>\n<a onclick='openImagesGallery(&quot;location=New York, USA&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=New York, USA&quot;)'></div>\n<figcaption>\nNew York, USA\n</figcaption>\n</figure>\n</a>\n</li>\n" \
166
135
  "</ul>\n" \
167
- "</section>\n" + photoswipe_markup,
168
- title: 'My Gallery'
136
+ "</section>\n",
137
+ title: 'My Gallery',
138
+ stylesheets: [
139
+ 'design/style.css',
140
+ 'design/photoswipe/photoswipe.css',
141
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
142
+ ],
143
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
169
144
  }
170
145
  )
171
146
 
172
147
  # Incorrect recents specification
173
- recents = [{ group_type: 'invalid', sort_by: 'id' }]
148
+ recents = [{ 'group_by' => 'invalid', 'sort_by' => 'location' }]
174
149
  @responder.instance_variable_set(:@recents, recents)
175
150
  code, mime, content = @responder.generate_page('/index.html', {})
176
151
  expect(code).to eql(404)
@@ -180,7 +155,8 @@ RSpec.describe Intranet::Pictures::Responder do
180
155
 
181
156
  it 'should return a partial HTML content showing all groups according to configuration' do
182
157
  # Nominal case without recents
183
- home_groups = [{ group_type: 'group2', asc: true, browse: 'group1', browse_sort_by: 'uri', browse_asc: true }]
158
+ home_groups = [{ 'group_by' => 'location', 'sort_by' => 'location',
159
+ 'browse_group_by' => 'author' }]
184
160
  @responder.instance_variable_set(:@home_groups, home_groups)
185
161
  code, mime, content = @responder.generate_page('/index.html', {})
186
162
  expect(code).to eql(206)
@@ -193,21 +169,28 @@ RSpec.describe Intranet::Pictures::Responder do
193
169
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
194
170
  "<li>My Gallery</li>\n" \
195
171
  "</ul>\n\n" \
196
- "<h3>#{I18n.t('pictures.browse_by.group2')}</h3>\n" \
172
+ "<h3>#{I18n.t('pictures.browse_by.location')}</h3>\n" \
197
173
  "<ul class='groups wide'>\n" \
198
- "<li title='Group 2, Title 1'>\n<a href='browse_group1.html?sort_by=uri&amp;group2=group2_title1'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
199
- "<li title='Group 2, Title 2'>\n<a href='browse_group1.html?sort_by=uri&amp;group2=group2_title2'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
200
- "<li title='Group 2, Title 3'>\n<a href='browse_group1.html?sort_by=uri&amp;group2=group2_title3'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
174
+ "<li title='New York, USA'>\n<a href='browse.html?group_by=author&amp;location=New York, USA'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=New York, USA&quot;)'></div>\n<figcaption>\nNew York, USA\n</figcaption>\n</figure>\n</a>\n</li>\n" \
175
+ "<li title='Paris, France'>\n<a href='browse.html?group_by=author&amp;location=Paris, France'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Paris, France&quot;)'></div>\n<figcaption>\nParis, France\n<br>\n<em>The City of Light</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
176
+ "<li title='Tokyo, Japan'>\n<a href='browse.html?group_by=author&amp;location=Tokyo, Japan'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Tokyo, Japan&quot;)'></div>\n<figcaption>\nTokyo, Japan\n</figcaption>\n</figure>\n</a>\n</li>\n" \
201
177
  "</ul>\n" \
202
178
  "</section>\n",
203
- title: 'My Gallery'
179
+ title: 'My Gallery',
180
+ stylesheets: [
181
+ 'design/style.css',
182
+ 'design/photoswipe/photoswipe.css',
183
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
184
+ ],
185
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
204
186
  }
205
187
  )
206
188
 
207
189
  # Nominal case with recents
208
- recents = [{ group_type: 'group2' }]
190
+ recents = [{ 'group_by' => 'location', 'sort_by' => 'location', 'sort_order' => 'asc' }]
209
191
  @responder.instance_variable_set(:@recents, recents)
210
- home_groups = [{ group_type: 'group1', asc: false, browse: 'group2', browse_sort_by: 'value', browse_asc: false }]
192
+ home_groups = [{ 'group_by' => 'author', 'sort_by' => 'author', 'sort_order' => 'asc',
193
+ 'browse_group_by' => 'location', 'browse_sort_by' => 'location', 'browse_sort_order' => 'desc' }]
211
194
  @responder.instance_variable_set(:@home_groups, home_groups)
212
195
  code, mime, content = @responder.generate_page('/index.html', {})
213
196
  expect(code).to eql(206)
@@ -220,29 +203,35 @@ RSpec.describe Intranet::Pictures::Responder do
220
203
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
221
204
  "<li>My Gallery</li>\n" \
222
205
  "</ul>\n\n" \
223
- "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
206
+ "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
224
207
  "<ul class='groups'>\n" \
225
- "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
226
- "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
227
- "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
208
+ "<li title='New York, USA'>\n<a onclick='openImagesGallery(&quot;location=New York, USA&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=New York, USA&quot;)'></div>\n<figcaption>\nNew York, USA\n</figcaption>\n</figure>\n</a>\n</li>\n" \
209
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;location=Paris, France&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Paris, France&quot;)'></div>\n<figcaption>\nParis, France\n<br>\n<em>The City of Light</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
210
+ "<li title='Tokyo, Japan'>\n<a onclick='openImagesGallery(&quot;location=Tokyo, Japan&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Tokyo, Japan&quot;)'></div>\n<figcaption>\nTokyo, Japan\n</figcaption>\n</figure>\n</a>\n</li>\n" \
228
211
  "</ul>\n" \
229
- "<h3>#{I18n.t('pictures.browse_by.group1')}</h3>\n" \
212
+ "<h3>#{I18n.t('pictures.browse_by.author')}</h3>\n" \
230
213
  "<ul class='groups wide'>\n" \
231
- "<li title='Group 1, Title 2'>\n<a href='browse_group2.html?sort_by=value&amp;sort_order=desc&amp;group1=group1_title2'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title2&quot;)'></div>\n<figcaption>\nGroup 1, Title 2\n</figcaption>\n</figure>\n</a>\n</li>\n" \
232
- "<li title='Group 1, Title 1'>\n<a href='browse_group2.html?sort_by=value&amp;sort_order=desc&amp;group1=group1_title1'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title1&quot;)'></div>\n<figcaption>\nGroup 1, Title 1\n<br>\n<em>brief_text_1</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
214
+ "<li title='Jane Doe'>\n<a href='browse.html?group_by=location&amp;sort_by=location&amp;sort_order=desc&amp;author=Jane Doe'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?author=Jane Doe&quot;)'></div>\n<figcaption>\nJane Doe\n</figcaption>\n</figure>\n</a>\n</li>\n" \
215
+ "<li title='John Doe'>\n<a href='browse.html?group_by=location&amp;sort_by=location&amp;sort_order=desc&amp;author=John Doe'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?author=John Doe&quot;)'></div>\n<figcaption>\nJohn Doe\n<br>\n<em>Best photographer ever</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
233
216
  "</ul>\n" \
234
- "</section>\n" + photoswipe_markup,
235
- title: 'My Gallery'
217
+ "</section>\n",
218
+ title: 'My Gallery',
219
+ stylesheets: [
220
+ 'design/style.css',
221
+ 'design/photoswipe/photoswipe.css',
222
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
223
+ ],
224
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
236
225
  }
237
226
  )
238
227
  end
239
228
  end
240
229
 
241
- context 'when asked for \'/browse_*.html\'' do
242
- it 'should return a partial HTML content with selected groups' do
243
- # Existing group, no selector nor sort order
244
- query = {}
245
- code, mime, content = @responder.generate_page('/browse_group1.html', query)
230
+ context 'when asked for \'/browse.html\'' do
231
+ it 'should return a partial HTML content with selected pictures grouped as requested' do
232
+ # No selector nor sort order
233
+ query = { 'group_by' => 'author' }
234
+ code, mime, content = @responder.generate_page('/browse.html', query)
246
235
  expect(code).to eql(206)
247
236
  expect(mime).to eql('text/html')
248
237
  expect(content).to eql(
@@ -252,20 +241,26 @@ RSpec.describe Intranet::Pictures::Responder do
252
241
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
253
242
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
254
243
  "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
255
- "<li>#{I18n.t('pictures.nav.group1')}</li>\n" \
244
+ "<li>#{I18n.t('pictures.nav.author')}</li>\n" \
256
245
  "</ul>\n\n" \
257
246
  "<ul class='groups'>\n" \
258
- "<li title='Group 1, Title 1'>\n<a onclick='openImagesGallery(&quot;group1=group1_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title1&quot;)'></div>\n<figcaption>\nGroup 1, Title 1\n<br>\n<em>brief_text_1</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
259
- "<li title='Group 1, Title 2'>\n<a onclick='openImagesGallery(&quot;group1=group1_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title2&quot;)'></div>\n<figcaption>\nGroup 1, Title 2\n</figcaption>\n</figure>\n</a>\n</li>\n" \
247
+ "<li title='John Doe'>\n<a onclick='openImagesGallery(&quot;author=John Doe&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?author=John Doe&quot;)'></div>\n<figcaption>\nJohn Doe\n<br>\n<em>Best photographer ever</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
248
+ "<li title='Jane Doe'>\n<a onclick='openImagesGallery(&quot;author=Jane Doe&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?author=Jane Doe&quot;)'></div>\n<figcaption>\nJane Doe\n</figcaption>\n</figure>\n</a>\n</li>\n" \
260
249
  "</ul>\n" \
261
- "</section>\n" + photoswipe_markup,
262
- title: 'My Gallery'
250
+ "</section>\n",
251
+ title: 'My Gallery',
252
+ stylesheets: [
253
+ 'design/style.css',
254
+ 'design/photoswipe/photoswipe.css',
255
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
256
+ ],
257
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
263
258
  }
264
259
  )
265
260
 
266
- # Existing group, valid selector, valid sort order
267
- query = { 'group1' => 'group1_title1', 'sort_by' => 'id', 'sort_order' => 'desc' }
268
- code, mime, content = @responder.generate_page('/browse_group2.html', query)
261
+ # Valid selector, valid sort order
262
+ query = { 'group_by' => 'location', 'author' => 'John Doe', 'sort_by' => 'location', 'sort_order' => 'desc' }
263
+ code, mime, content = @responder.generate_page('/browse.html', query)
269
264
  expect(code).to eql(206)
270
265
  expect(mime).to eql('text/html')
271
266
  expect(content).to eql(
@@ -275,20 +270,26 @@ RSpec.describe Intranet::Pictures::Responder do
275
270
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
276
271
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
277
272
  "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
278
- "<li>#{I18n.t('pictures.nav.group2')} (Group 1, Title 1)</li>\n" \
273
+ "<li>#{I18n.t('pictures.nav.location')} (John Doe)</li>\n" \
279
274
  "</ul>\n\n" \
280
275
  "<ul class='groups'>\n" \
281
- "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group1=group1_title1&amp;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
282
- "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group1=group1_title1&amp;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
276
+ "<li title='Tokyo, Japan'>\n<a onclick='openImagesGallery(&quot;author=John Doe&amp;location=Tokyo, Japan&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Tokyo, Japan&quot;)'></div>\n<figcaption>\nTokyo, Japan\n</figcaption>\n</figure>\n</a>\n</li>\n" \
277
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;author=John Doe&amp;location=Paris, France&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?location=Paris, France&quot;)'></div>\n<figcaption>\nParis, France\n<br>\n<em>The City of Light</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
283
278
  "</ul>\n" \
284
- "</section>\n" + photoswipe_markup,
285
- title: 'My Gallery'
279
+ "</section>\n",
280
+ title: 'My Gallery',
281
+ stylesheets: [
282
+ 'design/style.css',
283
+ 'design/photoswipe/photoswipe.css',
284
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
285
+ ],
286
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
286
287
  }
287
288
  )
288
289
 
289
290
  # Invalid selector
290
- query = { 'foo' => 'bar' }
291
- code, mime, content = @responder.generate_page('/browse_group2.html', query)
291
+ query = { 'group_by' => 'location', 'foo' => 'bar' }
292
+ code, mime, content = @responder.generate_page('/browse.html', query)
292
293
  expect(code).to eql(206)
293
294
  expect(mime).to eql('text/html')
294
295
  expect(content).to eql(
@@ -298,120 +299,104 @@ RSpec.describe Intranet::Pictures::Responder do
298
299
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
299
300
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
300
301
  "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
301
- "<li>#{I18n.t('pictures.nav.group2')} (bar)</li>\n" \
302
+ "<li>#{I18n.t('pictures.nav.location')} (bar)</li>\n" \
302
303
  "</ul>\n\n" \
303
304
  "<ul class='groups'>\n</ul>\n" \
304
- "</section>\n" + photoswipe_markup,
305
- title: 'My Gallery'
305
+ "</section>\n",
306
+ title: 'My Gallery',
307
+ stylesheets: [
308
+ 'design/style.css',
309
+ 'design/photoswipe/photoswipe.css',
310
+ 'design/photoswipe/photoswipe-dynamic-caption-plugin.css'
311
+ ],
312
+ scripts: [{ src: 'design/jpictures.js', type: 'module' }]
306
313
  }
307
314
  )
308
315
 
309
- # Invalid group type
310
- code, mime, content = @responder.generate_page('/browse_foo.html', {})
316
+ # Invalid grouping criteria
317
+ query = { 'group_by' => 'foo' }
318
+ code, mime, content = @responder.generate_page('/browse.html', query)
311
319
  expect(code).to eql(404)
312
320
  expect(mime).to be_empty
313
321
  expect(content).to be_empty
314
322
 
315
- # Invalid sort order
316
- query = { 'sort_order' => 'foo' }
317
- code, mime, content = @responder.generate_page('/browse_group2.html', query)
323
+ # Invalid sorting criteria
324
+ query = { 'group_by' => 'location', 'sort_order' => 'foo' }
325
+ code, mime, content = @responder.generate_page('/browse.html', query)
318
326
  expect(code).to eql(404)
319
327
  expect(mime).to be_empty
320
328
  expect(content).to be_empty
321
329
  end
322
330
  end
323
331
 
324
- context 'when asked for \'/api/groups\'' do
325
- it 'should return a JSON representation of the selected groups' do
326
- # Existing group, no selector nor sort order
327
- query = {}
328
- code, mime, content = @responder.generate_page('/api/groups/group1', query)
332
+ context 'when asked for \'/i18n.js\'' do
333
+ it 'should return the internationalized version of required JavaScript variables' do
334
+ code, mime, content = @responder.generate_page('/i18n.js', {})
329
335
  expect(code).to eql(200)
330
- expect(mime).to eql('application/json')
336
+ expect(mime).to eql('text/javascript')
331
337
  expect(content).to eql(
332
- [
333
- { 'id' => 'group1_title1', 'title' => 'Group 1, Title 1', 'brief' => 'brief_text_1' },
334
- { 'id' => 'group1_title2', 'title' => 'Group 1, Title 2' }
335
- ].to_json
338
+ "export default {\n" \
339
+ " viewer_close: '#{I18n.t('pictures.viewer.close')}',\n" \
340
+ " viewer_zoom: '#{I18n.t('pictures.viewer.zoom')}',\n" \
341
+ " viewer_previous: '#{I18n.t('pictures.viewer.previous')}',\n" \
342
+ " viewer_next: '#{I18n.t('pictures.viewer.next')}' };"
336
343
  )
344
+ end
345
+ end
337
346
 
338
- # Existing group, valid selector, no sort order
339
- query = { 'group1' => 'group1_title1' }
340
- code, mime, content = @responder.generate_page('/api/groups/group2', query)
347
+ context 'when asked for \'/api/group_thumbnail\'' do
348
+ it 'should return the selected group thumnail' do
349
+ # Existing group with thumbnail
350
+ query = { 'location' => 'New York, USA' }
351
+ code, mime, content = @responder.generate_page('/api/group_thumbnail', query)
341
352
  expect(code).to eql(200)
342
- expect(mime).to eql('application/json')
343
- expect(content).to eql(
344
- [
345
- { 'id' => 'group2_title2', 'title' => 'Group 2, Title 2', 'value' => 'bcde', 'brief' => 'brief_text_2' },
346
- { 'id' => 'group2_title3', 'title' => 'Group 2, Title 3', 'value' => 'aabb' }
347
- ].to_json
348
- )
353
+ expect(mime).to eql('image/png')
354
+ expect(content).to eql(File.read(File.join(__dir__, 'alpha.png')))
349
355
 
350
- # Existing group, valid selector, valid sort order
351
- query = { 'group1' => 'group1_title1', 'sort_by' => 'value', 'sort_order' => 'asc' }
352
- code, mime, content = @responder.generate_page('/api/groups/group2', query)
353
- expect(code).to eql(200)
354
- expect(mime).to eql('application/json')
355
- expect(content).to eql(
356
- [
357
- { 'id' => 'group2_title3', 'title' => 'Group 2, Title 3', 'value' => 'aabb' },
358
- { 'id' => 'group2_title2', 'title' => 'Group 2, Title 2', 'value' => 'bcde', 'brief' => 'brief_text_2' }
359
- ].to_json
360
- )
361
- query = { 'group1' => 'group1_title1', 'sort_by' => 'id', 'sort_order' => 'desc' }
362
- code, mime, content = @responder.generate_page('/api/groups/group2', query)
356
+ # Existing group with no specified thumbnail
357
+ query = { 'location' => 'Paris, France' }
358
+ code, mime, content = @responder.generate_page('/api/group_thumbnail', query)
363
359
  expect(code).to eql(200)
364
- expect(mime).to eql('application/json')
360
+ expect(mime).to eql('image/svg+xml')
365
361
  expect(content).to eql(
366
- [
367
- { 'id' => 'group2_title3', 'title' => 'Group 2, Title 3', 'value' => 'aabb' },
368
- { 'id' => 'group2_title2', 'title' => 'Group 2, Title 2', 'value' => 'bcde', 'brief' => 'brief_text_2' }
369
- ].to_json
362
+ File.read(File.join(__dir__, '../../../lib/intranet/resources/www/group_thumbnail.svg'))
370
363
  )
371
364
 
372
- # Invalid selector
373
- query = { 'foo' => 'bar' }
374
- code, mime, content = @responder.generate_page('/api/groups/group2', query)
375
- expect(code).to eql(200)
376
- expect(mime).to eql('application/json')
377
- expect(content).to eql([].to_json)
378
-
379
- # Invalid group type
380
- code, mime, content = @responder.generate_page('/api/groups/foo', {})
365
+ # Existing group with non-existant thumbnail
366
+ query = { 'author' => 'Jane Doe' }
367
+ code, mime, content = @responder.generate_page('/api/group_thumbnail', query)
381
368
  expect(code).to eql(404)
382
369
  expect(mime).to be_empty
383
370
  expect(content).to be_empty
384
371
 
385
- # Invalid sort order
386
- query = { 'sort_order' => 'foo' }
387
- code, mime, content = @responder.generate_page('/api/groups/group2', query)
372
+ # Invalid query
373
+ query = { 'location' => 'Tokyo, Japan', 'camera' => 'Canon EOS 5D MARK IV' }
374
+ code, mime, content = @responder.generate_page('/api/group_thumbnail', query)
388
375
  expect(code).to eql(404)
389
376
  expect(mime).to be_empty
390
377
  expect(content).to be_empty
391
378
  end
392
379
  end
393
380
 
394
- context 'when asked for \'/api/group\'' do
395
- it 'should return the selected group thumnail' do
396
- # Existing group with thumbnail
397
- query = { 'group2' => 'group2_title1' }
398
- code, mime, content = @responder.generate_page('/api/group/group2', query)
381
+ context 'when asked for \'/api/group_brief\'' do
382
+ it 'should return the selected group brief text' do
383
+ # Existing group with brief text
384
+ query = { 'location' => 'Paris, France' }
385
+ code, mime, content = @responder.generate_page('/api/group_brief', query)
399
386
  expect(code).to eql(200)
400
- expect(mime).to eql('image/png')
401
- expect(content).to eql(File.read(File.join(__dir__, 'alpha.png')))
387
+ expect(mime).to eql('application/json')
388
+ expect(content).to eql('The City of Light'.to_json)
402
389
 
403
- # Existing group with no specified thumbnail
404
- query = { 'group2' => 'group2_title2' }
405
- code, mime, content = @responder.generate_page('/api/group/group2', query)
390
+ # Existing group with no brief text
391
+ query = { 'location' => 'Tokyo, Japan' }
392
+ code, mime, content = @responder.generate_page('/api/group_brief', query)
406
393
  expect(code).to eql(200)
407
- expect(mime).to eql('image/svg+xml')
408
- expect(content).to eql(
409
- File.read(File.join(__dir__, '../../../lib/intranet/resources/www/group_thumbnail.svg'))
410
- )
394
+ expect(mime).to eql('application/json')
395
+ expect(content).to eql(''.to_json)
411
396
 
412
- # Existing group with non-existant thumbnail
413
- query = { 'group1' => 'group1_title2' }
414
- code, mime, content = @responder.generate_page('/api/group/group1', query)
397
+ # Invalid query
398
+ query = { 'location' => 'Tokyo, Japan', 'camera' => 'Canon EOS 5D MARK IV' }
399
+ code, mime, content = @responder.generate_page('/api/group_brief', query)
415
400
  expect(code).to eql(404)
416
401
  expect(mime).to be_empty
417
402
  expect(content).to be_empty
@@ -426,46 +411,46 @@ RSpec.describe Intranet::Pictures::Responder do
426
411
  expect(mime).to eql('application/json')
427
412
  expect(content).to eql(
428
413
  [
429
- { 'datetime' => '2019:07:22 09:41:31', 'group1' => 'group1_title1', 'group2' => 'group2_title2', 'group3' => 'group3_title1' },
430
- { 'datetime' => '2020:06:19 07:51:05', 'value' => false, 'group1' => 'group1_title2', 'group2' => 'group2_title2' },
431
- { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' },
432
- { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3', 'group3' => 'group3_title2' },
433
- { 'datetime' => '2019:07:22 09:45:17', 'group1' => 'group1_title1', 'group2' => 'group2_title3' }
414
+ { 'datetime' => '2019:07:22 09:41:31', 'author' => 'John Doe', 'location' => 'Paris, France', 'camera' => 'Apple iPhone 11' },
415
+ { 'datetime' => '2020:06:19 07:51:05', 'flash' => false, 'author' => 'Jane Doe', 'location' => 'Tokyo, Japan' },
416
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'New York, USA' },
417
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' },
418
+ { 'datetime' => '2019:07:22 09:45:17', 'author' => 'John Doe', 'location' => 'Tokyo, Japan' }
434
419
  ].to_json
435
420
  )
436
421
 
437
422
  # Valid selector, no sort order
438
- query = { 'group1' => 'group1_title2', 'value' => true }
423
+ query = { 'author' => 'Jane Doe', 'flash' => true }
439
424
  code, mime, content = @responder.generate_page('/api/pictures', query)
440
425
  expect(code).to eql(200)
441
426
  expect(mime).to eql('application/json')
442
427
  expect(content).to eql(
443
428
  [
444
- { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' },
445
- { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3', 'group3' => 'group3_title2' }
429
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'New York, USA' },
430
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' }
446
431
  ].to_json
447
432
  )
448
433
 
449
434
  # Valid selector, valid sort order
450
- query = { 'group1' => 'group1_title2', 'value' => true, 'sort_by' => 'datetime' }
435
+ query = { 'author' => 'Jane Doe', 'flash' => true, 'sort_by' => 'datetime' }
451
436
  code, mime, content = @responder.generate_page('/api/pictures', query)
452
437
  expect(code).to eql(200)
453
438
  expect(mime).to eql('application/json')
454
439
  expect(content).to eql(
455
440
  [
456
- { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3', 'group3' => 'group3_title2' },
457
- { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' }
441
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' },
442
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'New York, USA' }
458
443
  ].to_json
459
444
  )
460
- query = { 'group1' => 'group1_title2', 'value' => true, 'sort_by' => 'group2',
445
+ query = { 'author' => 'Jane Doe', 'flash' => true, 'sort_by' => 'location',
461
446
  'sort_order' => 'desc' }
462
447
  code, mime, content = @responder.generate_page('/api/pictures', query)
463
448
  expect(code).to eql(200)
464
449
  expect(mime).to eql('application/json')
465
450
  expect(content).to eql(
466
451
  [
467
- { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3', 'group3' => 'group3_title2' },
468
- { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' }
452
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' },
453
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'New York, USA' }
469
454
  ].to_json
470
455
  )
471
456