<?php

/**
 *  Methods used with the {@link resize()} method.
 */
define('ZEBRA_IMAGE_BOXED', 0);
define('ZEBRA_IMAGE_NOT_BOXED', 1);
define('ZEBRA_IMAGE_CROP_TOPLEFT', 2);
define('ZEBRA_IMAGE_CROP_TOPCENTER', 3);
define('ZEBRA_IMAGE_CROP_TOPRIGHT', 4);
define('ZEBRA_IMAGE_CROP_MIDDLELEFT', 5);
define('ZEBRA_IMAGE_CROP_CENTER', 6);
define('ZEBRA_IMAGE_CROP_MIDDLERIGHT', 7);
define('ZEBRA_IMAGE_CROP_BOTTOMLEFT', 8);
define('ZEBRA_IMAGE_CROP_BOTTOMCENTER', 9);
define('ZEBRA_IMAGE_CROP_BOTTOMRIGHT', 10);

// this enables handling of partially broken JPEG files without warnings/errors
ini_set('gd.jpeg_ignore_warning', true);

/**
 *  A compact, lightweight, object-oriented image manipulation library written in and for PHP, that provides methods
 *  for performing several types of image manipulation operations. It doesn't require any external libraries other than
 *  the GD2 extension (with which PHP usually comes precompiled with).
 *
 *  The code is heavily commented and generates no warnings/errors/notices when PHP's error reporting level is set to
 *  E_ALL.
 *
 *  With this library you can rescale, flip, rotate and crop images. It supports loading and saving images in the GIF,
 *  JPEG and PNG formats and preserves transparency for GIF, PNG and PNG24.
 *
 *  The cool thing about it is that it can resize images to exact given width and height and still maintain aspect
 *  ratio.
 *
 *  Visit {@link http://stefangabos.ro/php-libraries/zebra-image/} for more information.
 *
 *  For more resources visit {@link http://stefangabos.ro/}
 *
 * @author     Stefan Gabos <contact@stefangabos.ro>
 * @version    2.2.3 (last revision: July 14, 2013)
 * @copyright  (c) 2006 - 2013 Stefan Gabos
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
 * @package    Zebra_Image
 */
class Zebra_Image
{

    /**
     *  Indicates the file system permissions to be set for newly created images.
     *
     *  Better is to leave this setting as it is.
     *
     *  If you know what you are doing, here is how you can calculate the permission levels:
     *
     *  - 400 Owner Read
     *  - 200 Owner Write
     *  - 100 Owner Execute
     *  - 40 Group Read
     *  - 20 Group Write
     *  - 10 Group Execute
     *  - 4 Global Read
     *  - 2 Global Write
     *  - 1 Global Execute
     *
     *  Default is 0755
     *
     * @var integer
     */
    var $chmod_value;

    /**
     *  If set to FALSE, images having both width and height smaller than the required width and height, will be left
     *  untouched ({@link jpeg_quality} and {@link png_compression} will still apply).
     *
     *  Available only for the {@link resize()} method
     *
     *  Default is TRUE
     *
     * @var boolean
     */
    var $enlarge_smaller_images;

    /**
     *  In case of an error read this property's value to see the error's code.
     *
     *  Possible error codes are:
     *
     *  - 1:  source file could not be found
     *  - 2:  source file is not readable
     *  - 3:  could not write target file
     *  - 4:  unsupported source file format
     *  - 5:  unsupported target file format
     *  - 6:  GD library version does not support target file format
     *  - 7:  GD library is not installed!
     *  - 8:  "chmod" command is disabled via configuration
     *
     *  Default is 0 (no error).
     *
     * @var integer
     */
    var $error;

    /**
     *  Indicates the quality of the output image (better quality means bigger file size).
     *
     *  Used only if the file at {@link target_path} is a JPG/JPEG image.
     *
     *  Range is 0 - 100
     *
     *  Default is 85
     *
     * @var integer
     */
    var $jpeg_quality;

    /**
     *  Indicates the compression level of the output image (lower compression means bigger file size).
     *
     *  Available only if PHP version is 5.1.2+, and only if the file at {@link target_path} is a PNG image. It will be
     *  ignored otherwise.
     *
     *  Range is 0 - 9
     *
     *  Default is 9
     *
     * @since 2.2
     *
     * @var integer
     */
    var $png_compression;

    /**
     *  Specifies whether, upon resizing, images should preserve their aspect ratio.
     *
     *  Available only for the {@link resize()} method
     *
     *  Default is TRUE
     *
     * @var boolean
     */
    var $preserve_aspect_ratio;

    /**
     *  Indicates whether a target files should preserve the source file's date/time.
     *
     *  Default is TRUE
     *
     * @since 1.0.4
     *
     * @var boolean
     */
    var $preserve_time;

    /**
     *  Indicates whether the target image should have a "sharpen" filter applied to it.
     *
     *  Can be very useful when creating thumbnails and should be used only when creating thumbnails.
     *
     *  The sharpen filter relies on the "imageconvolution" PHP function which is available only for PHP version
     *  5.1.0+, and will leave the images unaltered for older versions!
     *
     *  Default is FALSE
     *
     * @since 2.2
     *
     * @var boolean
     */
    var $sharpen_images;

    /**
     *  Path to an image file to apply the transformations to.
     *
     *  Supported file types are GIF, PNG and JPEG.
     *
     * @var    string
     */
    var $source_path;

    /**
     *  Path (including file name) to where to save the transformed image.
     *
     *  Can be a different than {@link source_path} - the type of the transformed image will be as indicated by the
     *  file's extension (supported file types are GIF, PNG and JPEG).
     *
     * @var    string
     */
    var $target_path;

    /**
     *  Constructor of the class.
     *
     *  Initializes the class and the default properties
     *
     * @return void
     */
    function Zebra_Image()
    {

        // set default values for properties
        $this->chmod_value = 0755;

        $this->error = 0;

        $this->jpeg_quality = 85;

        $this->png_compression = 9;

        $this->preserve_aspect_ratio = $this->preserve_time = $this->enlarge_smaller_images = true;

        $this->sharpen_images = false;

        $this->source_path = $this->target_path = '';

    }

    /**
     *  Applies one or more filters to the image given as {@link source_path} and outputs it as the file specified as
     *  {@link target_path}.
     *
     *  <samp>This method is available only if the {@link http://php.net/manual/en/function.imagefilter.php imagefilter}
     *  function is available (available from PHP 5+), and will leave images unaltered otherwise.</samp>
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // apply the "grayscale" filter
     *  $img->apply_filter('grayscale');
     *
     *  // apply the "contrast" filter
     *  $img->apply_filter('contrast', -20);
     *
     *
     *  You can also apply multiple filters at once. In this case, the method requires a single argument, an array of
     *  arrays, containing the filters and associated arguments, where applicable:
     *
     *
     *  // create a sepia effect
     *  // note how we're applying multiple filters at once
     *  // each filter is in its own array
     *  $img->apply_filter(array(
     *
     *      // first we apply the "grayscale" filter
     *      array('grayscale'),
     *
     *      // then we apply the "colorize" filter with 90, 60, 40 as
     *      // the values for red, green and blue
     *      array('colorize', 90, 60, 40),
     *
     *  ));
     *
     *
     * @param  string $filter The (case-insensitive) name of the filter to apply. Can be one of the following:
     *
     *                              -   brightness       -   changes the brightness of the image; use arg1
     *                                                              to set the level of brightness; the range of brightness
     *                                                              is -255 to 255;
     *                              -   colorize         -   adds (subtracts) specified RGB values to each pixel;
     *                                                              use arg1, arg2 and arg3 in the
     *                                                              form of red, green, blue and arg4 for the alpha
     *                                                              channel. the range for each color is -255 to 255 and
     *                                                              0 to 127 for alpha; alpha support is available only
     *                                                              for PHP 5.2.5+;
     *                              -   contrast         -   changes the contrast of the image; use arg1
     *                                                              to set the level of contrast; the range of contrast
     *                                                              is -100 to 100;
     *                              -   gausian_blur     -   blurs the image using the Gaussian method;
     *                              -   grayscale        -   converts the image into grayscale;
     *                              -   edgedetect       -   uses edge detection to highlight the edges in the image;
     *                              -   emboss           -   embosses the image;
     *                              -   mean_removal     -   uses mean removal to achieve a "sketchy" effect;
     *                              -   negate           -   reverses all the colors of the image;
     *                              -   pixelate         -   applies pixelation effect to the image, use arg1
     *                                                              to set the block size and arg2 to set the
     *                                                              pixelation effect mode; this filter is available
     *                                                              only for PHP 5.3.0+;
     *                              -   selective_blur   -   blurs the image;
     *                              -   smooth           -   makes the image smoother. Use arg1 to set the
     *                                                              level of smoothness. applies a 9-cell convolution matrix
     *                                                              where center pixel has the weight of arg1 and
     *                                                              others weight of 1.0. the result is normalized by dividing
     *                                                              the sum with arg1 + 8.0 (sum of the matrix).
     *                                                              any float is accepted;
     *
     * @param  mixed $arg1 Used by the following filters:
     *                              -   brightness       -   sets the brightness level (-255 to 255)
     *                              -   contrast         -   sets the contrast level (-100 to 100)
     *                              -   colorize         -   sets the value of the red component (-255 to 255)
     *                              -   smooth           -   sets the smoothness level
     *                              -   pixelate         -   sets the block size, in pixels
     *
     * @param  mixed $arg2 Used by the following filters:
     *                              -   colorize         -   sets the value of the green component (-255 to 255)
     *                              -   pixelate         -   whether to use advanced pixelation effect or not (defaults to FALSE).
     *
     * @param  mixed $arg3 Used by the following filters:
     *                              -   colorize         -   sets the value of the blue component (-255 to 255)
     *
     * @param  mixed $arg4 Used by the following filters:
     *                              -   colorize         -   alpha channel; a value between 0 and 127. 0 indicates
     *                                                              completely opaque while 127 indicates completely
     *                                                              transparent.
     *
     * @since 2.2.2
     *
     * @return boolean             Returns TRUE on success or FALSE on error.
     *
     *                              If {@link http://php.net/manual/en/function.imagefilter.php imagefilter} is not
     *                              available the method will return FALSE without setting an {@link error} code.
     *
     *                              If the requested filter doesn't exist, or invalid arguments are passed, the method
     *                              will trigger a warning.
     *
     *                              If FALSE is returned and you are sure that
     *                              {@link http://php.net/manual/en/function.imagefilter.php imagefilter} exists and that
     *                              the requested filter is valid, check the {@link error} property to see the error code.
     */
    function apply_filter($filter, $arg1 = '', $arg2 = '', $arg3 = '', $arg4 = '')
    {

        // if "imagefilter" function exists and the requested filter exists
        if (function_exists('imagefilter')) // if image resource was successfully created
        {
            if ($this->_create_from_source()) {

                // prepare the target image
                $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);

                // copy the original image
                imagecopyresampled(

                    $target_identifier,
                    $this->source_identifier,
                    0,
                    0,
                    0,
                    0,
                    $this->source_width,
                    $this->source_height,
                    $this->source_width,
                    $this->source_height

                );

                // if multiple filters are to be applied at once
                if (is_array($filter)) {

                    // iterate through the filters
                    foreach ($filter as $arguments) // if filter exists
                    {
                        if (defined('IMG_FILTER_' . strtoupper($arguments[0]))) {

                            // try to apply the filter...
                            if (!@call_user_func_array(
                                'imagefilter',
                                array_merge(
                                    array($target_identifier, constant('IMG_FILTER_' . strtoupper($arguments[0]))),
                                    array_slice($arguments, 1)
                                )
                            )
                            ) // ...and trigger an error if the filter could not be applied
                            {
                                trigger_error(
                                    'Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter',
                                    E_USER_WARNING
                                );
                            }

                            // if filter doesn't exists, trigger an error
                        } else {
                            trigger_error(
                                'Filter "' . strtoupper($arguments[0]) . '" is not available',
                                E_USER_WARNING
                            );
                        }
                    }

                    // if a single filter is to be applied and it is available
                } elseif (defined('IMG_FILTER_' . strtoupper($filter))) {

                    // get all the arguments passed to the method
                    $arguments = func_get_args();

                    // try to apply the filter...
                    if (!@call_user_func_array(
                        'imagefilter',
                        array_merge(
                            array($target_identifier, constant('IMG_FILTER_' . strtoupper($filter))),
                            array_slice($arguments, 1)
                        )
                    )
                    ) // ...and trigger an error if the filter could not be applied
                    {
                        trigger_error(
                            'Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter',
                            E_USER_WARNING
                        );
                    }

                    // if filter doesn't exists, trigger an error
                } else {
                    trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
                }

                // write image
                return $this->_write_image($target_identifier);

            }
        }

        // if script gets this far, return false
        // note that we do not set the error level as it has been already set
        // by the _create_from_source() method earlier, if the case
        return false;

    }

    /**
     *  Crops a portion of the image given as {@link source_path} and outputs it as the file specified as {@link target_path}.
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // crop a rectangle of 100x100 pixels, starting from the top-left corner
     *  $img->crop(0, 0, 100, 100);
     *
     *
     * @param  integer $start_x x coordinate to start cropping from
     *
     * @param  integer $start_y y coordinate to start cropping from
     *
     * @param  integer $end_x x coordinate where to end the cropping
     *
     * @param  integer $end_y y coordinate where to end the cropping
     *
     * @since  1.0.4
     *
     * @return boolean     Returns TRUE on success or FALSE on error.
     *
     *                      If FALSE is returned, check the {@link error} property to see the error code.
     */
    function crop($start_x, $start_y, $end_x, $end_y)
    {

        // this method might be also called internally
        // in this case, there's a fifth argument that points to an already existing image identifier
        $args = func_get_args();

        // if fifth argument exists
        if (isset($args[4]) && is_resource($args[4])) {

            // that it is the image identifier that we'll be using further on
            $this->source_identifier = $args[4];

            // set this to true so that the script will continue to execute at the next IF
            $result = true;

            // if method is called as usually
            // try to create an image resource from source path
        } else {
            $result = $this->_create_from_source();
        }

        // if image resource was successfully created
        if ($result !== false) {

            // prepare the target image
            $target_identifier = $this->_prepare_image($end_x - $start_x, $end_y - $start_y, -1);

            // crop the image
            imagecopyresampled(

                $target_identifier,
                $this->source_identifier,
                0,
                0,
                $start_x,
                $start_y,
                $end_x - $start_x,
                $end_y - $start_y,
                $end_x - $start_x,
                $end_y - $start_y

            );

            // write image
            return $this->_write_image($target_identifier);

        }

        // if script gets this far, return false
        // note that we do not set the error level as it has been already set
        // by the _create_from_source() method earlier
        return false;

    }

    /**
     *  Flips both horizontally and vertically the image given as {@link source_path} and outputs the resulted image as
     *  {@link target_path}
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // flip the image both horizontally and vertically
     *  $img->flip_both();
     *
     *
     * @since 2.1
     *
     * @return boolean     Returns TRUE on success or FALSE on error.
     *
     *                      If FALSE is returned, check the {@link error} property to see the error code.
     */
    function flip_both()
    {

        return $this->_flip('both');

    }

    /**
     *  Flips horizontally the image given as {@link source_path} and outputs the resulted image as {@link target_path}
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // flip the image horizontally
     *  $img->flip_horizontal();
     *
     *
     * @return boolean     Returns TRUE on success or FALSE on error.
     *
     *                      If FALSE is returned, check the {@link error} property to see the error code.
     */
    function flip_horizontal()
    {

        return $this->_flip('horizontal');

    }

    /**
     *  Flips vertically the image given as {@link source_path} and outputs the resulted image as {@link target_path}
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // flip the image vertically
     *  $img->flip_vertical();
     *
     *
     * @return boolean     Returns TRUE on success or FALSE on error.
     *
     *                      If FALSE is returned, check the {@link error} property to see the error code.
     */
    function flip_vertical()
    {

        return $this->_flip('vertical');

    }

    /**
     *  Resizes the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // apply a "sharpen" filter to the resulting images
     *  $img->sharpen_images = true;
     *
     *  // resize the image to exactly 150x150 pixels, without altering aspect ratio, by using the CROP_CENTER method
     *  $img->resize(150, 150, ZEBRA_IMAGE_CROP_CENTER);
     *
     *
     * @param  integer $width The width to resize the image to.
     *
     *                                          If set to 0, the width will be automatically adjusted, depending
     *                                          on the value of the height argument so that the image preserves
     *                                          its aspect ratio.
     *
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
     *                                          height arguments are values greater than 0, the image will
     *                                          be resized to the exact required width and height and the aspect ratio
     *                                          will be preserved - (also see the description for the method
     *                                          argument below on how can this be done).
     *
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
     *                                          resized to the required width and the aspect ratio will be ignored.
     *
     *                                          If both width and height are set to 0, a copy of
     *                                          the source image will be created ({@link jpeg_quality} and
     *                                          {@link png_compression} will still apply).
     *
     *                                          If either width or height are set to 0, the script
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
     *                                          to TRUE regardless of its actual value!
     *
     * @param  integer $height The height to resize the image to.
     *
     *                                          If set to 0, the height will be automatically adjusted, depending
     *                                          on the value of the width argument so that the image preserves
     *                                          its aspect ratio.
     *
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
     *                                          width arguments are values greater than 0, the image will
     *                                          be resized to the exact required width and height and the aspect ratio
     *                                          will be preserved - (also see the description for the method
     *                                          argument below on how can this be done).
     *
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
     *                                          resized to the required height and the aspect ratio will be ignored.
     *
     *                                          If both width and height are set to 0, a copy of
     *                                          the source image will be created ({@link jpeg_quality} and
     *                                          {@link png_compression} will still apply).
     *
     *                                          If either height or width are set to 0, the script
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
     *                                          to TRUE regardless of its actual value!
     *
     * @param  int $method (Optional) Method to use when resizing images to exact width and height
     *                                          while preserving aspect ratio.
     *
     *                                          If the {@link preserve_aspect_ratio} property is set to TRUE and both the
     *                                          width and height arguments are values greater than 0,
     *                                          the image will be resized to the exact given width and height and the
     *                                          aspect ratio will be preserved by using on of the following methods:
     *
     *                                          -   ZEBRA_IMAGE_BOXED - the image will be scalled so that it will
     *                                              fit in a box with the given width and height (both width/height will
     *                                              be smaller or equal to the required width/height) and then it will
     *                                              be centered both horizontally and vertically. The blank area will be
     *                                              filled with the color specified by the bgcolor argument. (the
     *                                              blank area will be filled only if the image is not transparent!)
     *
     *                                          -   ZEBRA_IMAGE_NOT_BOXED - the image will be scalled so that it
     *                                              could fit in a box with the given width and height but will
     *                                              not be enclosed in a box with given width and height. The new width/
     *                                              height will be both smaller or equal to the required width/height
     *
     *                                          -   ZEBRA_IMAGE_CROP_TOPLEFT
     *                                          -   ZEBRA_IMAGE_CROP_TOPCENTER
     *                                          -   ZEBRA_IMAGE_CROP_TOPRIGHT
     *                                          -   ZEBRA_IMAGE_CROP_MIDDLELEFT
     *                                          -   ZEBRA_IMAGE_CROP_CENTER
     *                                          -   ZEBRA_IMAGE_CROP_MIDDLERIGHT
     *                                          -   ZEBRA_IMAGE_CROP_BOTTOMLEFT
     *                                          -   ZEBRA_IMAGE_CROP_BOTTOMCENTER
     *                                          -   ZEBRA_IMAGE_CROP_BOTTOMRIGHT
     *
     *                                          For the methods involving crop, first the image is scaled so that both
     *                                          its sides are equal or greater than the respective sizes of the bounding
     *                                          box; next, a region of required width and height will be cropped from
     *                                          indicated region of the resulted image.
     *
     *                                          Default is ZEBRA_IMAGE_CROP_CENTER
     *
     * @param  hexadecimal $background_color (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
     *                                          blank area. See the method argument.
     *
     *                                          When set to -1 the script will preserve transparency for transparent GIF
     *                                          and PNG images. For non-transparent images the background will be white
     *                                          in this case.
     *
     *                                          Default is #FFFFFF.
     *
     * @return boolean                         Returns TRUE on success or FALSE on error.
     *
     *                                          If FALSE is returned, check the {@link error} property to see what went
     *                                          wrong
     */
    function resize($width = 0, $height = 0, $method = ZEBRA_IMAGE_CROP_CENTER, $background_color = '#FFFFFF')
    {

        // if image resource was successfully created
        if ($this->_create_from_source()) {

            // if either width or height is to be adjusted automatically
            // set a flag telling the script that, even if $preserve_aspect_ratio is set to false
            // treat everything as if it was set to true
            if ($width == 0 || $height == 0) {
                $auto_preserve_aspect_ratio = true;
            }

            // if aspect ratio needs to be preserved
            if ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio)) {

                // if height is given and width is to be computed accordingly
                if ($width == 0 && $height > 0) {

                    // get the original image's aspect ratio
                    $aspect_ratio = $this->source_width / $this->source_height;

                    // the target image's height is as given as argument to the method
                    $target_height = $height;

                    // compute the target image's width, preserving the aspect ratio
                    $target_width = round($height * $aspect_ratio);

                    // if width is given and height is to be computed accordingly
                } elseif ($width > 0 && $height == 0) {

                    // get the original image's aspect ratio
                    $aspect_ratio = $this->source_height / $this->source_width;

                    // the target image's width is as given as argument to the method
                    $target_width = $width;

                    // compute the target image's height, preserving the aspect ratio
                    $target_height = round($width * $aspect_ratio);

                    // if both width and height are given and ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED methods are to be used
                } elseif ($width > 0 && $height > 0 && ($method == 0 || $method == 1)) {

                    // compute the horizontal and vertical aspect ratios
                    $vertical_aspect_ratio = $height / $this->source_height;
                    $horizontal_aspect_ratio = $width / $this->source_width;

                    // if the image's newly computed height would be inside the bounding box
                    if (round($horizontal_aspect_ratio * $this->source_height < $height)) {

                        // the target image's width is as given as argument to the method
                        $target_width = $width;

                        // compute the target image's height so that the image will stay inside the bounding box
                        $target_height = round($horizontal_aspect_ratio * $this->source_height);

                        // otherwise
                    } else {

                        // the target image's height is as given as argument to the method
                        $target_height = $height;

                        // compute the target image's width so that the image will stay inside the bounding box
                        $target_width = round($vertical_aspect_ratio * $this->source_width);

                    }

                    // if both width and height are given and image is to be cropped in order to get to the required size
                } elseif ($width > 0 && $height > 0 && $method > 1 && $method < 11) {

                    // compute the horizontal and vertical aspect ratios
                    $vertical_aspect_ratio = $this->source_height / $height;
                    $horizontal_aspect_ratio = $this->source_width / $width;

                    // we'll use one of the two
                    $aspect_ratio =

                        $vertical_aspect_ratio < $horizontal_aspect_ratio ?

                            $vertical_aspect_ratio :

                            $horizontal_aspect_ratio;

                    // compute the target image's width, preserving the aspect ratio
                    $target_width = round($this->source_width / $aspect_ratio);

                    // compute the target image's height, preserving the aspect ratio
                    $target_height = round($this->source_height / $aspect_ratio);

                    // for any other case
                } else {

                    // we will create a copy of the source image
                    $target_width = $this->source_width;
                    $target_height = $this->source_height;

                }

                // if aspect ratio does not need to be preserved
            } else {

                // compute the target image's width
                $target_width = ($width > 0 ? $width : $this->source_width);

                // compute the target image's height
                $target_height = ($height > 0 ? $height : $this->source_height);

            }

            // if
            if (

                // all images are to be resized - including images that are smaller than the given width/height
                $this->enlarge_smaller_images ||

                // smaller images than the given width/height are to be left untouched
                // but current image has at leas one side that is larger than the required width/height
                ($width > 0 && $height > 0 ?

                    ($this->source_width > $width || $this->source_height > $height) :

                    ($this->source_width > $target_width || $this->source_height > $target_height)

                )

            ) {

                // if
                if (

                    // aspect ratio needs to be preserved AND
                    ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio)) &&

                    // both width and height are given
                    ($width > 0 && $height > 0) &&

                    // images are to be cropped
                    ($method > 1 && $method < 11)

                ) {

                    // prepare the target image
                    $target_identifier = $this->_prepare_image($target_width, $target_height, $background_color);

                    imagecopyresampled(

                        $target_identifier,
                        $this->source_identifier,
                        0,
                        0,
                        0,
                        0,
                        $target_width,
                        $target_height,
                        $this->source_width,
                        $this->source_height

                    );

                    // do the crop according to the required method
                    switch ($method) {

                        // if image needs to be cropped from the top-left corner
                        case ZEBRA_IMAGE_CROP_TOPLEFT:

                            // crop accordingly
                            return $this->crop(
                                0,
                                0,
                                $width,
                                $height,
                                $target_identifier // crop this resource instead
                            );

                            break;

                        // if image needs to be cropped from the top-center
                        case ZEBRA_IMAGE_CROP_TOPCENTER:

                            // crop accordingly
                            return $this->crop(
                                floor(($target_width - $width) / 2),
                                0,
                                floor(($target_width - $width) / 2) + $width,
                                $height,
                                $target_identifier // crop this resource instead
                            );

                            break;

                        // if image needs to be cropped from the top-right corner
                        case ZEBRA_IMAGE_CROP_TOPRIGHT:

                            // crop accordingly
                            return $this->crop(
                                $target_width - $width,
                                0,
                                $target_width,
                                $height,
                                $target_identifier // crop this resource instead
                            );

                            break;

                        // if image needs to be cropped from the middle-left
                        case ZEBRA_IMAGE_CROP_MIDDLELEFT:

                            // crop accordingly
                            return $this->crop(

                                0,
                                floor(($target_height - $height) / 2),
                                $width,
                                floor(($target_height - $height) / 2) + $height,
                                $target_identifier // crop this resource instead

                            );

                            break;

                        // if image needs to be cropped from the center of the image
                        case ZEBRA_IMAGE_CROP_CENTER:

                            // crop accordingly
                            return $this->crop(

                                floor(($target_width - $width) / 2),
                                floor(($target_height - $height) / 2),
                                floor(($target_width - $width) / 2) + $width,
                                floor(($target_height - $height) / 2) + $height,
                                $target_identifier // crop this resource instead

                            );

                            break;

                        // if image needs to be cropped from the middle-right
                        case ZEBRA_IMAGE_CROP_MIDDLERIGHT:

                            // crop accordingly
                            return $this->crop(

                                $target_width - $width,
                                floor(($target_height - $height) / 2),
                                $target_width,
                                floor(($target_height - $height) / 2) + $height,
                                $target_identifier // crop this resource instead

                            );

                            break;

                        // if image needs to be cropped from the bottom-left corner
                        case ZEBRA_IMAGE_CROP_BOTTOMLEFT:

                            // crop accordingly
                            return $this->crop(

                                0,
                                $target_height - $height,
                                $width,
                                $target_height,
                                $target_identifier // crop this resource instead

                            );

                            break;

                        // if image needs to be cropped from the bottom-center
                        case ZEBRA_IMAGE_CROP_BOTTOMCENTER:

                            // crop accordingly
                            return $this->crop(

                                floor(($target_width - $width) / 2),
                                $target_height - $height,
                                floor(($target_width - $width) / 2) + $width,
                                $target_height,
                                $target_identifier // crop this resource instead

                            );

                            break;

                        // if image needs to be cropped from the bottom-right corner
                        case ZEBRA_IMAGE_CROP_BOTTOMRIGHT:

                            // crop accordingly
                            return $this->crop(

                                $target_width - $width,
                                $target_height - $height,
                                $target_width,
                                $target_height,
                                $target_identifier // crop this resource instead

                            );

                            break;

                    }

                    // if aspect ratio doesn't need to be preserved or
                    // it needs to be preserved and method is ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED
                } else {

                    // prepare the target image
                    $target_identifier = $this->_prepare_image(
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $width : $target_width),
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $height : $target_height),
                        $background_color
                    );

                    imagecopyresampled(

                        $target_identifier,
                        $this->source_identifier,
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($width - $target_width) / 2 : 0),
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($height - $target_height) / 2 : 0),
                        0,
                        0,
                        $target_width,
                        $target_height,
                        $this->source_width,
                        $this->source_height

                    );

                    // if script gets this far, write the image to disk
                    return $this->_write_image($target_identifier);

                }

                // if we get here it means that
                // smaller images than the given width/height are to be left untouched
                // therefore, we save the image as it is
            } else {
                return $this->_write_image($this->source_identifier);
            }

        }

        // if script gets this far return false
        // note that we do not set the error level as it has been already set
        // by the _create_from_source() method earlier
        return false;

    }

    /**
     *  Rotates the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
     *
     *
     *  // include the Zebra_Image library
     *  require 'path/to/Zebra_Image.php';
     *
     *  // instantiate the class
     *  $img = new Zebra_Image();
     *
     *  // a source image
     *  $img->source_path = 'path/to/source.ext';
     *
     *  // path to where should the resulting image be saved
     *  // note that by simply setting a different extension to the file will
     *  // instruct the script to create an image of that particular type
     *  $img->target_path = 'path/to/target.ext';
     *
     *  // rotate the image 45 degrees, clockwise
     *  $img->rotate(45);
     *
     *
     * @param  double $angle Angle by which to rotate the image clockwise.
     *
     *                                          Between 0 and 360.
     *
     * @param  mixed $background_color (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
     *                                          uncovered zone after the rotation.
     *
     *                                          When set to -1 the script will preserve transparency for transparent GIF
     *                                          and PNG images. For non-transparent images the background will be white
     *                                          in this case.
     *
     *                                          Default is -1.
     *
     * @return boolean                         Returns TRUE on success or FALSE on error.
     *
     *                                          If FALSE is returned, check the {@link error} property to see the error
     *                                          code.
     */
    function rotate($angle, $background_color = -1)
    {

        // if image resource was successfully created
        if ($this->_create_from_source()) {

            // angles are given clockwise but imagerotate works counterclockwise so we need to negate our value
            $angle = -$angle;

            // if source image is PNG
            if ($this->source_type == IMAGETYPE_PNG && $background_color == -1) {

                // rotate the image
                // but if using -1 as background color didn't work (as is the case for PNG8)
                if (!($target_identifier = imagerotate($this->source_identifier, $angle, -1))) {

                    // we will be using #FFF as the color to fill the uncovered zone after the rotation
                    $background_color = imagecolorallocate($this->source_identifier, 255, 255, 255);

                    // rotate the image
                    $target_identifier = imagerotate($this->source_identifier, $angle, $background_color);

                }

                // if source image is a transparent GIF
            } elseif ($this->source_type == IMAGETYPE_GIF && $this->source_transparent_color_index >= 0) {

                // convert the background color to RGB values
                $background_color = $this->_hex2rgb($background_color);

                // allocate the color to the image identifier
                $background_color = imagecolorallocate(

                    $this->source_identifier,
                    $background_color['r'],
                    $background_color['g'],
                    $background_color['b']

                );

                // rotate the image
                $this->source_identifier = imagerotate($this->source_identifier, $angle, $background_color);

                // get the width of rotated image
                $width = imagesx($this->source_identifier);

                // get the height of rotated image
                $height = imagesy($this->source_identifier);

                // create a blank image with the new width and height
                // (this intermediary step is for preserving transparency)
                $target_identifier = $this->_prepare_image($width, $height, -1);

                // copy the rotated image on to the new one
                imagecopyresampled(
                    $target_identifier,
                    $this->source_identifier,
                    0,
                    0,
                    0,
                    0,
                    $width,
                    $height,
                    $width,
                    $height
                );

                // for the other cases
            } else {

                // convert the color to RGB values
                $background_color = $this->_hex2rgb($background_color);

                // allocate the color to the image identifier
                $background_color = imagecolorallocate(

                    $this->source_identifier,
                    $background_color['r'],
                    $background_color['g'],
                    $background_color['b']

                );

                // rotate the image
                $target_identifier = imagerotate($this->source_identifier, $angle, $background_color);

            }

            // write image
            $this->_write_image($target_identifier);

        }

        // if script gets this far return false
        // note that we do not set the error level as it has been already set
        // by the _create_from_source() method earlier
        return false;

    }

    /**
     *  Returns an array containing the image identifier representing the image obtained from {@link $source_path}, the
     *  image's width and height and the image's type
     *
     * @access private
     */
    function _create_from_source()
    {

        // perform some error checking first
        // if the GD library is not installed
        if (!function_exists('gd_info')) {

            // save the error level and stop the execution of the script
            $this->error = 7;

            return false;

            // if source file does not exist
        } elseif (!is_file($this->source_path)) {

            // save the error level and stop the execution of the script
            $this->error = 1;

            return false;

            // if source file is not readable
        } elseif (!is_readable($this->source_path)) {

            // save the error level and stop the execution of the script
            $this->error = 2;

            return false;

            // if target file is same as source file and source file is not writable
        } elseif ($this->target_path == $this->source_path && !is_writable($this->source_path)) {

            // save the error level and stop the execution of the script
            $this->error = 3;

            return false;

            // try to get source file width, height and type
            // and if it founds an unsupported file type
        } elseif (!list($this->source_width, $this->source_height, $this->source_type) = @getimagesize(
            $this->source_path
        )
        ) {

            // save the error level and stop the execution of the script
            $this->error = 4;

            return false;

            // if no errors so far
        } else {

            // get target file's type based on the file extension
            $this->target_type = strtolower(substr($this->target_path, strrpos($this->target_path, '.') + 1));

            // create an image from file using extension dependant function
            // checks for file extension
            switch ($this->source_type) {

                // if GIF
                case IMAGETYPE_GIF:

                    // create an image from file
                    $identifier = imagecreatefromgif($this->source_path);

                    // get the index of the transparent color (if any)
                    if (($this->source_transparent_color_index = imagecolortransparent(
                            $identifier
                        )) >= 0
                    ) // get the transparent color's RGB values
                        // we have to mute errors because there are GIF images which *are* transparent and everything
                        // works as expected, but imagecolortransparent() returns a color that is outside the range of
                        // colors in the image's pallette...
                    {
                        $this->source_transparent_color = @imagecolorsforindex(
                            $identifier,
                            $this->source_transparent_color_index
                        );
                    }

                    break;

                // if JPEG
                case IMAGETYPE_JPEG:

                    // create an image from file
                    $identifier = imagecreatefromjpeg($this->source_path);

                    break;

                // if PNG
                case IMAGETYPE_PNG:

                    // create an image from file
                    $identifier = imagecreatefrompng($this->source_path);

                    // disable blending
                    imagealphablending($identifier, false);

                    break;

                default:

                    // if unsupported file type
                    // note that we call this if the file is not GIF, JPG or PNG even though the getimagesize function
                    // handles more image types
                    $this->error = 4;

                    return false;

            }

        }

        // if target file has to have the same timestamp as the source image
        // save it as a global property of the class
        if ($this->preserve_time) {
            $this->source_image_time = filemtime($this->source_path);
        }

        // make available the source image's identifier
        $this->source_identifier = $identifier;

        return true;

    }

    /**
     *  Converts a hexadecimal representation of a color (i.e. #123456 or #AAA) to a RGB representation.
     *
     *  The RGB values will be a value between 0 and 255 each.
     *
     * @param  string $color Hexadecimal representation of a color (i.e. #123456 or #AAA).
     *
     * @param  string $default_on_error Hexadecimal representation of a color to be used in case $color is not
     *                                      recognized as a hexadecimal color.
     *
     * @return array                       Returns an associative array with the values of (R)ed, (G)reen and (B)lue
     *
     * @access private
     */
    function _hex2rgb($color, $default_on_error = '#FFFFFF')
    {

        // if color is not formatted correctly
        // use the default color
        if (preg_match('/^#?([a-f]|[0-9]){3}(([a-f]|[0-9]){3})?$/i', $color) == 0) {
            $color = $default_on_error;
        }

        // trim off the "#" prefix from $background_color
        $color = ltrim($color, '#');

        // if color is given using the shorthand (i.e. "FFF" instead of "FFFFFF")
        if (strlen($color) == 3) {

            $tmp = '';

            // take each value
            // and duplicate it
            for ($i = 0 ; $i < 3 ; $i++) {
                $tmp .= str_repeat($color[$i], 2);
            }

            // the color in it's full, 6 characters length notation
            $color = $tmp;

        }

        // decimal representation of the color
        $int = hexdec($color);

        // extract and return the RGB values
        return array(

            'r' => 0xFF & ($int >> 0x10),
            'g' => 0xFF & ($int >> 0x8),
            'b' => 0xFF & $int

        );

    }

    /**
     *  Flips horizontally or vertically or both ways the image given as {@link source_path}.
     *
     * @since 2.1
     *
     * @access private
     *
     * @return boolean     Returns TRUE on success or FALSE on error.
     *
     *                      If FALSE is returned, check the {@link error} property to see the error code.
     */
    function _flip($orientation)
    {

        // if image resource was successfully created
        if ($this->_create_from_source()) {

            // prepare the target image
            $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);

            // flip according to $orientation
            switch ($orientation) {

                case 'horizontal':

                    imagecopyresampled(

                        $target_identifier,
                        $this->source_identifier,
                        0,
                        0,
                        ($this->source_width - 1),
                        0,
                        $this->source_width,
                        $this->source_height,
                        -$this->source_width,
                        $this->source_height

                    );

                    break;

                case 'vertical':

                    imagecopyresampled(

                        $target_identifier,
                        $this->source_identifier,
                        0,
                        0,
                        0,
                        ($this->source_height - 1),
                        $this->source_width,
                        $this->source_height,
                        $this->source_width,
                        -$this->source_height

                    );

                    break;

                case 'both':

                    imagecopyresampled(

                        $target_identifier,
                        $this->source_identifier,
                        0,
                        0,
                        ($this->source_width - 1),
                        ($this->source_height - 1),
                        $this->source_width,
                        $this->source_height,
                        -$this->source_width,
                        -$this->source_height

                    );

                    break;

            }

            // write image
            return $this->_write_image($target_identifier);

        }

        // if script gets this far, return false
        // note that we do not set the error level as it has been already set
        // by the _create_from_source() method earlier
        return false;

    }

    /**
     *  Creates a blank image of given width, height and background color.
     *
     * @param  integer $width Width of the new image.
     *
     * @param  integer $height Height of the new image.
     *
     * @param  string $background_color (Optional) The hexadecimal color of the background.
     *
     *                                          Can also be -1 case in which the script will try to create a transparent
     *                                          image, if possible.
     *
     *                                          Default is "#FFFFFF".
     *
     * @return                                 Returns the identifier of the newly created image.
     *
     * @access private
     */
    function _prepare_image($width, $height, $background_color = '#FFFFFF')
    {

        // create a blank image
        $identifier = imagecreatetruecolor((int)$width <= 0 ? 1 : (int)$width, (int)$height <= 0 ? 1 : (int)$height);

        // if we are creating a PNG image
        if ($this->target_type == 'png' && $background_color == -1) {

            // disable blending
            imagealphablending($identifier, false);

            // allocate a transparent color
            $transparent_color = imagecolorallocatealpha($identifier, 0, 0, 0, 127);

            // fill the image with the transparent color
            imagefill($identifier, 0, 0, $transparent_color);

            //save full alpha channel information
            imagesavealpha($identifier, true);

            // if source image is a transparent GIF
        } elseif ($this->target_type == 'gif' && $background_color == -1 && $this->source_transparent_color_index >= 0) {

            // allocate the source image's transparent color also to the new image resource
            $transparent_color = imagecolorallocate(
                $identifier,
                $this->source_transparent_color['red'],
                $this->source_transparent_color['green'],
                $this->source_transparent_color['blue']
            );

            // fill the background of the new image with transparent color
            imagefill($identifier, 0, 0, $transparent_color);

            // from now on, every pixel having the same RGB as the transparent color will be transparent
            imagecolortransparent($identifier, $transparent_color);

            // for other image types
        } else {

            // if transparent background color specified, revert to white
            if ($background_color == -1) {
                $background_color = '#FFFFFF';
            }

            // convert hex color to rgb
            $background_color = $this->_hex2rgb($background_color);

            // prepare the background color
            $background_color = imagecolorallocate(
                $identifier,
                $background_color['r'],
                $background_color['g'],
                $background_color['b']
            );

            // fill the image with the background color
            imagefill($identifier, 0, 0, $background_color);

        }

        // return the image's identifier
        return $identifier;

    }

    /**
     *  Sharpens images. Useful when creating thumbnails.
     *
     *  Code taken from the comments at {@link http://docs.php.net/imageconvolution}.
     *
     *  This function will yield a result only for PHP version 5.1.0+ and will leave the image unaltered for older
     *  versions!
     *
     * @param  $identifier identifier  An image identifier
     *
     * @access private
     */
    function _sharpen_image($image)
    {

        // if the "sharpen_images" is set to true and we're running an appropriate version of PHP
        // (the "imageconvolution" is available only for PHP 5.1.0+)
        if ($this->sharpen_images && version_compare(PHP_VERSION, '5.1.0') >= 0) {

            // the convolution matrix as an array of three arrays of three floats
            $matrix = array(
                array(-1.2, -1, -1.2),
                array(-1, 20, -1),
                array(-1.2, -1, -1.2),
            );

            // the divisor of the matrix
            $divisor = array_sum(array_map('array_sum', $matrix));

            // color offset
            $offset = 0;

            // sharpen image
            imageconvolution($image, $matrix, $divisor, $offset);

        }

        // return the image's identifier
        return $image;

    }

    /**
     *  Creates a new image from given image identifier having the extension as specified by {@link target_path}.
     *
     * @param  $identifier identifier  An image identifier
     *
     * @return boolean                 Returns TRUE on success or FALSE on error.
     *
     *                                  If FALSE is returned, check the {@link error} property to see the error code.
     *
     * @access private
     */
    function _write_image($identifier)
    {

        // sharpen image if it's required
        $this->_sharpen_image($identifier);

        // image saving process goes according to required extension
        switch ($this->target_type) {

            // if GIF
            case 'gif':

                // if GD support for this file type is not available
                // in version 1.6 of GD the support for GIF files was dropped see
                // http://php.net/manual/en/function.imagegif.php#function.imagegif.notes
                if (!function_exists('imagegif')) {

                    // save the error level and stop the execution of the script
                    $this->error = 6;

                    return false;

                    // if, for some reason, file could not be created
                } elseif (@!imagegif($identifier, $this->target_path)) {

                    // save the error level and stop the execution of the script
                    $this->error = 3;

                    return false;

                }

                break;

            // if JPEG
            case 'jpg':
            case 'jpeg':

                // if GD support for this file type is not available
                if (!function_exists('imagejpeg')) {

                    // save the error level and stop the execution of the script
                    $this->error = 6;

                    return false;

                    // if, for some reason, file could not be created
                } elseif (@!imagejpeg($identifier, $this->target_path, $this->jpeg_quality)) {

                    // save the error level and stop the execution of the script
                    $this->error = 3;

                    return false;

                }

                break;

            // if PNG
            case 'png':

                // save full alpha channel information
                imagesavealpha($identifier, true);

                // if GD support for this file type is not available
                if (!function_exists('imagepng')) {

                    // save the error level and stop the execution of the script
                    $this->error = 6;

                    return false;

                    // if, for some reason, file could not be created
                } elseif (@!imagepng($identifier, $this->target_path, $this->png_compression)) {

                    // save the error level and stop the execution of the script
                    $this->error = 3;

                    return false;

                }

                break;

            // if not a supported file extension
            default:

                // save the error level and stop the execution of the script
                $this->error = 5;

                return false;

        }

        // get a list of functions disabled via configuration
        $disabled_functions = @ini_get('disable_functions');

        // if the 'chmod' function is not disabled via configuration
        if ($disabled_functions == '' || strpos('chmod', $disabled_functions) === false) {

            // chmod the file
            chmod($this->target_path, intval($this->chmod_value, 8));

            // save the error level
        } else {
            $this->error = 8;
        }

        // if target file has to have the same timestamp as the source image
        if ($this->preserve_time && isset($this->source_image_time)) {

            // touch the newly created file
            @touch($this->target_path, $this->source_image_time);

        }

        // return true
        return true;

    }

}


<?php

/*
 * This file is part of the PhpMob package.
 *
 * (c) Ishmael Doss <nukboon@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace PhpMob\SettingsBundle\Twig\Extension;

use PhpMob\SettingsBundle\Twig\Helper\SettingHelper;

/**
 * @author Ishmael Doss <nukboon@gmail.com>
 */
class SettingExtension extends \Twig_Extension
{
    /**
     * @var SettingHelper
     */
    private $settingHelper;

    /**
     * @param SettingHelper $settingHelper
     */
    public function __construct(SettingHelper $settingHelper)
    {
        $this->settingHelper = $settingHelper;
    }

    /**
     * {@inheritdoc}
     */
    public function getFunctions()
    {
        return [
            new \Twig_SimpleFunction('settings_get', [$this, 'get']),
            new \Twig_SimpleFunction('settings_set', [$this, 'set']),
        ];
    }

    /**
     * @param string $path
     * @param null|string $owner
     *
     * @return mixed
     */
    public function get(string $path, ?string $owner = null)
    {
        return $this->settingHelper->get($path, $owner);
    }

    /**
     * @param string $path
     * @param $value
     * @param null|string $owner
     */
    public function set(string $path, $value, ?string $owner = null): void
    {
        $this->settingHelper->set($path, $value, $owner);
    }
}


<?php

/*
 * This file is part of the PhpMob package.
 *
 * (c) Ishmael Doss <nukboon@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace PhpMob\SettingsBundle\Form\Type;

use PhpMob\Settings\Schema\Section;
use PhpMob\Settings\Schema\SettingSchema;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Ishmael Doss <nukboon@gmail.com>
 */
class SectionUpdateType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $settings = $options['section']->getSettings();

        /** @var SettingSchema $setting */
        foreach ($settings as $setting) {
            if (!$setting->isEnabled()) {
                continue;
            }

            $blueprint = $setting->getBlueprint();
            $blueprintOptions = $blueprint->getOptions();
            $blueprintOptions['constraints'] = [];

            foreach ($blueprint->getConstraints() as $class => $constraintOptions) {
                if (!class_exists($class)) {
                    $class = sprintf("Symfony\\Component\\Validator\\Constraints\\".$class);
                }

                $blueprintOptions['constraints'][] = new $class($constraintOptions);
            }

            $type = $blueprint->getType();

            if (get_parent_class($type) === AbstractBlueprintType::class) {
                $blueprintOptions['description'] = $setting->getDescription();
            }

            $builder->add($setting->getKey(), $type, array_merge([
                'label' => $setting->getLabel(),
                'required' => false,
            ], $blueprintOptions));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setRequired('section');
        $resolver->setAllowedTypes('section', Section::class);
    }
}


<?php

/*
 * This file is part of the PhpMob package.
 *
 * (c) Ishmael Doss <nukboon@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

declare(strict_types=1);

namespace PhpMob\SettingsBundle\Controller;

use Doctrine\Common\Collections\Collection;
use PhpMob\Settings\Model\SettingInterface;
use PhpMob\Settings\Schema\Section;
use PhpMob\SettingsBundle\Form\Type\SectionUpdateType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * @author Ishmael Doss <nukboon@gmail.com>
 */
class UpdateController extends Controller
{
    /**
     * @param string $section
     *
     * @return Section
     */
    private function getSectionSchema(string $section)
    {
        try {
            return $this->get('phpmob.settings.schema_registry')->getSection($section);
        } catch (\Exception $e) {
            throw new NotFoundHttpException($e->getMessage());
        }
    }

    /**
     * @return Collection|SettingInterface[]
     */
    private function getGlobalSettings()
    {
        return $this->get('phpmob.settings.composite_provider')->findGlobalSettings();
    }

    /**
     * @return Collection|SettingInterface[]
     */
    private function getUserSettings()
    {
        return $this->get('phpmob.settings.composite_provider')->findUserSettings((string)$this->getUser());
    }

    /**
     * @param string $section
     * @param Collection $collection
     *
     * @return SettingInterface[]
     */
    private function getSectionedSettings(string $section, Collection $collection)
    {
        return $collection->filter(
            function (SettingInterface $setting) use ($section) {
                return $section === $setting->getSection();
            }
        )->toArray();
    }

    /**
     * @param SettingInterface $setting
     *
     * @return mixed
     */
    private function transformSettingValue(SettingInterface $setting)
    {
        return $this->get('phpmob.settings.transformer')
            ->reverse($setting->getSection(), $setting->getKey(), $setting->getValue());
    }

    /**
     * @param SettingInterface[] $settings
     *
     * @return array|null
     */
    private function createSettingData(array $settings)
    {
        if (empty($settings)) {
            return null;
        }

        $data = [];

        foreach ($settings as $setting) {
            $data[$setting->getKey()] = $this->transformSettingValue($setting);
        }

        return $data;
    }

    /**
     * @param Section $section
     * @param Collection $settings
     *
     * @return FormInterface
     */
    private function createSettingForm(Section $section, Collection $settings)
    {
        $data = $this->createSettingData($this->getSectionedSettings($section->getName(), $settings));

        return $this->get('form.factory')->createNamed('', SectionUpdateType::class, $data, [
            'section' => $section,
        ]);
    }

    /**
     * @param Request $request
     *
     * @return array
     */
    private function getRequestConfiguration(Request $request)
    {
        return [
            'template' => $request->get('_template', '@PhpMobSettings/default.html.twig'),
            'redirect' => $request->get('_redirect', $request->headers->get('referer')),
            'flash' => $request->get('_flash', 'phpmob.settings_update_successfuly'),
            'isHtml' => 'html' === $request->getRequestFormat(),
        ];
    }

    /**
     * @param string $message
     *
     * @return string
     */
    private function createFlashTranslateMessage(string $message)
    {
        $translator = $this->get('translator');
        $isTranlateKey = $message !== $translated = $translator->trans($message, [], 'flashes');

        if ($isTranlateKey) {
            return $translated;
        }

        return ucwords(trim(preg_replace('/phpmob|\.|_/', ' ', $message)));
    }

    /**
     * @param string|array $redirect
     *
     * @return RedirectResponse
     */
    private function createRedirectResponse($redirect)
    {
        if (!is_array($redirect) && preg_match('/\//', $redirect)) {
            return $this->redirect($redirect);
        }

        if (!is_array($redirect)) {
            $redirect = ['route' => $redirect, 'parameters' => []];
        }

        return $this->redirectToRoute($redirect['route'], $redirect['parameters']);
    }

    /**
     * @param Section $section
     * @param array $settings
     * @param null|string $owner
     */
    private function updateSettings(Section $section, array $settings = [], ?string $owner = null)
    {
        $manager = $this->get('phpmob.settings.manager');

        foreach ($settings as $key => $value) {
            $manager->setSetting($section->getName(), $key, $value, $owner);
        }

        $manager->flush();
    }

    /**
     * @param Request $request
     * @param Section $section
     * @param null|string $owner
     *
     * @return Response
     */
    public function update(Request $request, Section $section, ?string $owner = null)
    {
        $form = $this->createSettingForm($section, $owner ? $this->getUserSettings() : $this->getGlobalSettings());
        $config = $this->getRequestConfiguration($request);

        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
            $this->updateSettings($section, $form->getData(), $owner);

            if (!$config['isHtml']) {
                return JsonResponse::create(null, 204);
            }

            $this->addFlash('success', $this->createFlashTranslateMessage($config['flash']));

            return $this->createRedirectResponse($config['redirect']);
        }

        if ($owner) {
            $allSections = $this->get('phpmob.settings.schema_registry')->getOwners();
        } else {
            $allSections = $this->get('phpmob.settings.schema_registry')->getGlobals();
        }

        if (!$config['isHtml']) {
            return JsonResponse::create([
                'section' => $section,
                'sections' => $allSections,
            ]);
        }

        return $this->render($config['template'], [
            'section' => $section,
            'sections' => $allSections,
            'form' => $form->createView(),
        ]);
    }

    /**
     * @param Request $request
     * @param string $section
     *
     * @return Response
     */
    public function globalUpdateAction(Request $request, string $section)
    {
        $section = $this->getSectionSchema($section);

        if ($section->isOwnerAware()) {
            throw new NotFoundHttpException("Wrong setting section update.");
        }

        return $this->update($request, $section);
    }

    /**
     * @param Request $request
     * @param string $section
     *
     * @return Response
     */
    public function userUpdateAction(Request $request, string $section)
    {
        $section = $this->getSectionSchema($section);

        if (!$section->isOwnerAware()) {
            throw new NotFoundHttpException("Wrong setting section update.");
        }

        if (!$this->isGranted($request->attributes->get('_role', 'ROLE_USER'))) {
            throw new AccessDeniedHttpException("Access Denied!");
        }

        return $this->update($request, $section, (string)$this->getUser());
    }
}


<?php
App::uses('File', 'Utility');
App::uses('ConnectionManager', 'Model');

class InstallController extends GalleryAppController
{
    public function configuration_form()
    {
        $this->render('Gallery.Install/config', 'Gallery.default');
    }

    /**
     * Configure galleries and create mysql tables
     */
    public function configure()
    {
        $files_path = WWW_ROOT . 'files' . DS;
        if (!file_exists($files_path)) {
            mkdir($files_path, 0755);
        }
        $galleries_path = $files_path . 'gallery' . DS;
        if (!file_exists($galleries_path)) {
            mkdir($galleries_path, 0755);
        }

        $this->_configureDatabase();

        $this->Session->setFlash('Success! Gallery is now installed in your app.');

        $this->redirect(
            array(
                'controller' => 'gallery',
                'action' => 'index',
                'plugin' => 'gallery'
            )
        );

        $this->render(false, false);
    }

    /**
     * Configure database to use this plugin
     */
    private function _configureDatabase()
    {
        try {
            $db = ConnectionManager::getDataSource('default');

            if (!$db->isConnected()) {
                throw new Exception("You need to connect to a MySQL Database to use this Plugin.");
            }

            /** Verify if the tables already exists */
            if (!$this->_checkTables($db->listSources())) {
                $this->_setupDatabase($db);
            }

            # Create config file
            $this->_createConfigFile();

            return;

        } catch (Exception $e) {
            $this->Session->setFlash($e->getMessage());
            $this->redirect('/');
        }
    }

    /**
     * @param $tables
     * @return bool
     */
    private function _checkTables($tables)
    {
        return !!in_array('gallery_albums', $tables) && !!in_array('gallery_pictures', $tables);
    }

    /**
     * Execute Config/cakegallery.sql to create the tables
     * Create the config File
     * @param $db
     */
    private function _setupDatabase($db)
    {
        # Execute the SQL to create the tables
        $sqlFile = new File(App::pluginPath('Gallery') . 'Config' . DS . 'cakegallery.sql', false);
        $db->rawQuery($sqlFile->read());
        $sqlFile->close();
    }

    /**
     * Create the config file copying the config.php.install file
     */
    private function _createConfigFile()
    {
        $destinationPath = App::pluginPath('Gallery') . 'config' . DS . 'config.php';

        if (!file_exists($destinationPath)) {
            $configFile = new File(App::pluginPath('Gallery') . 'config' . DS . 'config.php.install');

            $configFile->copy($destinationPath);
        }
    }
}


<?php
/**
 * Generate API documentation.
 *
 * Run in command line with:
 *     php -f generate-api.docs.php
 *
 * Copy output into README.md
 */
$loader = require __DIR__ . '/vendor/autoload.php';
$loader->add('Basecamp\\Test\\', __DIR__ . '/tests');

$docs = new Generate_Docs();

$docs->checkSummaries();
$docs->generate();
$docs->save();

class Generate_Docs {

	/**
	 * @link  http://guzzle3.readthedocs.org/webservice-client/guzzle-service-descriptions.html#parameter-schema
	 * @var  array Guzzle service description.
	 */
	protected $service;

	/**
	 * @var string Generated docs Markdown
	 */
	protected $docs_markdown;

	/**
	 * Readme filename. Converted to full path on __construct()
	 *
	 * @var string
	 */
	protected $readme_file = 'README.md';

	/**
	 * Markers for identifying start and end of API Calls to be replaced in readme file.
	 *
	 * @link( http://stackoverflow.com/a/4829998, Triple hyphen for Pandoc compatibility )
	 *
	 * @var string
	 */
	protected $start_marker = '<!--- START API Generate with: php -f `generate-api-docs.php` -->';
	protected $end_marker = '<!--- END API -->';

	protected $tmpl_heading           = "\n### @summary \n\n";
	protected $tmpl_heading_tests     = "\n### Unit Test Coverage \n\nThe following service operations are not (yet) covered by unit tests:\n\n";

	protected $tmpl_code_block_start  = "```php \n";
	protected $tmpl_code_block_end    = "``` \n";

	protected $tmpl_method_without_params     = "\$response = \$client->@method(); \n";
	protected $tmpl_method_with_params_start  = "\$response = \$client->@method( array( \n";
	protected $tmpl_method_with_params_end    = ") ); \n";
	protected $tmpl_method_parameter          = "    '@param' => @example,  // @required @description \n";

	protected $tmpl_tests_bullet      = "* @method \n";

	/**
	 * Parameter code examples in order of priority.
	 *
	 * Note the use of single quotes within double quotes
	 * so that strings will output as valid PHP.
	 *
	 * @var array
	 */
	protected $examples = array(
		// Parameter names
		'since'       => "'2012-03-24T11:00:00-06:00'",
		'description' => "'Example description'",
		'content'     => "'Example content'",
		'name'        => "'Example name'",
		'data'        => "file_get_contents( 'image.jpg' )",
		'mimeType'    => "'image/jpeg'",
		'attachments' => "array( array( 'token' => \$upload_token, 'name' => 'file.jpg' ) )",
		'assignee'    => "array( 'id' => 1234567, 'type' => 'Person' )",

		// Parameter types
		'string'      => "'example'",
		'integer'     => 1234567,
	);

	function __construct() {
		$this->service = require __DIR__ . '/src/Basecamp/Resources/service.php';
		$this->readme_file = __DIR__ . '/' . $this->readme_file;
	}

	/**
	 * Verify that all service operations have summaries set before continuing
	 */
	public function checkSummaries() {
		$need_summaries = array();

		foreach ( $this->service['operations'] as $method => $info ) {
			if ( ! isset( $info['summary'] ) ) {
				$need_summaries[] = $method;
			}
		}

		if ( ! empty( $need_summaries ) ) {

			echo PHP_EOL;
			echo 'Please set summaries for all operations to generate docs.' . PHP_EOL;
			echo 'The following operations are missing a \'summary\' value in service.php:' . PHP_EOL;

			foreach ( $need_summaries as $method ) {
				echo '	' . $method . PHP_EOL;
			}

			echo PHP_EOL;

			exit;

		}
	}

	/**
	 * Work out which serice operations still need unit tests written
	 */
	public function checkTestCoverage() {
		$tests_needed = array();

		foreach ( $this->service['operations'] as $method => $info ) {
			if ( ! method_exists( new Basecamp\Test\BasecampClientTest(), 'test' . ucfirst($method) ) ) {
				$tests_needed[] = $method;
			}
		}

		if ( ! empty( $tests_needed ) ) {

			// Test Coverage Heading
			echo $this->template( $this->tmpl_heading_tests, array() );

			foreach ( $tests_needed as $method ) {
				// Test Coverage Heading
				echo $this->template( $this->tmpl_tests_bullet, array('method' => $method) );
			}

		}
	}

	/**
	 * Output docs for each service operation in Markdown format.
	 */
	public function generate() {

		ob_start();

		foreach ( $this->service['operations'] as $method => $info ) {

			// Add method to info array for more convenient templates
			$info['method'] = $method;

			// Heading
			echo $this->template( $this->tmpl_heading, $info );

			// Code Block start
			echo $this->template( $this->tmpl_code_block_start, $info );

			if ( isset( $info['parameters'] ) ) {

				// Method call (with parameters) start
				echo $this->template( $this->tmpl_method_with_params_start, $info );

				// Parameters
				foreach ( $info['parameters'] as $param => $param_info ) {
					$param_info['param'] = $param;
					echo $this->template( $this->tmpl_method_parameter, $param_info );
				}

				// Method call end
				echo $this->template( $this->tmpl_method_with_params_end, $info );

			}else {

				// Method call (no parameters)
				echo $this->template( $this->tmpl_method_without_params, $info );

			}

			// Code Block end
			echo $this->template( $this->tmpl_code_block_end, $info );

		}

		$this->checkTestCoverage();

		$this->docs_markdown = ob_get_clean();

		return $this->docs_markdown;
	}

	public function save() {
		if ( ! isset( $this->docs_markdown ) ) {
			echo "\nPlease run \$this->generate() before \$this->save()";
			exit;
		}

		if ( ! is_readable( $this->readme_file ) || ! is_writable( $this->readme_file ) ) {
			echo "\nCould not read or write to readme file. Please check permissions.\n";
			echo "Readme path: {$this->readme_file}\n\n";
			exit;
		}

		$readme = file_get_contents( $this->readme_file );

		$this->insertWithMarkers( $this->readme_file, $this->docs_markdown );

		echo PHP_EOL . basename( $this->readme_file ) . ' updated.' . PHP_EOL . PHP_EOL;
	}

	/**
	 * Inserts an array of strings into a file (.htaccess ), placing it between
	 * BEGIN and END markers. Replaces existing marked info. Retains surrounding
	 * data. Creates file if none exists.
	 *
	 * @author  Unknown. Adapted from WordPress core.
	 *
	 * @param string $filename
	 * @param string $marker
	 * @param string|array $insertion
	 * @return bool True on write success, false on failure.
	 */
	function insertWithMarkers( $filename, $insertion ) {

		if ( ! is_array( $insertion ) ) {
			$insertion = explode( "\n", $insertion );
		}

		if ( ! file_exists( $filename ) || is_writable( $filename ) ) {
			if ( ! file_exists( $filename ) ) {
				$markerdata = '';
			} else {
				$markerdata = explode( "\n", implode( '', file( $filename ) ) );
			}

			if ( !$f = @fopen( $filename, 'w' ) )
				return false;

			$foundit = false;
			if ( $markerdata ) {

				$state = true;

				foreach ( $markerdata as $n => $markerline ) {
					if (strpos($markerline, $this->start_marker) !== false) {
						$state = false;
					}

					if ( $state ) {
						if ( $n + 1 < count( $markerdata ) )
							fwrite( $f, "{$markerline}\n" );
						else
							fwrite( $f, "{$markerline}" );
					}

					if ( strpos( $markerline, $this->end_marker ) !== false) {
						fwrite( $f, $this->start_marker . "\n" );

						if ( is_array( $insertion ) ) {
							foreach ( $insertion as $insertline ) {
								fwrite( $f, "{$insertline}\n" );
							}
						}

						fwrite( $f, $this->end_marker . "\n" );
						$state = true;
						$foundit = true;
					}
				}
			}
			if ( ! $foundit ) {
				fwrite( $f, "\n" . $this->start_marker . "\n" );
				foreach ( $insertion as $insertline ) {
					fwrite( $f, "{$insertline}\n" );
				}
				fwrite( $f, $this->end_marker . "\n" );
			}

			fclose( $f );
			return true;

		}

		return false;

	}

	/**
	 * Replace @variables with values in template string.
	 *
	 * @param  string $string Template string. @variable used for variables.
	 * @param  array $args   Array with keys as variable name, values as value
	 * @return string
	 */
	protected function template( $string, array $args ) {

		// Example data formats
		$string = str_replace( '@example', $this->getExample( $args ), $string );

		// Required or Optional
		$string = str_replace( '@required', ( empty( $args['required'] ) ? 'Optional.' : 'Required.' ), $string );

		// Vars from passed array
		foreach ( $args as $key => $value ) {
			if ( is_string( $value ) ) {
				$string = str_replace( '@' . $key, $value, $string );
			}
		}

		// Clear remaining variables
		$string = preg_replace( '/@[a-zA-Z0-9]+/', '', $string );

		return $string;
	}

	protected function getExample( array $args ) {
		if ( ! isset( $args['param'] ) ) {
			return false;
		}

		foreach ( $this->examples as $key => $code ) {
			if ( $key == $args['param'] || $key == @ $args['type'] ) {
				return $code;
			}
		}

		return "''";
	}

}


<?php
App::uses('AppHelper', 'View/Helper');
App::uses('FormHelper', 'View/Helper');
App::uses('Album', 'Gallery.Model');

class GalleryHelper extends AppHelper
{
    /**
     * Gallery link helper
     *
     * This helper will create an link to a new or existing gallery
     *
     * If no arguments are passed this will create a new gallery everytime the link are clicked.
     *
     * When model & model_id arguments are passed, this will redirect to this specific gallery (it will also create a
     * new one if didn't exists)
     *
     * Example:
     * <?php echo $this->Gallery->button('product', 1) ?>
     *
     * @param null $model
     * @param null $model_id
     * @param array $html_options
     * @return string
     */
    public function link($model = null, $model_id = null, $html_options = array())
    {
        return $this->_View->Html->link(
            __('Upload pictures'),
            array(
                'controller' => 'albums',
                'action' => 'upload',
                'plugin' => 'gallery',
                $model,
                $model_id
            ),
            $html_options
        );
    }

    /**
     * Get cover picture from an album (The first picture)
     *
     * @example
     * <?php echo $this->Gallery->cover('product', 10) ?>
     *
     * @param null $model
     * @param null $model_id
     * @param null $album_id
     * @param string $style
     * @return string
     */
    public function cover($model = null, $model_id = null, $album_id = null, $style = 'medium')
    {
        if ($album = $this->getAlbum($model, $model_id, $album_id)) {
            if (!empty($album['Picture'][0])) {
                return '';
            }
        }
    }

    /**
     * Render a gallery with thumbnails
     *
     * @example
     * <?php $this->Gallery->showroom('product', 10) ?>
     *
     * @param null $model
     * @param null $model_id
     * @param null $album_id
     * @return string
     */
    public function showroom(
        $model = null,
        $model_id = null,
        $album_id = null,
        $style = 'medium',
        $html_options = array('jquery' => true, 'swipebox' => true))
    {

        $album = $this->getAlbum($model, $model_id, $album_id);

        if (!empty($album)) {
            # Load scripts for the showroom (jquery, bootstrap, swipebox)
            $this->_loadScripts($html_options);

            # Render the showroom
            $this->showroomTmpl($album, $style);
        } else {
            # Album doesn't exists
            $this->_noPhotosMessageTmpl();
        }

        return;
    }

    /**
     * This method use a album object to loop in all pictures and
     * show them using a bootstrap html pattern.
     *
     * @param $album
     */
    public function showroomTmpl($album, $style = 'medium')
    {
        if (empty($album['Picture'])) {
            $this->_noPhotosMessageTmpl();
        } else {
            foreach ($album['Picture'] as $picture) {
                $this->_thumbnailTmpl($picture, $style);
            }
        }
    }

    /**
     * Get an album from $model & $model_id or $album_id
     *
     * @param null $model
     * @param null $model_id
     * @param null $album_id
     */
    private function getAlbum($model = null, $model_id = null, $album_id = null)
    {
        $Album = new Album();

        if ($album_id) {
            return $Album->findById($album_id);
        } else if ($model && $model_id) {
            return $Album->getAttachedAlbum($model, $model_id);
        }
    }

    /**
     * Load scripts for using on showroom
     *
     * @param bool $jquery
     */
    private function _loadScripts($scripts = array('jquery' => true, 'swipebox' => true))
    {
        if (!isset($scripts['jquery']) || (isset($scripts['jquery']) && $scripts['jquery'] == true)) {
            echo $this->_View->Html->script(
                'http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js',
                array('block' => 'script'));
        }

        if (!isset($scripts['swipebox']) || (isset($scripts['swipebox']) && $scripts['swipebox'] == true)) {
            echo $this->_View->Html->css(
                'https://cdnjs.cloudflare.com/ajax/libs/jquery.swipebox/1.3.0.2/css/swipebox.min.css',
                array('block' => 'css'));
            echo $this->_View->Html->script(array(
                'https://cdnjs.cloudflare.com/ajax/libs/jquery.swipebox/1.3.0.2/js/jquery.swipebox.min.js',
                'Gallery.interface'
            ),
                array('block' => 'script'));
        }
    }

    /**
     * Thumbnail template for rendering a picture.
     *
     * The default style are 'medium'
     * @see Config/bootstrap.php#35
     *
     * @param $picture
     * @param string $style
     */
    private function _thumbnailTmpl($picture, $style = 'medium')
    {
        echo '

        ';
    }

    /**
     * Message for displaying when there is no album or pictures on specific album
     *
     * @param string $message
     */
    private function _noPhotosMessageTmpl($message = 'This album has no photos yet.')
    {
        echo '

' . $message . '

            ';
    }
}