diff --git a/administrator/com_joomgallery/src/Controller/DisplayController.php b/administrator/com_joomgallery/src/Controller/DisplayController.php index b6ff9721f..d353b81c6 100644 --- a/administrator/com_joomgallery/src/Controller/DisplayController.php +++ b/administrator/com_joomgallery/src/Controller/DisplayController.php @@ -64,7 +64,7 @@ class DisplayController extends BaseController * * @since 4.0.0 */ - public function __construct($config = array(), MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) + public function __construct($config = array(), ?MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) { parent::__construct($config, $factory, $app, $input); diff --git a/administrator/com_joomgallery/src/Controller/ImagesController.php b/administrator/com_joomgallery/src/Controller/ImagesController.php index 2c730041e..d5b225f2d 100644 --- a/administrator/com_joomgallery/src/Controller/ImagesController.php +++ b/administrator/com_joomgallery/src/Controller/ImagesController.php @@ -41,7 +41,7 @@ class ImagesController extends JoomAdminController * * @since 4.0.0 */ - public function __construct($config = array(), MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) + public function __construct($config = array(), ?MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) { parent::__construct($config, $factory, $app, $input); diff --git a/administrator/com_joomgallery/src/Controller/JoomAdminController.php b/administrator/com_joomgallery/src/Controller/JoomAdminController.php index 9adfe3a5c..8913adce9 100644 --- a/administrator/com_joomgallery/src/Controller/JoomAdminController.php +++ b/administrator/com_joomgallery/src/Controller/JoomAdminController.php @@ -67,7 +67,7 @@ class JoomAdminController extends BaseAdminController * * @since 4.0.0 */ - public function __construct($config = array(), MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) + public function __construct($config = array(), ?MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null) { parent::__construct($config, $factory, $app, $input); diff --git a/administrator/com_joomgallery/src/Controller/JoomFormController.php b/administrator/com_joomgallery/src/Controller/JoomFormController.php index 8b32dab72..df6ab0897 100644 --- a/administrator/com_joomgallery/src/Controller/JoomFormController.php +++ b/administrator/com_joomgallery/src/Controller/JoomFormController.php @@ -15,6 +15,7 @@ use \Joomla\Input\Input; use \Joomla\CMS\User\CurrentUserInterface; use \Joomla\CMS\Application\CMSApplication; +use \Joomla\CMS\Form\FormFactoryInterface; use \Joomla\CMS\MVC\Factory\MVCFactoryInterface; use \Joomla\CMS\MVC\Controller\FormController as BaseFormController; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; @@ -68,7 +69,7 @@ class JoomFormController extends BaseFormController * * @since 4.0.0 */ - public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null, FormFactoryInterface $formFactory = null) + public function __construct($config = [], ?MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null, ?FormFactoryInterface $formFactory = null) { parent::__construct($config, $factory, $app, $input, $formFactory); diff --git a/administrator/com_joomgallery/src/Controller/MigrationController.php b/administrator/com_joomgallery/src/Controller/MigrationController.php index 02d26cf8d..d267177a3 100644 --- a/administrator/com_joomgallery/src/Controller/MigrationController.php +++ b/administrator/com_joomgallery/src/Controller/MigrationController.php @@ -80,7 +80,7 @@ class MigrationController extends BaseController implements FormFactoryAwareInte * * @since 4.0.0 */ - public function __construct($config = [], MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null, FormFactoryInterface $formFactory = null) + public function __construct($config = [], ?MVCFactoryInterface $factory = null, ?CMSApplication $app = null, ?Input $input = null, ?FormFactoryInterface $formFactory = null) { parent::__construct($config, $factory, $app, $input); diff --git a/administrator/com_joomgallery/src/Extension/MessageTrait.php b/administrator/com_joomgallery/src/Extension/MessageTrait.php index 20e09e76e..b778bb11b 100644 --- a/administrator/com_joomgallery/src/Extension/MessageTrait.php +++ b/administrator/com_joomgallery/src/Extension/MessageTrait.php @@ -140,7 +140,7 @@ public function msgFromSession() * * @since 4.0.0 */ - protected function addLogger(string $name = null) + protected function addLogger(?string $name = null) { if(!$this->log) { @@ -166,7 +166,7 @@ protected function addLogger(string $name = null) * * @since 4.0.0 */ - public function setLogger(string $name = null) + public function setLogger(?string $name = null) { $this->addLogger($name); $this->logName = $name; @@ -182,7 +182,7 @@ public function setLogger(string $name = null) * * @since 4.0.0 */ - public function addLog(string $txt, $priority = 8, string $name = null) + public function addLog(string $txt, $priority = 8, ?string $name = null) { $this->addLogger($name); @@ -218,7 +218,7 @@ public function addLog(string $txt, $priority = 8, string $name = null) * * @since 4.0.0 */ - public function setLog(string $txt, $priority = 8, string $name = null) + public function setLog(string $txt, $priority = 8, ?string $name = null) { return $this->addLog($txt, $priority, $name); } diff --git a/administrator/com_joomgallery/src/MVC/MVCFactory.php b/administrator/com_joomgallery/src/MVC/MVCFactory.php index 8278a43f7..0e59a93ab 100644 --- a/administrator/com_joomgallery/src/MVC/MVCFactory.php +++ b/administrator/com_joomgallery/src/MVC/MVCFactory.php @@ -57,7 +57,7 @@ public function getIdentity() * * @since 4.0.0 */ - public function loadIdentity(User $identity = null) + public function loadIdentity(?User $identity = null) { if(\is_null($identity)) { diff --git a/administrator/com_joomgallery/src/Model/CategoryModel.php b/administrator/com_joomgallery/src/Model/CategoryModel.php index 6527f3a84..05be57e77 100644 --- a/administrator/com_joomgallery/src/Model/CategoryModel.php +++ b/administrator/com_joomgallery/src/Model/CategoryModel.php @@ -606,8 +606,21 @@ public function save($data) // Get path back from old location temporarily $table->setPathWithLocation(true); + // Get new folder name + $folder_name = $table->alias; + if($this->component->getConfig()->get('jg_compatibility_mode', 0)) + { + $static_name = \basename($table->static_path); + if(\preg_match('/_([0-9]+)$/', $static_name)) + { + // We found a numeric value at the end of the folder name: e.g alias_6 + // Therefore we use the static folder name instead + $folder_name = $static_name; + } + } + // Rename folder - if(!$manager->renameCategory($old_table, $table->alias)) + if(!$manager->renameCategory($old_table, $folder_name)) { $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_ERROR_RENAME_CATEGORY', $manager->paths['src'], $manager->paths['dest'])); $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_ERROR_RENAME_CATEGORY', $manager->paths['src'], $manager->paths['dest']), 'error', 'jerror'); diff --git a/administrator/com_joomgallery/src/Model/ImageModel.php b/administrator/com_joomgallery/src/Model/ImageModel.php index 290231de7..18dd3ac25 100644 --- a/administrator/com_joomgallery/src/Model/ImageModel.php +++ b/administrator/com_joomgallery/src/Model/ImageModel.php @@ -66,7 +66,7 @@ class ImageModel extends JoomAdminModel * @since 4.0.0 * @throws \Exception */ - public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) + public function __construct($config = [], ?MVCFactoryInterface $factory = null, ?FormFactoryInterface $formFactory = null) { parent::__construct($config, $factory, $formFactory); diff --git a/administrator/com_joomgallery/src/Model/JoomAdminModel.php b/administrator/com_joomgallery/src/Model/JoomAdminModel.php index b37b1cd26..95045432b 100644 --- a/administrator/com_joomgallery/src/Model/JoomAdminModel.php +++ b/administrator/com_joomgallery/src/Model/JoomAdminModel.php @@ -109,7 +109,7 @@ abstract class JoomAdminModel extends AdminModel * @since 4.0.0 * @throws \Exception */ - public function __construct($config = [], MVCFactoryInterface $factory = null, FormFactoryInterface $formFactory = null) + public function __construct($config = [], ?MVCFactoryInterface $factory = null, ?FormFactoryInterface $formFactory = null) { parent::__construct($config, $factory, $formFactory); diff --git a/administrator/com_joomgallery/src/Router/RouterFactory.php b/administrator/com_joomgallery/src/Router/RouterFactory.php index d8078880b..b885bf67b 100644 --- a/administrator/com_joomgallery/src/Router/RouterFactory.php +++ b/administrator/com_joomgallery/src/Router/RouterFactory.php @@ -63,7 +63,7 @@ class RouterFactory extends RouterFactoryBase implements RouterFactoryInterface * * @since 4.0.0 */ - public function __construct($namespace, CategoryFactoryInterface $categoryFactory = null, DatabaseInterface $db = null) + public function __construct($namespace, ?CategoryFactoryInterface $categoryFactory = null, ?DatabaseInterface $db = null) { $this->namespace = $namespace; $this->categoryFactory = $categoryFactory; diff --git a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php index dc5675391..8bbcdc15b 100644 --- a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php +++ b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php @@ -1116,6 +1116,12 @@ public function renameCategory($cat, $foldername, $logfile='jerror'): bool // Create renamed category foldername $folder_new = \substr($folder_orig, 0, strrpos($folder_orig, \basename($folder_orig))) . $foldername; + if($folder_new == $folder_orig) + { + // No renaming of folders needed + continue; + } + // Rename folder try { diff --git a/administrator/com_joomgallery/src/Service/TusServer/Exception/FileNotFoundException.php b/administrator/com_joomgallery/src/Service/TusServer/Exception/FileNotFoundException.php index d0589b4bd..32b2212eb 100644 --- a/administrator/com_joomgallery/src/Service/TusServer/Exception/FileNotFoundException.php +++ b/administrator/com_joomgallery/src/Service/TusServer/Exception/FileNotFoundException.php @@ -17,7 +17,7 @@ */ class FileNotFoundException extends IOException { - public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) + public function __construct($message = null, $code = 0, ?\Exception $previous = null, $path = null) { if (null === $message) { if (null === $path) { diff --git a/administrator/com_joomgallery/src/Service/TusServer/Exception/IOException.php b/administrator/com_joomgallery/src/Service/TusServer/Exception/IOException.php index b0cfb5382..70ab4afc9 100644 --- a/administrator/com_joomgallery/src/Service/TusServer/Exception/IOException.php +++ b/administrator/com_joomgallery/src/Service/TusServer/Exception/IOException.php @@ -20,7 +20,7 @@ class IOException extends \RuntimeException implements IOExceptionInterface { private $path; - public function __construct($message, $code = 0, \Exception $previous = null, $path = null) + public function __construct($message, $code = 0, ?\Exception $previous = null, $path = null) { $this->path = $path; diff --git a/joomgallery.xml b/joomgallery.xml index d173c66f7..7028ef350 100644 --- a/joomgallery.xml +++ b/joomgallery.xml @@ -1,13 +1,13 @@ com_joomgallery - 2025-03-12 + 2025-04-03 2008 - 2025 JoomGallery::ProjectTeam GNU General Public License version 3 or later JoomGallery::ProjectTeam team@joomgalleryfriends.net https://www.joomgalleryfriends.net/ - 4.0.0 + 4.0.1 JoomGallery 4, a native gallery component for Joomla! 4.x and 5.x Joomgallery\Component\Joomgallery diff --git a/media/com_joomgallery/css/site.css b/media/com_joomgallery/css/site.css index 015a7ef8e..e0fd29683 100644 --- a/media/com_joomgallery/css/site.css +++ b/media/com_joomgallery/css/site.css @@ -1,6 +1,4 @@ :root { - --jg-color-link: #000000; - --jg-color-hover: #000000; --jg-gray-100: #f9fafb; --jg-gray-200: #eaedf0; --jg-gray-300: #dfe3e7; @@ -224,12 +222,10 @@ table.itemList th.sort-cell .form-check-input { font-family: revert; } a.jg-link:not(.btn) { - color: var(--jg-color-link); text-decoration: none; word-break: break-all; } a.jg-link:not(.btn):hover, a.jg-link:not(.btn):focus { - color: var(--jg-color-hover); text-decoration: underline 2px; } .load-more-container { diff --git a/media/com_joomgallery/joomla.asset.json b/media/com_joomgallery/joomla.asset.json index e04bbfb38..ab461cf4f 100644 --- a/media/com_joomgallery/joomla.asset.json +++ b/media/com_joomgallery/joomla.asset.json @@ -128,7 +128,7 @@ { "name": "com_joomgallery.justified", "type": "script", - "uri": "com_joomgallery/imgjust.min.js", + "uri": "com_joomgallery/imgjust/imgjust.min.js", "attributes": { "defer": true } diff --git a/media/com_joomgallery/js/.DS_Store b/media/com_joomgallery/js/.DS_Store index 5008ddfcf..4f3991708 100644 Binary files a/media/com_joomgallery/js/.DS_Store and b/media/com_joomgallery/js/.DS_Store differ diff --git a/media/com_joomgallery/js/imgjust.min.js b/media/com_joomgallery/js/imgjust.min.js deleted file mode 100644 index 8a0778a7a..000000000 --- a/media/com_joomgallery/js/imgjust.min.js +++ /dev/null @@ -1 +0,0 @@ -class ImgJust{idealHeight=150;maxRowImgs=16;rowGap=0;columnGap=0;paddingLeft=0;paddingRight=0;paddingTop=0;paddingBottom=0;reload(){let t=this.container;getComputedStyle(t);let e=this.imgs,s=t.getBoundingClientRect().width-this.paddingLeft-this.paddingRight;if(s<=0)throw"ImgJust Error: No room for images. Client width is too small.";let i=[];for(var a=0;a=p;c--){g+=i[c]+this.columnGap;let w=g-s,y=w*w+n[p-1].get(c-1);y=0;p--){let R=_-1,I=o[p].get(R);v.unshift({start:I,end:R}),_=I}for(var G of v){for(var B=0,a=G.start;a<=G.end;a++)B+=i[a],e[a].style.marginBottom=this.rowGap+"px";let L=s-(G.end-G.start)*this.columnGap,H=Math.round(this.idealHeight*L/B);for(var J=0,a=G.start;athis.reload())}} diff --git a/media/com_joomgallery/js/imgjust/README.md b/media/com_joomgallery/js/imgjust/README.md new file mode 100644 index 000000000..6b68093a5 --- /dev/null +++ b/media/com_joomgallery/js/imgjust/README.md @@ -0,0 +1,97 @@ +# ImgJust +## Credits +Thanks to the original repository of this library: +https://github.com/hikir1/ImgJust + +## Justified Image Wall Generator +Use ImgJust to create a wall of images on your website. +It's based on the LaTex paragraph justifying algorithm. +Images are scaled while maintaing their aspect ratios. +The source code is simple: it fits on 2 pages and uses +VanillaJS, so you don't have to worry about dependencies, +efficiency, or page load times. + +## Algorithm + +Given an ideal height, images are carefully put in rows +so that the width of each row is as close to the width of their +container as possible. Then the rows are scaled to fit their +container perfectly. + +## Usage + +Include the source code in your web page: +```html + +``` + +Once the page has loaded, use the ImgJust constructor, +passing in the container for the wall, an array +of HTMLImageElement objects, and an optional options object: +```js +addEventListener("load", _ => { + const container = document.querySelector(".imgjust"); + const imgs = document.querySelectorAll(".imgjust img"); + const options = { + idealHeight: 150, + }; + const imgjust = new ImgJust(container, imgs, options); +}); +``` + +## Options & Defaults + +- idealHeight: 150 +- rowGap = 0 +- columnGap = 0 +- paddingLeft = 0 +- paddingRight = 0 +- paddingTop = 0 +- paddingBottom = 0 + +## ImgJust Public Methods + +- `constructor(container, imgs=[], options={})` +- `reload()` +- `addImages(imgs)` + +## Styling + +Style the components normally using CSS. + +*Note: if adding space, make sure to account for +it by changing the above mentioned padding and gap options.* + +*Another Note: You may want to set the root `overflow-y` +property to `scroll` so that the width of the ImgJust +container doesn't change after being rendered.* + +```css +:root { + overflow-y: scroll; +} +.imgjust { + background: red; +} +.imgjust img { + border: 1px solid black; +} +``` + +## Nested Elements + +The DOM is not altered in any way, +so images can safely be placed in other +elements. + +```html +
+ + + + + + + ... +
+``` diff --git a/media/com_joomgallery/js/imgjust/imgjust.js b/media/com_joomgallery/js/imgjust/imgjust.js new file mode 100644 index 000000000..b1b40201b --- /dev/null +++ b/media/com_joomgallery/js/imgjust/imgjust.js @@ -0,0 +1,149 @@ +class ImgJust { + idealHeight = 150 + rowGap = 0 + columnGap = 0 + paddingLeft = 0 + paddingRight = 0 + paddingTop = 0 + paddingBottom = 0 + reload() { + const container = this.container; + const computedStyle = getComputedStyle(container); + const imgs = this.imgs; + const containerWidth = container.getBoundingClientRect().width + - this.paddingLeft - this.paddingRight; + if (containerWidth <= 0) + throw "ImgJust Error: No room for images. Client width is too small." + const idealImgWidths = []; + + // Compute ideal + for (var i = 0; i < imgs.length; i++) + idealImgWidths.push(imgs[i].naturalWidth * this.idealHeight / imgs[i].naturalHeight); + + const rowBadness = []; + const rowStart = []; + + // Complete first row + var badness = new Map(); + var start = new Map(); + var totalWidth = -this.columnGap; + for (var end = 0; end < imgs.length; end++) { + totalWidth += idealImgWidths[end] + this.columnGap; + const dif = totalWidth - containerWidth; + badness.set(end, dif * dif); + start.set(end, 0); + } + rowBadness.push(badness); + rowStart.push(start); + + // All other rows + for (var row = 1; row < imgs.length; row++) { + badness = new Map(); + start = new Map(); + + // for each possible terminating image + for (var end = row; end < imgs.length; end++) { + + // compute best starting image + totalWidth = idealImgWidths[end]; + const dif = totalWidth - containerWidth; + var minBad = dif * dif + rowBadness[row - 1].get(end - 1); + var bestBeg = end; + for (var beg = end - 1; beg >= row; beg--) { + totalWidth += idealImgWidths[beg] + this.columnGap; + const dif = totalWidth - containerWidth; + const bad = dif * dif + rowBadness[row - 1].get(beg - 1); + if (bad < minBad) { + minBad = bad; + bestBeg = beg; + } + } + badness.set(end, minBad); + start.set(end, bestBeg); + } + rowBadness.push(badness); + rowStart.push(start); + } + + // Find best terminating row + var row = 0; + var bestLastRow = row; + var minBad = rowBadness[row].get(imgs.length - 1); + for (row++; row < rowBadness.length; row++) { + const bad = rowBadness[row].get(imgs.length - 1); + if (bad < minBad) { + minBad = bad; + bestLastRow = row; + } + } + + // Assign start and stop index to each row + var rowRanges = []; + var prevRowStart = imgs.length; + for (var row = bestLastRow; row >= 0; row--) { + const end = prevRowStart - 1; + const start = rowStart[row].get(end); + rowRanges.unshift({start: start, end: end}); + prevRowStart = start; + } + + // Format images + for (var range of rowRanges) { + var totalImgWidth = 0; + for (var i = range.start; i <= range.end; i++) { + totalImgWidth += idealImgWidths[i]; + imgs[i].style.marginBottom = this.rowGap + "px"; + } + const containerWidthSpace = containerWidth - (range.end - range.start) * this.columnGap; + const newHeight = Math.round(this.idealHeight * containerWidthSpace / totalImgWidth); + var newRowWidth = 0; + for (var i = range.start; i < range.end; i++) { + const newWidth = Math.round(idealImgWidths[i] * containerWidthSpace / totalImgWidth); + newRowWidth += newWidth + this.columnGap; + imgs[i].style.width = newWidth + "px"; + imgs[i].style.height = newHeight + "px"; + imgs[i].style.marginRight = this.columnGap + "px"; + } + imgs[range.end].style.width = containerWidth - newRowWidth + "px"; + imgs[range.end].style.height = newHeight + "px"; + imgs[range.end].style.marginRight = "0"; + } + const lastRange = rowRanges[rowRanges.length - 1]; + for (var i = lastRange.start; i <= lastRange.end; i++) + imgs[i].style.marginBottom = "0"; + + // All done. Now make visible. + for (const img of imgs) + img.style.display = "block"; + } + + addImages(imgs) { + for (const img of imgs) { + img.style.display = "none"; + this.imgs.push(img); + } + } + + constructor(container, imgs = [], options = {}) { + for (var [key, val] of Object.entries(options)) + this[key] = val; + if (!container) + throw "ImgJust Error: missing first argument in constructor: container" + if (!imgs) + throw "ImgJust Error: missing second argument in constructor: imgs" + this.container = container; + this.imgs = []; + + container.style.display = "flex"; + container.style.flexFlow = "row wrap"; + container.style.paddingTop = this.paddingTop + "px"; + container.style.paddingBottom = this.paddingBottom + "px"; + container.style.paddingLeft = this.paddingLeft + "px"; + container.style.paddingRight = this.paddingRight + "px"; + + this.addImages(imgs); + this.reload() + + addEventListener("resize", _ => this.reload()); + } +} \ No newline at end of file diff --git a/media/com_joomgallery/js/imgjust/imgjust.min.js b/media/com_joomgallery/js/imgjust/imgjust.min.js new file mode 100644 index 000000000..c9db0972d --- /dev/null +++ b/media/com_joomgallery/js/imgjust/imgjust.min.js @@ -0,0 +1 @@ +class ImgJust{idealHeight=150;rowGap=0;columnGap=0;paddingLeft=0;paddingRight=0;paddingTop=0;paddingBottom=0;reload(){let t=this.container;getComputedStyle(t);let e=this.imgs,i=t.getBoundingClientRect().width-this.paddingLeft-this.paddingRight;if(i<=0)throw"ImgJust Error: No room for images. Client width is too small.";let s=[];for(var n=0;n=p;c--){h+=s[c]+this.columnGap;let y=h-i,$=y*y+r[p-1].get(c-1);$=0;p--){let G=_-1,R=o[p].get(G);x.unshift({start:R,end:G}),_=R}for(var B of x){for(var I=0,n=B.start;n<=B.end;n++)I+=s[n],e[n].style.marginBottom=this.rowGap+"px";let H=i-(B.end-B.start)*this.columnGap,J=Math.round(this.idealHeight*H/I);for(var L=0,n=B.start;nthis.reload())}} \ No newline at end of file diff --git a/media/com_joomgallery/js/joomgrid.js b/media/com_joomgallery/js/joomgrid.js index 1cb132458..a466fd4ab 100644 --- a/media/com_joomgallery/js/joomgrid.js +++ b/media/com_joomgallery/js/joomgrid.js @@ -79,7 +79,6 @@ var callback = function() { const imgs = document.querySelectorAll('.' + window.joomGrid.gridclass + ' img'); const options = { idealHeight: window.joomGrid.justified.height, - maxRowImgs: 16, rowGap: window.joomGrid.justified.gap, columnGap: window.joomGrid.justified.gap, };