intranet-pictures 2.1.0 → 3.0.0.rc1

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.
@@ -21,7 +21,8 @@ RSpec.describe Intranet::Pictures::Responder do
21
21
  @core = Intranet::Core.new(logger)
22
22
 
23
23
  @provider = Intranet::Pictures::JsonDbProvider.new(File.join(__dir__, 'sample-db.json'))
24
- @responder = described_class.new(@provider)
24
+ defaults = { 'group_by' => 'location', 'sort_by' => 'datetime', 'sort_order' => 'desc' }
25
+ @responder = described_class.new(@provider, defaults)
25
26
  @core.register_module(
26
27
  @responder, ['pictures'], File.absolute_path('../../../lib/intranet/resources', __dir__)
27
28
  )
@@ -29,14 +30,14 @@ RSpec.describe Intranet::Pictures::Responder do
29
30
 
30
31
  describe '#in_menu?' do
31
32
  it 'should return the value provided at initialization' do
32
- expect(described_class.new(nil, [], [], false).in_menu?).to be false
33
- expect(described_class.new(nil, [], [], true).in_menu?).to be true
33
+ expect(described_class.new(nil, {}, false).in_menu?).to be false
34
+ expect(described_class.new(nil, {}, true).in_menu?).to be true
34
35
  end
35
36
  end
36
37
 
37
38
  describe '#resources_dir' do
38
39
  it 'should return the absolute path of the resources directory' do
39
- expect(described_class.new(nil, [], [], false).resources_dir).to eql(
40
+ expect(described_class.new(nil, {}, false).resources_dir).to eql(
40
41
  File.absolute_path('../../../lib/intranet/resources', __dir__)
41
42
  )
42
43
  end
@@ -44,35 +45,42 @@ RSpec.describe Intranet::Pictures::Responder do
44
45
 
45
46
  describe '#title' do
46
47
  it 'should return the title of the webpage provided by the module' do
47
- expect(@responder.title).to eql('My Gallery')
48
+ expect(@responder.title).to eql(I18n.t('pictures.menu'))
48
49
  end
49
50
  end
50
51
 
51
52
  describe '#generate_page' do
52
53
  context 'when asked for \'/index.html\'' do
53
- it 'should return a partial HTML content showing recent groups according to configuration' do
54
- # Nominal case with limit
55
- recents = [{ 'group_by' => 'location', 'sort_by' => 'datetime', 'sort_order' => 'desc', 'limit' => 2 }]
56
- @responder.instance_variable_set(:@recents, recents)
57
- code, mime, content = @responder.generate_page('/index.html', {})
54
+ it 'should return a partial HTML content with selected pictures grouped as requested' do
55
+ # No parameter in query: use default configuration provided at initialization & show all pictures
56
+ query = {}
57
+ code, mime, content = @responder.generate_page('/index.html', query)
58
58
  expect(code).to eql(206)
59
59
  expect(mime).to eql('text/html')
60
60
  expect(content).to eql(
61
61
  {
62
- content: "<section>\n<h2>My Gallery</h2>\n" \
62
+ content: "<section>\n<h2>#{I18n.t('pictures.menu')}</h2>\n" \
63
63
  "<ul class='breadcrumb'>\n" \
64
64
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
65
65
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
66
- "<li>My Gallery</li>\n" \
67
66
  "</ul>\n\n" \
68
- "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
67
+ "<h4>Tags</h4>\n" \
68
+ "<div id='tags'>\n" \
69
+ "<button class='tag' onclick='filterByTag(&quot;Cherry blossom&quot;);' type='button'>\nCherry blossom\n</button>\n" \
70
+ "<button class='tag' onclick='filterByTag(&quot;Louvre&quot;);' type='button'>\nLouvre\n</button>\n" \
71
+ "<button class='tag' onclick='filterByTag(&quot;MoMAK&quot;);' type='button'>\nMoMAK\n</button>\n" \
72
+ "<button class='tag' onclick='filterByTag(&quot;Modern art&quot;);' type='button'>\nModern art\n</button>\n" \
73
+ "<button class='tag' onclick='filterByTag(&quot;Pyramid&quot;);' type='button'>\nPyramid\n</button>\n" \
74
+ "<button class='tag' onclick='filterByTag(&quot;Urban photography&quot;);' type='button'>\nUrban photography\n</button>\n" \
75
+ "</div>\n" \
76
+ "<h4>#{I18n.t('pictures.nav.location')}</h4>\n" \
69
77
  "<ul class='groups'>\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" \
78
+ "<li title='New York, USA'>\n<a onclick='openImagesGallery(&quot;location=New York, USA&amp;sort_by=datetime&amp;sort_order=asc&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" \
79
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;location=Paris, France&amp;sort_by=datetime&amp;sort_order=asc&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" \
80
+ "<li title='Tokyo, Japan'>\n<a onclick='openImagesGallery(&quot;location=Tokyo, Japan&amp;sort_by=datetime&amp;sort_order=asc&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" \
72
81
  "</ul>\n" \
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
82
  "</section>\n",
75
- title: 'My Gallery',
83
+ title: I18n.t('pictures.menu'),
76
84
  stylesheets: [
77
85
  'design/style.css',
78
86
  'design/photoswipe/photoswipe.css',
@@ -82,28 +90,34 @@ RSpec.describe Intranet::Pictures::Responder do
82
90
  }
83
91
  )
84
92
 
85
- # Nominal case with limit
86
- recents = [{ 'group_by' => 'location', 'sort_by' => 'datetime', 'limit' => 2 }]
87
- @responder.instance_variable_set(:@recents, recents)
88
- code, mime, content = @responder.generate_page('/index.html', {})
93
+ # Query overrides group_by field
94
+ query = { 'group_by' => 'author' }
95
+ code, mime, content = @responder.generate_page('/index.html', query)
89
96
  expect(code).to eql(206)
90
97
  expect(mime).to eql('text/html')
91
98
  expect(content).to eql(
92
99
  {
93
- content: "<section>\n<h2>My Gallery</h2>\n" \
100
+ content: "<section>\n<h2>#{I18n.t('pictures.menu')}</h2>\n" \
94
101
  "<ul class='breadcrumb'>\n" \
95
102
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
96
103
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
97
- "<li>My Gallery</li>\n" \
98
104
  "</ul>\n\n" \
99
- "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
105
+ "<h4>Tags</h4>\n" \
106
+ "<div id='tags'>\n" \
107
+ "<button class='tag' onclick='filterByTag(&quot;Cherry blossom&quot;);' type='button'>\nCherry blossom\n</button>\n" \
108
+ "<button class='tag' onclick='filterByTag(&quot;Louvre&quot;);' type='button'>\nLouvre\n</button>\n" \
109
+ "<button class='tag' onclick='filterByTag(&quot;MoMAK&quot;);' type='button'>\nMoMAK\n</button>\n" \
110
+ "<button class='tag' onclick='filterByTag(&quot;Modern art&quot;);' type='button'>\nModern art\n</button>\n" \
111
+ "<button class='tag' onclick='filterByTag(&quot;Pyramid&quot;);' type='button'>\nPyramid\n</button>\n" \
112
+ "<button class='tag' onclick='filterByTag(&quot;Urban photography&quot;);' type='button'>\nUrban photography\n</button>\n" \
113
+ "</div>\n" \
114
+ "<h4>#{I18n.t('pictures.nav.author')}</h4>\n" \
100
115
  "<ul class='groups'>\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" \
116
+ "<li title='Jane Doe'>\n<a onclick='openImagesGallery(&quot;author=Jane Doe&amp;sort_by=datetime&amp;sort_order=asc&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" \
117
+ "<li title='John Doe'>\n<a onclick='openImagesGallery(&quot;author=John Doe&amp;sort_by=datetime&amp;sort_order=asc&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" \
103
118
  "</ul>\n" \
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
119
  "</section>\n",
106
- title: 'My Gallery',
120
+ title: I18n.t('pictures.menu'),
107
121
  stylesheets: [
108
122
  'design/style.css',
109
123
  'design/photoswipe/photoswipe.css',
@@ -113,28 +127,34 @@ RSpec.describe Intranet::Pictures::Responder do
113
127
  }
114
128
  )
115
129
 
116
- # Nominal case without limit
117
- recents = [{ 'group_by' => 'location', 'sort_by' => 'location', 'sort_order' => 'desc' }]
118
- @responder.instance_variable_set(:@recents, recents)
119
- code, mime, content = @responder.generate_page('/index.html', {})
130
+ # Query overrides group_by field. This field is present for some pictures only.
131
+ query = { 'group_by' => 'camera' }
132
+ code, mime, content = @responder.generate_page('/index.html', query)
120
133
  expect(code).to eql(206)
121
134
  expect(mime).to eql('text/html')
122
135
  expect(content).to eql(
123
136
  {
124
- content: "<section>\n<h2>My Gallery</h2>\n" \
137
+ content: "<section>\n<h2>#{I18n.t('pictures.menu')}</h2>\n" \
125
138
  "<ul class='breadcrumb'>\n" \
126
139
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
127
140
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
128
- "<li>My Gallery</li>\n" \
129
141
  "</ul>\n\n" \
130
- "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
142
+ "<h4>Tags</h4>\n" \
143
+ "<div id='tags'>\n" \
144
+ "<button class='tag' onclick='filterByTag(&quot;Cherry blossom&quot;);' type='button'>\nCherry blossom\n</button>\n" \
145
+ "<button class='tag' onclick='filterByTag(&quot;Louvre&quot;);' type='button'>\nLouvre\n</button>\n" \
146
+ "<button class='tag' onclick='filterByTag(&quot;MoMAK&quot;);' type='button'>\nMoMAK\n</button>\n" \
147
+ "<button class='tag' onclick='filterByTag(&quot;Modern art&quot;);' type='button'>\nModern art\n</button>\n" \
148
+ "<button class='tag' onclick='filterByTag(&quot;Pyramid&quot;);' type='button'>\nPyramid\n</button>\n" \
149
+ "<button class='tag' onclick='filterByTag(&quot;Urban photography&quot;);' type='button'>\nUrban photography\n</button>\n" \
150
+ "</div>\n" \
151
+ "<h4>#{I18n.t('pictures.nav.camera')}</h4>\n" \
131
152
  "<ul class='groups'>\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" \
153
+ "<li title='Canon EOS 5D MARK IV'>\n<a onclick='openImagesGallery(&quot;camera=Canon EOS 5D MARK IV&amp;sort_by=datetime&amp;sort_order=asc&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?camera=Canon EOS 5D MARK IV&quot;)'></div>\n<figcaption>\nCanon EOS 5D MARK IV\n</figcaption>\n</figure>\n</a>\n</li>\n" \
154
+ "<li title='Apple iPhone 11'>\n<a onclick='openImagesGallery(&quot;camera=Apple iPhone 11&amp;sort_by=datetime&amp;sort_order=asc&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group_thumbnail?camera=Apple iPhone 11&quot;)'></div>\n<figcaption>\nApple iPhone 11\n<br>\n<em>Best smartphone ever</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
135
155
  "</ul>\n" \
136
156
  "</section>\n",
137
- title: 'My Gallery',
157
+ title: I18n.t('pictures.menu'),
138
158
  stylesheets: [
139
159
  'design/style.css',
140
160
  'design/photoswipe/photoswipe.css',
@@ -144,111 +164,30 @@ RSpec.describe Intranet::Pictures::Responder do
144
164
  }
145
165
  )
146
166
 
147
- # Incorrect recents specification
148
- recents = [{ 'group_by' => 'invalid', 'sort_by' => 'location' }]
149
- @responder.instance_variable_set(:@recents, recents)
150
- code, mime, content = @responder.generate_page('/index.html', {})
151
- expect(code).to eql(404)
152
- expect(mime).to be_empty
153
- expect(content).to be_empty
154
- end
155
-
156
- it 'should return a partial HTML content showing all groups according to configuration' do
157
- # Nominal case without recents
158
- home_groups = [{ 'group_by' => 'location', 'sort_by' => 'location',
159
- 'browse_group_by' => 'author' }]
160
- @responder.instance_variable_set(:@home_groups, home_groups)
161
- code, mime, content = @responder.generate_page('/index.html', {})
162
- expect(code).to eql(206)
163
- expect(mime).to eql('text/html')
164
- expect(content).to eql(
165
- {
166
- content: "<section>\n<h2>My Gallery</h2>\n" \
167
- "<ul class='breadcrumb'>\n" \
168
- "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
169
- "<li>#{I18n.t('pictures.menu')}</li>\n" \
170
- "<li>My Gallery</li>\n" \
171
- "</ul>\n\n" \
172
- "<h3>#{I18n.t('pictures.browse_by.location')}</h3>\n" \
173
- "<ul class='groups wide'>\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" \
177
- "</ul>\n" \
178
- "</section>\n",
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' }]
186
- }
187
- )
188
-
189
- # Nominal case with recents
190
- recents = [{ 'group_by' => 'location', 'sort_by' => 'location', 'sort_order' => 'asc' }]
191
- @responder.instance_variable_set(:@recents, recents)
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' }]
194
- @responder.instance_variable_set(:@home_groups, home_groups)
195
- code, mime, content = @responder.generate_page('/index.html', {})
196
- expect(code).to eql(206)
197
- expect(mime).to eql('text/html')
198
- expect(content).to eql(
199
- {
200
- content: "<section>\n<h2>My Gallery</h2>\n" \
201
- "<ul class='breadcrumb'>\n" \
202
- "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
203
- "<li>#{I18n.t('pictures.menu')}</li>\n" \
204
- "<li>My Gallery</li>\n" \
205
- "</ul>\n\n" \
206
- "<h3>#{I18n.t('pictures.recents.location')}</h3>\n" \
207
- "<ul class='groups'>\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" \
211
- "</ul>\n" \
212
- "<h3>#{I18n.t('pictures.browse_by.author')}</h3>\n" \
213
- "<ul class='groups wide'>\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" \
216
- "</ul>\n" \
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' }]
225
- }
226
- )
227
- end
228
- end
229
-
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)
167
+ # Query overrides defaults & specifies a valid selector
168
+ query = { 'group_by' => 'location', 'author' => 'John Doe', 'sort_by' => 'location', 'sort_order' => 'desc' }
169
+ code, mime, content = @responder.generate_page('/index.html', query)
235
170
  expect(code).to eql(206)
236
171
  expect(mime).to eql('text/html')
237
172
  expect(content).to eql(
238
173
  {
239
- content: "<section>\n<h2>My Gallery</h2>\n" \
174
+ content: "<section>\n<h2>#{I18n.t('pictures.menu')}</h2>\n" \
240
175
  "<ul class='breadcrumb'>\n" \
241
176
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
242
177
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
243
- "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
244
- "<li>#{I18n.t('pictures.nav.author')}</li>\n" \
245
178
  "</ul>\n\n" \
179
+ "<h4>Tags</h4>\n" \
180
+ "<div id='tags'>\n" \
181
+ "<button class='tag' onclick='filterByTag(&quot;Louvre&quot;);' type='button'>\nLouvre\n</button>\n" \
182
+ "<button class='tag' onclick='filterByTag(&quot;Modern art&quot;);' type='button'>\nModern art\n</button>\n" \
183
+ "</div>\n" \
184
+ "<h4>#{I18n.t('pictures.nav.location')}</h4>\n" \
246
185
  "<ul class='groups'>\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" \
186
+ "<li title='Tokyo, Japan'>\n<a onclick='openImagesGallery(&quot;author=John Doe&amp;location=Tokyo, Japan&amp;sort_by=datetime&amp;sort_order=asc&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" \
187
+ "<li title='Paris, France'>\n<a onclick='openImagesGallery(&quot;author=John Doe&amp;location=Paris, France&amp;sort_by=datetime&amp;sort_order=asc&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" \
249
188
  "</ul>\n" \
250
189
  "</section>\n",
251
- title: 'My Gallery',
190
+ title: I18n.t('pictures.menu'),
252
191
  stylesheets: [
253
192
  'design/style.css',
254
193
  'design/photoswipe/photoswipe.css',
@@ -258,26 +197,24 @@ RSpec.describe Intranet::Pictures::Responder do
258
197
  }
259
198
  )
260
199
 
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)
200
+ # Invalid selector
201
+ query = { 'group_by' => 'location', 'foo' => 'bar' }
202
+ code, mime, content = @responder.generate_page('/index.html', query)
264
203
  expect(code).to eql(206)
265
204
  expect(mime).to eql('text/html')
266
205
  expect(content).to eql(
267
206
  {
268
- content: "<section>\n<h2>My Gallery</h2>\n" \
207
+ content: "<section>\n<h2>#{I18n.t('pictures.menu')}</h2>\n" \
269
208
  "<ul class='breadcrumb'>\n" \
270
209
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
271
210
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
272
- "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
273
- "<li>#{I18n.t('pictures.nav.location')} (John Doe)</li>\n" \
274
211
  "</ul>\n\n" \
275
- "<ul class='groups'>\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" \
278
- "</ul>\n" \
212
+ "<h4>Tags</h4>\n" \
213
+ "<p>\n<em>#{I18n.t('pictures.no_result_found')}</em>\n</p>\n" \
214
+ "<h4>#{I18n.t('pictures.nav.location')}</h4>\n" \
215
+ "<p>\n<em>#{I18n.t('pictures.no_result_found')}</em>\n</p>\n" \
279
216
  "</section>\n",
280
- title: 'My Gallery',
217
+ title: I18n.t('pictures.menu'),
281
218
  stylesheets: [
282
219
  'design/style.css',
283
220
  'design/photoswipe/photoswipe.css',
@@ -287,23 +224,31 @@ RSpec.describe Intranet::Pictures::Responder do
287
224
  }
288
225
  )
289
226
 
290
- # Invalid selector
291
- query = { 'group_by' => 'location', 'foo' => 'bar' }
292
- code, mime, content = @responder.generate_page('/browse.html', query)
227
+ # Invalid grouping criteria
228
+ query = { 'group_by' => 'foo' }
229
+ code, mime, content = @responder.generate_page('/index.html', query)
293
230
  expect(code).to eql(206)
294
231
  expect(mime).to eql('text/html')
295
232
  expect(content).to eql(
296
233
  {
297
- content: "<section>\n<h2>My Gallery</h2>\n" \
234
+ content: "<section>\n<h2>#{I18n.t('pictures.menu')}</h2>\n" \
298
235
  "<ul class='breadcrumb'>\n" \
299
236
  "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
300
237
  "<li>#{I18n.t('pictures.menu')}</li>\n" \
301
- "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
302
- "<li>#{I18n.t('pictures.nav.location')} (bar)</li>\n" \
303
238
  "</ul>\n\n" \
304
- "<ul class='groups'>\n</ul>\n" \
239
+ "<h4>Tags</h4>\n" \
240
+ "<div id='tags'>\n" \
241
+ "<button class='tag' onclick='filterByTag(&quot;Cherry blossom&quot;);' type='button'>\nCherry blossom\n</button>\n" \
242
+ "<button class='tag' onclick='filterByTag(&quot;Louvre&quot;);' type='button'>\nLouvre\n</button>\n" \
243
+ "<button class='tag' onclick='filterByTag(&quot;MoMAK&quot;);' type='button'>\nMoMAK\n</button>\n" \
244
+ "<button class='tag' onclick='filterByTag(&quot;Modern art&quot;);' type='button'>\nModern art\n</button>\n" \
245
+ "<button class='tag' onclick='filterByTag(&quot;Pyramid&quot;);' type='button'>\nPyramid\n</button>\n" \
246
+ "<button class='tag' onclick='filterByTag(&quot;Urban photography&quot;);' type='button'>\nUrban photography\n</button>\n" \
247
+ "</div>\n" \
248
+ "<h4>#{I18n.t('pictures.nav.foo')}</h4>\n" \
249
+ "<p>\n<em>#{I18n.t('pictures.no_result_found')}</em>\n</p>\n" \
305
250
  "</section>\n",
306
- title: 'My Gallery',
251
+ title: I18n.t('pictures.menu'),
307
252
  stylesheets: [
308
253
  'design/style.css',
309
254
  'design/photoswipe/photoswipe.css',
@@ -313,16 +258,9 @@ RSpec.describe Intranet::Pictures::Responder do
313
258
  }
314
259
  )
315
260
 
316
- # Invalid grouping criteria
317
- query = { 'group_by' => 'foo' }
318
- code, mime, content = @responder.generate_page('/browse.html', query)
319
- expect(code).to eql(404)
320
- expect(mime).to be_empty
321
- expect(content).to be_empty
322
-
323
261
  # Invalid sorting criteria
324
262
  query = { 'group_by' => 'location', 'sort_order' => 'foo' }
325
- code, mime, content = @responder.generate_page('/browse.html', query)
263
+ code, mime, content = @responder.generate_page('/index.html', query)
326
264
  expect(code).to eql(404)
327
265
  expect(mime).to be_empty
328
266
  expect(content).to be_empty
@@ -339,7 +277,8 @@ RSpec.describe Intranet::Pictures::Responder do
339
277
  " viewer_close: '#{I18n.t('pictures.viewer.close')}',\n" \
340
278
  " viewer_zoom: '#{I18n.t('pictures.viewer.zoom')}',\n" \
341
279
  " viewer_previous: '#{I18n.t('pictures.viewer.previous')}',\n" \
342
- " viewer_next: '#{I18n.t('pictures.viewer.next')}' };"
280
+ " viewer_next: '#{I18n.t('pictures.viewer.next')}',\n" \
281
+ " viewer_toggle_caption: '#{I18n.t('pictures.viewer.toggle_caption')}' };"
343
282
  )
344
283
  end
345
284
  end
@@ -394,6 +333,13 @@ RSpec.describe Intranet::Pictures::Responder do
394
333
  expect(mime).to eql('application/json')
395
334
  expect(content).to eql(''.to_json)
396
335
 
336
+ # Non-existing group
337
+ query = { 'flash' => 'true' }
338
+ code, mime, content = @responder.generate_page('/api/group_brief', query)
339
+ expect(code).to eql(404)
340
+ expect(mime).to be_empty
341
+ expect(content).to be_empty
342
+
397
343
  # Invalid query
398
344
  query = { 'location' => 'Tokyo, Japan', 'camera' => 'Canon EOS 5D MARK IV' }
399
345
  code, mime, content = @responder.generate_page('/api/group_brief', query)
@@ -405,52 +351,53 @@ RSpec.describe Intranet::Pictures::Responder do
405
351
 
406
352
  context 'when asked for \'/api/pictures\'' do
407
353
  it 'should return a JSON representation of the selected pictures' do
408
- # All pictures (no selector nor sort order)
354
+ # All pictures (no selector & default sort order)
409
355
  code, mime, content = @responder.generate_page('/api/pictures', {})
410
356
  expect(code).to eql(200)
411
357
  expect(mime).to eql('application/json')
412
358
  expect(content).to eql(
413
359
  [
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' }
360
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'New York, USA', 'tags' => 'Urban photography' },
361
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV', 'tags' => ['Louvre', 'Pyramid', 'Urban photography'] },
362
+ { 'datetime' => '2020:06:19 07:51:05', 'flash' => 'false', 'author' => 'Jane Doe', 'location' => 'Tokyo, Japan', 'tags' => ['MoMAK', 'Modern art', 'Cherry blossom'] },
363
+ { 'datetime' => '2019:07:22 09:45:17', 'author' => 'John Doe', 'location' => 'Tokyo, Japan' },
364
+ { 'datetime' => '2019:07:22 09:41:31', 'author' => 'John Doe', 'location' => 'Paris, France', 'camera' => 'Apple iPhone 11', 'tags' => ['Modern art', 'Louvre'] }
419
365
  ].to_json
420
366
  )
421
367
 
422
- # Valid selector, no sort order
423
- query = { 'author' => 'Jane Doe', 'flash' => true }
368
+ # Valid selector & default sort order
369
+ query = { 'author' => 'Jane Doe', 'flash' => 'true' }
424
370
  code, mime, content = @responder.generate_page('/api/pictures', query)
425
371
  expect(code).to eql(200)
426
372
  expect(mime).to eql('application/json')
427
373
  expect(content).to eql(
428
374
  [
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' }
375
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'New York, USA', 'tags' => 'Urban photography' },
376
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV', 'tags' => ['Louvre', 'Pyramid', 'Urban photography'] }
431
377
  ].to_json
432
378
  )
433
379
 
434
380
  # Valid selector, valid sort order
435
- query = { 'author' => 'Jane Doe', 'flash' => true, 'sort_by' => 'datetime' }
381
+ query = { 'author' => 'Jane Doe', 'tags' => 'Urban photography', 'sort_by' => 'datetime',
382
+ 'sort_order' => 'asc' }
436
383
  code, mime, content = @responder.generate_page('/api/pictures', query)
437
384
  expect(code).to eql(200)
438
385
  expect(mime).to eql('application/json')
439
386
  expect(content).to eql(
440
387
  [
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' }
388
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV', 'tags' => ['Louvre', 'Pyramid', 'Urban photography'] },
389
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'New York, USA', 'tags' => 'Urban photography' }
443
390
  ].to_json
444
391
  )
445
- query = { 'author' => 'Jane Doe', 'flash' => true, 'sort_by' => 'location',
392
+ query = { 'author' => 'Jane Doe', 'flash' => 'true', 'sort_by' => 'location',
446
393
  'sort_order' => 'desc' }
447
394
  code, mime, content = @responder.generate_page('/api/pictures', query)
448
395
  expect(code).to eql(200)
449
396
  expect(mime).to eql('application/json')
450
397
  expect(content).to eql(
451
398
  [
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' }
399
+ { 'datetime' => '2020:06:20 06:09:54', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV', 'tags' => ['Louvre', 'Pyramid', 'Urban photography'] },
400
+ { 'datetime' => '2020:06:20 18:14:09', 'flash' => 'true', 'author' => 'Jane Doe', 'location' => 'New York, USA', 'tags' => 'Urban photography' }
454
401
  ].to_json
455
402
  )
456
403
 
@@ -1,5 +1,4 @@
1
1
  {
2
- "title": "My Gallery",
3
2
  "groups": {
4
3
  "author": [
5
4
  {
@@ -27,7 +26,8 @@
27
26
  ],
28
27
  "camera": [
29
28
  {
30
- "id": "Apple iPhone 11"
29
+ "id": "Apple iPhone 11",
30
+ "brief": "Best smartphone ever"
31
31
  },
32
32
  {
33
33
  "id": "Canon EOS 5D MARK IV"
@@ -40,29 +40,33 @@
40
40
  "datetime": "2019:07:22 09:41:31",
41
41
  "author": "John Doe",
42
42
  "location": "Paris, France",
43
- "camera": "Apple iPhone 11"
43
+ "camera": "Apple iPhone 11",
44
+ "tags": [ "Modern art", "Louvre" ]
44
45
  },
45
46
  {
46
47
  "uri": "./alpha.png",
47
48
  "datetime": "2020:06:19 07:51:05",
48
- "flash": false,
49
+ "flash": "false",
49
50
  "author": "Jane Doe",
50
- "location": "Tokyo, Japan"
51
+ "location": "Tokyo, Japan",
52
+ "tags": [ "MoMAK", "Modern art", "Cherry blossom" ]
51
53
  },
52
54
  {
53
55
  "uri": "pic3.jpg",
54
56
  "datetime": "2020:06:20 18:14:09",
55
- "flash": true,
57
+ "flash": "true",
56
58
  "author": "Jane Doe",
57
- "location": "New York, USA"
59
+ "location": "New York, USA",
60
+ "tags": "Urban photography"
58
61
  },
59
62
  {
60
63
  "uri": "sample-db.json",
61
64
  "datetime": "2020:06:20 06:09:54",
62
- "flash": true,
65
+ "flash": "true",
63
66
  "author": "Jane Doe",
64
67
  "location": "Paris, France",
65
- "camera": "Canon EOS 5D MARK IV"
68
+ "camera": "Canon EOS 5D MARK IV",
69
+ "tags": [ "Louvre", "Pyramid", "Urban photography" ]
66
70
  },
67
71
  {
68
72
  "datetime": "2019:07:22 09:45:17",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intranet-pictures
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ebling Mis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-25 00:00:00.000000000 Z
11
+ date: 2025-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: intranet-core
@@ -53,7 +53,6 @@ files:
53
53
  - lib/intranet/pictures/json_db_provider.rb
54
54
  - lib/intranet/pictures/responder.rb
55
55
  - lib/intranet/pictures/version.rb
56
- - lib/intranet/resources/haml/pictures_browse.haml
57
56
  - lib/intranet/resources/haml/pictures_home.haml
58
57
  - lib/intranet/resources/locales/en.yml
59
58
  - lib/intranet/resources/locales/fr.yml
@@ -89,9 +88,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
88
  version: '3.0'
90
89
  required_rubygems_version: !ruby/object:Gem::Requirement
91
90
  requirements:
92
- - - ">="
91
+ - - ">"
93
92
  - !ruby/object:Gem::Version
94
- version: '0'
93
+ version: 1.3.1
95
94
  requirements: []
96
95
  rubygems_version: 3.3.15
97
96
  signing_key: