Skip to content

Commit

Permalink
Initial upload
Browse files Browse the repository at this point in the history
  • Loading branch information
maloja committed Feb 5, 2020
1 parent 6683fdc commit 50492ad
Show file tree
Hide file tree
Showing 25 changed files with 4,923 additions and 0 deletions.
229 changes: 229 additions & 0 deletions PicoFotofolder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
<?php
/**
* PicoFotofolder. A Mansory/Lightbox plugin for galleries
* Edited December 2019 by Maloja
*
* @author Maloja
* @license http://opensource.org/licenses/MIT The MIT License
* @link http://github.com/maloja/pico-fotofolder
*
*/
class PicoFotofolder extends AbstractPicoPlugin {

const API_VERSION = 2;
protected $enabled = true;
protected $dependsOn = array();

/**
* This private variables
*/
private $p_keyword = 'fotofolder';
private $p_count = 0;
private $image_src = array();

/**
*
* Triggered after Pico has prepared the raw file contents for parsing
*/
public function onContentParsed(&$content) {
$content = preg_replace_callback( '/<p>\s*\(%\s+' . $this->p_keyword .'\s*\(\s*(.*?)\s*\)\s+%\)\s*<\/p>/', function($match) {

if ($match[1]) {
list ($this->image_src['path'],
$this->image_src['sort'],
$this->image_src['order']) = explode(',', str_replace('"', '', $match[1]));

$this->image_src['path'] = trim($this->image_src['path']);
$this->image_src['sort'] = trim($this->image_src['sort']);
$this->image_src['order'] = trim($this->image_src['order']);
if ($this->image_src['sort'] == "") $this->image_src['sort'] = 'name';
if ($this->image_src['order'] == "") $this->image_src['order'] = 'dsc';

$img_metas = $this->readMetaArray();

if (count($img_metas) > 0) {
$out = $this->createOutput($img_metas);
$this->p_count++;
}
}
return $out;
}, $content);
}


/**
* Triggered after Pico has rendered the page
*/
public function onPageRendered(&$output ) {
// add required elements in head tag
if ($this->p_count > 0) {
$jsh = ' <!-- Fotofolder Elements -->' . "\n";
$jsh .= ' <link href="' . $this->getConfig('plugins_url') . 'PicoFotofolder/assets/css/fotofolder.css" rel="stylesheet">' . "\n";
$jsh .= ' <script src="' . $this->getConfig('plugins_url') . 'PicoFotofolder/vendor/lazyload/dist/lazyload.min.js"></script>' . "\n";
$jsh .= ' <link href="' . $this->getConfig('plugins_url') . 'PicoFotofolder/vendor/baguettebox/dist/baguetteBox.css" rel="stylesheet">' . "\n";
$jsh .= ' <script src="' . $this->getConfig('plugins_url') . 'PicoFotofolder/vendor/baguettebox/dist/baguetteBox.min.js"></script>' . "\n";
$jsh .= '</head>' . "\n" . '<body>' . "\n";
$output = preg_replace('/\\<\\/head\\>\s*\n\s*\\<body\\>/', $jsh, $output, 1);

// Add LazyLoad
$jsh = '<script>' . "\n";
$jsh .= ' var lazyLoadInstance = new LazyLoad({ ' . "\n";
$jsh .= ' elements_selector: ".lazy", ' . "\n";
$jsh .= ' load_delay: 500' . "\n";
$jsh .= ' });' . "\n";
$jsh .= '</script>' . "\n";
$jsh .= '</body>' . "\n" . '</html>' . "\n";
$output = preg_replace('/\\<\\/body\\>\s*\n\s*\\<\/html\\>/', $jsh, $output, 1);
}
}


/***************************************************************
* Private Functions
*/

/***************************************************************/
private function readMetaArray() {
$dir = $_SERVER['DOCUMENT_ROOT'] . $this->image_src['path'];
$img_metas = array();
$pattern = '{,.}*.{[jJ][pP][gG],[jJ][pP][eE][gG],[pP][nN][gG],[gG][iI][fF],dat}';
$filelist = glob($dir . '/' . $pattern, GLOB_BRACE);
usort($filelist, create_function('$a,$b', 'return filemtime($b) - filemtime($a);'));

//check if metafile is still up to date or if we have to create a new one
if (strpos($filelist[0], '.fotofolder.dat') == true) {
$string_data = file_get_contents($filelist[0]);
$img_metas = unserialize($string_data);
}
//ok recreate it
else {
foreach ($filelist as $img) {
if (strpos($img, '.fotofolder.dat') == false) {
list($width, $height, $type, $attr) = getimagesize($img);
$exif = (exif_read_data($img, 0, true));
$url = str_replace($_SERVER['DOCUMENT_ROOT'], '', $img);
$img_name = pathinfo($img, PATHINFO_BASENAME);

// handle thumbnails
if (!file_exists( $_SERVER['DOCUMENT_ROOT'] . $this->image_src['path'] . '/thumbnails' )) {
mkdir( $_SERVER['DOCUMENT_ROOT'] . $this->image_src['path'] . '/thumbnails', 0777, true);
}
$thumb_name = $_SERVER['DOCUMENT_ROOT'] . $this->image_src['path'] . '/thumbnails/thumb_' . $img_name;
if ( !file_exists($thumb_name) ) {
$this->scaleImageCopy($img, $thumb_name, 300, 300);
}
elseif ( filemtime($thumb_name) < filemtime($img) ) {
$this->scaleImageCopy($img, $thumb_name, 300, 300);
}
if ($width > $height) $format = 'landscape';
if ($width < $height) $format = 'portrait';
if ( abs( 1 - $width / $height) > 0.8 ) $format = 'square';


$thumb_date = filemtime($thumb_name);
$thumb_url = $this->image_src['path'] . '/thumbnails/thumb_' . $img_name;

array_push( $img_metas, array( 'filename' => $img,
'url' => $url,
'imgname' => $img_name,
'date' => $exif['FILE']['FileDateTime'],
'width' => $width,
'height' => $height,
'type' => $type,
'attr' => $attr,
'format' => $format,
'thumb_url' => $thumb_url,
'thumb_date' => $thumb_date ));

}
}
$string_data = serialize($img_metas);
file_put_contents($_SERVER['DOCUMENT_ROOT'] . $this->image_src['path'] . '/.fotofolder.dat', $string_data);
}
return($img_metas);
}

/***************************************************************/
private function createOutput($img_metas) {

if ( $image_src['order'] == 'asc') {
usort($img_metas, function($a, $b) {
return $a[$this->image_src['sort']] <=> $b[$this->image_src['sort']];
});
}
else {
usort($img_metas, function($a, $b) {
return $b[$this->image_src['sort']] <=> $a[$this->image_src['sort']];
});
}

$out = '<div class="mgrid baguette_' . $this->p_count . '">' . "\n";
foreach ($img_metas as $pic) {
$out .= " <a href=\"{$pic['url']}\" class=\"mgrid-item {$pic['format']}\"> \n";
$out .= " <img class=\"lazy\" data-src=\"{$pic['thumb_url']}\" src=\"#\" alt=\" \">\n";
$out .= " <div class=\"zoomicon\" style=\"background-image: url('{$this->getConfig('plugins_url')}PicoFotofolder/assets/circleplus.png')\"> </div> \n";
$out .= ' </a>' . "\n";
}
$out .= "</div>\n";

$out .= "<script>\n";
$out .= " baguetteBox.run('.baguette_{$this->p_count}', { \n";
$out .= " fullScreen: true, \n";
$out .= " });\n";
$out .= "</script>\n";
return $out;
}




/**
* Resize image - preserve ratio of width and height.
* @param string $sourceImage path to source JPEG image
* @param string $targetImage path to final JPEG image file
* @param int $maxWidth maximum width of final image (value 0 - width is optional)
* @param int $maxHeight maximum height of final image (value 0 - height is optional)
* @param int $quality quality of final image (0-100)
* @return bool
*/
private function scaleImageCopy($sourceImage, $targetImage, $maxWidth, $maxHeight, $quality = 80) {
// Obtain image from given source file.
if (!$image = @imagecreatefromjpeg($sourceImage)) {
return false;
}

// Get dimensions of source image.
list($origWidth, $origHeight) = getimagesize($sourceImage);

if ($maxWidth == 0) $maxWidth = $origWidth;
if ($maxHeight == 0) $maxHeight = $origHeight;

// do not grow the image
if ( ($origWidth < $maxWidth) && ($origHeight < $maxHeight) ) {
$maxWidth = $origWidth;
$maxHeight = $origHeight;
}

// Calculate ratio of desired maximum sizes and original sizes.
$widthRatio = $maxWidth / $origWidth;
$heightRatio = $maxHeight / $origHeight;

// Ratio used for calculating new image dimensions.
$ratio = min($widthRatio, $heightRatio);

// Calculate new image dimensions.
$newWidth = (int)$origWidth * $ratio;
$newHeight = (int)$origHeight * $ratio;

// Create final image with new dimensions.
$newImage = imagecreatetruecolor($newWidth, $newHeight);
imagecopyresampled($newImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $origWidth, $origHeight);
imagejpeg($newImage, $targetImage, $quality);

// Free up the memory.
imagedestroy($image);
imagedestroy($newImage);

return true;
}
}
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# PicoFotofolder

A masonry like gallery for [Pico CMS](http://picocms.org) including a fullscreen and lazyload capabilities. It will display all images within a folder as a gallery. It is based on the two small "vanilla" JavaScripts [baguetteBox](https://github.com/feimosi/baguetteBox.js) and [Lazyload](https://github.com/verlok/lazyload).

## Screenshot

![Screenshot](images/pico-fotofolder-screenshot.png "Fotofolder Screenshot")

## Installation

Copy the files from Github https://github.com/maloja/pico-fotofolder into your Pico CMS plugins folder `plugins/PicoFotofolder`.

or clone directly from Github in `plugins/PicoFotofolder`

cd plugins
git clone https://github.com/maloja/pico-fotofolder

or, if you installed Pico CMS with composer

composer require maloja/pico-fotofolder

## Usage

Add the following expression in your Markdown file:

(% fotofolder ( /path/to/your/images [sort] [order] %)

Optional arguments:
- `[sort]` Can be 'date or 'name'. This will sort the images according date, which means the exif image date not the file date, or according the filename. Default is 'name'.

- `[order]` Can be 'asc' or 'dsc'. Ascending or descending sort order. Default is 'dsc'

### Caching

In order to increase page loading speed for PicoFotofolder, an additional subfolder `/thumbnails` for thumbnails is automatically created in your image folder. Therefore, make sure that write rights are granted for your image folder. A hidden file ".fotofolder.dat" is also created. This file stores meta information of your images. The subfolder `/thumbnails` as well as the file" .fotofolder.dat "are created automatically and can be deleted at any time if incorrect information is displayed in the gallery.
Binary file added assets/circleplus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions assets/css/fotofolder.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.mgrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); /* Make columns adjust according to the available viewport */
grid-gap: 10px;
grid-auto-rows: 260px; /*Set the height for implicitly-created row track */
grid-auto-flow: dense;
height: fit-content;
}


@media screen and (max-width: 820) {
.mgrid {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
grid-auto-rows: 160px;
}
}

.mgrid-item.landscape {
grid-column-end: span 2;
}
.mgrid-item.portrait {
grid-row-end: span 1;
}

.mgrid-item {
position: relative;
overflow: hidden;
border-radius: 3px;
box-sizing: border-box;
}

.mgrid-item img {
height: 100%;
width: 100%;
object-fit: cover;
}

.zoomicon {
position: absolute;
top: 50%;
left: 50%;
margin: -24px auto 0 -24px;
width: 48px;
height:48px;
z-index: 10;
background-repeat: no-repeat;
display: none;
}

.mgrid-item:hover img {
cursor: pointer;
filter: brightness(50%);
}

.mgrid-item:hover .zoomicon {
display: block;
}
26 changes: 26 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "maloja/pico-fotofolder",
"type": "pico-plugin",
"description": "A masonry like gallery for Pico CMS",
"keywords": [ "pico", "picocms", "picocms-plugin", "pico-fotofolder", "gallery" ],
"homepage": "https://github.com/maloja/pico-fotofolder",
"license": "MIT",
"authors": [
{
"name": "Maloja",
"homepage": "https://github.com/maloja/pico-fotofolder",
"role": "Lead Developer"
}
],
"support": {
"docs": "http://picocms.org/docs/pico-http-params/",
"issues": "https://github.com/maloja/pico-fotofolder/issues",
"source": "https://github.com/maloja/pico-fotofolder"
},
"require": {
"php": ">=5.3.6"
},
"autoload": {
"classmap": [ "PicoFotofolder.php" ]
}
}
Binary file added images/pico-fotofolder-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions vendor/baguettebox/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2017 Marek Grzybek

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading

0 comments on commit 50492ad

Please sign in to comment.