redmine_apijs 6.8.2 → 6.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README +4 -4
  3. data/app/controllers/apijs_controller.rb +41 -41
  4. data/app/views/attachments/_links.html.erb +3 -3
  5. data/app/views/settings/_apijs.html.erb +5 -3
  6. data/assets/javascripts/apijs-redmine.js +6 -5
  7. data/assets/javascripts/apijs-redmine.min.js +1 -1
  8. data/assets/javascripts/apijs-redmine.min.js.map +1 -1
  9. data/assets/javascripts/apijs.min.js +2 -2
  10. data/assets/javascripts/apijs.min.js.map +1 -1
  11. data/assets/stylesheets/apijs-print.min.css +1 -1
  12. data/assets/stylesheets/apijs-print.min.css.map +1 -1
  13. data/assets/stylesheets/apijs-screen-rtl.min.css +2 -2
  14. data/assets/stylesheets/apijs-screen-rtl.min.css.map +1 -1
  15. data/assets/stylesheets/apijs-screen.min.css +2 -2
  16. data/assets/stylesheets/apijs-screen.min.css.map +1 -1
  17. data/config/locales/cs.yml +1 -1
  18. data/config/locales/de.yml +1 -1
  19. data/config/locales/el.yml +1 -1
  20. data/config/locales/es.yml +1 -1
  21. data/config/locales/hu.yml +1 -1
  22. data/config/locales/it.yml +1 -1
  23. data/config/locales/ja.yml +1 -1
  24. data/config/locales/nl.yml +1 -1
  25. data/config/locales/pl.yml +1 -1
  26. data/config/locales/pt-BR.yml +1 -1
  27. data/config/locales/pt.yml +1 -1
  28. data/config/locales/ro.yml +1 -1
  29. data/config/locales/ru.yml +1 -1
  30. data/config/locales/sk.yml +1 -1
  31. data/config/locales/tr.yml +15 -15
  32. data/config/locales/uk.yml +1 -1
  33. data/config/locales/zh.yml +1 -1
  34. data/init.rb +27 -7
  35. data/lib/apijs_attachment.rb +3 -3
  36. data/lib/image.py +119 -19
  37. data/lib/redmine_apijs.rb +34 -11
  38. data/lib/useragentparser.rb +9 -9
  39. data/redmine_apijs.gemspec +2 -4
  40. metadata +5 -6
  41. data/lib/apijs_const.rb +0 -35
  42. data/lib/video.py +0 -80
@@ -1,7 +1,7 @@
1
1
  tr:
2
- apijs_check_extension: "The file extension can not be changed."
3
- apijs_clearcache_done: "The image cache was cleaned."
4
- apijs_browser_warning: "<strong>Warning:</strong> your browser <strong>%{n} %{v}</strong> is outdated, please <a %{l}>upgrade your browser</a>."
2
+ apijs_check_extension: "Dosya uzantısı değiştirilemez."
3
+ apijs_clearcache_done: "Resim ön belleği temizlendi."
4
+ apijs_browser_warning: "<strong>UYARI :</strong> tarayıcınız <strong>%{n} %{v}</strong> eski, lütfen <a %{l}>güncelleyiniz</a>."
5
5
  apijs_title_album: "Fotoğraf Galerisi"
6
6
  apijs_title_download: "İndir (%{v})"
7
7
  apijs_title_files: "Eklenti listesi"
@@ -10,25 +10,25 @@ tr:
10
10
  apijs_example1_title: "PHP Kılavuzu"
11
11
  apijs_example1_text: "Yalnız şuna dikkat edin : Epostanın sunucu tarafından teslim alınması, epostanın alıcısına ulaştığı anlamına gelmez."
12
12
  apijs_example: "Örnek %{v}"
13
- apijs_example2_title: "Surprise!"
14
- apijs_example2_text: "You are watching this video without proprietary extension thanks to the new [em]video[/em] tag included in the [strong]HTML 5[/strong] language."
13
+ apijs_example2_title: "Sürpriz!"
14
+ apijs_example2_text: "Bu videoyu [strong]HTML 5[/strong] dilindeki [em]video[/em] tanımlayıcısı sayesinde herhangi bir eklentiye ihtiyaç olmadan izlemektesiniz."
15
15
  apijs_example2_link: "Web sitesi : %{v}"
16
16
  apijs_config_sort_order: "Eklentileri alfabetik olarak sırala"
17
- apijs_config_browser: "Check browser version"
18
- apijs_config_browser_help: "Firefox 36+, Chrome 32+, Opera 19+, Edge 16+, Safari 9+ (checks even if the apijs is disabled)."
19
- permission_edit_attachments: "(apijs) Eklenti açıklamalarını düzenle"
20
- permission_rename_attachments: "(apijs) Rename files"
21
- permission_delete_attachments: "(apijs) Eklenti Sil"
22
- apijs_config_directories: "Directories size"
23
- apijs_config_clearcache: "clear cache"
17
+ apijs_config_browser: "Tarayıcı versiyonunu kontrol et"
18
+ apijs_config_browser_help: "Firefox 36+, Chrome 32+, Opera 19+, Edge 16+, Safari 9+ (apijs etkin değilken de kontrol edilir)."
19
+ permission_edit_attachments: "(apijs) Açıklama düzenle"
20
+ permission_rename_attachments: "(apijs) Dosya adı değiştir"
21
+ permission_delete_attachments: "(apijs) Dosya Sil"
22
+ apijs_config_directories: "Klasör boyutu"
23
+ apijs_config_clearcache: "önbelleği temizle"
24
24
  apijs_config_album: "Fotoğraf Albümü"
25
25
  apijs_config_show_album: "Fotoğraf albümünü görüntüle"
26
- apijs_config_show_album_infos: "Display all information"
26
+ apijs_config_show_album_infos: "Tüm bilgileri göster"
27
27
  apijs_config_show_filename: "Fotoğraf adını göster"
28
28
  apijs_config_show_exifdate: "Fotoğraf tarihini göster"
29
- apijs_config_mimetypes: "Use the following files"
29
+ apijs_config_mimetypes: "Bu dosyaları kullan"
30
30
  apijs_config_album_exclude_name: "Fotoğraf isimleri bununla<br />başlayanları hariç tur"
31
31
  apijs_config_album_exclude_desc: "Fotoğraf açıklamaları bunlarla<br />başlayanları hariç tut"
32
- apijs_config_create_all: "Generate the three images"
32
+ apijs_config_create_all: "Ağaç görünümü oluştur"
33
33
  apijs_config_create_all_info: "Simge görüntüsünü ve ön görünümü aynı anda oluşturmak sayfayı ilk görüntülemeyi yavaşlatır fakat slayt gösterisini hızlandırır."
34
34
  apijs_config_programs: "Program versiyonu"
@@ -1,6 +1,6 @@
1
1
  uk:
2
2
  apijs_check_extension: "The file extension can not be changed."
3
- apijs_clearcache_done: "The image cache was cleaned."
3
+ apijs_clearcache_done: "Кеш зображення було очищено."
4
4
  apijs_browser_warning: "<strong>Warning:</strong> your browser <strong>%{n} %{v}</strong> is outdated, please <a %{l}>upgrade your browser</a>."
5
5
  apijs_title_album: "Photo gallery"
6
6
  apijs_title_download: "Завантажити (%{v})"
@@ -1,6 +1,6 @@
1
1
  zh:
2
2
  apijs_check_extension: "The file extension can not be changed."
3
- apijs_clearcache_done: "The image cache was cleaned."
3
+ apijs_clearcache_done: "图片缓存被清除了。"
4
4
  apijs_browser_warning: "<strong>Warning:</strong> your browser <strong>%{n} %{v}</strong> is outdated, please <a %{l}>upgrade your browser</a>."
5
5
  apijs_title_album: "照片库"
6
6
  apijs_title_download: "下载(%{v})"
data/init.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # Created L/21/05/2012
3
- # Updated V/22/10/2021
3
+ # Updated M/05/07/2022
4
4
  #
5
5
  # Copyright 2008-2022 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
6
6
  # https://www.luigifab.fr/redmine/apijs
@@ -15,18 +15,38 @@
15
15
  # merchantability or fitness for a particular purpose. See the
16
16
  # GNU General Public License (GPL) for more details.
17
17
 
18
- require 'redmine'
19
- require 'apijs_const'
20
- require 'apijs_files'
21
- require 'apijs_attachment'
22
- require 'useragentparser'
18
+ if Rails::VERSION::MAJOR < 6 or ENV['redmine_apijs_gem']
19
+ require 'redmine'
20
+ require 'apijs_files'
21
+ require 'apijs_attachment'
22
+ require 'useragentparser'
23
+ end
24
+
25
+ if Redmine::VERSION::MAJOR >= 3
26
+ if defined? Redmine.root
27
+ ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(Redmine.root, 'files')
28
+ APIJS_ROOT = File.join(Redmine.root, 'tmp', 'apijs')
29
+ else
30
+ ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, 'files')
31
+ APIJS_ROOT = File.join(Rails.root, 'tmp', 'apijs')
32
+ end
33
+ elsif ENV['RAILS_TMP']
34
+ ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(ENV['RAILS_VAR'], 'files')
35
+ APIJS_ROOT = File.join(ENV['RAILS_TMP'], 'tmp', 'apijs')
36
+ elsif ENV['RAILS_CACHE']
37
+ ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(ENV['RAILS_VAR'], 'files')
38
+ APIJS_ROOT = File.join(ENV['RAILS_CACHE'], 'tmp', 'apijs')
39
+ else
40
+ ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, 'files')
41
+ APIJS_ROOT = File.join(Rails.root, 'tmp', 'apijs')
42
+ end
23
43
 
24
44
  Redmine::Plugin.register :redmine_apijs do
25
45
 
26
46
  name 'Redmine Apijs plugin'
27
47
  author 'Fabrice Creuzot'
28
48
  description 'Integrate the apijs JavaScript library into Redmine. Provides a gallery for image and video attachments.'
29
- version '6.8.2-gem'
49
+ version '6.9.0-gem'
30
50
  url 'https://www.luigifab.fr/redmine/apijs'
31
51
  author_url 'https://www.luigifab.fr/'
32
52
 
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # Created V/27/12/2013
3
- # Updated J/18/02/2021
3
+ # Updated J/21/04/2022
4
4
  #
5
5
  # Copyright 2008-2022 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
6
6
  # https://www.luigifab.fr/redmine/apijs
@@ -182,7 +182,7 @@ module ApijsAttachment
182
182
  cmd = getPython
183
183
  cmd = 'notfound' if not cmd or cmd.length == 0
184
184
 
185
- script = File.join(File.dirname(__FILE__), (self.isPhoto? ? 'image.py' : 'video.py'))
185
+ script = File.join(File.dirname(__FILE__), 'image.py')
186
186
  return cmd + ' ' + script.to_s + ' ' + source.to_s + ' ' + target.to_s + ' ' +
187
187
  width.to_s + ' ' + height.to_s + (fixed ? ' 90 fixed' : ' 90') + ' 2>&1'
188
188
  end
@@ -232,7 +232,7 @@ module ApijsAttachment
232
232
  logger.info 'APIJS::ApijsAttachment#update_date: ' + cmd + ' (' + result + ')'
233
233
 
234
234
  # 2014:06:14 16:43:53 (utilise le fuseau horaire de l'utilisateur)
235
- if result =~ /^[0-9]{4}.[0-9]{2}.[0-9]{2} [0-9]{2}.[0-9]{2}.[0-9]{2}/
235
+ if result =~ /^\d{4}.\d{2}.\d{2} \d{2}.\d{2}.\d{2}/
236
236
 
237
237
  date = result[0..9].gsub(':', '-') + ' ' + result[11..18]
238
238
  zone = User.current.time_zone
data/lib/image.py CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/python3
2
2
  # -*- coding: utf8 -*-
3
3
  # Created J/26/12/2013
4
- # Updated M/11/05/2021
4
+ # Updated M/05/07/2022
5
5
  #
6
6
  # Copyright 2008-2022 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
7
7
  # Copyright 2020-2022 | Fabrice Creuzot <fabrice~cellublue~com>
@@ -17,25 +17,31 @@
17
17
  # merchantability or fitness for a particular purpose. See the
18
18
  # GNU General Public License (GPL) for more details.
19
19
 
20
- import os
21
- import sys
20
+ import os, sys
22
21
 
23
22
  try:
24
23
  filein = str(sys.argv[1])
25
24
  fileout = str(sys.argv[2])
26
25
  size = (int(sys.argv[3]), int(sys.argv[4]))
27
- quality = int(sys.argv[5])
28
- fixed = len(sys.argv) == 7 and sys.argv[6] == 'fixed'
26
+ quality = int(sys.argv[5]) if len(sys.argv) >= 6 else 0
27
+ fixed = len(sys.argv) >= 7 and sys.argv[6] == 'fixed'
29
28
  except:
30
29
  print("Usage: image.py source destination width height [quality=0=auto] [fixed]")
31
- print("source: all supported format by python-pil (including animated gif/png/webp) or svg")
32
- print("destination: jpg,png,gif,webp or svg")
30
+ print("source: all supported format by python-pil (including animated gif/png/webp),")
31
+ print(" or all supported format by ffmpegthumbnailer,")
32
+ print(" or svg")
33
+ print("destination: jpg,gif,png,webp or svg")
33
34
  exit(-1)
34
35
 
36
+ # https://stackoverflow.com/a/273227/2980105
35
37
  if not os.path.exists(os.path.dirname(fileout)):
36
- os.makedirs(os.path.dirname(fileout))
38
+ os.makedirs(os.path.dirname(fileout), exist_ok=True)
37
39
 
40
+ same = filein == fileout
38
41
  fileout = fileout + '.save'
42
+ if os.path.isfile(fileout):
43
+ exit(0)
44
+ os.close(os.open(fileout, os.O_CREAT))
39
45
 
40
46
 
41
47
  # tools
@@ -55,10 +61,13 @@ def calcSize(source, size):
55
61
 
56
62
  def createThumb(source, size, fixed, new=False):
57
63
 
64
+ # https://pillow.readthedocs.io/en/latest/releasenotes/7.0.0.html?highlight=thumbnail (reducing_gap)
58
65
  # https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.thumbnail
59
66
  # https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters-comparison-table
60
67
  # https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.paste
61
- if hasattr(Image, '__version__') and versionTuple(Image.__version__) > (7,0,0):
68
+ if hasattr(Image, '__version__') and versionTuple(Image.__version__) >= (9,1,0):
69
+ source.thumbnail(size, Image.Resampling.LANCZOS, 4)
70
+ elif hasattr(Image, '__version__') and versionTuple(Image.__version__) >= (7,0,0):
62
71
  source.thumbnail(size, Image.LANCZOS, 4)
63
72
  else:
64
73
  source.thumbnail(size, Image.ANTIALIAS)
@@ -80,6 +89,31 @@ def createThumb(source, size, fixed, new=False):
80
89
 
81
90
  return dest
82
91
 
92
+ def videoToImage(filein, fileout, size):
93
+
94
+ os.system('ffmpegthumbnailer -i ' + filein + ' -o ' + fileout + ' -q 10 -s ' + str(size[0]))
95
+ if os.path.isfile(fileout) and os.path.getsize(fileout) > 0:
96
+ img = Image.open(fileout)
97
+ else:
98
+ img = Image.new('RGBA', size, (0,0,0,0))
99
+
100
+ return img
101
+
102
+ def hasTransparency(img):
103
+
104
+ # https://stackoverflow.com/a/58567453/2980105
105
+ if img.mode == 'P':
106
+ transparent = img.info.get('transparency', -1)
107
+ for _, index in img.getcolors():
108
+ if index == transparent:
109
+ return True
110
+ elif img.mode == 'RGBA':
111
+ extrema = img.getextrema()
112
+ if extrema[3][0] < 255:
113
+ return True
114
+
115
+ return False
116
+
83
117
 
84
118
  # Animated resizing
85
119
  # based on https://stackoverflow.com/a/41827681/2980105
@@ -94,8 +128,12 @@ def resizeAnimatedGif(source, size, fixed):
94
128
  while True:
95
129
  # If the GIF uses local colour tables, each frame will have its own palette.
96
130
  # If not, we need to apply the global palette to the new frame.
97
- if not source.getpalette():
98
- source.putpalette(colors)
131
+ # Ignore ValueError (illegal image mode)
132
+ try:
133
+ if not source.getpalette():
134
+ source.putpalette(colors)
135
+ except ValueError:
136
+ pass
99
137
 
100
138
  frame = Image.new('RGBA', source.size, (255,255,255,1))
101
139
  frame.paste(source, (0,0), source.convert('RGBA'))
@@ -211,6 +249,14 @@ def saveJpg(dest, fileout, quality):
211
249
  elif quality > 95:
212
250
  quality = 95
213
251
 
252
+ # https://github.com/wanadev/pyguetzli
253
+ # warning: extremely slow performance (https://github.com/google/guetzli/issues/50)
254
+ #try:
255
+ # import pyguetzli
256
+ # filefinal = pyguetzli.process_pil_image(dest.convert('RGB'), quality)
257
+ # output = open(fileout, "wb")
258
+ # output.write(filefinal)
259
+ #except ImportError:
214
260
  dest.convert('RGB').save(fileout, 'JPEG', optimize=True, subsampling=0, quality=quality)
215
261
 
216
262
 
@@ -227,21 +273,75 @@ if ".svg" in fileout:
227
273
  # python-pil
228
274
  else:
229
275
  from PIL import Image, ImageSequence
230
- source = Image.open(filein)
231
- size = calcSize(source, size)
232
276
 
233
- if source.format == 'GIF' and ".gif" in fileout:
277
+ if ".ogv" in filein or ".webm" in filein or ".mp4" in filein:
278
+ # from video
279
+ source = videoToImage(filein, fileout, size)
280
+ imgext = 'VIDEO'
281
+ else:
282
+ # from image
283
+ source = Image.open(filein)
284
+ imgext = source.format
285
+ size = calcSize(source, size)
286
+
287
+ # filein = fileout = /tmp/phpA5kbkc when replace tmp image with re-sampled copy to exclude images with malicious data
288
+ if imgext == 'GIF' and (same or ".gif" in fileout):
234
289
  dest = resizeAnimatedGif(source, size, fixed)
235
290
  saveGif(dest, fileout, quality)
236
- elif source.format == 'PNG' and ".png" in fileout:
291
+ elif imgext == 'PNG' and (same or ".png" in fileout):
237
292
  dest = resizeAnimatedPng(source, size, fixed)
238
293
  savePng(dest, fileout, quality)
239
- elif source.format == 'WEBP' and ".webp" in fileout:
240
- dest = resizeAnimatedWeb(source, size, fixed)
294
+ elif imgext == 'WEBP' and (same or ".webp" in fileout):
295
+ dest = resizeAnimatedWebp(source, size, fixed)
241
296
  saveWebp(dest, fileout, quality)
242
297
  else:
243
- dest = createThumb(source, size, fixed, True)
244
- saveJpg(dest, fileout, quality)
298
+ if imgext == 'VIDEO':
299
+ # from video
300
+ offset_x = max(int((size[0] - source.size[0]) / 2), 0)
301
+ offset_y = max(int((size[1] - source.size[1]) / 2), 0)
302
+ # Color detection
303
+ # white background black player OR black background white player
304
+ pixels = source.getcolors(size[0] * size[1])
305
+ pixels = sorted(pixels, key=lambda t: t[0])
306
+ if (pixels[-1][1] > (127,127,127)):
307
+ dest = Image.new('RGBA', size, (255,255,255,0))
308
+ dest.paste(source, (offset_x, offset_y))
309
+ # https://stackoverflow.com/a/59082116 (replace only last lib)
310
+ # /var/lib/gems/xyz/gems/redmine_apijs-xyz/lib » /var/lib/gems/xyz/gems/redmine_apijs-xyz/assets/images/...
311
+ path = os.path.dirname(__file__)
312
+ path = ('/assets/images/apijs/player-black-' + str(size[0]) + '.png').join(path.rsplit('/lib', 1))
313
+ play = Image.open(path)
314
+ dest.paste(play, (0, 0), play)
315
+ else:
316
+ dest = Image.new('RGBA', size, (0,0,0,0))
317
+ dest.paste(source, (offset_x, offset_y))
318
+ # https://stackoverflow.com/a/59082116 (replace only last lib)
319
+ # /var/lib/gems/xyz/gems/redmine_apijs-xyz/lib » /var/lib/gems/xyz/gems/redmine_apijs-xyz/assets/images/...
320
+ path = os.path.dirname(__file__)
321
+ path = ('/assets/images/apijs/player-white-' + str(size[0]) + '.png').join(path.rsplit('/lib', 1))
322
+ play = Image.open(path)
323
+ dest.paste(play, (0, 0), play)
324
+ else:
325
+ # from image
326
+ # Auto rotate image
327
+ # https://github.com/python-pillow/Pillow/commit/1ba774ae7faf93355b85c7b005fbaf0b0e66d426
328
+ if hasattr(Image, '__version__') and versionTuple(Image.__version__) >= (6,0,0) and source.getexif().get(0x0112):
329
+ from PIL import ImageOps
330
+ source = ImageOps.exif_transpose(source)
331
+ # create thumbnail
332
+ dest = createThumb(source, size, fixed, True)
333
+
334
+ if hasTransparency(source) is False:
335
+ dest = dest.convert('RGB')
336
+
337
+ if ".gif" in fileout or (same and imgext == 'GIF'):
338
+ saveGif(dest, fileout, quality)
339
+ elif ".png" in fileout or (same and imgext == 'PNG'):
340
+ savePng(dest, fileout, quality)
341
+ elif ".webp" in fileout or (same and imgext == 'WEBP'):
342
+ saveWebp(dest, fileout, quality)
343
+ else:
344
+ saveJpg(dest, fileout, quality)
245
345
 
246
346
  if os.path.isfile(fileout):
247
347
  os.rename(fileout, fileout.replace('.save', ''))
data/lib/redmine_apijs.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # Created L/13/07/2020
3
- # Updated D/27/08/2020
3
+ # Updated M/05/07/2022
4
4
  #
5
5
  # Copyright 2008-2022 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
6
6
  # https://www.luigifab.fr/redmine/apijs
@@ -20,29 +20,52 @@ require 'rails/engine'
20
20
  module RedmineApijs
21
21
 
22
22
  class Plugin < ::Rails::Engine
23
+
23
24
  config.after_initialize do
24
25
 
25
- # Gemify redmine plugin (https://github.com/koppen/redmine_github_hook/commit/a82bcccfd0731503509771d3f715d8fb1e6f1bfe)
26
+ # for Redmine 5.0+ with Rails 6.0+
27
+ ENV['redmine_apijs_gem'] = 'yes'
28
+
29
+ # Gemify redmine plugin (based on https://github.com/koppen/redmine_github_hook/commit/a82bcccfd0731503509771d3f715d8fb1e6f1bfe)
30
+ # it works out of box with Redmine 3.0 - 4.0, for Redmine 4.1+ see https://www.redmine.org/issues/31110
26
31
  # run the classic redmine plugin initializer after rails boot
27
32
  require File.expand_path('../../init', __FILE__)
28
33
  ::ActionController::Base.prepend_view_path(File.expand_path('../../app/views/', __FILE__))
29
34
 
30
35
  # and copy plugin assets
31
36
  unless ::Redmine::Configuration['mirror_plugins_assets_on_startup'] == false
32
- module ::Redmine
33
- class Plugin
34
- def assets_directory
35
- if directory =~ /redmine_apijs/
36
- File.join(File.expand_path('../../', __FILE__), 'assets')
37
- else
38
- File.join(directory, 'assets')
37
+ if Redmine::VERSION::MAJOR >= 5
38
+ # https://github.com/redmine/redmine/blob/9aa34eb651e2941aeae69cc3acf6e1c1b909729e/lib/redmine/plugin_loader.rb#L41
39
+ # Update the name of the plugin directory
40
+ module ::Redmine
41
+ class PluginPath
42
+ def update_dir(dir)
43
+ @dir = dir
44
+ end
45
+ end
46
+ end
47
+ # go
48
+ obj = Redmine::PluginPath.new(File.expand_path('../../', __FILE__))
49
+ obj.update_dir('redmine_apijs')
50
+ obj.mirror_assets
51
+ else
52
+ # https://github.com/redmine/redmine/blob/94f3510036690d049ac7425d19fa7c055044ae57/lib/redmine/plugin.rb#L207
53
+ # Returns the absolute path to the plugin assets directory
54
+ module ::Redmine
55
+ class Plugin
56
+ def assets_directory
57
+ if directory =~ /redmine_apijs/
58
+ File.join(File.expand_path('../../', __FILE__), 'assets')
59
+ else
60
+ File.join(directory, 'assets')
61
+ end
39
62
  end
40
63
  end
41
64
  end
65
+ # go
66
+ Redmine::Plugin.mirror_assets('redmine_apijs')
42
67
  end
43
- Redmine::Plugin.mirror_assets('redmine_apijs')
44
68
  end
45
-
46
69
  end
47
70
  end
48
71
  end
@@ -1,10 +1,10 @@
1
1
  # encoding: utf-8
2
2
  #
3
- # Copyright 2013-2021 | Jesse G. Donat <donatj~gmail~com>
3
+ # Copyright 2013-2022 | Jesse G. Donat <donatj~gmail~com>
4
4
  # https://github.com/donatj/PhpUserAgent
5
5
  #
6
- # Copyright 2019-2021 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
7
- # https://gist.github.com/luigifab/19a68d9aa98fa80f2961809d7cec59c0 (1.5.0-fork1)
6
+ # Copyright 2019-2022 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
7
+ # https://gist.github.com/luigifab/19a68d9aa98fa80f2961809d7cec59c0 (1.6.1-fork1)
8
8
  #
9
9
  # Parses a user agent string into its important parts
10
10
  # Licensed under the MIT License
@@ -49,13 +49,13 @@ class Useragentparser
49
49
  end
50
50
 
51
51
  result = userAgent.to_enum(:scan, # ["browser" => ["Firefox"...], "version" => ["45.0"...]]
52
- /(?<browser>Camino|Kindle(\ Fire)?|Firefox|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit|TizenBrowser|(?:Headless)?Chrome|YaBrowser|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|Edge|Edg|CriOS|UCBrowser|Puffin|OculusBrowser|SamsungBrowser|SailfishBrowser|XiaoMi\/MiuiBrowser|Baiduspider|Applebot|Facebot|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl|Valve\ Steam\ Tenfoot|NintendoBrowser|PLAYSTATION\ (\d|Vita)+)\)?;?(?:[:\/ ](?<version>[0-9A-Z.]+)|\/[A-Z]*)/ix
52
+ /(?<browser>Camino|Kindle(\ Fire)?|Firefox|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit|TizenBrowser|(?:Headless)?Chrome|YaBrowser|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|(?-i:Edge)|EdgA?|CriOS|UCBrowser|Puffin|OculusBrowser|SamsungBrowser|SailfishBrowser|XiaoMi\/MiuiBrowser|Baiduspider|Applebot|Facebot|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl|Valve\ Steam\ Tenfoot|NintendoBrowser|PLAYSTATION\ (\d|Vita)+)\)?;?(?:[:\/ ](?<version>[\dA-Z.]+)|\/[A-Z]*)/ix
53
53
  ).map { Regexp.last_match.names.collect{ |x| {x => $~[x]} }.reduce({}, :merge) }
54
54
  .reduce({}) { |h,pairs| pairs.each {|k,v| (h[k] ||= []) << v}; h }
55
55
 
56
56
  # If nothing matched, return nil (to avoid undefined index errors)
57
57
  if !result['browser'] || !result['browser'][0] || !result['version'] || !result['version'][0]
58
- result = userAgent.match(/^(?!Mozilla)(?<browser>[A-Z0-9\-]+)(\/(?<version>[0-9A-Z.]+))?/ix)
58
+ result = userAgent.match(/^(?!Mozilla)(?<browser>[A-Z\d\-]+)(\/(?<version>[\dA-Z.]+))?/ix)
59
59
  if result
60
60
  return {
61
61
  'platform' => platform ? platform : nil,
@@ -66,7 +66,7 @@ class Useragentparser
66
66
  return empty
67
67
  end
68
68
 
69
- rv_result = userAgent.match(/rv:(?<version>[0-9A-Z.]+)/i)
69
+ rv_result = userAgent.match(/rv:(?<version>[\dA-Z.]+)/i)
70
70
  if rv_result && rv_result['version']
71
71
  rv_result = rv_result['version']
72
72
  end
@@ -80,16 +80,16 @@ class Useragentparser
80
80
  refpla = [platform]
81
81
  refval = ['']
82
82
 
83
- if findt(lowerBrowser, {'OPR' => 'Opera', 'Facebot' => 'iMessageBot', 'UCBrowser' => 'UC Browser', 'YaBrowser' => 'Yandex', 'Iceweasel' => 'Firefox', 'Icecat' => 'Firefox', 'CriOS' => 'Chrome', 'Edg' => 'Edge', 'XiaoMi/MiuiBrowser' => 'MiuiBrowser'}, refkey, refbro)
83
+ if findt(lowerBrowser, {'OPR' => 'Opera', 'Facebot' => 'iMessageBot', 'UCBrowser' => 'UC Browser', 'YaBrowser' => 'Yandex', 'Iceweasel' => 'Firefox', 'Icecat' => 'Firefox', 'CriOS' => 'Chrome', 'Edg' => 'Edge', 'EdgA' => 'Edge', 'XiaoMi/MiuiBrowser' => 'MiuiBrowser'}, refkey, refbro)
84
84
  browser = refbro[0]
85
- version = result['version'][refkey[0]][0] =~ /[0-9]/ ? result['version'][refkey[0]] : nil
85
+ version = result['version'][refkey[0]][0] =~ /\d/ ? result['version'][refkey[0]] : nil
86
86
  elsif find(lowerBrowser, 'Playstation Vita', refkey, platform)
87
87
  platform = 'PlayStation Vita'
88
88
  browser = 'Browser'
89
89
  elsif find(lowerBrowser, ['Kindle Fire', 'Silk'], refkey, refval)
90
90
  browser = refval[0] == 'Silk' ? 'Silk' : 'Kindle'
91
91
  platform = 'Kindle Fire'
92
- if !(version = result['version'][refkey[0]]) || !(version[0] =~ /[0-9]/ ? true : false)
92
+ if !(version = result['version'][refkey[0]]) || !(version[0] =~ /\d/ ? true : false)
93
93
  version = result['version'][result['browser'].index('Version')]
94
94
  end
95
95
  elsif platform == 'Nintendo 3DS' || find(lowerBrowser, 'NintendoBrowser', refkey)
@@ -1,9 +1,9 @@
1
1
  # coding: utf-8
2
2
  Gem::Specification.new do |s|
3
3
  s.name = 'redmine_apijs'
4
- s.version = '6.8.2'
4
+ s.version = '6.9.0'
5
5
  s.summary = 'Redmine Apijs plugin'
6
- s.description = 'Integrate the apijs JavaScript library into Redmine. Provides a gallery for image and video attachments.'
6
+ s.description = 'Integrate the apijs JavaScript library into Redmine. Provides a gallery for image and video attachments. Gem for Redmine 3.0+ (tested with 3.0..5.0), for Redmine 4.1+, read https://redmine.org/issues/31110#note-8'
7
7
  s.homepage = 'https://github.com/luigifab/redmine-apijs'
8
8
  s.license = 'GPL-2.0-or-later'
9
9
  s.authors = ['Fabrice Creuzot']
@@ -66,12 +66,10 @@ Gem::Specification.new do |s|
66
66
  Gemfile
67
67
  init.rb
68
68
  lib/apijs_attachment.rb
69
- lib/apijs_const.rb
70
69
  lib/apijs_files.rb
71
70
  lib/image.py
72
71
  lib/redmine_apijs.rb
73
72
  lib/useragentparser.rb
74
- lib/video.py
75
73
  LICENSE
76
74
  README
77
75
  redmine_apijs.gemspec
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redmine_apijs
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.8.2
4
+ version: 6.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fabrice Creuzot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-01 00:00:00.000000000 Z
11
+ date: 2022-07-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Integrate the apijs JavaScript library into Redmine. Provides a gallery
14
- for image and video attachments.
14
+ for image and video attachments. Gem for Redmine 3.0+ (tested with 3.0..5.0), for
15
+ Redmine 4.1+, read https://redmine.org/issues/31110#note-8
15
16
  email: code@luigifab.fr
16
17
  executables: []
17
18
  extensions: []
@@ -70,12 +71,10 @@ files:
70
71
  - config/routes.rb
71
72
  - init.rb
72
73
  - lib/apijs_attachment.rb
73
- - lib/apijs_const.rb
74
74
  - lib/apijs_files.rb
75
75
  - lib/image.py
76
76
  - lib/redmine_apijs.rb
77
77
  - lib/useragentparser.rb
78
- - lib/video.py
79
78
  - redmine_apijs.gemspec
80
79
  homepage: https://github.com/luigifab/redmine-apijs
81
80
  licenses:
@@ -99,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
98
  - !ruby/object:Gem::Version
100
99
  version: '0'
101
100
  requirements: []
102
- rubygems_version: 3.2.27
101
+ rubygems_version: 3.3.15
103
102
  signing_key:
104
103
  specification_version: 4
105
104
  summary: Redmine Apijs plugin
data/lib/apijs_const.rb DELETED
@@ -1,35 +0,0 @@
1
- # encoding: utf-8
2
- # Created L/01/09/2014
3
- # Updated J/23/07/2020
4
- #
5
- # Copyright 2008-2022 | Fabrice Creuzot (luigifab) <code~luigifab~fr>
6
- # https://www.luigifab.fr/redmine/apijs
7
- #
8
- # This program is free software, you can redistribute it or modify
9
- # it under the terms of the GNU General Public License (GPL) as published
10
- # by the free software foundation, either version 2 of the license, or
11
- # (at your option) any later version.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but without any warranty, without even the implied warranty of
15
- # merchantability or fitness for a particular purpose. See the
16
- # GNU General Public License (GPL) for more details.
17
-
18
- if Redmine::VERSION::MAJOR >= 3
19
- if defined? Redmine.root
20
- ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(Redmine.root, 'files')
21
- APIJS_ROOT = File.join(Redmine.root, 'tmp', 'apijs')
22
- else
23
- ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, 'files')
24
- APIJS_ROOT = File.join(Rails.root, 'tmp', 'apijs')
25
- end
26
- elsif ENV['RAILS_TMP']
27
- ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(ENV['RAILS_VAR'], 'files')
28
- APIJS_ROOT = File.join(ENV['RAILS_TMP'], 'tmp', 'apijs')
29
- elsif ENV['RAILS_CACHE']
30
- ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(ENV['RAILS_VAR'], 'files')
31
- APIJS_ROOT = File.join(ENV['RAILS_CACHE'], 'tmp', 'apijs')
32
- else
33
- ALL_FILES = Redmine::Configuration['attachments_storage_path'] || File.join(Rails.root, 'files')
34
- APIJS_ROOT = File.join(Rails.root, 'tmp', 'apijs')
35
- end