intranet-pictures 2.2.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
@@ -395,6 +333,13 @@ RSpec.describe Intranet::Pictures::Responder do
395
333
  expect(mime).to eql('application/json')
396
334
  expect(content).to eql(''.to_json)
397
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
+
398
343
  # Invalid query
399
344
  query = { 'location' => 'Tokyo, Japan', 'camera' => 'Canon EOS 5D MARK IV' }
400
345
  code, mime, content = @responder.generate_page('/api/group_brief', query)
@@ -406,52 +351,53 @@ RSpec.describe Intranet::Pictures::Responder do
406
351
 
407
352
  context 'when asked for \'/api/pictures\'' do
408
353
  it 'should return a JSON representation of the selected pictures' do
409
- # All pictures (no selector nor sort order)
354
+ # All pictures (no selector & default sort order)
410
355
  code, mime, content = @responder.generate_page('/api/pictures', {})
411
356
  expect(code).to eql(200)
412
357
  expect(mime).to eql('application/json')
413
358
  expect(content).to eql(
414
359
  [
415
- { 'datetime' => '2019:07:22 09:41:31', 'author' => 'John Doe', 'location' => 'Paris, France', 'camera' => 'Apple iPhone 11' },
416
- { 'datetime' => '2020:06:19 07:51:05', 'flash' => false, 'author' => 'Jane Doe', 'location' => 'Tokyo, Japan' },
417
- { 'datetime' => '2020:06:20 18:14:09', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'New York, USA' },
418
- { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' },
419
- { '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'] }
420
365
  ].to_json
421
366
  )
422
367
 
423
- # Valid selector, no sort order
424
- query = { 'author' => 'Jane Doe', 'flash' => true }
368
+ # Valid selector & default sort order
369
+ query = { 'author' => 'Jane Doe', 'flash' => 'true' }
425
370
  code, mime, content = @responder.generate_page('/api/pictures', query)
426
371
  expect(code).to eql(200)
427
372
  expect(mime).to eql('application/json')
428
373
  expect(content).to eql(
429
374
  [
430
- { 'datetime' => '2020:06:20 18:14:09', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'New York, USA' },
431
- { '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'] }
432
377
  ].to_json
433
378
  )
434
379
 
435
380
  # Valid selector, valid sort order
436
- query = { 'author' => 'Jane Doe', 'flash' => true, 'sort_by' => 'datetime' }
381
+ query = { 'author' => 'Jane Doe', 'tags' => 'Urban photography', 'sort_by' => 'datetime',
382
+ 'sort_order' => 'asc' }
437
383
  code, mime, content = @responder.generate_page('/api/pictures', query)
438
384
  expect(code).to eql(200)
439
385
  expect(mime).to eql('application/json')
440
386
  expect(content).to eql(
441
387
  [
442
- { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' },
443
- { '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' }
444
390
  ].to_json
445
391
  )
446
- query = { 'author' => 'Jane Doe', 'flash' => true, 'sort_by' => 'location',
392
+ query = { 'author' => 'Jane Doe', 'flash' => 'true', 'sort_by' => 'location',
447
393
  'sort_order' => 'desc' }
448
394
  code, mime, content = @responder.generate_page('/api/pictures', query)
449
395
  expect(code).to eql(200)
450
396
  expect(mime).to eql('application/json')
451
397
  expect(content).to eql(
452
398
  [
453
- { 'datetime' => '2020:06:20 06:09:54', 'flash' => true, 'author' => 'Jane Doe', 'location' => 'Paris, France', 'camera' => 'Canon EOS 5D MARK IV' },
454
- { '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' }
455
401
  ].to_json
456
402
  )
457
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.2.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-26 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:
@@ -1,14 +0,0 @@
1
- %section
2
- = to_markup 'title_and_breadcrumb', {title: title, nav: nav}
3
- %ul.groups
4
- - groups.each do |group_name|
5
- %li{ title: group_name }
6
- %a{ onclick: 'openImagesGallery("' + gallery_url(group_key, group_name, filters) + '");' }
7
- %figure
8
- %div{ style: 'background-image: url("api/group_thumbnail?' + group_key + '=' + group_name + '")' }
9
- %figcaption
10
- = group_name
11
- - group_brief = api_group_brief({ group_key => group_name })
12
- - unless group_brief.empty?
13
- %br
14
- %em= group_brief