add plugins
This commit is contained in:
parent
14a425a261
commit
f14dfdc726
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 Graker
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,254 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums;
|
||||||
|
|
||||||
|
use Backend;
|
||||||
|
use System\Classes\PluginBase;
|
||||||
|
use Event;
|
||||||
|
use Backend\Widgets\Form;
|
||||||
|
use Lang;
|
||||||
|
use Graker\PhotoAlbums\Widgets\PhotoSelector;
|
||||||
|
use Graker\PhotoAlbums\Classes\MenuItemsProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PhotoAlbums Plugin Information File
|
||||||
|
*/
|
||||||
|
class Plugin extends PluginBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information about this plugin.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function pluginDetails()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'graker.photoalbums::lang.plugin.name',
|
||||||
|
'description' => 'graker.photoalbums::lang.plugin.description',
|
||||||
|
'author' => 'Graker',
|
||||||
|
'icon' => 'icon-camera-retro',
|
||||||
|
'homepage' => 'https://github.com/graker/oc-photoalbums-plugin',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers any front-end components implemented in this plugin.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function registerComponents()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Graker\PhotoAlbums\Components\Photo' => 'singlePhoto',
|
||||||
|
'Graker\PhotoAlbums\Components\Album' => 'photoAlbum',
|
||||||
|
'Graker\PhotoAlbums\Components\AlbumList' => 'albumList',
|
||||||
|
'Graker\PhotoAlbums\Components\RandomPhotos' => 'randomPhotos',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers any back-end permissions used by this plugin.
|
||||||
|
* At the moment there's one permission allowing overall management of albums and photos
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function registerPermissions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'graker.photoalbums.manage_albums' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.manage_albums',
|
||||||
|
'tab' => 'graker.photoalbums::lang.plugin.tab',
|
||||||
|
],
|
||||||
|
'graker.photoalbums.access_settings' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.access_permission',
|
||||||
|
'tab' => 'graker.photoalbums::lang.plugin.tab',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers back-end navigation items for this plugin.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function registerNavigation()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'photoalbums' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.tab',
|
||||||
|
'url' => Backend::url('graker/photoalbums/albums'),
|
||||||
|
'icon' => 'icon-camera-retro',
|
||||||
|
'permissions' => ['graker.photoalbums.manage_albums'],
|
||||||
|
'order' => 500,
|
||||||
|
|
||||||
|
'sideMenu' => [
|
||||||
|
'upload_photos' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.upload_photos',
|
||||||
|
'icon' => 'icon-upload',
|
||||||
|
'url' => Backend::url('graker/photoalbums/upload/form'),
|
||||||
|
'permissions' => ['graker.photoalbums.manage_albums'],
|
||||||
|
],
|
||||||
|
'new_album' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.new_album',
|
||||||
|
'icon' => 'icon-plus',
|
||||||
|
'url' => Backend::url('graker/photoalbums/albums/create'),
|
||||||
|
'permissions' => ['graker.photoalbums.manage_albums'],
|
||||||
|
],
|
||||||
|
'albums' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.albums',
|
||||||
|
'icon' => 'icon-copy',
|
||||||
|
'url' => Backend::url('graker/photoalbums/albums'),
|
||||||
|
'permissions' => ['graker.photoalbums.manage_albums'],
|
||||||
|
],
|
||||||
|
'new_photo' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.new_photo',
|
||||||
|
'icon' => 'icon-plus-square-o',
|
||||||
|
'url' => Backend::url('graker/photoalbums/photos/create'),
|
||||||
|
'permissions' => ['graker.photoalbums.manage_albums'],
|
||||||
|
],
|
||||||
|
'photos' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.photos',
|
||||||
|
'icon' => 'icon-picture-o',
|
||||||
|
'url' => Backend::url('graker/photoalbums/photos'),
|
||||||
|
'permissions' => ['graker.photoalbums.manage_albums'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Registers plugin's settings
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function registerSettings()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'settings' => [
|
||||||
|
'label' => 'graker.photoalbums::lang.plugin.name',
|
||||||
|
'description' => 'graker.photoalbums::lang.plugin.settings_description',
|
||||||
|
'icon' => 'icon-camera-retro',
|
||||||
|
'class' => 'Graker\PhotoAlbums\Models\Settings',
|
||||||
|
'order' => 100,
|
||||||
|
'permissions' => ['graker.photoalbums.access_settings'],
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Custom column types definition
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function registerListColumnTypes() {
|
||||||
|
return [
|
||||||
|
'is_front' => [$this, 'evalIsFrontListColumn'],
|
||||||
|
'image' => [$this, 'evalImageListColumn'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Special column to show photo set to be album's front in album's relations list
|
||||||
|
*
|
||||||
|
* @param $value
|
||||||
|
* @param $column
|
||||||
|
* @param $record
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function evalIsFrontListColumn($value, $column, $record) {
|
||||||
|
return ($value == $record->id) ? Lang::get('graker.photoalbums::lang.plugin.bool_positive') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Column to render image thumb for Photo model
|
||||||
|
*
|
||||||
|
* @param $value
|
||||||
|
* @param $column
|
||||||
|
* @param $record
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function evalImageListColumn($value, $column, $record) {
|
||||||
|
if ($record->has('image')) {
|
||||||
|
$thumb = $record->image->getThumb(
|
||||||
|
isset($column->config['width']) ? $column->config['width'] : 200,
|
||||||
|
isset($column->config['height']) ? $column->config['height'] : 200,
|
||||||
|
['mode' => 'auto']
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// in case the file attachment was manually deleted for some reason
|
||||||
|
$thumb = '';
|
||||||
|
}
|
||||||
|
return "<img src=\"$thumb\" />";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* boot() implementation
|
||||||
|
* - Register listener to markdown.parse
|
||||||
|
* - Add button to blog post form to insert photos from albums
|
||||||
|
*/
|
||||||
|
public function boot() {
|
||||||
|
Event::listen('markdown.parse', 'Graker\PhotoAlbums\Classes\MarkdownPhotoInsert@parse');
|
||||||
|
$this->extendBlogPostForm();
|
||||||
|
$this->registerMenuItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends Blog post form by adding a new button: Insert photo from albums
|
||||||
|
*/
|
||||||
|
protected function extendBlogPostForm() {
|
||||||
|
Event::listen('backend.form.extendFields', function (Form $widget) {
|
||||||
|
// attach to post forms only
|
||||||
|
$controller = $widget->getController();
|
||||||
|
if (!($controller instanceof \RainLab\Blog\Controllers\Posts)) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
if (!($widget->model instanceof \RainLab\Blog\Models\Post)) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add PhotoSelector widget to Post controller
|
||||||
|
$photo_selector = new PhotoSelector($controller);
|
||||||
|
$photo_selector->alias = 'photoSelector';
|
||||||
|
$photo_selector->bindToController();
|
||||||
|
|
||||||
|
// add javascript extending Markdown editor with new button
|
||||||
|
$widget->addJs('/plugins/graker/photoalbums/assets/js/extend-markdown-editor.js');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen to events from RainLab.Pages plugin to register and resolve new menu items
|
||||||
|
*/
|
||||||
|
protected function registerMenuItems() {
|
||||||
|
// register items
|
||||||
|
Event::listen('pages.menuitem.listTypes', function() {
|
||||||
|
return MenuItemsProvider::listTypes();
|
||||||
|
});
|
||||||
|
|
||||||
|
// return item type info
|
||||||
|
Event::listen('pages.menuitem.getTypeInfo', function($type) {
|
||||||
|
if (in_array($type, array_keys(MenuItemsProvider::listTypes()))) {
|
||||||
|
return MenuItemsProvider::getMenuTypeInfo($type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// resolve item
|
||||||
|
Event::listen('pages.menuitem.resolveItem', function($type, $item, $url, $theme) {
|
||||||
|
if (in_array($type, array_keys(MenuItemsProvider::listTypes()))) {
|
||||||
|
return MenuItemsProvider::resolveMenuItem($type, $item, $url, $theme);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Photo Albums plugin
|
||||||
|
|
||||||
|
This is [OctoberCMS](http://octobercms.com) plugin allowing to create, edit and display photos arranged in albums. Each Photo is a model with image attached to it.
|
||||||
|
And Album is an another model, owning multiple of Photos.
|
||||||
|
|
||||||
|
The aim of this approach is to treat each photo as a separate entity which can be displayed separately, have it's own title, description, could have comments of its own etc.
|
||||||
|
And at the same time, photos are grouped in albums and can be displayed on album's page with pagination.
|
||||||
|
|
||||||
|
Also now you can insert photos from galleries right into the blog posts (see below).
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
There are 4 components in the plugin: Photo, Album, Albums List and Random Photos.
|
||||||
|
|
||||||
|
### Photo
|
||||||
|
|
||||||
|
Photo component should be used to output a single photo. Data available for this single photo:
|
||||||
|
|
||||||
|
* photo's title and description
|
||||||
|
* photo's created date
|
||||||
|
* image path
|
||||||
|
* parent album's title and url
|
||||||
|
* mini-navigator to go to the previous or the next photo
|
||||||
|
|
||||||
|
### Album
|
||||||
|
|
||||||
|
This component is used to output album's photos. Data available:
|
||||||
|
|
||||||
|
* album's title and description
|
||||||
|
* each photo's title, thumb and url
|
||||||
|
* pagination
|
||||||
|
|
||||||
|
### Albums list
|
||||||
|
|
||||||
|
Use this component to output all albums (pagination is supported). For each album you can output title, image thumb and photos count.
|
||||||
|
Image thumb is generated from selected front photo which you can set on album's edit page in the photos list (check the photo, click "Set as front" button).
|
||||||
|
If no photo is selected is front, the latest uploaded photo will be used for thumb.
|
||||||
|
|
||||||
|
### Random Photos
|
||||||
|
|
||||||
|
Displays given number of random photos. Note that for big database tables, selects with random sorting can slow down your site, so use the component with caution and make use of cache lifetime to avoid running the query on each component show. Also note that due to the use of RAND() function for sorting, the component would work with MySQL and Sqlite databases only. To use the component with other databases, you'd need to rewrite orderBy() call, otherwise it will just return non-random collection. After October updates to Laravel 5.5, DB-independent function will be used.
|
||||||
|
|
||||||
|
## Uploading
|
||||||
|
|
||||||
|
At the moment, there are 3 ways to upload photos:
|
||||||
|
|
||||||
|
* Add single photo using the New photo form
|
||||||
|
* Add single photo using relations manager when in album update form
|
||||||
|
* Add multiple photos to an album from the Upload photos form
|
||||||
|
|
||||||
|
Uploading multiple photos is supported with the [Dropzone.js](http://www.dropzonejs.com/) plugin. You don't need to install it as it is already a part of October.
|
||||||
|
|
||||||
|
## Insert photos from galleries
|
||||||
|
|
||||||
|
### Dialog to insert photos into Blog posts
|
||||||
|
|
||||||
|
You can insert photos from galleries created by this plugin into [Blog](https://octobercms.com/plugin/rainlab-blog) posts.
|
||||||
|
Just click on a camera icon near media manager in the post markdown editor, then select album and photo. Markdown code for selected photo will appear in the editor.
|
||||||
|
|
||||||
|
### Markdown syntax
|
||||||
|
|
||||||
|
To change the code template, go to Settings -> Photo Albums tab. The syntax is explained below and you can use `%id%` and `%title%` placeholders for photo id and title.
|
||||||
|
You can use placeholders multiple times. For example, you can type in template like this:
|
||||||
|
|
||||||
|
```[![%title%]([photo:%id%:640:480:crop]){.img-responsive}]([photo:%id%] "%title%"){.magnific}```
|
||||||
|
|
||||||
|
It will result in image 640x480 cropped thumb with title having `img-responsive` class, linked to full-size image with title and `magnific` class.
|
||||||
|
Note that you can't use quote symbol in the template, you have to replace quotes with `"`.
|
||||||
|
|
||||||
|
The syntax for [photo] part is as follows:
|
||||||
|
|
||||||
|
```[photo:id:width:height:mode]```
|
||||||
|
|
||||||
|
Here:
|
||||||
|
* `id` is a photo model id (you can get it from url).
|
||||||
|
* `width` and `height` are optional, if they are provided, photo will be inserted as a thumbnail with these width and height.
|
||||||
|
* `mode` is an optional mode for thumbnail generation, possible values are: `auto`, `exact`, `portrait`, `landscape`, `crop` (see October thumbs generation for more info). Defaults to `auto`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
* `[photo:123:640:480:crop]` for cropped thumbnail 640x480 of photo with id 123
|
||||||
|
* `[photo:123:200:200]` for thumbnail 200x200 of photo with id 123
|
||||||
|
* `[photo:123]` for image as is, no thumb
|
||||||
|
|
||||||
|
The placeholder will be replaced with path to image (or thumb), for example: `/storage/app/uploads/public/57a/24e/bff/thumb_301_640x480_0_0_auto.jpg`.
|
||||||
|
|
||||||
|
You can use this code to insert photos in any markdown-processed text.
|
||||||
|
|
||||||
|
Note that to avoid possible conflicts, placeholders are only replaced inside `src=""` and `href=""` clauses.
|
||||||
|
So if you add placeholder in href for anchor tag or in src for img tag (or into Markdown link or image), it will be replaced. And if you add it into plain text, it will be ignored.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
### Attachments location
|
||||||
|
|
||||||
|
Right now plugin uses System\Models\File to attach images so they are stored in system uploads, each one in separate directory with random names.
|
||||||
|
It could be nice to put them in one directory per album.
|
||||||
|
|
||||||
|
### Categories support for albums
|
||||||
|
|
||||||
|
It would be nice to be able to separate albums by categories, to group them by categories in the AlbumsList component etc.
|
|
@ -0,0 +1,392 @@
|
||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright (c) 2012 Matias Meno <m@tias.me>
|
||||||
|
*/
|
||||||
|
@-webkit-keyframes passing-through {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); }
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(-40px);
|
||||||
|
-moz-transform: translateY(-40px);
|
||||||
|
-ms-transform: translateY(-40px);
|
||||||
|
-o-transform: translateY(-40px);
|
||||||
|
transform: translateY(-40px); } }
|
||||||
|
@-moz-keyframes passing-through {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); }
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(-40px);
|
||||||
|
-moz-transform: translateY(-40px);
|
||||||
|
-ms-transform: translateY(-40px);
|
||||||
|
-o-transform: translateY(-40px);
|
||||||
|
transform: translateY(-40px); } }
|
||||||
|
@keyframes passing-through {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); }
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(-40px);
|
||||||
|
-moz-transform: translateY(-40px);
|
||||||
|
-ms-transform: translateY(-40px);
|
||||||
|
-o-transform: translateY(-40px);
|
||||||
|
transform: translateY(-40px); } }
|
||||||
|
@-webkit-keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); } }
|
||||||
|
@-moz-keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); } }
|
||||||
|
@keyframes slide-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translateY(40px);
|
||||||
|
-moz-transform: translateY(40px);
|
||||||
|
-ms-transform: translateY(40px);
|
||||||
|
-o-transform: translateY(40px);
|
||||||
|
transform: translateY(40px); }
|
||||||
|
30% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: translateY(0px);
|
||||||
|
-moz-transform: translateY(0px);
|
||||||
|
-ms-transform: translateY(0px);
|
||||||
|
-o-transform: translateY(0px);
|
||||||
|
transform: translateY(0px); } }
|
||||||
|
@-webkit-keyframes pulse {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); }
|
||||||
|
10% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
-moz-transform: scale(1.1);
|
||||||
|
-ms-transform: scale(1.1);
|
||||||
|
-o-transform: scale(1.1);
|
||||||
|
transform: scale(1.1); }
|
||||||
|
20% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); } }
|
||||||
|
@-moz-keyframes pulse {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); }
|
||||||
|
10% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
-moz-transform: scale(1.1);
|
||||||
|
-ms-transform: scale(1.1);
|
||||||
|
-o-transform: scale(1.1);
|
||||||
|
transform: scale(1.1); }
|
||||||
|
20% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); } }
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); }
|
||||||
|
10% {
|
||||||
|
-webkit-transform: scale(1.1);
|
||||||
|
-moz-transform: scale(1.1);
|
||||||
|
-ms-transform: scale(1.1);
|
||||||
|
-o-transform: scale(1.1);
|
||||||
|
transform: scale(1.1); }
|
||||||
|
20% {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
transform: scale(1); } }
|
||||||
|
.dropzone, .dropzone * {
|
||||||
|
box-sizing: border-box; }
|
||||||
|
|
||||||
|
.dropzone {
|
||||||
|
min-height: 150px;
|
||||||
|
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||||
|
background: white;
|
||||||
|
padding: 20px 20px; }
|
||||||
|
.dropzone.dz-clickable {
|
||||||
|
cursor: pointer; }
|
||||||
|
.dropzone.dz-clickable * {
|
||||||
|
cursor: default; }
|
||||||
|
.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
|
||||||
|
cursor: pointer; }
|
||||||
|
.dropzone.dz-started .dz-message {
|
||||||
|
display: none; }
|
||||||
|
.dropzone.dz-drag-hover {
|
||||||
|
border-style: solid; }
|
||||||
|
.dropzone.dz-drag-hover .dz-message {
|
||||||
|
opacity: 0.5; }
|
||||||
|
.dropzone .dz-message {
|
||||||
|
text-align: center;
|
||||||
|
margin: 2em 0; }
|
||||||
|
.dropzone .dz-preview {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin: 16px;
|
||||||
|
min-height: 100px; }
|
||||||
|
.dropzone .dz-preview:hover {
|
||||||
|
z-index: 1000; }
|
||||||
|
.dropzone .dz-preview:hover .dz-details {
|
||||||
|
opacity: 1; }
|
||||||
|
.dropzone .dz-preview.dz-file-preview .dz-image {
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #999;
|
||||||
|
background: linear-gradient(to bottom, #eee, #ddd); }
|
||||||
|
.dropzone .dz-preview.dz-file-preview .dz-details {
|
||||||
|
opacity: 1; }
|
||||||
|
.dropzone .dz-preview.dz-image-preview {
|
||||||
|
background: white; }
|
||||||
|
.dropzone .dz-preview.dz-image-preview .dz-details {
|
||||||
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
-moz-transition: opacity 0.2s linear;
|
||||||
|
-ms-transition: opacity 0.2s linear;
|
||||||
|
-o-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear; }
|
||||||
|
.dropzone .dz-preview .dz-remove {
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none; }
|
||||||
|
.dropzone .dz-preview .dz-remove:hover {
|
||||||
|
text-decoration: underline; }
|
||||||
|
.dropzone .dz-preview:hover .dz-details {
|
||||||
|
opacity: 1; }
|
||||||
|
.dropzone .dz-preview .dz-details {
|
||||||
|
z-index: 20;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 0;
|
||||||
|
font-size: 13px;
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 2em 1em;
|
||||||
|
text-align: center;
|
||||||
|
color: rgba(0, 0, 0, 0.9);
|
||||||
|
line-height: 150%; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-size {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
font-size: 16px; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename {
|
||||||
|
white-space: nowrap; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename:hover span {
|
||||||
|
border: 1px solid rgba(200, 200, 200, 0.8);
|
||||||
|
background-color: rgba(255, 255, 255, 0.8); }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
|
||||||
|
border: 1px solid transparent; }
|
||||||
|
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
|
||||||
|
background-color: rgba(255, 255, 255, 0.4);
|
||||||
|
padding: 0 0.4em;
|
||||||
|
border-radius: 3px; }
|
||||||
|
.dropzone .dz-preview:hover .dz-image img {
|
||||||
|
-webkit-transform: scale(1.05, 1.05);
|
||||||
|
-moz-transform: scale(1.05, 1.05);
|
||||||
|
-ms-transform: scale(1.05, 1.05);
|
||||||
|
-o-transform: scale(1.05, 1.05);
|
||||||
|
transform: scale(1.05, 1.05);
|
||||||
|
-webkit-filter: blur(8px);
|
||||||
|
filter: blur(8px); }
|
||||||
|
.dropzone .dz-preview .dz-image {
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
z-index: 10; }
|
||||||
|
.dropzone .dz-preview .dz-image img {
|
||||||
|
display: block; }
|
||||||
|
.dropzone .dz-preview.dz-success .dz-success-mark {
|
||||||
|
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||||
|
.dropzone .dz-preview.dz-error .dz-error-mark {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
|
||||||
|
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
|
||||||
|
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 500;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -27px;
|
||||||
|
margin-top: -27px; }
|
||||||
|
.dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {
|
||||||
|
display: block;
|
||||||
|
width: 54px;
|
||||||
|
height: 54px; }
|
||||||
|
.dropzone .dz-preview.dz-processing .dz-progress {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transition: all 0.2s linear;
|
||||||
|
-moz-transition: all 0.2s linear;
|
||||||
|
-ms-transition: all 0.2s linear;
|
||||||
|
-o-transition: all 0.2s linear;
|
||||||
|
transition: all 0.2s linear; }
|
||||||
|
.dropzone .dz-preview.dz-complete .dz-progress {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.4s ease-in;
|
||||||
|
-moz-transition: opacity 0.4s ease-in;
|
||||||
|
-ms-transition: opacity 0.4s ease-in;
|
||||||
|
-o-transition: opacity 0.4s ease-in;
|
||||||
|
transition: opacity 0.4s ease-in; }
|
||||||
|
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
|
||||||
|
-webkit-animation: pulse 6s ease infinite;
|
||||||
|
-moz-animation: pulse 6s ease infinite;
|
||||||
|
-ms-animation: pulse 6s ease infinite;
|
||||||
|
-o-animation: pulse 6s ease infinite;
|
||||||
|
animation: pulse 6s ease infinite; }
|
||||||
|
.dropzone .dz-preview .dz-progress {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
height: 16px;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -8px;
|
||||||
|
width: 80px;
|
||||||
|
margin-left: -40px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden; }
|
||||||
|
.dropzone .dz-preview .dz-progress .dz-upload {
|
||||||
|
background: #333;
|
||||||
|
background: linear-gradient(to bottom, #666, #444);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 0;
|
||||||
|
-webkit-transition: width 300ms ease-in-out;
|
||||||
|
-moz-transition: width 300ms ease-in-out;
|
||||||
|
-ms-transition: width 300ms ease-in-out;
|
||||||
|
-o-transition: width 300ms ease-in-out;
|
||||||
|
transition: width 300ms ease-in-out; }
|
||||||
|
.dropzone .dz-preview.dz-error .dz-error-message {
|
||||||
|
display: block; }
|
||||||
|
.dropzone .dz-preview.dz-error:hover .dz-error-message {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto; }
|
||||||
|
.dropzone .dz-preview .dz-error-message {
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transition: opacity 0.3s ease;
|
||||||
|
-moz-transition: opacity 0.3s ease;
|
||||||
|
-ms-transition: opacity 0.3s ease;
|
||||||
|
-o-transition: opacity 0.3s ease;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
top: 130px;
|
||||||
|
left: -10px;
|
||||||
|
width: 140px;
|
||||||
|
background: #be2626;
|
||||||
|
background: linear-gradient(to bottom, #be2626, #a92222);
|
||||||
|
padding: 0.5em 1.2em;
|
||||||
|
color: white; }
|
||||||
|
.dropzone .dz-preview .dz-error-message:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 64px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 6px solid transparent;
|
||||||
|
border-right: 6px solid transparent;
|
||||||
|
border-bottom: 6px solid #be2626;
|
||||||
|
}
|
||||||
|
.dropzone input.form-control {
|
||||||
|
width: 120px;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* Script to extend Markdown editor for Blog post form
|
||||||
|
* - add button to insert photos from albums
|
||||||
|
* - add visual dialog for this button
|
||||||
|
*/
|
||||||
|
|
||||||
|
+function ($) {
|
||||||
|
|
||||||
|
$(document).one('ready', function () {
|
||||||
|
var editor = $('[data-control="markdowneditor"]').data('oc.markdownEditor');
|
||||||
|
|
||||||
|
// to preserve last selected album so user won't open it again and again
|
||||||
|
var currentAlbum = 0;
|
||||||
|
|
||||||
|
// FIXME Localize label when it is supported
|
||||||
|
var button = {
|
||||||
|
label: 'Insert photo from Photoalbums',
|
||||||
|
cssClass: 'oc-autumn-button oc-icon-camera-retro',
|
||||||
|
insertAfter: 'mediaimage',
|
||||||
|
action: 'showAlbumsDialog',
|
||||||
|
template: '$1'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Markdown editor method to show photo selection dialog
|
||||||
|
*
|
||||||
|
* @param template
|
||||||
|
*/
|
||||||
|
editor.showAlbumsDialog = function (template) {
|
||||||
|
var editor = this.editor,
|
||||||
|
pos = this.editor.getCursorPosition();
|
||||||
|
|
||||||
|
new $.oc.photoselector.popup({
|
||||||
|
alias: 'photoSelector',
|
||||||
|
album: currentAlbum,
|
||||||
|
onInsert: function (code, album) {
|
||||||
|
editor.insert(template.replace('$1', code));
|
||||||
|
editor.moveCursorToPosition(pos);
|
||||||
|
editor.focus();
|
||||||
|
this.hide();
|
||||||
|
// save current album
|
||||||
|
currentAlbum = album;
|
||||||
|
console.log(currentAlbum);
|
||||||
|
console.log(album);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//add button to editor
|
||||||
|
editor.addToolbarButton('photoalbums', button);
|
||||||
|
});
|
||||||
|
|
||||||
|
}(window.jQuery);
|
|
@ -0,0 +1,83 @@
|
||||||
|
/**
|
||||||
|
* Dropzone multiupload support to upload photos to album
|
||||||
|
*/
|
||||||
|
|
||||||
|
+function ($) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* File is being removed from the list
|
||||||
|
* We need to remove it on the server
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
var removeFile = function (file) {
|
||||||
|
var $preview = $(file.previewElement);
|
||||||
|
var fileData = {
|
||||||
|
file_id: $preview.data('id'),
|
||||||
|
_token: $('input[name="_token"]').attr('value')
|
||||||
|
};
|
||||||
|
$(this).request('onFileRemove', {data: fileData});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* File is being sent to the server
|
||||||
|
* Used to add CSRF token to the form
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* @param xhr
|
||||||
|
* @param formData
|
||||||
|
*/
|
||||||
|
var sendingData = function (file, xhr, formData) {
|
||||||
|
var token = $('input[name="_token"]').attr('value');
|
||||||
|
formData.append('_token', token);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* File upload success callback
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
|
var uploadSuccess = function (file, response) {
|
||||||
|
var $preview = $(file.previewElement);
|
||||||
|
if (response.id) {
|
||||||
|
$preview.data('id', response.id);
|
||||||
|
// hidden value to pass file id when saving form
|
||||||
|
$preview.append('<input type="hidden" name="file-id[' + response.id + ']" value="' + response.id + '">');
|
||||||
|
$preview.append('<div class="form-group"><input type="text" name="file-title[' + response.id + ']" class="form-control"></div>');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes Dropzone
|
||||||
|
*/
|
||||||
|
var initDropzone = function () {
|
||||||
|
// register removed file callback
|
||||||
|
this.on('removedfile', removeFile);
|
||||||
|
// register before-send callback
|
||||||
|
this.on('sending', sendingData);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize file upload
|
||||||
|
*/
|
||||||
|
$(document).ready(function () {
|
||||||
|
$("div.field-fileupload").each(function () {
|
||||||
|
var uploadUrl = $(this).attr('data-url');
|
||||||
|
$(this).dropzone({
|
||||||
|
url: uploadUrl,
|
||||||
|
init: initDropzone,
|
||||||
|
addRemoveLinks: true,
|
||||||
|
previewsContainer: '#filesContainer',
|
||||||
|
success: uploadSuccess
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} (window.jQuery);
|
|
@ -0,0 +1,117 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Graker\PhotoAlbums\Classes;
|
||||||
|
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MarkdownPhotoInsert
|
||||||
|
* Parses Markdown text to replace [photo:123:640:480:auto] placeholders with links to actual images from photo gallery
|
||||||
|
*
|
||||||
|
* @package Graker\PhotoAlbums\Classes
|
||||||
|
*/
|
||||||
|
class MarkdownPhotoInsert {
|
||||||
|
|
||||||
|
const PHOTO_A_REGEXP = '(href=\"(\[photo\:[0-9]+(?:\:[0-9]+\:[0-9]+(?:\:auto|\:exact|\:portrait|\:landscape|\:crop)?)?\])\")';
|
||||||
|
const PHOTO_IMG_REGEXP = '(src=\"(\[photo\:[0-9]+(?:\:[0-9]+\:[0-9]+(?:\:auto|\:exact|\:portrait|\:landscape|\:crop)?)?\])\")';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Replace [photo:123:640:480:auto] placeholders with links to actual images from photo gallery
|
||||||
|
* where
|
||||||
|
* - photo is a constant string
|
||||||
|
* - 123 is a photo id
|
||||||
|
* - 640:480 is an optional size of thumbnail to be created
|
||||||
|
* - auto is an optional crop mode to be used
|
||||||
|
* Placeholders are allowed (i.e. replaced) only inside `src=""` and `href=""`
|
||||||
|
*
|
||||||
|
* @param string $text original text
|
||||||
|
* @param \stdClass $data contains text property with text processed so far
|
||||||
|
*/
|
||||||
|
public function parse($text, $data) {
|
||||||
|
$images = array();
|
||||||
|
$links = array();
|
||||||
|
|
||||||
|
preg_match_all(self::PHOTO_A_REGEXP, $data->text, $links, PREG_SET_ORDER);
|
||||||
|
preg_match_all(self::PHOTO_IMG_REGEXP, $data->text, $images, PREG_SET_ORDER);
|
||||||
|
|
||||||
|
if (!empty($images)) {
|
||||||
|
$data->text = $this->replaceMatches($images, $data->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($links)) {
|
||||||
|
$data->text = $this->replaceMatches($links, $data->text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Goes over all matches and replaces them in text
|
||||||
|
* Returns processed text
|
||||||
|
*
|
||||||
|
* @param $matches
|
||||||
|
* @param $text
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function replaceMatches($matches, $text) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
list($entry, $placeholder) = $match;
|
||||||
|
$replacement = $this->getReplacement($entry, $placeholder);
|
||||||
|
$text = str_replace($entry, $replacement, $text);
|
||||||
|
}
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns replacement for text
|
||||||
|
* (replaces [photo:id:width:height:mode] with resulting photo's image path)
|
||||||
|
*
|
||||||
|
* @param $entry
|
||||||
|
* @param $placeholder
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getReplacement($entry, $placeholder) {
|
||||||
|
list($id, $width, $height, $mode) = $this->getPhotoParams($placeholder);
|
||||||
|
$photo = Photo::where('id', $id)
|
||||||
|
->with('image')
|
||||||
|
->first();
|
||||||
|
if (!$photo) {
|
||||||
|
return $placeholder;
|
||||||
|
} else {
|
||||||
|
if ($width && $height) {
|
||||||
|
$path = $photo->image->getThumb($width, $height, ['mode' => $mode]);
|
||||||
|
} else {
|
||||||
|
$path = $photo->image->path;
|
||||||
|
}
|
||||||
|
return str_replace($placeholder, $path, $entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Parses parameters of image from the tag and returns them in array
|
||||||
|
* [$id, $width, $height, $mode]
|
||||||
|
* Width, height and mode are optional and will return 0 and empty string
|
||||||
|
* if omitted in the tag
|
||||||
|
*
|
||||||
|
* @param string $placeholder
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getPhotoParams($placeholder) {
|
||||||
|
// remove brackets
|
||||||
|
$values = str_replace('[', '', $placeholder);
|
||||||
|
$values = str_replace(']', '', $values);
|
||||||
|
// get parameters
|
||||||
|
$values = explode(':', $values);
|
||||||
|
$id = $values[1];
|
||||||
|
$width = isset($values[2]) ? $values[2] : 0;
|
||||||
|
$height = isset($values[3]) ? $values[3] : 0;
|
||||||
|
$mode = isset($values[4]) ? $values[4] : 'auto';
|
||||||
|
return array($id, $width, $height, $mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,349 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Graker\PhotoAlbums\Classes;
|
||||||
|
|
||||||
|
use Cms\Classes\Page as CmsPage;
|
||||||
|
use Cms\Classes\Theme;
|
||||||
|
use RainLab\Pages\Classes\MenuItem;
|
||||||
|
use Graker\PhotoAlbums\Models\Album;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
use Url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class MenuItemsProvider
|
||||||
|
* RainLab.Pages plugin integration (for menu items and/or XML sitemap)
|
||||||
|
*/
|
||||||
|
class MenuItemsProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns array of items info for sitemap
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function listTypes() {
|
||||||
|
return [
|
||||||
|
'all-photo-albums' => 'graker.photoalbums::lang.plugin.all_photo_albums',
|
||||||
|
'all-photos' => 'graker.photoalbums::lang.plugin.all_photos',
|
||||||
|
'photo-album' => 'graker.photoalbums::lang.plugin.album',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns an array of info about menu item type
|
||||||
|
*
|
||||||
|
* @param string $type item name
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function getMenuTypeInfo($type) {
|
||||||
|
switch ($type) {
|
||||||
|
case 'all-photo-albums' :
|
||||||
|
$result = self::getAllAlbumsInfo();
|
||||||
|
break;
|
||||||
|
case 'all-photos' :
|
||||||
|
$result = self::getAllPhotosInfo();
|
||||||
|
break;
|
||||||
|
case 'photo-album' :
|
||||||
|
$result = self::getSingleAlbumInfo();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns information about a menu item
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param MenuItem $item
|
||||||
|
* @param string $url
|
||||||
|
* @param Theme $theme
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function resolveMenuItem($type, $item, $url, $theme) {
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'all-photo-albums' :
|
||||||
|
$result = self::resolveAllAlbumsItem($item, $url, $theme);
|
||||||
|
break;
|
||||||
|
case 'all-photos' :
|
||||||
|
$result = self::resolveAllPhotosItem($item, $url, $theme);
|
||||||
|
break;
|
||||||
|
case 'photo-album' :
|
||||||
|
$result = self::resolveSingleAlbumItem($item, $url, $theme);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$result = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Generates url for the item to be resolved
|
||||||
|
*
|
||||||
|
* @param int $year - year number
|
||||||
|
* @param string $pageCode - page code to be used
|
||||||
|
* @param $theme
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function getUrl($year, $pageCode, $theme) {
|
||||||
|
$page = CmsPage::loadCached($theme, $pageCode);
|
||||||
|
if (!$page) return '';
|
||||||
|
|
||||||
|
$properties = $page->getComponentProperties('blogArchive');
|
||||||
|
if (!isset($properties['yearParam'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// get year url param and strip it of {{ :<name> }} to get pure name
|
||||||
|
$paramName = str_replace(array('{', '}', ' ', ':'), '', $properties['yearParam']);
|
||||||
|
$url = CmsPage::url($page->getBaseFileName(), [$paramName => $year]);
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns menu type info for all-photo-albums menu item
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function getAllAlbumsInfo() {
|
||||||
|
$result = ['dynamicItems' => TRUE,];
|
||||||
|
$result['cmsPages'] = self::getCmsPages('photoAlbum');
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns menu type info for all-photo-albums menu item
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function getSingleAlbumInfo() {
|
||||||
|
$result = [
|
||||||
|
'dynamicItems' => FALSE,
|
||||||
|
'nesting' => FALSE,
|
||||||
|
];
|
||||||
|
$result['cmsPages'] = self::getCmsPages('photoAlbum');
|
||||||
|
|
||||||
|
$references = [];
|
||||||
|
$albums = Album::all();
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
$references[$album->id] = $album->title;
|
||||||
|
}
|
||||||
|
$result['references'] = $references;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns menu type info for all-photos menu item
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function getAllPhotosInfo() {
|
||||||
|
$result = ['dynamicItems' => true,];
|
||||||
|
$result['cmsPages'] = self::getCmsPages('singlePhoto');
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Return array of Cms pages having $component attached
|
||||||
|
*
|
||||||
|
* @param string $component
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function getCmsPages($component) {
|
||||||
|
$theme = Theme::getActiveTheme();
|
||||||
|
|
||||||
|
$pages = CmsPage::listInTheme($theme, true);
|
||||||
|
$cmsPages = [];
|
||||||
|
|
||||||
|
foreach ($pages as $page) {
|
||||||
|
if (!$page->hasComponent($component)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmsPages[] = $page;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cmsPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Resolves All Albums menu item
|
||||||
|
*
|
||||||
|
* @param MenuItem $item
|
||||||
|
* @param string $url
|
||||||
|
* @param Theme $theme
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function resolveAllAlbumsItem($item, $url, $theme) {
|
||||||
|
$result = [
|
||||||
|
'items' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$albums = Album::all();
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
$albumItem = [
|
||||||
|
'title' => $album->title,
|
||||||
|
'url' => self::getAlbumUrl($album, $item->cmsPage, $theme),
|
||||||
|
'mtime' => $album->updated_at,
|
||||||
|
];
|
||||||
|
$albumItem['isActive'] = ($albumItem['url'] == $url);
|
||||||
|
$result['items'][] = $albumItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Resolves single Album menu item
|
||||||
|
*
|
||||||
|
* @param MenuItem $item
|
||||||
|
* @param string $url
|
||||||
|
* @param Theme $theme
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function resolveSingleAlbumItem($item, $url, $theme) {
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
if (!$item->reference || !$item->cmsPage) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$album = Album::find($item->reference);
|
||||||
|
if (!$album) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageUrl = self::getAlbumUrl($album, $item->cmsPage, $theme);
|
||||||
|
if (!$pageUrl) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$pageUrl = Url::to($pageUrl);
|
||||||
|
|
||||||
|
$result['url'] = $pageUrl;
|
||||||
|
$result['isActive'] = ($pageUrl == $url);
|
||||||
|
$result['mtime'] = $album->updated_at;
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Resolves All Photos menu item
|
||||||
|
*
|
||||||
|
* @param MenuItem $item
|
||||||
|
* @param string $url
|
||||||
|
* @param Theme $theme
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected static function resolveAllPhotosItem($item, $url, $theme) {
|
||||||
|
$result = [
|
||||||
|
'items' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$photos = Photo::all();
|
||||||
|
foreach ($photos as $photo) {
|
||||||
|
$photoItem = [
|
||||||
|
'title' => $photo->title,
|
||||||
|
'url' => self::getPhotoUrl($photo, $item->cmsPage, $theme),
|
||||||
|
'mtime' => $photo->updated_at,
|
||||||
|
];
|
||||||
|
$photoItem['isActive'] = ($photoItem['url'] == $url);
|
||||||
|
$result['items'][] = $photoItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Generates url for album
|
||||||
|
*
|
||||||
|
* @param Album $album
|
||||||
|
* @param string $pageCode
|
||||||
|
* @param Theme $theme
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function getAlbumUrl($album, $pageCode, $theme) {
|
||||||
|
$page = CmsPage::loadCached($theme, $pageCode);
|
||||||
|
if (!$page) return '';
|
||||||
|
|
||||||
|
$properties = $page->getComponentProperties('photoAlbum');
|
||||||
|
if (!isset($properties['slug'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\{\{([^\}]+)\}\}$/', $properties['slug'], $matches)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramName = substr(trim($matches[1]), 1);
|
||||||
|
$params = [
|
||||||
|
$paramName => $album->slug,
|
||||||
|
'id' => $album->id,
|
||||||
|
];
|
||||||
|
$url = CmsPage::url($page->getBaseFileName(), $params);
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Generates url for photo
|
||||||
|
*
|
||||||
|
* @param Photo $photo
|
||||||
|
* @param string $pageCode
|
||||||
|
* @param Theme $theme
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function getPhotoUrl($photo, $pageCode, $theme) {
|
||||||
|
$page = CmsPage::loadCached($theme, $pageCode);
|
||||||
|
if (!$page) return '';
|
||||||
|
|
||||||
|
$properties = $page->getComponentProperties('singlePhoto');
|
||||||
|
if (!isset($properties['id'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('/^\{\{([^\}]+)\}\}$/', $properties['id'], $matches)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramName = substr(trim($matches[1]), 1);
|
||||||
|
$params = [
|
||||||
|
$paramName => $photo->id,
|
||||||
|
'album_slug' => $photo->album->slug,
|
||||||
|
];
|
||||||
|
$url = CmsPage::url($page->getBaseFileName(), $params);
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Components;
|
||||||
|
|
||||||
|
use Cms\Classes\Page;
|
||||||
|
use Cms\Classes\ComponentBase;
|
||||||
|
use Graker\PhotoAlbums\Models\Album as AlbumModel;
|
||||||
|
use Redirect;
|
||||||
|
|
||||||
|
class Album extends ComponentBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var AlbumModel reference to album being displayed
|
||||||
|
*/
|
||||||
|
public $album;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int current page number
|
||||||
|
*/
|
||||||
|
public $currentPage;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int last page number
|
||||||
|
*/
|
||||||
|
public $lastPage;
|
||||||
|
|
||||||
|
|
||||||
|
public function componentDetails()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'graker.photoalbums::lang.plugin.album',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.album_description'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array of component properties
|
||||||
|
*/
|
||||||
|
public function defineProperties()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.plugin.slug_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.plugin.slug_description',
|
||||||
|
'default' => '{{ :slug }}',
|
||||||
|
'type' => 'string'
|
||||||
|
],
|
||||||
|
'photoPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.photo_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.photo_page_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'photoalbums/album/photo',
|
||||||
|
],
|
||||||
|
'thumbMode' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_mode_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_mode_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'auto',
|
||||||
|
],
|
||||||
|
'thumbWidth' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_width_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_width_description',
|
||||||
|
'default' => 640,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.thumb_width_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'thumbHeight' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_height_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_height_description',
|
||||||
|
'default' => 480,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.thumbs_height_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'photosOnPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.photos_on_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.photos_on_page_description',
|
||||||
|
'default' => 12,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.photos_on_page_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns pages list for album page select box setting
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPhotoPageOptions() {
|
||||||
|
return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns thumb resize mode options for thumb mode select box setting
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getThumbModeOptions() {
|
||||||
|
return [
|
||||||
|
'auto' => 'Auto',
|
||||||
|
'exact' => 'Exact',
|
||||||
|
'portrait' => 'Portrait',
|
||||||
|
'landscape' => 'Landscape',
|
||||||
|
'crop' => 'Crop',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get photo page number from query
|
||||||
|
*/
|
||||||
|
protected function setCurrentPage() {
|
||||||
|
if (isset($_GET['page'])) {
|
||||||
|
if (ctype_digit($_GET['page']) && ($_GET['page'] > 0)) {
|
||||||
|
$this->currentPage = $_GET['page'];
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->currentPage = 1;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads album on onRun event
|
||||||
|
*/
|
||||||
|
public function onRun() {
|
||||||
|
if (!$this->setCurrentPage()) {
|
||||||
|
// if page parameter is invalid, redirect to the first page
|
||||||
|
return Redirect::to($this->currentPageUrl() . '?page=1');
|
||||||
|
}
|
||||||
|
$this->album = $this->page->album = $this->loadAlbum();
|
||||||
|
// if current page is greater than number of pages, redirect to the last page
|
||||||
|
// check for > 1 to avoid infinite redirect when there are no photos
|
||||||
|
if (($this->currentPage > 1) && ($this->currentPage > $this->lastPage)) {
|
||||||
|
return Redirect::to($this->currentPageUrl() . '?page=' . $this->lastPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Loads album model with it's photos
|
||||||
|
*
|
||||||
|
* @return AlbumModel
|
||||||
|
*/
|
||||||
|
protected function loadAlbum() {
|
||||||
|
$slug = $this->property('slug');
|
||||||
|
$album = AlbumModel::where('slug', $slug)
|
||||||
|
->with(['photos' => function ($query) {
|
||||||
|
$query->orderBy('created_at', 'desc');
|
||||||
|
$query->with('image');
|
||||||
|
$query->paginate($this->property('photosOnPage'), $this->currentPage);
|
||||||
|
}])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($album) {
|
||||||
|
//prepare photo urls and thumbs
|
||||||
|
foreach ($album->photos as $photo) {
|
||||||
|
$photo->url = $photo->setUrl($this->property('photoPage'), $this->controller);
|
||||||
|
$photo->thumb = $photo->image->getThumb(
|
||||||
|
$this->property('thumbWidth'),
|
||||||
|
$this->property('thumbHeight'),
|
||||||
|
['mode' => $this->property('thumbMode')]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//setup page numbers
|
||||||
|
$this->lastPage = ceil($album->photosCount / $this->property('photosOnPage'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $album;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Components;
|
||||||
|
|
||||||
|
use Cms\Classes\Page;
|
||||||
|
use Cms\Classes\ComponentBase;
|
||||||
|
use Graker\PhotoAlbums\Models\Album as AlbumModel;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Redirect;
|
||||||
|
|
||||||
|
class AlbumList extends ComponentBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection of albums to display
|
||||||
|
*/
|
||||||
|
public $albums;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int current page number
|
||||||
|
*/
|
||||||
|
public $currentPage;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int last page number
|
||||||
|
*/
|
||||||
|
public $lastPage;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array of component details
|
||||||
|
*/
|
||||||
|
public function componentDetails()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'graker.photoalbums::lang.components.albums_list',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.albums_list_description'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Define properties
|
||||||
|
*
|
||||||
|
* @return array of component properties
|
||||||
|
*/
|
||||||
|
public function defineProperties()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'albumPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.album_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.album_page_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'photoalbums/album',
|
||||||
|
],
|
||||||
|
'thumbMode' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_mode_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_mode_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'auto',
|
||||||
|
],
|
||||||
|
'thumbWidth' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_width_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_width_description',
|
||||||
|
'default' => 640,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.thumb_width_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'thumbHeight' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_height_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_height_description',
|
||||||
|
'default' => 480,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.thumb_height_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'albumsOnPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.albums_on_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.albums_on_page_description',
|
||||||
|
'default' => 12,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.albums_on_page_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns pages list for album page select box setting
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAlbumPageOptions() {
|
||||||
|
return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns thumb resize mode options for thumb mode select box setting
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getThumbModeOptions() {
|
||||||
|
return [
|
||||||
|
'auto' => 'Auto',
|
||||||
|
'exact' => 'Exact',
|
||||||
|
'portrait' => 'Portrait',
|
||||||
|
'landscape' => 'Landscape',
|
||||||
|
'crop' => 'Crop',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get photo page number from query
|
||||||
|
*/
|
||||||
|
protected function setCurrentPage() {
|
||||||
|
if (isset($_GET['page'])) {
|
||||||
|
if (ctype_digit($_GET['page']) && ($_GET['page'] > 0)) {
|
||||||
|
$this->currentPage = $_GET['page'];
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->currentPage = 1;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OnRun implementation
|
||||||
|
* Setup pager
|
||||||
|
* Load albums
|
||||||
|
*/
|
||||||
|
public function onRun() {
|
||||||
|
if (!$this->setCurrentPage()) {
|
||||||
|
return Redirect::to($this->currentPageUrl() . '?page=1');
|
||||||
|
}
|
||||||
|
$this->albums = $this->loadAlbums();
|
||||||
|
$this->prepareAlbums();
|
||||||
|
|
||||||
|
$this->lastPage = $this->albums->lastPage();
|
||||||
|
// if current page is greater than number of pages, redirect to the last page
|
||||||
|
// only if lastPage > 0 to avoid redirect loop when there are no elements
|
||||||
|
if ($this->lastPage && ($this->currentPage > $this->lastPage)) {
|
||||||
|
return Redirect::to($this->currentPageUrl() . '?page=' . $this->lastPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns array of site's albums to be used in component
|
||||||
|
* Albums are sorted by created date desc, each one loaded with one latest photo (or photo set to be front)
|
||||||
|
* Empty albums won't be displayed
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function loadAlbums() {
|
||||||
|
$albums = AlbumModel::orderBy('created_at', 'desc')
|
||||||
|
->has('photos')
|
||||||
|
->with(['latestPhoto' => function ($query) {
|
||||||
|
$query->with('image');
|
||||||
|
}])
|
||||||
|
->with(['front' => function ($query) {
|
||||||
|
$query->with('image');
|
||||||
|
}])
|
||||||
|
->with('photosCount')
|
||||||
|
->paginate($this->property('albumsOnPage'), $this->currentPage);
|
||||||
|
|
||||||
|
return $albums;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Prepares array of album models to be displayed:
|
||||||
|
* - set up album urls
|
||||||
|
* - set up photo counts
|
||||||
|
* - set up album thumb
|
||||||
|
*/
|
||||||
|
protected function prepareAlbums() {
|
||||||
|
//set up photo count and url
|
||||||
|
foreach ($this->albums as $album) {
|
||||||
|
$album->photo_count = $album->photosCount;
|
||||||
|
$album->url = $album->setUrl($this->property('albumPage'), $this->controller);
|
||||||
|
|
||||||
|
// prepare thumb from $album->front if it is set or from latestPhoto otherwise
|
||||||
|
$image = ($album->front) ? $album->front->image : $album->latestPhoto->image;
|
||||||
|
$album->latestPhoto->thumb = $image->getThumb(
|
||||||
|
$this->property('thumbWidth'),
|
||||||
|
$this->property('thumbHeight'),
|
||||||
|
['mode' => $this->property('thumbMode')]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Components;
|
||||||
|
|
||||||
|
use Cms\Classes\ComponentBase;
|
||||||
|
use Cms\Classes\Page;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo as PhotoModel;
|
||||||
|
|
||||||
|
class Photo extends ComponentBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public $photo;
|
||||||
|
|
||||||
|
public function componentDetails()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'graker.photoalbums::lang.plugin.photo',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.photo_description'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Properties of component
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function defineProperties()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.id_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.id_description',
|
||||||
|
'default' => '{{ :id }}',
|
||||||
|
'type' => 'string'
|
||||||
|
],
|
||||||
|
'albumPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.album_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.album_page_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'photoalbums/album',
|
||||||
|
],
|
||||||
|
'photoPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.photo_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.photo_page_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'photoalbums/album/photo',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns pages list for album page select box setting
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAlbumPageOptions() {
|
||||||
|
return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns pages list for photo page select box setting
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPhotoPageOptions() {
|
||||||
|
return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads photo on onRun event
|
||||||
|
*/
|
||||||
|
public function onRun() {
|
||||||
|
$this->photo = $this->page->photo = $this->loadPhoto();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Loads photo to be displayed in this component
|
||||||
|
*
|
||||||
|
* @return PhotoModel
|
||||||
|
*/
|
||||||
|
protected function loadPhoto() {
|
||||||
|
$id = $this->property('id');
|
||||||
|
$photo = PhotoModel::where('id', $id)
|
||||||
|
->with('image')
|
||||||
|
->with('album')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($photo) {
|
||||||
|
// set url so we can have back link to the parent album
|
||||||
|
$photo->album->url = $photo->album->setUrl($this->property('albumPage'), $this->controller);
|
||||||
|
|
||||||
|
//set next and previous photos
|
||||||
|
$photo->next = $photo->nextPhoto();
|
||||||
|
if ($photo->next) {
|
||||||
|
$photo->next->url = $photo->next->setUrl($this->property('photoPage'), $this->controller);
|
||||||
|
}
|
||||||
|
$photo->previous = $photo->previousPhoto();
|
||||||
|
if ($photo->previous) {
|
||||||
|
$photo->previous->url = $photo->previous->setUrl($this->property('photoPage'), $this->controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Components;
|
||||||
|
|
||||||
|
use Cms\Classes\ComponentBase;
|
||||||
|
use Cms\Classes\Page;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo as PhotoModel;
|
||||||
|
use Cache;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class RandomPhotos extends ComponentBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function componentDetails()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'graker.photoalbums::lang.components.random_photos',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.random_photos_description',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defineProperties()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'photosCount' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.photos_count_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.photos_count_description',
|
||||||
|
'default' => 5,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.photos_count_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'cacheLifetime' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.cache_lifetime_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.cache_lifetime_description',
|
||||||
|
'default' => 0,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.cache_lifetime_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'thumbMode' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_mode_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_mode_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'auto',
|
||||||
|
],
|
||||||
|
'thumbWidth' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_width_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_width_description',
|
||||||
|
'default' => 640,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.thumb_width_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'thumbHeight' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.thumb_height_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.thumb_height_description',
|
||||||
|
'default' => 480,
|
||||||
|
'type' => 'string',
|
||||||
|
'validationMessage' => 'graker.photoalbums::lang.errors.thumb_height_error',
|
||||||
|
'validationPattern' => '^[0-9]+$',
|
||||||
|
'required' => FALSE,
|
||||||
|
],
|
||||||
|
'photoPage' => [
|
||||||
|
'title' => 'graker.photoalbums::lang.components.photo_page_label',
|
||||||
|
'description' => 'graker.photoalbums::lang.components.photo_page_description',
|
||||||
|
'type' => 'dropdown',
|
||||||
|
'default' => 'blog/post',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns pages list for album page select box setting
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPhotoPageOptions() {
|
||||||
|
return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns thumb resize mode options for thumb mode select box setting
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getThumbModeOptions() {
|
||||||
|
return [
|
||||||
|
'auto' => 'Auto',
|
||||||
|
'exact' => 'Exact',
|
||||||
|
'portrait' => 'Portrait',
|
||||||
|
'landscape' => 'Landscape',
|
||||||
|
'crop' => 'Crop',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns an array of photosCount random photos
|
||||||
|
* Array is returned if from Cache, Collection otherwise
|
||||||
|
*
|
||||||
|
* @return array|Collection
|
||||||
|
*/
|
||||||
|
public function photos() {
|
||||||
|
$photos = [];
|
||||||
|
if ($this->property('cacheLifetime')) {
|
||||||
|
$photos = Cache::get('photoalbums_random_photos');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($photos)) {
|
||||||
|
$photos = $this->getPhotos();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $photos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns a collection of random photos
|
||||||
|
* Works for MySQL and Sqlite, for other drivers returns non-random selection
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function getPhotos() {
|
||||||
|
$count = $this->property('photosCount');
|
||||||
|
if (DB::connection()->getDriverName() == 'mysql') {
|
||||||
|
$photos = PhotoModel::orderBy(DB::raw('RAND()'));
|
||||||
|
} else if (DB::connection()->getDriverName() == 'sqlite') {
|
||||||
|
$photos = PhotoModel::orderBy(DB::raw('RANDOM()'));
|
||||||
|
} else {
|
||||||
|
$photos = PhotoModel::orderBy('id');
|
||||||
|
}
|
||||||
|
$photos = $photos->with('image')->take($count)->get();
|
||||||
|
|
||||||
|
foreach ($photos as $photo) {
|
||||||
|
$photo->url = $photo->setUrl($this->property('photoPage'), $this->controller);
|
||||||
|
$photo->thumb = $photo->image->getThumb(
|
||||||
|
$this->property('thumbWidth'),
|
||||||
|
$this->property('thumbHeight'),
|
||||||
|
['mode' => $this->property('thumbMode')]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cachePhotos($photos);
|
||||||
|
|
||||||
|
return $photos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Cache photos if caching is enabled
|
||||||
|
*
|
||||||
|
* @param Collection $photos
|
||||||
|
*/
|
||||||
|
protected function cachePhotos($photos) {
|
||||||
|
$cache = $this->property('cacheLifetime');
|
||||||
|
if ($cache) {
|
||||||
|
Cache::put('photoalbums_random_photos', $photos->toArray(), $cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{% set album = __SELF__.album %}
|
||||||
|
|
||||||
|
<h1>{{ album.title }}</h1>
|
||||||
|
|
||||||
|
{% if album.description %}
|
||||||
|
<div class="album-description row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
{{ album.description|raw }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="album-photos row">
|
||||||
|
{% for photo in album.photos %}
|
||||||
|
<div class="photo col-xs-12 col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<a href="{{ photo.url }}"><img
|
||||||
|
data-src="{{ photo.thumb }}"
|
||||||
|
src="{{ photo.thumb }}"
|
||||||
|
alt="{{ photo.title }}"
|
||||||
|
title="{{ photo.title }}"
|
||||||
|
style="max-width: 100%" />
|
||||||
|
</a>
|
||||||
|
<a href="{{ photo.url }}"><strong>{{ photo.title }}</strong></a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col-xs-12 no-data">Album doesn't have any photos yet</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if __SELF__.lastPage > 1 %}
|
||||||
|
<ul class="pagination">
|
||||||
|
{% if __SELF__.currentPage > 1 %}
|
||||||
|
<li><a href="{{ this.page.baseFileName|page }}?page={{ __SELF__.currentPage-1 }}">← Prev</a></li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for page in 1..__SELF__.lastPage %}
|
||||||
|
<li class="{{ __SELF__.currentPage == page ? 'active' : null }}">
|
||||||
|
<a href="{{ this.page.baseFileName|page }}?page={{ page }}">{{ page }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if __SELF__.lastPage > __SELF__.currentPage %}
|
||||||
|
<li><a href="{{ this.page.baseFileName|page }}?page={{ __SELF__.currentPage+1 }}">Next →</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<div class="albums-list row">
|
||||||
|
{% for album in __SELF__.albums %}
|
||||||
|
<div class="album-preview col-xs-12 col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<h3><a href="{{ album.url }}">{{ album.title }}</a></h3>
|
||||||
|
<a href="{{ album.url }}">
|
||||||
|
<img
|
||||||
|
data-src="{{ album.latestPhoto.thumb }}"
|
||||||
|
src="{{ album.latestPhoto.thumb }}"
|
||||||
|
style="max-width: 100%" />
|
||||||
|
</a>
|
||||||
|
Created on {{ album.created_at|date('M d, Y') }}
|
||||||
|
{{ album.photo_count }} images
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col-xs-12 no-data">You have not created any albums yet</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if __SELF__.lastPage > 1 %}
|
||||||
|
<ul class="pagination">
|
||||||
|
{% if __SELF__.currentPage > 1 %}
|
||||||
|
<li><a href="{{ this.page.baseFileName|page }}?page={{ __SELF__.currentPage-1 }}">← Prev</a></li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for page in 1..__SELF__.lastPage %}
|
||||||
|
<li class="{{ __SELF__.currentPage == page ? 'active' : null }}">
|
||||||
|
<a href="{{ this.page.baseFileName|page }}?page={{ page }}">{{ page }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if __SELF__.lastPage > __SELF__.currentPage %}
|
||||||
|
<li><a href="{{ this.page.baseFileName|page }}?page={{ __SELF__.currentPage+1 }}">Next →</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{% set photo = __SELF__.photo %}
|
||||||
|
|
||||||
|
{% if photo.title %}
|
||||||
|
<h1>{{ photo.title }}</h1>
|
||||||
|
{% endif %}
|
||||||
|
<div class="photo-image row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<img
|
||||||
|
data-src="{{ photo.image.filename }}"
|
||||||
|
src="{{ photo.image.path }}"
|
||||||
|
alt="{{ photo.title }}"
|
||||||
|
title="{{ photo.title }}"
|
||||||
|
style="max-width: 100%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="photo-description row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
{{ photo.created_at|date('Y/m/d') }}
|
||||||
|
<a href="{{ photo.album.url }}">{{ photo.album.title }}</a>
|
||||||
|
{% if photo.description %}
|
||||||
|
{{ photo.description | raw }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="text-center col-xs-12">
|
||||||
|
{% if photo.previous %}
|
||||||
|
<a href="{{ photo.previous.url }}">Previous photo</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="disabled">Previous photo</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if photo.next %}
|
||||||
|
<a href="{{ photo.next.url }}">Next photo</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="disabled">Next photo</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="random-photos">
|
||||||
|
{% for photo in __SELF__.photos() %}
|
||||||
|
<div class="photo">
|
||||||
|
<a href="{{ photo.url }}"><img src="{{ photo.thumb }}" /></a>
|
||||||
|
{% if photo.title %}
|
||||||
|
<a href="{{ photo.url }}">{{ photo.title }}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
You have not created any photos
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Controllers;
|
||||||
|
|
||||||
|
use BackendMenu;
|
||||||
|
use Backend\Classes\Controller;
|
||||||
|
use Input;
|
||||||
|
use Graker\PhotoAlbums\Models\Album;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
use ApplicationException;
|
||||||
|
use Response;
|
||||||
|
use Request;
|
||||||
|
use Lang;
|
||||||
|
|
||||||
|
use League\Flysystem\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Albums Back-end Controller
|
||||||
|
*/
|
||||||
|
class Albums extends Controller
|
||||||
|
{
|
||||||
|
public $implement = [
|
||||||
|
'Backend.Behaviors.FormController',
|
||||||
|
'Backend.Behaviors.ListController',
|
||||||
|
'Backend.Behaviors.RelationController',
|
||||||
|
];
|
||||||
|
|
||||||
|
public $formConfig = 'config_form.yaml';
|
||||||
|
public $listConfig = 'config_list.yaml';
|
||||||
|
public $relationConfig = 'config_relation.yaml';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
BackendMenu::setContext('Graker.PhotoAlbums', 'photoalbums', 'albums');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ajax callback to set Photo as Album's front photo
|
||||||
|
*
|
||||||
|
* @param null|int $recordId album id
|
||||||
|
* @return string relation refresh or error string in json if there's error
|
||||||
|
*/
|
||||||
|
public function update_onRelationButtonSetFront($recordId = NULL) {
|
||||||
|
// get album
|
||||||
|
$album = Album::find($recordId);
|
||||||
|
if (!$album) {
|
||||||
|
// album not found
|
||||||
|
return Response::json(Lang::get('graker.photoalbums::lang.errors.album_not_found'), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get first checked photo
|
||||||
|
$input = Input::all();
|
||||||
|
$checked = $input['checked'];
|
||||||
|
$photo_id = array_shift($checked);
|
||||||
|
|
||||||
|
// validate photo
|
||||||
|
$photo = Photo::find($photo_id);
|
||||||
|
try {
|
||||||
|
if (!$photo) {
|
||||||
|
throw new ApplicationException(Lang::get('graker.photoalbums::lang.errors.cant_find_selected'));
|
||||||
|
}
|
||||||
|
if ($photo->album_id != $album->id) {
|
||||||
|
// attempt to use other album's photo
|
||||||
|
throw new ApplicationException(Lang::get('graker.photoalbums::lang.errors.not_this_album'));
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return Response::json($e->getMessage(), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set front id
|
||||||
|
$album->front_id = $photo->id;
|
||||||
|
$album->save();
|
||||||
|
|
||||||
|
$this->initRelation($album, 'photos');
|
||||||
|
return $this->relationRefresh('photos');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns path to reorder current album
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function getReorderPath() {
|
||||||
|
if (!isset($this->vars['formModel']->id)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$uri = \Backend::url('graker/photoalbums/reorder/album/' . $this->vars['formModel']->id);
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Controllers;
|
||||||
|
|
||||||
|
use BackendMenu;
|
||||||
|
use Backend\Classes\Controller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Photos Back-end Controller
|
||||||
|
*/
|
||||||
|
class Photos extends Controller
|
||||||
|
{
|
||||||
|
public $implement = [
|
||||||
|
'Backend.Behaviors.FormController',
|
||||||
|
'Backend.Behaviors.ListController'
|
||||||
|
];
|
||||||
|
|
||||||
|
public $formConfig = 'config_form.yaml';
|
||||||
|
public $listConfig = 'config_list.yaml';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
BackendMenu::setContext('Graker.PhotoAlbums', 'photoalbums', 'photos');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Controllers;
|
||||||
|
|
||||||
|
use BackendMenu;
|
||||||
|
use Backend\Classes\Controller;
|
||||||
|
use Graker\PhotoAlbums\Models\Album;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
use Lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder Back-end Controller
|
||||||
|
*/
|
||||||
|
class Reorder extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Album model reference
|
||||||
|
*
|
||||||
|
* @var null
|
||||||
|
*/
|
||||||
|
public $model = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Display the reorder interface for album
|
||||||
|
*
|
||||||
|
* @param null|int $album_id
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function album($album_id = NULL) {
|
||||||
|
$album = Album::find($album_id);
|
||||||
|
if (!$album) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->model = $album;
|
||||||
|
$this->addJs('/modules/backend/behaviors/reordercontroller/assets/js/october.reorder.js', 'core');
|
||||||
|
|
||||||
|
$this->pageTitle = Lang::get('graker.photoalbums::lang.plugin.reorder_title', ['name' => $album->title]);
|
||||||
|
|
||||||
|
return $this->makePartial('reorder', ['reorderRecords' => $this->model->photos,]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to save reorder information
|
||||||
|
* Calls function from Sortable trait on the model
|
||||||
|
*/
|
||||||
|
public function onReorder() {
|
||||||
|
if (!$ids = post('record_ids')) return;
|
||||||
|
if (!$orders = post('sort_orders')) return;
|
||||||
|
|
||||||
|
$model = new Photo();
|
||||||
|
$model->setSortableOrder($ids, $orders);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
BackendMenu::setContext('Graker.PhotoAlbums', 'photoalbums', 'albums');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Controllers;
|
||||||
|
|
||||||
|
use BackendMenu;
|
||||||
|
use Backend\Classes\Controller;
|
||||||
|
use Graker\PhotoAlbums\Models\Album as AlbumModel;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo as PhotoModel;
|
||||||
|
use Redirect;
|
||||||
|
use Backend;
|
||||||
|
use Flash;
|
||||||
|
use Input;
|
||||||
|
use Request;
|
||||||
|
use Response;
|
||||||
|
use Validator;
|
||||||
|
use ValidationException;
|
||||||
|
use ApplicationException;
|
||||||
|
use System\Models\File;
|
||||||
|
use Lang;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload Back-end Controller
|
||||||
|
*/
|
||||||
|
class Upload extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the form
|
||||||
|
*/
|
||||||
|
public function form() {
|
||||||
|
$this->pageTitle = Lang::get('graker.photoalbums::lang.plugin.upload_photos');
|
||||||
|
$this->addJs('/modules/backend/assets/vendor/dropzone/dropzone.js');
|
||||||
|
$this->addJs('/plugins/graker/photoalbums/assets/js/upload.js');
|
||||||
|
$this->addCss('/plugins/graker/photoalbums/assets/css/dropzone.css');
|
||||||
|
return $this->makePartial('form');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File upload controller
|
||||||
|
*/
|
||||||
|
public function post_files() {
|
||||||
|
try {
|
||||||
|
if (!Input::hasFile('file')) {
|
||||||
|
throw new ApplicationException(Lang::get('graker.photoalbums::lang.errors.no_file'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$upload = Input::file('file');
|
||||||
|
|
||||||
|
$validationRules = ['max:' . File::getMaxFilesize()];
|
||||||
|
|
||||||
|
$validation = Validator::make(
|
||||||
|
['file' => $upload],
|
||||||
|
['file' => $validationRules]
|
||||||
|
);
|
||||||
|
if ($validation->fails()) {
|
||||||
|
throw new ValidationException($validation);
|
||||||
|
}
|
||||||
|
if (!$upload->isValid()) {
|
||||||
|
throw new ApplicationException(Lang::get('graker.photoalbums::lang.errors.invalid_file', ['name' => $upload->getClientOriginalName()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = new File;
|
||||||
|
$file->data = $upload;
|
||||||
|
$file->is_public = true;
|
||||||
|
$file->save();
|
||||||
|
return Response::json(['id' => $file->id], 200);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return Response::json($e->getMessage(), 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form save callback
|
||||||
|
*/
|
||||||
|
public function onSave() {
|
||||||
|
$input = Input::all();
|
||||||
|
|
||||||
|
$album = AlbumModel::find($input['album']);
|
||||||
|
if ($album && !empty($input['file-id'])) {
|
||||||
|
$this->savePhotos($album, $input['file-id'], $input['file-title']);
|
||||||
|
Flash::success(Lang::get('graker.photoalbums::lang.messages.photos_saved'));
|
||||||
|
return Redirect::to(Backend::url('graker/photoalbums/albums/update/' . $album->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Flash::error(Lang::get('graker.photoalbums::lang.errors.album_not_found'));
|
||||||
|
return Redirect::to(Backend::url('graker/photoalbums/albums'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File remove callback
|
||||||
|
*/
|
||||||
|
public function onFileRemove() {
|
||||||
|
if (Input::has('file_id')) {
|
||||||
|
$file_id = Input::get('file_id');
|
||||||
|
$file = File::find($file_id);
|
||||||
|
if ($file) {
|
||||||
|
$file->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Saves photos with files attached from $file_ids and attaches them to album
|
||||||
|
*
|
||||||
|
* @param AlbumModel $album
|
||||||
|
* @param array $file_ids
|
||||||
|
* @param string[] $file_titles arrray of titles
|
||||||
|
*/
|
||||||
|
protected function savePhotos($album, $file_ids, $file_titles) {
|
||||||
|
$files = File::whereIn('id', $file_ids)->get();
|
||||||
|
$photos = array();
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$photo = new PhotoModel();
|
||||||
|
$photo->title = isset($file_titles[$file->id]) ? $file_titles[$file->id] : '';
|
||||||
|
$photo->save();
|
||||||
|
$photo->image()->save($file);
|
||||||
|
$photos[] = $photo;
|
||||||
|
}
|
||||||
|
$album->photos()->saveMany($photos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array of [album id => album title] to use in select list
|
||||||
|
*/
|
||||||
|
protected function getAlbumsList() {
|
||||||
|
$albums = AlbumModel::orderBy('created_at', 'desc')->get();
|
||||||
|
$options = [];
|
||||||
|
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
$options[$album->id] = $album->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
BackendMenu::setContext('Graker.PhotoAlbums', 'photoalbums', 'upload');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div data-control="toolbar">
|
||||||
|
<a
|
||||||
|
href="<?= Backend::url('graker/photoalbums/albums/create') ?>"
|
||||||
|
class="btn btn-primary oc-icon-plus">
|
||||||
|
<?= e(trans('graker.photoalbums::lang.plugin.new_album')) ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
|
@ -0,0 +1,53 @@
|
||||||
|
<div data-control="toolbar">
|
||||||
|
<a
|
||||||
|
data-control="popup"
|
||||||
|
data-size="huge"
|
||||||
|
data-handler="onRelationButtonCreate"
|
||||||
|
href="javascript:;"
|
||||||
|
class="btn btn-sm btn-secondary oc-icon-file">
|
||||||
|
<?= e(trans('backend::lang.relation.create_name', ['name' => trans($relationLabel)])) ?>
|
||||||
|
</a>
|
||||||
|
<?php if ($relationViewMode == 'single'): ?>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-secondary oc-icon-trash-o"
|
||||||
|
data-request="onRelationButtonDelete"
|
||||||
|
data-request-confirm="<?= e(trans('backend::lang.relation.delete_confirm')) ?>"
|
||||||
|
data-stripe-load-indicator>
|
||||||
|
<?= e(trans('backend::lang.relation.delete')) ?>
|
||||||
|
</button>
|
||||||
|
<?php else: ?>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-secondary oc-icon-trash-o"
|
||||||
|
onclick="$(this).data('request-data', {
|
||||||
|
checked: $('#<?= $this->relationGetId('view') ?> .control-list').listWidget('getChecked')
|
||||||
|
})"
|
||||||
|
disabled="disabled"
|
||||||
|
data-request="onRelationButtonDelete"
|
||||||
|
data-request-confirm="<?= e(trans('backend::lang.relation.delete_confirm')) ?>"
|
||||||
|
data-trigger-action="enable"
|
||||||
|
data-trigger="#<?= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
|
||||||
|
data-trigger-condition="checked"
|
||||||
|
data-stripe-load-indicator>
|
||||||
|
<?= e(trans('backend::lang.relation.delete')) ?>
|
||||||
|
</button>
|
||||||
|
<?php endif ?>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-secondary oc-icon-picture-o"
|
||||||
|
onclick="$(this).data('request-data', {
|
||||||
|
checked: $('#<?= $this->relationGetId('view') ?> .control-list').listWidget('getChecked')
|
||||||
|
})"
|
||||||
|
disabled="disabled"
|
||||||
|
data-request="onRelationButtonSetFront"
|
||||||
|
data-request-confirm="<?= e(trans('graker.photoalbums::lang.messages.set_front')) ?>"
|
||||||
|
data-trigger-action="enable"
|
||||||
|
data-trigger="#<?= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
|
||||||
|
data-trigger-condition="checked"
|
||||||
|
data-stripe-load-indicator>
|
||||||
|
<?= e(trans('graker.photoalbums::lang.plugin.set_front_button')) ?>
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="<?= $this->getReorderPath() ?>"
|
||||||
|
class="btn btn-sm btn-secondary oc-icon-refresh">
|
||||||
|
<?= e(trans('graker.photoalbums::lang.plugin.reorder_button')) ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
|
@ -0,0 +1,31 @@
|
||||||
|
# ===================================
|
||||||
|
# Form Behavior Config
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
# Record name
|
||||||
|
name: graker.photoalbums::lang.plugin.album
|
||||||
|
|
||||||
|
# Model Form Field configuration
|
||||||
|
form: $/graker/photoalbums/models/album/fields.yaml
|
||||||
|
|
||||||
|
# Model Class name
|
||||||
|
modelClass: Graker\Photoalbums\Models\Album
|
||||||
|
|
||||||
|
# Default redirect location
|
||||||
|
defaultRedirect: graker/photoalbums/albums
|
||||||
|
|
||||||
|
# Create page
|
||||||
|
create:
|
||||||
|
title: graker.photoalbums::lang.plugin.create_album
|
||||||
|
redirect: graker/photoalbums/albums/update/:id
|
||||||
|
redirectClose: graker/photoalbums/albums
|
||||||
|
|
||||||
|
# Update page
|
||||||
|
update:
|
||||||
|
title: graker.photoalbums::lang.plugin.edit_album
|
||||||
|
redirect: graker/photoalbums/albums
|
||||||
|
redirectClose: graker/photoalbums/albums
|
||||||
|
|
||||||
|
# Preview page
|
||||||
|
preview:
|
||||||
|
title: graker.photoalbums::lang.plugin.preview_album
|
|
@ -0,0 +1,44 @@
|
||||||
|
# ===================================
|
||||||
|
# List Behavior Config
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
# Model List Column configuration
|
||||||
|
list: $/graker/photoalbums/models/album/columns.yaml
|
||||||
|
|
||||||
|
# Model Class name
|
||||||
|
modelClass: Graker\Photoalbums\Models\Album
|
||||||
|
|
||||||
|
# List Title
|
||||||
|
title: graker.photoalbums::lang.plugin.list_title
|
||||||
|
|
||||||
|
# Link URL for each record
|
||||||
|
recordUrl: graker/photoalbums/albums/update/:id
|
||||||
|
|
||||||
|
# Message to display if the list is empty
|
||||||
|
noRecordsMessage: backend::lang.list.no_records
|
||||||
|
|
||||||
|
# Records to display per page
|
||||||
|
recordsPerPage: 20
|
||||||
|
|
||||||
|
# Displays the list column set up button
|
||||||
|
showSetup: true
|
||||||
|
|
||||||
|
# Displays the sorting link on each column
|
||||||
|
showSorting: true
|
||||||
|
|
||||||
|
# Default sorting column
|
||||||
|
# defaultSort:
|
||||||
|
# column: created_at
|
||||||
|
# direction: desc
|
||||||
|
|
||||||
|
# Display checkboxes next to each record
|
||||||
|
# showCheckboxes: true
|
||||||
|
|
||||||
|
# Toolbar widget configuration
|
||||||
|
toolbar:
|
||||||
|
# Partial for toolbar buttons
|
||||||
|
buttons: list_toolbar
|
||||||
|
|
||||||
|
# Search widget configuration
|
||||||
|
search:
|
||||||
|
prompt: backend::lang.list.search_prompt
|
|
@ -0,0 +1,12 @@
|
||||||
|
# ===================================
|
||||||
|
# Relation Behavior Config
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
photos:
|
||||||
|
label: graker.photoalbums::lang.plugin.photo
|
||||||
|
manage:
|
||||||
|
form: $/graker/photoalbums/models/photo/fields.yaml
|
||||||
|
list: $/graker/photoalbums/models/photo/columns.yaml
|
||||||
|
view:
|
||||||
|
list: $/graker/photoalbums/models/photo/columns.yaml
|
||||||
|
toolbarPartial: relation_toolbar
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('graker.photoalbums::lang.plugin.albums')) ?></a></li>
|
||||||
|
<li><?= e($this->pageTitle) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
|
||||||
|
<?= Form::open(['class' => 'layout']) ?>
|
||||||
|
|
||||||
|
<div class="layout-row">
|
||||||
|
<?= $this->formRender() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layout-row">
|
||||||
|
<?= $this->relationRender('photos') ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-buttons">
|
||||||
|
<div class="loading-indicator-container">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-request="onSave"
|
||||||
|
data-hotkey="ctrl+s, cmd+s"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.creating_album')) ?>"
|
||||||
|
class="btn btn-primary">
|
||||||
|
<?= e(trans('backend::lang.form.create')) ?>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-request="onSave"
|
||||||
|
data-request-data="close:1"
|
||||||
|
data-hotkey="ctrl+enter, cmd+enter"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.creating_album')) ?>"
|
||||||
|
class="btn btn-default">
|
||||||
|
<?= e(trans('backend::lang.form.create_and_close')) ?>
|
||||||
|
</button>
|
||||||
|
<span class="btn-text">
|
||||||
|
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= Form::close() ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/albums') ?>" class="btn btn-default"><?= e(trans('graker.photoalbums::lang.errors.return_to_albums')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
<?= $this->listRender() ?>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('graker.photoalbums::lang.plugin.albums')) ?></a></li>
|
||||||
|
<li><?= e($this->pageTitle) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
|
||||||
|
<div class="form-preview">
|
||||||
|
<?= $this->formRenderPreview() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/albums') ?>" class="btn btn-default"><?= e(trans('graker.photoalbums::lang.errors.return_to_albums')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('graker.photoalbums::lang.plugin.albums')) ?></a></li>
|
||||||
|
<li><?= e($this->pageTitle) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
|
||||||
|
<?= Form::open(['class' => 'layout']) ?>
|
||||||
|
|
||||||
|
<div class="layout-row">
|
||||||
|
<?= $this->formRender() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layout-row">
|
||||||
|
<?= $this->relationRender('photos') ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-buttons">
|
||||||
|
<div class="loading-indicator-container">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-request="onSave"
|
||||||
|
data-request-data="redirect:0"
|
||||||
|
data-hotkey="ctrl+s, cmd+s"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.saving_album')) ?>"
|
||||||
|
class="btn btn-primary">
|
||||||
|
<?= e(trans('backend::lang.form.save')) ?>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-request="onSave"
|
||||||
|
data-request-data="close:1"
|
||||||
|
data-hotkey="ctrl+enter, cmd+enter"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.saving_album')) ?>"
|
||||||
|
class="btn btn-default">
|
||||||
|
<?= e(trans('backend::lang.form.save_and_close')) ?>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="oc-icon-trash-o btn-icon danger pull-right"
|
||||||
|
data-request="onDelete"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.deleting_album')) ?>"
|
||||||
|
data-request-confirm="<?= e(trans('graker.photoalbums::lang.messages.delete')) ?>">
|
||||||
|
</button>
|
||||||
|
<span class="btn-text">
|
||||||
|
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= Form::close() ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/albums') ?>" class="btn btn-default"><?= e(trans('backend::lang.errors.return_to_albums')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div data-control="toolbar">
|
||||||
|
<a
|
||||||
|
href="<?= Backend::url('graker/photoalbums/photos/create') ?>"
|
||||||
|
class="btn btn-primary oc-icon-plus">
|
||||||
|
<?= e(trans('graker.photoalbums::lang.plugin.new_photo')) ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
|
@ -0,0 +1,31 @@
|
||||||
|
# ===================================
|
||||||
|
# Form Behavior Config
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
# Record name
|
||||||
|
name: graker.photoalbums::lang.plugin.photo
|
||||||
|
|
||||||
|
# Model Form Field configuration
|
||||||
|
form: $/graker/photoalbums/models/photo/fields.yaml
|
||||||
|
|
||||||
|
# Model Class name
|
||||||
|
modelClass: Graker\PhotoAlbums\Models\Photo
|
||||||
|
|
||||||
|
# Default redirect location
|
||||||
|
defaultRedirect: graker/photoalbums/photos
|
||||||
|
|
||||||
|
# Create page
|
||||||
|
create:
|
||||||
|
title: graker.photoalbums::lang.plugin.create_photo
|
||||||
|
redirect: graker/photoalbums/photos/update/:id
|
||||||
|
redirectClose: graker/photoalbums/photos
|
||||||
|
|
||||||
|
# Update page
|
||||||
|
update:
|
||||||
|
title: graker.photoalbums::lang.plugin.edit_photo
|
||||||
|
redirect: graker/photoalbums/photos
|
||||||
|
redirectClose: graker/photoalbums/photos
|
||||||
|
|
||||||
|
# Preview page
|
||||||
|
preview:
|
||||||
|
title: graker.photoalbums::lang.plugin.preview_photo
|
|
@ -0,0 +1,36 @@
|
||||||
|
# ===================================
|
||||||
|
# List Behavior Config
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
# Model List Column configuration
|
||||||
|
list: $/graker/photoalbums/models/photo/columns.yaml
|
||||||
|
|
||||||
|
# Model Class name
|
||||||
|
modelClass: Graker\PhotoAlbums\Models\Photo
|
||||||
|
|
||||||
|
# List Title
|
||||||
|
title: graker.photoalbums::lang.plugin.manage_photos
|
||||||
|
|
||||||
|
# Link URL for each record
|
||||||
|
recordUrl: graker/photoalbums/photos/update/:id
|
||||||
|
|
||||||
|
# Message to display if the list is empty
|
||||||
|
noRecordsMessage: backend::lang.list.no_records
|
||||||
|
|
||||||
|
# Records to display per page
|
||||||
|
recordsPerPage: 20
|
||||||
|
|
||||||
|
# Displays the list column set up button
|
||||||
|
showSetup: true
|
||||||
|
|
||||||
|
# Displays the sorting link on each column
|
||||||
|
showSorting: true
|
||||||
|
|
||||||
|
# Toolbar widget configuration
|
||||||
|
toolbar:
|
||||||
|
# Partial for toolbar buttons
|
||||||
|
buttons: list_toolbar
|
||||||
|
|
||||||
|
# Search widget configuration
|
||||||
|
search:
|
||||||
|
prompt: backend::lang.list.search_prompt
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/photos') ?>"><?= e(trans('graker.photoalbums::lang.plugin.photos')) ?></a></li>
|
||||||
|
<li><?= e($this->pageTitle) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
|
||||||
|
<?= Form::open(['class' => 'layout']) ?>
|
||||||
|
|
||||||
|
<div class="layout-row">
|
||||||
|
<?= $this->formRender() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-buttons">
|
||||||
|
<div class="loading-indicator-container">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-request="onSave"
|
||||||
|
data-hotkey="ctrl+s, cmd+s"
|
||||||
|
data-load-indicator="<?= e(trans('backend::lang.form.creating_photo')) ?>"
|
||||||
|
class="btn btn-primary">
|
||||||
|
<?= e(trans('backend::lang.form.create')) ?>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-request="onSave"
|
||||||
|
data-request-data="close:1"
|
||||||
|
data-hotkey="ctrl+enter, cmd+enter"
|
||||||
|
data-load-indicator="<?= e(trans('backend::lang.form.creating_photo')) ?>"
|
||||||
|
class="btn btn-default">
|
||||||
|
<?= e(trans('backend::lang.form.create_and_close')) ?>
|
||||||
|
</button>
|
||||||
|
<span class="btn-text">
|
||||||
|
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('graker/photoalbums/photos') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= Form::close() ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/photos') ?>" class="btn btn-default"><?= e(trans('backend::lang.errors.return_to_photos')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
<?= $this->listRender() ?>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/photos') ?>"><?= e(trans('backend::lang.form.preview_photo')) ?></a></li>
|
||||||
|
<li><?= e($this->pageTitle) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
|
||||||
|
<div class="form-preview">
|
||||||
|
<?= $this->formRenderPreview() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/photos') ?>" class="btn btn-default"><?= e(trans('backend::lang.errors.return_to_photos')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/photos') ?>"><?= e(trans('graker.photoalbums::lang.plugin.photos')) ?></a></li>
|
||||||
|
<li><?= e($this->pageTitle) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
|
||||||
|
<?= Form::open(['class' => 'layout']) ?>
|
||||||
|
|
||||||
|
<div class="layout-row">
|
||||||
|
<?= $this->formRender() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-buttons">
|
||||||
|
<div class="loading-indicator-container">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-request="onSave"
|
||||||
|
data-request-data="redirect:0"
|
||||||
|
data-hotkey="ctrl+s, cmd+s"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.saving_photo')) ?>"
|
||||||
|
class="btn btn-primary">
|
||||||
|
<?= e(trans('backend::lang.form.save')) ?>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-request="onSave"
|
||||||
|
data-request-data="close:1"
|
||||||
|
data-hotkey="ctrl+enter, cmd+enter"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.saving_photo')) ?>"
|
||||||
|
class="btn btn-default">
|
||||||
|
<?= e(trans('backend::lang.form.save_and_close')) ?>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="oc-icon-trash-o btn-icon danger pull-right"
|
||||||
|
data-request="onDelete"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.deleting_photo')) ?>"
|
||||||
|
data-request-confirm="<?= e(trans('graker.photoalbums::lang.messages.delete_photo')) ?>">
|
||||||
|
</button>
|
||||||
|
<span class="btn-text">
|
||||||
|
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('graker/photoalbums/photos') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= Form::close() ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/photos') ?>" class="btn btn-default"><?= e(trans('graker.photoalbums::lang.errors.return_to_photos')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php foreach ($records as $record): ?>
|
||||||
|
|
||||||
|
<li data-record-id="<?= $record->getKey() ?>" data-record-sort-order="<?= $record->{$record->getSortOrderColumn()} ?>" >
|
||||||
|
<div class="record">
|
||||||
|
<a href="javascript:;" class="move"><img src="<?= $record->image->getThumb(100, 100, ['mode' => 'auto']); ?>"/></a>
|
||||||
|
<span><?= $record->title ?></span>
|
||||||
|
<input name="record_ids[]" type="hidden" value="<?= $record->getKey() ?>" />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<?php endforeach ?>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('graker.photoalbums::lang.plugin.albums')) ?></a></li>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/albums/update/' . $this->model->id) ?>"><?= e($this->model->title) ?></a></li>
|
||||||
|
<li><?= e(trans('graker.photoalbums::lang.plugin.reorder')) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
<!-- Reorder List -->
|
||||||
|
<?= Form::open() ?>
|
||||||
|
<div
|
||||||
|
id="reorderTreeList"
|
||||||
|
class="control-treelist"
|
||||||
|
data-control="treelist"
|
||||||
|
data-nested="0"
|
||||||
|
data-handle="> li > .record > a.move"
|
||||||
|
data-stripe-load-indicator>
|
||||||
|
<?php if ($reorderRecords): ?>
|
||||||
|
<ol id="reorderRecords">
|
||||||
|
<?= $this->makePartial('records', ['records' => $reorderRecords]) ?>
|
||||||
|
</ol>
|
||||||
|
<?php else: ?>
|
||||||
|
<p><?= Lang::get('backend::lang.reorder.no_records') ?></p>
|
||||||
|
<?php endif ?>
|
||||||
|
</div>
|
||||||
|
<?= Form::close() ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$.oc.reorderBehavior.initSorting('simple')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/albums') ?>" class="btn btn-default"><?= e(trans('graker.photoalbums::lang.errors.return_to_albums')) ?></a></p>
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php Block::put('breadcrumb') ?>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('graker.photoalbums::lang.plugin.albums')) ?></a></li>
|
||||||
|
<li><?= e(trans('graker.photoalbums::lang.plugin.upload_photos_title')) ?></li>
|
||||||
|
</ul>
|
||||||
|
<?php Block::endPut() ?>
|
||||||
|
|
||||||
|
<?php if (!$this->fatalError): ?>
|
||||||
|
<?= Form::open(['file' => TRUE, 'class' => 'layout',]) ?>
|
||||||
|
<div class="layout-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<?= Form::label('album', e(trans('graker.photoalbums::lang.plugin.album_to_upload')) ) ?>
|
||||||
|
<?= Form::select('album', $this->getAlbumsList()) ?>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="field-fileupload" data-url="<?= Backend::url('graker/photoalbums/upload/post_files') ?>">
|
||||||
|
<div class="btn btn-primary dz-message">
|
||||||
|
<?= e(trans('graker.photoalbums::lang.plugin.upload_photos')) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div id="filesContainer" class="dropzone-previews dropzone">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-buttons">
|
||||||
|
<div class="loading-indicator-container">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
data-request="onSave"
|
||||||
|
data-hotkey="ctrl+s, cmd+s"
|
||||||
|
data-load-indicator="<?= e(trans('graker.photoalbums::lang.plugin.saving_upload')) ?>"
|
||||||
|
class="btn btn-primary">
|
||||||
|
<?= e(trans('graker.photoalbums::lang.plugin.save_upload')) ?>
|
||||||
|
</button>
|
||||||
|
<span class="btn-text">
|
||||||
|
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('graker/photoalbums/albums') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= Form::close() ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
|
||||||
|
<p><a href="<?= Backend::url('graker/photoalbums/albums') ?>" class="btn btn-default"><?= e(trans('graker.photoalbums::lang.errors.return_to_albums')) ?></a></p>
|
||||||
|
|
||||||
|
<?php endif ?>
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'plugin' => [
|
||||||
|
'name' => 'Photo Albums',
|
||||||
|
'description' => 'Create, display and manage galleries of photos arranged in albums.',
|
||||||
|
'settings_description' => 'Photo Albums plugin settings',
|
||||||
|
'tab' => 'Photo Albums',
|
||||||
|
'manage_albums' => 'Manage photo albums',
|
||||||
|
'access_permission' => 'Access Settings',
|
||||||
|
'upload_photos' => 'Upload photos',
|
||||||
|
'new_album' => 'New album',
|
||||||
|
'create_album' => 'Create album',
|
||||||
|
'edit_album' => 'Edit album',
|
||||||
|
'preview_album' => 'Preview album',
|
||||||
|
'creating_album' => 'Creating album...',
|
||||||
|
'saving_album' => 'Saving album...',
|
||||||
|
'deleting_album' => 'Deleting album...',
|
||||||
|
'list_title' => 'Manage albums',
|
||||||
|
'album' => 'Album',
|
||||||
|
'albums' => 'Albums',
|
||||||
|
'manage_photos' => 'Manage photos',
|
||||||
|
'new_photo' => 'New photo',
|
||||||
|
'create_photo' => 'Create photo',
|
||||||
|
'edit_photo' => 'Edit photo',
|
||||||
|
'preview_photo' => 'Preview photo',
|
||||||
|
'creating_photo' => 'Creating photo...',
|
||||||
|
'saving_photo' => 'Saving photo...',
|
||||||
|
'deleting_photo' => 'Deleting photo...',
|
||||||
|
'photo' => 'Photo',
|
||||||
|
'photos' => 'Photos',
|
||||||
|
'photo_description' => 'Description',
|
||||||
|
'set_front_button' => 'Set as front',
|
||||||
|
'reorder_button' => 'Reorder photos',
|
||||||
|
'bool_positive' => 'Yes',
|
||||||
|
'reorder_title' => 'Reorder album :name',
|
||||||
|
'reorder' => 'Reorder',
|
||||||
|
'saving_upload' => 'Saving upload...',
|
||||||
|
'upload_photos_title' => 'Upload multiple photos',
|
||||||
|
'album_to_upload' => 'Album to upload to',
|
||||||
|
'save_upload' => 'Save upload',
|
||||||
|
'title_label' => 'Title',
|
||||||
|
'title_placeholder_album' => 'Album title',
|
||||||
|
'title_placeholder_photo' => 'Photo title',
|
||||||
|
'created_label' => 'Created',
|
||||||
|
'updated_label' => 'Updated',
|
||||||
|
'slug_label' => 'Slug',
|
||||||
|
'slug_description' => 'URL slug parameter',
|
||||||
|
'slug_placeholder_album' => 'album-title',
|
||||||
|
'description_label' => 'Description',
|
||||||
|
'front_label' => 'Front',
|
||||||
|
'code_label' => 'Code',
|
||||||
|
'code_description' => 'Type in default markdown to use for photo insert. There are two placeholders: %id% and %title%, they will be replaced with photo id and photo title automatically.',
|
||||||
|
'selecting_photo' => 'Selecting photo',
|
||||||
|
'insert' => 'Insert',
|
||||||
|
'not_selected' => 'Not selected',
|
||||||
|
'back_to_albums' => 'Back to albums',
|
||||||
|
'all_photo_albums' => 'All Photo Albums',
|
||||||
|
'all_photos' => 'All Photos',
|
||||||
|
],
|
||||||
|
'errors' => [
|
||||||
|
'album_not_found' => 'Album not found!',
|
||||||
|
'cant_find_selected' => 'Can\'t find selected photo!',
|
||||||
|
'not_this_album' => 'Selected photo doesn\'t belong to this album!',
|
||||||
|
'return_to_albums' => 'Return to albums list',
|
||||||
|
'return_to_photos' => 'Return to photos list',
|
||||||
|
'no_file' => 'No file in request',
|
||||||
|
'invalid_file' => 'File :name is not valid.',
|
||||||
|
'thumb_width_error' => 'Thumb width must be a number',
|
||||||
|
'thumb_height_error' => 'Thumb height must be a number',
|
||||||
|
'photos_on_page_error' => 'Photos on page value must be a number',
|
||||||
|
'albums_on_page_error' => 'Albums on page value must be a number',
|
||||||
|
'photos_count_error' => 'Photos count must be a number',
|
||||||
|
'cache_lifetime_error' => 'Cache lifetime must be a number',
|
||||||
|
'no_albums' => 'You don\'t have any albums yet.',
|
||||||
|
],
|
||||||
|
'messages' => [
|
||||||
|
'set_front' => 'Are you sure to set this photo as front for the album?',
|
||||||
|
'delete' => 'Do you really want to delete this album?',
|
||||||
|
'delete_photo' => 'Do you really want to delete this photo?',
|
||||||
|
'photos_saved' => 'Photos are saved!',
|
||||||
|
],
|
||||||
|
'components' => [
|
||||||
|
'photo_description' => 'Single photo component',
|
||||||
|
'album_description' => 'Component to output one photo album with all its photos.',
|
||||||
|
'photo_page_label' => 'Photo page',
|
||||||
|
'photo_page_description' => 'Page used to display a single photo',
|
||||||
|
'thumb_mode_label' => 'Thumb mode',
|
||||||
|
'thumb_mode_description' => 'Mode of thumb generation',
|
||||||
|
'thumb_width_label' => 'Thumb width',
|
||||||
|
'thumb_width_description' => 'Width of the thumb to be generated',
|
||||||
|
'thumb_height_label' => 'Thumb height',
|
||||||
|
'thumb_height_description' => 'Height of the thumb to be generated',
|
||||||
|
'photos_on_page_label' => 'Photos on page',
|
||||||
|
'photos_on_page_description' => 'Amount of photos on one page (to use in pagination)',
|
||||||
|
'albums_on_page_label' => 'Albums on page',
|
||||||
|
'albums_on_page_description' => 'Amount of albums on one page (to use in pagination)',
|
||||||
|
'albums_list' => 'Albums list',
|
||||||
|
'albums_list_description' => 'Lists all photo albums on site',
|
||||||
|
'album_page_label' => 'Album page',
|
||||||
|
'album_page_description' => 'Page used to display photo albums',
|
||||||
|
'id_label' => 'ID',
|
||||||
|
'id_description' => 'Photo id parameter',
|
||||||
|
'random_photos' => 'Random Photos',
|
||||||
|
'random_photos_description' => 'Output predefined number of random photos',
|
||||||
|
'photos_count_label' => 'Photos to output',
|
||||||
|
'photos_count_description' => 'Amount of random photos to output',
|
||||||
|
'cache_lifetime_label' => 'Cache lifetime',
|
||||||
|
'cache_lifetime_description' => 'Number of minutes selected photos are stored in cache. 0 for no caching.',
|
||||||
|
],
|
||||||
|
];
|
|
@ -0,0 +1,119 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Models;
|
||||||
|
|
||||||
|
use Model;
|
||||||
|
use System\Models\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Album Model
|
||||||
|
*/
|
||||||
|
class Album extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string The database table used by the model.
|
||||||
|
*/
|
||||||
|
public $table = 'graker_photoalbums_albums';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array of validation rules
|
||||||
|
*/
|
||||||
|
public $rules = [
|
||||||
|
'title' => 'required',
|
||||||
|
'slug' => ['required', 'regex:/^[a-z0-9\/\:_\-\*\[\]\+\?\|]*$/i', 'unique:graker_photoalbums_albums'],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Relations
|
||||||
|
*/
|
||||||
|
public $hasMany = [
|
||||||
|
'photos' => [
|
||||||
|
'Graker\PhotoAlbums\Models\Photo',
|
||||||
|
'order' => 'sort_order desc',
|
||||||
|
]
|
||||||
|
];
|
||||||
|
public $belongsTo = [
|
||||||
|
'user' => ['Backend\Models\User'],
|
||||||
|
'front' => ['Graker\PhotoAlbums\Models\Photo'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This relation allows us to eager-load 1 latest photo per album
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function latestPhoto() {
|
||||||
|
return $this->hasOne('Graker\PhotoAlbums\Models\Photo')->latest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This relation allows us to count photos
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function photosCount() {
|
||||||
|
return $this->hasOne('Graker\PhotoAlbums\Models\Photo')
|
||||||
|
->selectRaw('album_id, count(*) as aggregate')
|
||||||
|
->orderBy('album_id')
|
||||||
|
->groupBy('album_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Getter for photos count
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getPhotosCountAttribute() {
|
||||||
|
// if relation is not loaded already, let's do it first
|
||||||
|
if (!array_key_exists('photosCount', $this->relations)) {
|
||||||
|
$this->load('photosCount');
|
||||||
|
}
|
||||||
|
$related = $this->getRelation('photosCount');
|
||||||
|
|
||||||
|
return ($related) ? (int) $related->aggregate : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns image file of photo set as album front or image in the latest photo of the album
|
||||||
|
*
|
||||||
|
* @return File
|
||||||
|
*/
|
||||||
|
public function getImage() {
|
||||||
|
if ($this->front) {
|
||||||
|
return $this->front->image;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->latestPhoto) {
|
||||||
|
return $this->latestPhoto->image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Sets and returns url for this model using provided page name and controller
|
||||||
|
* For now we expose just id and slug for URL parameters
|
||||||
|
*
|
||||||
|
* @param string $pageName
|
||||||
|
* @param CMS\Classes\Controller $controller
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function setUrl($pageName, $controller) {
|
||||||
|
$params = [
|
||||||
|
'id' => $this->id,
|
||||||
|
'slug' => $this->slug,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->url = $controller->pageUrl($pageName, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Photo Model
|
||||||
|
*/
|
||||||
|
class Photo extends Model
|
||||||
|
{
|
||||||
|
|
||||||
|
// Photos must be sortable
|
||||||
|
use \October\Rain\Database\Traits\Sortable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string The database table used by the model.
|
||||||
|
*/
|
||||||
|
public $table = 'graker_photoalbums_photos';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array of validation rules
|
||||||
|
*/
|
||||||
|
public $rules = [
|
||||||
|
'title' => 'required',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array of fillable fields to use in mass assignment
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'title', 'description',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Relations
|
||||||
|
*/
|
||||||
|
public $belongsTo = [
|
||||||
|
'user' => ['Backend\Models\User'],
|
||||||
|
'album' => ['Graker\PhotoAlbums\Models\Album'],
|
||||||
|
];
|
||||||
|
public $attachOne = [
|
||||||
|
'image' => ['System\Models\File'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns next photo or NULL if this is the last in the album
|
||||||
|
*
|
||||||
|
* @return Photo
|
||||||
|
*/
|
||||||
|
public function nextPhoto() {
|
||||||
|
$next = NULL;
|
||||||
|
$current_found = FALSE;
|
||||||
|
|
||||||
|
foreach ($this->album->photos as $photo) {
|
||||||
|
if ($current_found) {
|
||||||
|
// previous iteration was current photo, so we found the next one
|
||||||
|
$next = $photo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($photo->id == $this->id) {
|
||||||
|
$current_found = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns previous photo or NULL if this is the first in the album
|
||||||
|
*
|
||||||
|
* @return Photo
|
||||||
|
*/
|
||||||
|
public function previousPhoto() {
|
||||||
|
$previous = NULL;
|
||||||
|
|
||||||
|
foreach ($this->album->photos as $photo) {
|
||||||
|
if ($photo->id == $this->id) {
|
||||||
|
// found current photo
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$previous = $photo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Sets and returns url for this model using provided page name and controller
|
||||||
|
* For now we expose photo id and album's slug
|
||||||
|
*
|
||||||
|
* @param string $pageName
|
||||||
|
* @param CMS\Classes\Controller $controller
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function setUrl($pageName, $controller) {
|
||||||
|
$params = [
|
||||||
|
'id' => $this->id,
|
||||||
|
'album_slug' => $this->album->slug,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->url = $controller->pageUrl($pageName, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* beforeDelete() event
|
||||||
|
* Using it to delete attached
|
||||||
|
*/
|
||||||
|
public function beforeDelete() {
|
||||||
|
if ($this->image) {
|
||||||
|
$this->image->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PhotoAlbums settings model
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Graker\PhotoAlbums\Models;
|
||||||
|
|
||||||
|
use Model;
|
||||||
|
|
||||||
|
class Settings extends Model {
|
||||||
|
|
||||||
|
public $implement = ['System.Behaviors.SettingsModel '];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string unique code to access settings
|
||||||
|
*/
|
||||||
|
public $settingsCode = 'photoalbums_settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string file with setting fields
|
||||||
|
*/
|
||||||
|
public $settingsFields = 'fields.yaml';
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
# ===================================
|
||||||
|
# List Column Definitions
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
columns:
|
||||||
|
title:
|
||||||
|
label: graker.photoalbums::lang.plugin.title_label
|
||||||
|
searchable: true
|
||||||
|
|
||||||
|
created_at:
|
||||||
|
label: graker.photoalbums::lang.plugin.created_label
|
||||||
|
type: date
|
||||||
|
|
||||||
|
updated_at:
|
||||||
|
label: graker.photoalbums::lang.plugin.updated_label
|
||||||
|
type: date
|
|
@ -0,0 +1,24 @@
|
||||||
|
# ===================================
|
||||||
|
# Form Field Definitions
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
fields:
|
||||||
|
|
||||||
|
title:
|
||||||
|
label: graker.photoalbums::lang.plugin.title_label
|
||||||
|
span: left
|
||||||
|
placeholder: graker.photoalbums::lang.plugin.title_placeholder_album
|
||||||
|
|
||||||
|
slug:
|
||||||
|
label: graker.photoalbums::lang.plugin.slug_label
|
||||||
|
span: right
|
||||||
|
placeholder: graker.photoalbums::lang.plugin.slug_placeholder_album
|
||||||
|
preset:
|
||||||
|
field: title
|
||||||
|
type: slug
|
||||||
|
|
||||||
|
description:
|
||||||
|
label: graker.photoalbums::lang.plugin.description_label
|
||||||
|
type: richeditor
|
||||||
|
size: large
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
# ===================================
|
||||||
|
# List Column Definitions
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
columns:
|
||||||
|
id:
|
||||||
|
label: graker.photoalbums::lang.plugin.photo
|
||||||
|
type: image
|
||||||
|
width: 100
|
||||||
|
height: 100
|
||||||
|
|
||||||
|
title:
|
||||||
|
label: graker.photoalbums::lang.plugin.title_label
|
||||||
|
searchable: true
|
||||||
|
|
||||||
|
front:
|
||||||
|
label: graker.photoalbums::lang.plugin.front_label
|
||||||
|
relation: album
|
||||||
|
select: front_id
|
||||||
|
type: is_front
|
||||||
|
|
||||||
|
album:
|
||||||
|
label: graker.photoalbums::lang.plugin.album
|
||||||
|
valueFrom: title
|
||||||
|
searchable: true
|
||||||
|
relation: album
|
||||||
|
|
||||||
|
created_at:
|
||||||
|
label: graker.photoalbums::lang.plugin.created_label
|
||||||
|
type: date
|
||||||
|
|
||||||
|
updated_at:
|
||||||
|
label: graker.photoalbums::lang.plugin.updated_label
|
||||||
|
type: date
|
|
@ -0,0 +1,31 @@
|
||||||
|
# ===================================
|
||||||
|
# Form Field Definitions
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
fields:
|
||||||
|
|
||||||
|
image:
|
||||||
|
label: graker.photoalbums::lang.plugin.photo
|
||||||
|
type: fileupload
|
||||||
|
mode: image
|
||||||
|
imageWidth: 320
|
||||||
|
imageHeight: 240
|
||||||
|
span: right
|
||||||
|
|
||||||
|
title:
|
||||||
|
label: graker.photoalbums::lang.plugin.title_label
|
||||||
|
span: left
|
||||||
|
placeholder: graker.photoalbums::lang.plugin.title_placeholder_photo
|
||||||
|
span: left
|
||||||
|
|
||||||
|
album:
|
||||||
|
label: graker.photoalbums::lang.plugin.album
|
||||||
|
type: relation
|
||||||
|
nameFrom: title
|
||||||
|
emptyOption: graker.photoalbums::lang.plugin.not_selected
|
||||||
|
span: left
|
||||||
|
|
||||||
|
description:
|
||||||
|
label: graker.photoalbums::lang.plugin.photo_description
|
||||||
|
type: richeditor
|
||||||
|
size: large
|
|
@ -0,0 +1,10 @@
|
||||||
|
# ===================================
|
||||||
|
# Field Definitions
|
||||||
|
# ===================================
|
||||||
|
|
||||||
|
fields:
|
||||||
|
|
||||||
|
code:
|
||||||
|
label: graker.photoalbums::lang.plugin.code_label
|
||||||
|
comment: graker.photoalbums::lang.plugin.code_description
|
||||||
|
default: '![%title%]([photo:%id%])'
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
bootstrap="../../../tests/bootstrap.php"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
syntaxCheck="true"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Plugin Unit Test Suite">
|
||||||
|
<directory>./tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<php>
|
||||||
|
<env name="APP_ENV" value="testing"/>
|
||||||
|
<env name="CACHE_DRIVER" value="array"/>
|
||||||
|
<env name="SESSION_DRIVER" value="array"/>
|
||||||
|
</php>
|
||||||
|
</phpunit>
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Graker\PhotoAlbums\Tests;
|
||||||
|
|
||||||
|
use PluginTestCase;
|
||||||
|
use Graker\PhotoAlbums\Components\RandomPhotos;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
use Graker\PhotoAlbums\Models\Album;
|
||||||
|
use Cms\Classes\ComponentManager;
|
||||||
|
use Cms\Classes\Page;
|
||||||
|
use Cms\Classes\Layout;
|
||||||
|
use Cms\Classes\Controller;
|
||||||
|
use Cms\Classes\Theme;
|
||||||
|
use Cms\Classes\CodeParser;
|
||||||
|
use Faker;
|
||||||
|
use Storage;
|
||||||
|
|
||||||
|
class RandomPhotosTest extends PluginTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that random photos are generated
|
||||||
|
*/
|
||||||
|
public function testRandomPhotos() {
|
||||||
|
// create album and 7 photos
|
||||||
|
$album = $this->createAlbum();
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
$photos[] = $this->createPhoto($album);
|
||||||
|
|
||||||
|
// get random photos
|
||||||
|
$component = $this->createRandomPhotosComponent();
|
||||||
|
$random_photos = $component->photos();
|
||||||
|
|
||||||
|
// assert all photos are from generated array
|
||||||
|
self::assertEquals(5, count($random_photos), 'There are 5 random photos');
|
||||||
|
$found_all = TRUE;
|
||||||
|
foreach ($random_photos as $random_photo) {
|
||||||
|
$found = FALSE;
|
||||||
|
foreach ($photos as $photo) {
|
||||||
|
if ($photo->id == $random_photo->id) {
|
||||||
|
$found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
$found_all = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::assertTrue($found_all, 'All photos exist in original array');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Creates album model
|
||||||
|
*
|
||||||
|
* @return \Graker\PhotoAlbums\Models\Album
|
||||||
|
*/
|
||||||
|
protected function createAlbum() {
|
||||||
|
$faker = Faker\Factory::create();
|
||||||
|
$album = new Album();
|
||||||
|
$album->title = $faker->sentence(3);
|
||||||
|
$album->slug = str_slug($album->title);
|
||||||
|
$album->description = $faker->text();
|
||||||
|
$album->save();
|
||||||
|
return $album;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Creates photo model and put it into album
|
||||||
|
*
|
||||||
|
* @param \Graker\PhotoAlbums\Models\Album $album
|
||||||
|
* @return \Graker\PhotoAlbums\Models\Photo
|
||||||
|
*/
|
||||||
|
protected function createPhoto(Album $album) {
|
||||||
|
$faker = Faker\Factory::create();
|
||||||
|
$photo = new Photo();
|
||||||
|
$photo->title = $faker->sentence(3);
|
||||||
|
$photo->description = $faker->text();
|
||||||
|
$photo->image = $faker->image();
|
||||||
|
$photo->album = $album;
|
||||||
|
$photo->save();
|
||||||
|
return $photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Creates randomPhotos component to test
|
||||||
|
*
|
||||||
|
* @return \Graker\PhotoAlbums\Components\RandomPhotos
|
||||||
|
*/
|
||||||
|
protected function createRandomPhotosComponent() {
|
||||||
|
// Spoof all the objects we need to make a page object
|
||||||
|
$theme = Theme::load('test');
|
||||||
|
$page = Page::load($theme, 'index.htm');
|
||||||
|
$layout = Layout::load($theme, 'content.htm');
|
||||||
|
$controller = new Controller($theme);
|
||||||
|
$parser = new CodeParser($page);
|
||||||
|
$pageObj = $parser->source($page, $layout, $controller);
|
||||||
|
$manager = ComponentManager::instance();
|
||||||
|
$object = $manager->makeComponent('randomPhotos', $pageObj);
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Updates;
|
||||||
|
|
||||||
|
use Schema;
|
||||||
|
use October\Rain\Database\Updates\Migration;
|
||||||
|
|
||||||
|
class AddAlbumFront extends Migration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('graker_photoalbums_albums', function($table)
|
||||||
|
{
|
||||||
|
$table->integer('front_id')->unsigned()->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('graker_photoalbums_albums', function($table)
|
||||||
|
{
|
||||||
|
$table->dropColumn('front_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Updates;
|
||||||
|
|
||||||
|
use Schema;
|
||||||
|
use October\Rain\Database\Updates\Migration;
|
||||||
|
|
||||||
|
class AddSortOrder extends Migration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('graker_photoalbums_photos', function($table)
|
||||||
|
{
|
||||||
|
$table->integer('sort_order')->unsigned()->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('graker_photoalbums_photos', function($table)
|
||||||
|
{
|
||||||
|
$table->dropColumn('sort_order');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Updates;
|
||||||
|
|
||||||
|
use Schema;
|
||||||
|
use October\Rain\Database\Updates\Migration;
|
||||||
|
|
||||||
|
class CreateAlbumsTable extends Migration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('graker_photoalbums_albums', function($table)
|
||||||
|
{
|
||||||
|
$table->engine = 'InnoDB';
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('user_id')->unsigned()->nullable()->index();
|
||||||
|
$table->string('title')->nullable();
|
||||||
|
$table->string('slug')->index();
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('graker_photoalbums_albums');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Updates;
|
||||||
|
|
||||||
|
use Schema;
|
||||||
|
use October\Rain\Database\Updates\Migration;
|
||||||
|
|
||||||
|
class CreatePhotosTable extends Migration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('graker_photoalbums_photos', function($table)
|
||||||
|
{
|
||||||
|
$table->engine = 'InnoDB';
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('user_id')->unsigned()->nullable()->index();
|
||||||
|
$table->integer('album_id')->unsigned()->nullable()->index();
|
||||||
|
$table->string('title')->nullable();
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('graker_photoalbums_photos');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php namespace Graker\PhotoAlbums\Updates;
|
||||||
|
|
||||||
|
use Schema;
|
||||||
|
use October\Rain\Database\Updates\Migration;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
|
||||||
|
class UpdateSortOrderOnExistingPhotos extends Migration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
// fill sort_order values for existing photos with photo ids
|
||||||
|
foreach (Photo::all() as $photo) {
|
||||||
|
$photo->sort_order = $photo->id;
|
||||||
|
$photo->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
1.0.1: First version of PhotoAlbums
|
||||||
|
1.0.2:
|
||||||
|
- Update with migrations to create albums and photos table
|
||||||
|
- create_albums_table.php
|
||||||
|
- create_photos_table.php
|
||||||
|
1.1.0:
|
||||||
|
- Add ability to select front photo for album from the interface
|
||||||
|
- add_album_front.php
|
||||||
|
1.2.0:
|
||||||
|
- Added ability to reorder photos in the album
|
||||||
|
- add_sort_order_field.php
|
||||||
|
1.2.1:
|
||||||
|
- Fill default sort_order values for existing photos
|
||||||
|
- update_sort_order_on_existing_photos.php
|
||||||
|
1.2.2:
|
||||||
|
- Sqlite support for RandomPhotos component
|
||||||
|
1.2.3:
|
||||||
|
- Added helper method to get album's cover photo
|
||||||
|
1.2.4:
|
||||||
|
- Fix for album front photo eager loading
|
||||||
|
1.2.5:
|
||||||
|
- Fix for photos count in only_full_group_by sql mode
|
||||||
|
1.3.0:
|
||||||
|
- New dialog to insert photos into blog posts
|
||||||
|
1.4.0:
|
||||||
|
- Integration with RainLab.Pages to use Albums and Photos in Menu Items (and Sitemap)
|
||||||
|
1.4.1:
|
||||||
|
- Improved layout of Photo form (thanks to gergo85)
|
||||||
|
- Improved lang.php strings (thanks to gergo85)
|
||||||
|
- Localized Menu Item types
|
||||||
|
1.4.2:
|
||||||
|
- Fixed second insert photo icon from occuring in blog post form
|
|
@ -0,0 +1,184 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Graker\PhotoAlbums\Widgets;
|
||||||
|
|
||||||
|
use Backend\Classes\WidgetBase;
|
||||||
|
use Graker\PhotoAlbums\Models\Album;
|
||||||
|
use Graker\PhotoAlbums\Models\Photo;
|
||||||
|
use Graker\PhotoAlbums\Models\Settings;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PhotoSelector
|
||||||
|
* Creates a widget allowing to navigate through albums and photos
|
||||||
|
* in order to select one of the photos (to insert it into text, for example)
|
||||||
|
*
|
||||||
|
* @package Graker\PhotoAlbums\Widgets
|
||||||
|
*/
|
||||||
|
class PhotoSelector extends WidgetBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string unique widget alias
|
||||||
|
*/
|
||||||
|
protected $defaultAlias = 'photoSelector';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Render the widget
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function render() {
|
||||||
|
// if we have current album id, open the album, otherwise open albums list
|
||||||
|
$album_id = input('album');
|
||||||
|
|
||||||
|
if ($album_id) {
|
||||||
|
$this->vars['albums'] = NULL;
|
||||||
|
$this->vars['album'] = $this->album($album_id);
|
||||||
|
} else {
|
||||||
|
$this->vars['albums'] = $this->albums();
|
||||||
|
$this->vars['album'] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $this->makePartial('body');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads widget assets
|
||||||
|
*/
|
||||||
|
protected function loadAssets() {
|
||||||
|
$this->addJs('js/photoselector.js');
|
||||||
|
$this->addCss('css/photoselector.css');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Callback for when the dialog is initially open
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function onDialogOpen() {
|
||||||
|
return $this->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Callback to generate albums list
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function onAlbumListLoad() {
|
||||||
|
$this->vars['albums'] = $this->albums();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'#listContainer' => $this->makePartial('albums'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Callback to generate photos list
|
||||||
|
* Photos list is to replace albums list in dialog markup
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function onAlbumLoad() {
|
||||||
|
$album_id = input('id');
|
||||||
|
$album = $this->album($album_id);
|
||||||
|
$this->vars['album'] = $album;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'#listContainer' => $this->makePartial('photos'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns a collection of all user's albums
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
protected function albums() {
|
||||||
|
$albums = Album::orderBy('created_at', 'desc')
|
||||||
|
->has('photos')
|
||||||
|
->with(['latestPhoto' => function ($query) {
|
||||||
|
$query->with('image');
|
||||||
|
}])
|
||||||
|
->with(['front' => function ($query) {
|
||||||
|
$query->with('image');
|
||||||
|
}])
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($albums as $album) {
|
||||||
|
// prepare thumb from $album->front if it is set or from latestPhoto otherwise
|
||||||
|
$image = ($album->front) ? $album->front->image : $album->latestPhoto->image;
|
||||||
|
$album->thumb = $image->getThumb(
|
||||||
|
160,
|
||||||
|
120,
|
||||||
|
['mode' => 'crop']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $albums;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Returns album with its photos loaded and prepared for display in dialog
|
||||||
|
*
|
||||||
|
* @param int $album_id
|
||||||
|
* @return Album
|
||||||
|
*/
|
||||||
|
protected function album($album_id) {
|
||||||
|
$album = Album::where('id', $album_id)
|
||||||
|
->with(['photos' => function ($query) {
|
||||||
|
$query->orderBy('sort_order', 'desc');
|
||||||
|
$query->with('image');
|
||||||
|
// TODO implement pagination
|
||||||
|
}])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($album) {
|
||||||
|
//prepare photo urls and thumbs
|
||||||
|
foreach ($album->photos as $photo) {
|
||||||
|
// set thumb
|
||||||
|
$photo->thumb = $photo->image->getThumb(
|
||||||
|
160,
|
||||||
|
120,
|
||||||
|
['mode' => 'crop']
|
||||||
|
);
|
||||||
|
// set code
|
||||||
|
$photo->code = $this->createPhotoCode($photo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $album;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Create an insert markdown code for photo from plugin settings
|
||||||
|
*
|
||||||
|
* @param Photo $photo
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function createPhotoCode($photo) {
|
||||||
|
$code_template = Settings::get('code', '![%title%]([photo:%id%])');
|
||||||
|
$code = str_replace(
|
||||||
|
array('%id%', '%title%'),
|
||||||
|
array($photo->id, $photo->title),
|
||||||
|
$code_template
|
||||||
|
);
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#photosList .photo-link.image-link {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photosList .photo-link.image-link.selected {
|
||||||
|
border: 1px solid;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photosList .photo-link.title-link.selected {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#photosList .back-to-albums {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: block;
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PhotoSelector dialog
|
||||||
|
*/
|
||||||
|
|
||||||
|
+function () {
|
||||||
|
|
||||||
|
if ($.oc.photoselector === undefined) {
|
||||||
|
$.oc.photoselector = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var Base = $.oc.foundation.base,
|
||||||
|
BaseProto = Base.prototype;
|
||||||
|
|
||||||
|
var PhotoSelector = function (options) {
|
||||||
|
this.$dialog = $('<div/>');
|
||||||
|
this.options = $.extend({}, PhotoSelector.DEFAULTS, options);
|
||||||
|
|
||||||
|
Base.call(this);
|
||||||
|
|
||||||
|
this.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PhotoSelector.prototype = Object.create(BaseProto);
|
||||||
|
PhotoSelector.prototype.constructor = PhotoSelector;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and show the dialog
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.show = function () {
|
||||||
|
this.$dialog.one('complete.oc.popup', this.proxy(this.onPopupShown));
|
||||||
|
this.$dialog.popup({
|
||||||
|
size: 'large',
|
||||||
|
extraData: {album: this.options.album },
|
||||||
|
handler: this.options.alias + '::onDialogOpen'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when the popup is loaded and shown
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* @param element
|
||||||
|
* @param popup
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.onPopupShown = function (event, element, popup) {
|
||||||
|
this.$dialog = popup;
|
||||||
|
// bind clicks for album thumb and title links
|
||||||
|
if (this.options.album) {
|
||||||
|
this.bindPhotosListHandlers();
|
||||||
|
} else {
|
||||||
|
$('#albumsList .album-link', popup).one('click', this.proxy(this.onAlbumClicked));
|
||||||
|
}
|
||||||
|
$('div.photo-selection-dialog').find('button.btn-insert').click(this.proxy(this.onInsertClicked));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Album clicked callback
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.onAlbumClicked = function (event) {
|
||||||
|
var link_id = $(event.currentTarget).data('request-data');
|
||||||
|
var selector = this;
|
||||||
|
$.request('onAlbumLoad', {
|
||||||
|
data: {id: link_id},
|
||||||
|
update: {photos: '#listContainer'},
|
||||||
|
loading: $.oc.stripeLoadIndicator,
|
||||||
|
success: function (data) {
|
||||||
|
this.success(data);
|
||||||
|
selector.bindPhotosListHandlers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind event handlers for photos list
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.bindPhotosListHandlers = function () {
|
||||||
|
// bind photo link click and double click events
|
||||||
|
$('#photosList').find('a.photo-link').click(this.proxy(this.onPhotoSelected));
|
||||||
|
$('#photosList').find('a.photo-link').dblclick(this.proxy(this.onPhotoDoubleClicked));
|
||||||
|
// bind back to albums click event
|
||||||
|
$('#photosList').find('a.back-to-albums').one('click', this.proxy(this.onBackToAlbums));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Photo clicked callback
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.onPhotoSelected = function (event) {
|
||||||
|
// remove old selected classes
|
||||||
|
$('#photosList').find('a.selected').removeClass('selected');
|
||||||
|
|
||||||
|
// add new selected classes
|
||||||
|
var wrapper = $(event.currentTarget).parents('.photo-links-wrapper');
|
||||||
|
wrapper.find('a.photo-link').addClass('selected');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Back to albums clicked callback
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.onBackToAlbums = function (event) {
|
||||||
|
var selector = this;
|
||||||
|
$.request('onAlbumListLoad', {
|
||||||
|
'update': { albums: '#listContainer'},
|
||||||
|
loading: $.oc.stripeLoadIndicator,
|
||||||
|
success: function (data) {
|
||||||
|
this.success(data);
|
||||||
|
$('#albumsList').find('.album-link').one('click', selector.proxy(selector.onAlbumClicked));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Photo insert button callback
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.onInsertClicked = function (event) {
|
||||||
|
var selected = $('#photosList').find('a.selected').first();
|
||||||
|
if (!selected.length) {
|
||||||
|
// FIXME Localize when it is supported
|
||||||
|
alert('You have to select a photo first. Click on the photo, then click "Insert". Or just double-click the photo.');
|
||||||
|
} else {
|
||||||
|
var code = selected.data('request-data');
|
||||||
|
var album = $('#photosList').data('request-data');
|
||||||
|
this.options.onInsert.call(this, code, album);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Double click callback
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.onPhotoDoubleClicked = function (event) {
|
||||||
|
// select the photo and insert it
|
||||||
|
var link = $(event.currentTarget);
|
||||||
|
link.trigger('click');
|
||||||
|
$('div.photo-selection-dialog').find('button.btn-insert').trigger('click');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide popup
|
||||||
|
*/
|
||||||
|
PhotoSelector.prototype.hide = function () {
|
||||||
|
if (this.$dialog) {
|
||||||
|
this.$dialog.trigger('close.oc.popup');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default options
|
||||||
|
*/
|
||||||
|
PhotoSelector.DEFAULTS = {
|
||||||
|
alias: undefined,
|
||||||
|
album: 0,
|
||||||
|
onInsert: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
$.oc.photoselector.popup = PhotoSelector;
|
||||||
|
|
||||||
|
} (window.jQuery);
|
|
@ -0,0 +1,16 @@
|
||||||
|
<div id="albumsList" class="row">
|
||||||
|
<?php foreach ($albums as $album) : ?>
|
||||||
|
<div class="col-xs-12 col-sm-4">
|
||||||
|
<p class="text-center">
|
||||||
|
<a href="javascript: ;" class="album-link" data-request-data="<?= $album->id ?>">
|
||||||
|
<img src="<?= $album->thumb; ?>" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="text-center">
|
||||||
|
<a href="javascript: ;" class="album-link" data-request-data="<?= $album->id ?>">
|
||||||
|
<?= $album->title; ?>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<div class="modal-lg modal-dialog photo-selection-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
<h4><?= e(trans('graker.photoalbums::lang.plugin.selecting_photo')) ?></h4>
|
||||||
|
</div>
|
||||||
|
<div id="listContainer" class="modal-body">
|
||||||
|
<?php if ($albums) : ?>
|
||||||
|
<?= $this->makePartial('albums') ?>
|
||||||
|
<?php elseif ($album) : ?>
|
||||||
|
<?= $this->makePartial('photos') ?>
|
||||||
|
<?php else : ?>
|
||||||
|
<p><?= e(trans('graker.photoalbums::lang.errors.no_albums')) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-primary btn-insert"><?= e(trans('graker.photoalbums::lang.plugin.insert')) ?></button>
|
||||||
|
<button class="btn btn-default btn-cancel" data-dismiss="modal"><?= e(trans('backend::lang.form.cancel')) ?></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<div id="photosList" class="row" data-request-data="<?= $album->id ?>">
|
||||||
|
<h4 class="text-center"><?= $album->title; ?></h4>
|
||||||
|
<?php foreach ($album->photos as $photo) : ?>
|
||||||
|
<div class="col-xs-12 col-sm-4">
|
||||||
|
<div class="photo-links-wrapper">
|
||||||
|
<p class="text-center">
|
||||||
|
<a href="javascript: ;" class="photo-link image-link" data-request-data="<?= $photo->code ?>">
|
||||||
|
<img src="<?= $photo->thumb; ?>" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p class="text-center">
|
||||||
|
<a href="javascript: ;" class="photo-link title-link" data-request-data="<?= $photo->code ?>">
|
||||||
|
<?php if ($photo->title) : ?>
|
||||||
|
<?= $photo->title; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<div class="col-xs-11 text-right">
|
||||||
|
<a href="javascript: ;" class="back-to-albums"><?= e(trans('graker.photoalbums::lang.plugin.back_to_albums')) ?></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,19 @@
|
||||||
|
# MIT license
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
namespace manogi\Mediathumb;
|
||||||
|
|
||||||
|
use Backend;
|
||||||
|
use System\Classes\PluginBase;
|
||||||
|
use Config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mediathumb Plugin Information File
|
||||||
|
*/
|
||||||
|
class Plugin extends PluginBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns information about this plugin.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function pluginDetails()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'manogi.mediathumb::lang.plugin.name',
|
||||||
|
'description' => 'manogi.mediathumb::lang.plugin.description',
|
||||||
|
'author' => 'manogi',
|
||||||
|
'icon' => 'icon-compress',
|
||||||
|
'homepage' => 'https://github.com/manogi/october-mediathumb'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerMarkupTags()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'filters' => [
|
||||||
|
'mediathumb_resize' => [$this, 'mediathumb_resize']
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mediathumb_resize($img, $mode = null, $size = null, $quality = null)
|
||||||
|
{
|
||||||
|
return mediathumbResize($img, $mode, $size, $quality, 'media');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
# Thumbnail images for Media
|
||||||
|
|
||||||
|
+ Twig filter for automatic thumbnail images for your media images.
|
||||||
|
+ Static PHP helper function for automatic thumbnail images for your media images in your backend files.
|
||||||
|
|
||||||
|
You can find this plugin in the OctoberCMS Plugins page [here](http://octobercms.com/plugin/manogi-mediathumb).
|
||||||
|
|
||||||
|
After installing the plugin you can use it...
|
||||||
|
|
||||||
|
...as a Twig filter in other plugins or in your theme files:
|
||||||
|
|
||||||
|
<img src="{{ 'path/to/image.jpg'|mediathumb_resize(mode, size, quality) }}">
|
||||||
|
|
||||||
|
... as a static PHP helper function in your backend PHP and .htm files:
|
||||||
|
|
||||||
|
<img src="<?= getMediathumb('path/to/image.jpg', mode, size, quality) ?>">
|
||||||
|
|
||||||
|
The filter supports three arguments:
|
||||||
|
|
||||||
|
+ _mode_: can bei either 'auto', 'width' or 'height'. 'auto' is the default.
|
||||||
|
+ _size_: an integer describing the length in pixels (defaults to 200) of
|
||||||
|
- the longer edge of the image in 'auto' mode
|
||||||
|
- the width in 'width' mode
|
||||||
|
- the height in 'height' mode
|
||||||
|
+ _quality_: an integer from 1 – 100 to set the quality of the image. Only applies to JPGs. Defaults to 90.
|
||||||
|
|
||||||
|
The static PHP helper function needs the image path as a string as the first argument.
|
||||||
|
|
||||||
|
## Examples:
|
||||||
|
|
||||||
|
### Twig (frontend code)
|
||||||
|
|
||||||
|
<img src="{{ 'path/to/image.jpg'|mediathumb_resize() }}">
|
||||||
|
|
||||||
|
Creates and displays a 200px wide thumbnail image of an landscape image or a 200px high thumbnail image of a portrait image.
|
||||||
|
|
||||||
|
|
||||||
|
<img src="{{ 'path/to/image.jpg'|mediathumb_resize('height', 400) }}">
|
||||||
|
|
||||||
|
Creates and displays a 400px high thumbnail image, no matter if the original is a landscape or a portrait image.
|
||||||
|
|
||||||
|
|
||||||
|
<img src="{{ 'path/to/image.jpg'|mediathumb_resize('width', 800, 96) }}">
|
||||||
|
|
||||||
|
Creates and displays a 800px wide thumbnail image with a quality of 96, no matter if the original is a landscape or a portrait image.
|
||||||
|
|
||||||
|
|
||||||
|
### Static PHP helper function (backend code)
|
||||||
|
|
||||||
|
The static PHP helper function needs the image path as a string as the first argument. You can use it for example when you display a list of items in the backend, using the default `$record` variable you get when using the default OctoberCMS `$this->listRender()` function:
|
||||||
|
|
||||||
|
<img src="<?= mediathumbResize($record->image, 'height', 180, 96) ?>">
|
||||||
|
|
||||||
|
Creates and displays a 180px high thumbnail image, no matter if the original is a landscape or a portrait image.
|
||||||
|
|
||||||
|
While of course `$record->image` might be something else in your case. "image" is here the name of the field you store your image in.
|
||||||
|
|
||||||
|
You can of course also use the defaults like so:
|
||||||
|
|
||||||
|
<img src="<?= mediathumbResize($record->image) ?>">
|
||||||
|
|
||||||
|
Creates and displays a 200px wide thumbnail image of an landscape image or a 200px high thumbnail image of a portrait image.
|
||||||
|
|
||||||
|
_until now this function was called `getMediathumb` instead of `mediathumbResize`. This name will still work, I left an alias for it that will stay in there forever ;-)_
|
||||||
|
|
||||||
|
|
||||||
|
###Uploads images functionality (for example "featured images" in Blog and Pro Blog):
|
||||||
|
|
||||||
|
We made the functionality also available for so called "uploads" - these are for example those images that are uploaded directly when editing a Model instance, like the "featured images" of a blog post in the Blog and Pro Blog plugins. You can use the following with all mediathumb features:
|
||||||
|
|
||||||
|
<img src=" {{ post.featured_images[0]['path']|mediathumb_resize() }}">
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
<img src="<?= mediathumbResize($post->featured_images[0]['path'], mode, size, quality) ?>">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##Configuration
|
||||||
|
|
||||||
|
### Custom folder name:
|
||||||
|
|
||||||
|
The default folder name "_mediathumbs" can be changed (also to a subfolder like "some/sub/folder") in the config/config.php file of the plugin.
|
||||||
|
|
||||||
|
|
||||||
|
### Defaults:
|
||||||
|
|
||||||
|
The defaults for `mode`, `size` and `quality` can be changed in the config/config.php file of the plugin.
|
||||||
|
|
||||||
|
|
||||||
|
## How does it work:
|
||||||
|
|
||||||
|
The plugin checks if a thumbnail for the original image was already created - if not, it creates the thumbnail.
|
||||||
|
Then the thumbnail path is returned.
|
||||||
|
|
||||||
|
## What if I overwrite the original with an altered version?
|
||||||
|
|
||||||
|
The plugin uses the filetime and filesize in naming the thumbnail to make sure that altered images with the same name don't produce old thumbnails.
|
||||||
|
|
||||||
|
## Where are the thumbnails stored?
|
||||||
|
|
||||||
|
In a mediathumb folder in your storage media folder (which is created automatically, also see "Custom folder name" above).
|
||||||
|
|
||||||
|
## Does it work with Amazon S3?
|
||||||
|
|
||||||
|
Yep.
|
||||||
|
|
||||||
|
## What happens to the thumbnail files once I delete the original?
|
||||||
|
|
||||||
|
So far they just stay in the mediathumb folder. I am working on a solution to have them deleted together with the originals, but remember you can easily empty or delete the mediathumbs folder altogether - the thumbnails will just start being re-created when people hit your website.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
+ Adding a `mediathumb_square` filter for creating automatic square thumbs.
|
||||||
|
+ ... (let me know if you have feature requests. No promises, though...)
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
// Set the mediathumb fold inside your media directory
|
||||||
|
// can be a sub folder like "some/sub/folder"
|
||||||
|
// Set to "_mediathumbs" by default
|
||||||
|
|
||||||
|
'folder' => '_mediathumbs',
|
||||||
|
|
||||||
|
// Set the default for creating mediathumbs
|
||||||
|
|
||||||
|
'default' => [
|
||||||
|
'mode' => 'auto',
|
||||||
|
'size' => 200,
|
||||||
|
'quality' => 90,
|
||||||
|
]
|
||||||
|
];
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'plugin' => [
|
||||||
|
'name' => 'Mediathumb',
|
||||||
|
'description' => 'Přidává nový Twig filtr mediathumb.'
|
||||||
|
]
|
||||||
|
];
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'plugin' => [
|
||||||
|
'name' => 'Mediathumb',
|
||||||
|
'description' => 'Fügt den mediathumb Twig-Filter hinzu.'
|
||||||
|
]
|
||||||
|
];
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'plugin' => [
|
||||||
|
'name' => 'Mediathumb',
|
||||||
|
'description' => 'Twig filter for automatic thumbnail images for your media images.'
|
||||||
|
]
|
||||||
|
];
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'plugin' => [
|
||||||
|
'name' => 'Képméretezés',
|
||||||
|
'description' => 'A Média képeinek dinamikus átméretezése.'
|
||||||
|
]
|
||||||
|
];
|
|
@ -0,0 +1,19 @@
|
||||||
|
0.0.0: First version of Mediathumb
|
||||||
|
0.0.1: Added Amazon S3 support
|
||||||
|
0.1.0: Added config/config.php file for setting defaults and added backend support (major change, please see documentation)
|
||||||
|
0.2.0: Renamed the helper function getMediathumb(). Left an alias for mediathumbGetThumb() to prevent breaking change.
|
||||||
|
0.2.1: Fixed an error when an empty string was passed as image path.
|
||||||
|
0.2.2: cs_CZ language added - thanks to Vojta Svoboda
|
||||||
|
0.2.3: Hungarian language added and minor typos corrected - thanks to Szabó Gergő
|
||||||
|
0.2.4: Added MIT lincence - thanks to Szabó Gergő
|
||||||
|
0.2.5: Corrected a bug to make it work in installations of OctoberCMS in a subfolder.
|
||||||
|
0.3.0: Added option to change mediathumb folder name (including subfolders) in the config file.
|
||||||
|
0.3.1: Fixed a bug which made the plugin return an exception when failing to create a thumbnail for unsupported file types.
|
||||||
|
0.3.2: Making sure the static helper function does not overwrite an existing function (Thanks to Tobias Kündig).
|
||||||
|
0.3.3: Fixed syntax error in helper function
|
||||||
|
0.4.0: Added functionality for uploads files like featured images in blogposts
|
||||||
|
0.4.1: Fixed bug in autoload file
|
||||||
|
0.4.2: Fixed bug that made Amazon S3 not working correctly for uploads
|
||||||
|
0.4.3: Fixed bug that made Mediathumb not work in OctoberCMS installed in a sub directory using local disk
|
||||||
|
0.4.4: Fix for octobercms breaking change
|
||||||
|
0.4.5: Slugify the thumb filename, copy Gifs without resizing (because animated gifs)
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload.php @generated by Composer
|
||||||
|
|
||||||
|
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||||
|
|
||||||
|
return ComposerAutoloaderInit6c3de2f9e71443bc1a465e7aa469c6e4::getLoader();
|
|
@ -0,0 +1,413 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||||
|
*
|
||||||
|
* $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
*
|
||||||
|
* // register classes with namespaces
|
||||||
|
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||||
|
* $loader->add('Symfony', __DIR__.'/framework');
|
||||||
|
*
|
||||||
|
* // activate the autoloader
|
||||||
|
* $loader->register();
|
||||||
|
*
|
||||||
|
* // to enable searching the include path (eg. for PEAR packages)
|
||||||
|
* $loader->setUseIncludePath(true);
|
||||||
|
*
|
||||||
|
* In this example, if you try to use a class in the Symfony\Component
|
||||||
|
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||||
|
* the autoloader will first look for the class under the component/
|
||||||
|
* directory, and it will then fallback to the framework/ directory if not
|
||||||
|
* found before giving up.
|
||||||
|
*
|
||||||
|
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||||
|
*
|
||||||
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
* @see http://www.php-fig.org/psr/psr-0/
|
||||||
|
* @see http://www.php-fig.org/psr/psr-4/
|
||||||
|
*/
|
||||||
|
class ClassLoader
|
||||||
|
{
|
||||||
|
// PSR-4
|
||||||
|
private $prefixLengthsPsr4 = array();
|
||||||
|
private $prefixDirsPsr4 = array();
|
||||||
|
private $fallbackDirsPsr4 = array();
|
||||||
|
|
||||||
|
// PSR-0
|
||||||
|
private $prefixesPsr0 = array();
|
||||||
|
private $fallbackDirsPsr0 = array();
|
||||||
|
|
||||||
|
private $useIncludePath = false;
|
||||||
|
private $classMap = array();
|
||||||
|
|
||||||
|
private $classMapAuthoritative = false;
|
||||||
|
|
||||||
|
public function getPrefixes()
|
||||||
|
{
|
||||||
|
if (!empty($this->prefixesPsr0)) {
|
||||||
|
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrefixesPsr4()
|
||||||
|
{
|
||||||
|
return $this->prefixDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirs()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFallbackDirsPsr4()
|
||||||
|
{
|
||||||
|
return $this->fallbackDirsPsr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassMap()
|
||||||
|
{
|
||||||
|
return $this->classMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $classMap Class to filename map
|
||||||
|
*/
|
||||||
|
public function addClassMap(array $classMap)
|
||||||
|
{
|
||||||
|
if ($this->classMap) {
|
||||||
|
$this->classMap = array_merge($this->classMap, $classMap);
|
||||||
|
} else {
|
||||||
|
$this->classMap = $classMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix, either
|
||||||
|
* appending or prepending to the ones previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 root directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*/
|
||||||
|
public function add($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr0 = array_merge(
|
||||||
|
$this->fallbackDirsPsr0,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$first = $prefix[0];
|
||||||
|
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($prepend) {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixesPsr0[$first][$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||||
|
$this->prefixesPsr0[$first][$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace, either
|
||||||
|
* appending or prepending to the ones previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
* @param bool $prepend Whether to prepend the directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function addPsr4($prefix, $paths, $prepend = false)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
// Register directories for the root namespace.
|
||||||
|
if ($prepend) {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->fallbackDirsPsr4
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->fallbackDirsPsr4 = array_merge(
|
||||||
|
$this->fallbackDirsPsr4,
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||||
|
// Register directories for a new namespace.
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
} elseif ($prepend) {
|
||||||
|
// Prepend directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
(array) $paths,
|
||||||
|
$this->prefixDirsPsr4[$prefix]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Append directories for an already registered namespace.
|
||||||
|
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||||
|
$this->prefixDirsPsr4[$prefix],
|
||||||
|
(array) $paths
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-0 directories for a given prefix,
|
||||||
|
* replacing any others previously set for this prefix.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix
|
||||||
|
* @param array|string $paths The PSR-0 base directories
|
||||||
|
*/
|
||||||
|
public function set($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr0 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a set of PSR-4 directories for a given namespace,
|
||||||
|
* replacing any others previously set for this namespace.
|
||||||
|
*
|
||||||
|
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||||
|
* @param array|string $paths The PSR-4 base directories
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function setPsr4($prefix, $paths)
|
||||||
|
{
|
||||||
|
if (!$prefix) {
|
||||||
|
$this->fallbackDirsPsr4 = (array) $paths;
|
||||||
|
} else {
|
||||||
|
$length = strlen($prefix);
|
||||||
|
if ('\\' !== $prefix[$length - 1]) {
|
||||||
|
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||||
|
}
|
||||||
|
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||||
|
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on searching the include path for class files.
|
||||||
|
*
|
||||||
|
* @param bool $useIncludePath
|
||||||
|
*/
|
||||||
|
public function setUseIncludePath($useIncludePath)
|
||||||
|
{
|
||||||
|
$this->useIncludePath = $useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to check if the autoloader uses the include path to check
|
||||||
|
* for classes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getUseIncludePath()
|
||||||
|
{
|
||||||
|
return $this->useIncludePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns off searching the prefix and fallback directories for classes
|
||||||
|
* that have not been registered with the class map.
|
||||||
|
*
|
||||||
|
* @param bool $classMapAuthoritative
|
||||||
|
*/
|
||||||
|
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||||
|
{
|
||||||
|
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should class lookup fail if not found in the current class map?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClassMapAuthoritative()
|
||||||
|
{
|
||||||
|
return $this->classMapAuthoritative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers this instance as an autoloader.
|
||||||
|
*
|
||||||
|
* @param bool $prepend Whether to prepend the autoloader or not
|
||||||
|
*/
|
||||||
|
public function register($prepend = false)
|
||||||
|
{
|
||||||
|
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters this instance as an autoloader.
|
||||||
|
*/
|
||||||
|
public function unregister()
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array($this, 'loadClass'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given class or interface.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
* @return bool|null True if loaded, null otherwise
|
||||||
|
*/
|
||||||
|
public function loadClass($class)
|
||||||
|
{
|
||||||
|
if ($file = $this->findFile($class)) {
|
||||||
|
includeFile($file);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the path to the file where the class is defined.
|
||||||
|
*
|
||||||
|
* @param string $class The name of the class
|
||||||
|
*
|
||||||
|
* @return string|false The path if found, false otherwise
|
||||||
|
*/
|
||||||
|
public function findFile($class)
|
||||||
|
{
|
||||||
|
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||||
|
if ('\\' == $class[0]) {
|
||||||
|
$class = substr($class, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class map lookup
|
||||||
|
if (isset($this->classMap[$class])) {
|
||||||
|
return $this->classMap[$class];
|
||||||
|
}
|
||||||
|
if ($this->classMapAuthoritative) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $this->findFileWithExtension($class, '.php');
|
||||||
|
|
||||||
|
// Search for Hack files if we are running on HHVM
|
||||||
|
if ($file === null && defined('HHVM_VERSION')) {
|
||||||
|
$file = $this->findFileWithExtension($class, '.hh');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file === null) {
|
||||||
|
// Remember that this class does not exist.
|
||||||
|
return $this->classMap[$class] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findFileWithExtension($class, $ext)
|
||||||
|
{
|
||||||
|
// PSR-4 lookup
|
||||||
|
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
|
||||||
|
$first = $class[0];
|
||||||
|
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||||
|
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-4 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 lookup
|
||||||
|
if (false !== $pos = strrpos($class, '\\')) {
|
||||||
|
// namespaced class name
|
||||||
|
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||||
|
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||||
|
} else {
|
||||||
|
// PEAR-like class name
|
||||||
|
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->prefixesPsr0[$first])) {
|
||||||
|
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||||
|
if (0 === strpos($class, $prefix)) {
|
||||||
|
foreach ($dirs as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 fallback dirs
|
||||||
|
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||||
|
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSR-0 include paths.
|
||||||
|
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope isolated include.
|
||||||
|
*
|
||||||
|
* Prevents access to $this/self from included files.
|
||||||
|
*/
|
||||||
|
function includeFile($file)
|
||||||
|
{
|
||||||
|
include $file;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
Copyright (c) 2016 Nils Adermann, Jordi Boggiano
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is furnished
|
||||||
|
to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_classmap.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_files.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
|
'c573a7e5893a138545b4829bb4a11fcc' => $vendorDir . '/manogi/mediathumb/resize_helper.php',
|
||||||
|
);
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_namespaces.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
);
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_psr4.php @generated by Composer
|
||||||
|
|
||||||
|
$vendorDir = dirname(dirname(__FILE__));
|
||||||
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'Urodoz\\Truncate\\' => array($vendorDir . '/urodoz/truncate-html/src'),
|
||||||
|
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
|
||||||
|
'Intervention\\Image\\' => array($vendorDir . '/intervention/image/src/Intervention/Image'),
|
||||||
|
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||||
|
);
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
|
class ComposerAutoloaderInit6c3de2f9e71443bc1a465e7aa469c6e4
|
||||||
|
{
|
||||||
|
private static $loader;
|
||||||
|
|
||||||
|
public static function loadClassLoader($class)
|
||||||
|
{
|
||||||
|
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||||
|
require __DIR__ . '/ClassLoader.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLoader()
|
||||||
|
{
|
||||||
|
if (null !== self::$loader) {
|
||||||
|
return self::$loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('ComposerAutoloaderInit6c3de2f9e71443bc1a465e7aa469c6e4', 'loadClassLoader'), true, true);
|
||||||
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||||
|
spl_autoload_unregister(array('ComposerAutoloaderInit6c3de2f9e71443bc1a465e7aa469c6e4', 'loadClassLoader'));
|
||||||
|
|
||||||
|
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->set($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map = require __DIR__ . '/autoload_psr4.php';
|
||||||
|
foreach ($map as $namespace => $path) {
|
||||||
|
$loader->setPsr4($namespace, $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||||
|
if ($classMap) {
|
||||||
|
$loader->addClassMap($classMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader->register(true);
|
||||||
|
|
||||||
|
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||||
|
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||||
|
composerRequire6c3de2f9e71443bc1a465e7aa469c6e4($fileIdentifier, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $loader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function composerRequire6c3de2f9e71443bc1a465e7aa469c6e4($fileIdentifier, $file)
|
||||||
|
{
|
||||||
|
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||||
|
require $file;
|
||||||
|
|
||||||
|
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// autoload_static.php @generated by Composer
|
||||||
|
|
||||||
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
|
class ComposerStaticInit6c3de2f9e71443bc1a465e7aa469c6e4
|
||||||
|
{
|
||||||
|
public static $files = array (
|
||||||
|
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||||
|
'c573a7e5893a138545b4829bb4a11fcc' => __DIR__ . '/..' . '/manogi/mediathumb/resize_helper.php',
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixLengthsPsr4 = array (
|
||||||
|
'U' =>
|
||||||
|
array (
|
||||||
|
'Urodoz\\Truncate\\' => 16,
|
||||||
|
),
|
||||||
|
'P' =>
|
||||||
|
array (
|
||||||
|
'Psr\\Http\\Message\\' => 17,
|
||||||
|
),
|
||||||
|
'I' =>
|
||||||
|
array (
|
||||||
|
'Intervention\\Image\\' => 19,
|
||||||
|
),
|
||||||
|
'G' =>
|
||||||
|
array (
|
||||||
|
'GuzzleHttp\\Psr7\\' => 16,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $prefixDirsPsr4 = array (
|
||||||
|
'Urodoz\\Truncate\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/urodoz/truncate-html/src',
|
||||||
|
),
|
||||||
|
'Psr\\Http\\Message\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||||
|
),
|
||||||
|
'Intervention\\Image\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/intervention/image/src/Intervention/Image',
|
||||||
|
),
|
||||||
|
'GuzzleHttp\\Psr7\\' =>
|
||||||
|
array (
|
||||||
|
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
{
|
||||||
|
return \Closure::bind(function () use ($loader) {
|
||||||
|
$loader->prefixLengthsPsr4 = ComposerStaticInit6c3de2f9e71443bc1a465e7aa469c6e4::$prefixLengthsPsr4;
|
||||||
|
$loader->prefixDirsPsr4 = ComposerStaticInit6c3de2f9e71443bc1a465e7aa469c6e4::$prefixDirsPsr4;
|
||||||
|
|
||||||
|
}, null, ClassLoader::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "urodoz/truncate-html",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"version_normalized": "1.0.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/urodoz/truncateHTML.git",
|
||||||
|
"reference": "88fb29fd3a30c95b879f1642e08fd8746dd05bd7"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/urodoz/truncateHTML/zipball/88fb29fd3a30c95b879f1642e08fd8746dd05bd7",
|
||||||
|
"reference": "88fb29fd3a30c95b879f1642e08fd8746dd05bd7",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "~4",
|
||||||
|
"symfony/dependency-injection": "~2.4",
|
||||||
|
"symfony/http-kernel": "~2.4",
|
||||||
|
"twig/twig": "~1"
|
||||||
|
},
|
||||||
|
"time": "2014-05-25 22:50:06",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Urodoz\\Truncate\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Albert Lacarta",
|
||||||
|
"email": "urodoz@gmail.com",
|
||||||
|
"homepage": "http://www.rqlogic.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Handle truncate action on HTML strings",
|
||||||
|
"keywords": [
|
||||||
|
"content",
|
||||||
|
"html",
|
||||||
|
"shorten",
|
||||||
|
"truncate",
|
||||||
|
"truncating"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/http-message",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"version_normalized": "1.0.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/http-message.git",
|
||||||
|
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||||
|
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.0"
|
||||||
|
},
|
||||||
|
"time": "2016-08-06 14:39:51",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Http\\Message\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "http://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common interface for HTTP messages",
|
||||||
|
"homepage": "https://github.com/php-fig/http-message",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"http-message",
|
||||||
|
"psr",
|
||||||
|
"psr-7",
|
||||||
|
"request",
|
||||||
|
"response"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/psr7",
|
||||||
|
"version": "1.4.2",
|
||||||
|
"version_normalized": "1.4.2.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/psr7.git",
|
||||||
|
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
|
||||||
|
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0",
|
||||||
|
"psr/http-message": "~1.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/http-message-implementation": "1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "~4.0"
|
||||||
|
},
|
||||||
|
"time": "2017-03-20 17:10:46",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.4-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\Psr7\\": "src/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/functions_include.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||||
|
"keywords": [
|
||||||
|
"http",
|
||||||
|
"message",
|
||||||
|
"request",
|
||||||
|
"response",
|
||||||
|
"stream",
|
||||||
|
"uri",
|
||||||
|
"url"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "intervention/image",
|
||||||
|
"version": "2.4.1",
|
||||||
|
"version_normalized": "2.4.1.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Intervention/image.git",
|
||||||
|
"reference": "3603dbcc9a17d307533473246a6c58c31cf17919"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Intervention/image/zipball/3603dbcc9a17d307533473246a6c58c31cf17919",
|
||||||
|
"reference": "3603dbcc9a17d307533473246a6c58c31cf17919",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-fileinfo": "*",
|
||||||
|
"guzzlehttp/psr7": "~1.1",
|
||||||
|
"php": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "~0.9.2",
|
||||||
|
"phpunit/phpunit": "^4.8 || ^5.7"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-gd": "to use GD library based image processing.",
|
||||||
|
"ext-imagick": "to use Imagick based image processing.",
|
||||||
|
"intervention/imagecache": "Caching extension for the Intervention Image library"
|
||||||
|
},
|
||||||
|
"time": "2017-09-21 16:29:17",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.3-dev"
|
||||||
|
},
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Intervention\\Image\\ImageServiceProvider"
|
||||||
|
],
|
||||||
|
"aliases": {
|
||||||
|
"Image": "Intervention\\Image\\Facades\\Image"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Intervention\\Image\\": "src/Intervention/Image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Oliver Vogel",
|
||||||
|
"email": "oliver@olivervogel.com",
|
||||||
|
"homepage": "http://olivervogel.com/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Image handling and manipulation library with support for Laravel integration",
|
||||||
|
"homepage": "http://image.intervention.io/",
|
||||||
|
"keywords": [
|
||||||
|
"gd",
|
||||||
|
"image",
|
||||||
|
"imagick",
|
||||||
|
"laravel",
|
||||||
|
"thumbnail",
|
||||||
|
"watermark"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,110 @@
|
||||||
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 1.4.2 - 2017-03-20
|
||||||
|
|
||||||
|
* Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
|
||||||
|
calls to `trigger_error` when deprecated methods are invoked.
|
||||||
|
|
||||||
|
## 1.4.1 - 2017-02-27
|
||||||
|
|
||||||
|
* Reverted BC break by reintroducing behavior to automagically fix a URI with a
|
||||||
|
relative path and an authority by adding a leading slash to the path. It's only
|
||||||
|
deprecated now.
|
||||||
|
* Added triggering of silenced deprecation warnings.
|
||||||
|
|
||||||
|
## 1.4.0 - 2017-02-21
|
||||||
|
|
||||||
|
* Fix `Stream::read` when length parameter <= 0.
|
||||||
|
* `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
|
||||||
|
* Fix `ServerRequest::getUriFromGlobals` when `Host` header contains port.
|
||||||
|
* Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
|
||||||
|
* Allow `parse_response` to parse a response without delimiting space and reason.
|
||||||
|
* Ensure each URI modification results in a valid URI according to PSR-7 discussions.
|
||||||
|
Invalid modifications will throw an exception instead of returning a wrong URI or
|
||||||
|
doing some magic.
|
||||||
|
- `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
|
||||||
|
because the path of a URI with an authority must start with a slash "/" or be empty
|
||||||
|
- `(new Uri())->withScheme('http')` will return `'http://localhost'`
|
||||||
|
* Fix compatibility of URIs with `file` scheme and empty host.
|
||||||
|
* Added common URI utility methods based on RFC 3986 (see documentation in the readme):
|
||||||
|
- `Uri::isDefaultPort`
|
||||||
|
- `Uri::isAbsolute`
|
||||||
|
- `Uri::isNetworkPathReference`
|
||||||
|
- `Uri::isAbsolutePathReference`
|
||||||
|
- `Uri::isRelativePathReference`
|
||||||
|
- `Uri::isSameDocumentReference`
|
||||||
|
- `Uri::composeComponents`
|
||||||
|
- `UriNormalizer::normalize`
|
||||||
|
- `UriNormalizer::isEquivalent`
|
||||||
|
- `UriResolver::relativize`
|
||||||
|
* Deprecated `Uri::resolve` in favor of `UriResolver::resolve`
|
||||||
|
* Deprecated `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
|
||||||
|
|
||||||
|
## 1.3.1 - 2016-06-25
|
||||||
|
|
||||||
|
* Fix `Uri::__toString` for network path references, e.g. `//example.org`.
|
||||||
|
* Fix missing lowercase normalization for host.
|
||||||
|
* Fix handling of URI components in case they are `'0'` in a lot of places,
|
||||||
|
e.g. as a user info password.
|
||||||
|
* Fix `Uri::withAddedHeader` to correctly merge headers with different case.
|
||||||
|
* Fix trimming of header values in `Uri::withAddedHeader`. Header values may
|
||||||
|
be surrounded by whitespace which should be ignored according to RFC 7230
|
||||||
|
Section 3.2.4. This does not apply to header names.
|
||||||
|
* Fix `Uri::withAddedHeader` with an array of header values.
|
||||||
|
* Fix `Uri::resolve` when base path has no slash and handling of fragment.
|
||||||
|
* Fix handling of encoding in `Uri::with(out)QueryValue` so one can pass the
|
||||||
|
key/value both in encoded as well as decoded form to those methods. This is
|
||||||
|
consistent with withPath, withQuery etc.
|
||||||
|
* Fix `ServerRequest::withoutAttribute` when attribute value is null.
|
||||||
|
|
||||||
|
## 1.3.0 - 2016-04-13
|
||||||
|
|
||||||
|
* Added remaining interfaces needed for full PSR7 compatibility
|
||||||
|
(ServerRequestInterface, UploadedFileInterface, etc.).
|
||||||
|
* Added support for stream_for from scalars.
|
||||||
|
* Can now extend Uri.
|
||||||
|
* Fixed a bug in validating request methods by making it more permissive.
|
||||||
|
|
||||||
|
## 1.2.3 - 2016-02-18
|
||||||
|
|
||||||
|
* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
|
||||||
|
streams, which can sometimes return fewer bytes than requested with `fread`.
|
||||||
|
* Fixed handling of gzipped responses with FNAME headers.
|
||||||
|
|
||||||
|
## 1.2.2 - 2016-01-22
|
||||||
|
|
||||||
|
* Added support for URIs without any authority.
|
||||||
|
* Added support for HTTP 451 'Unavailable For Legal Reasons.'
|
||||||
|
* Added support for using '0' as a filename.
|
||||||
|
* Added support for including non-standard ports in Host headers.
|
||||||
|
|
||||||
|
## 1.2.1 - 2015-11-02
|
||||||
|
|
||||||
|
* Now supporting negative offsets when seeking to SEEK_END.
|
||||||
|
|
||||||
|
## 1.2.0 - 2015-08-15
|
||||||
|
|
||||||
|
* Body as `"0"` is now properly added to a response.
|
||||||
|
* Now allowing forward seeking in CachingStream.
|
||||||
|
* Now properly parsing HTTP requests that contain proxy targets in
|
||||||
|
`parse_request`.
|
||||||
|
* functions.php is now conditionally required.
|
||||||
|
* user-info is no longer dropped when resolving URIs.
|
||||||
|
|
||||||
|
## 1.1.0 - 2015-06-24
|
||||||
|
|
||||||
|
* URIs can now be relative.
|
||||||
|
* `multipart/form-data` headers are now overridden case-insensitively.
|
||||||
|
* URI paths no longer encode the following characters because they are allowed
|
||||||
|
in URIs: "(", ")", "*", "!", "'"
|
||||||
|
* A port is no longer added to a URI when the scheme is missing and no port is
|
||||||
|
present.
|
||||||
|
|
||||||
|
## 1.0.0 - 2015-05-19
|
||||||
|
|
||||||
|
Initial release.
|
||||||
|
|
||||||
|
Currently unsupported:
|
||||||
|
|
||||||
|
- `Psr\Http\Message\ServerRequestInterface`
|
||||||
|
- `Psr\Http\Message\UploadedFileInterface`
|
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,739 @@
|
||||||
|
# PSR-7 Message Implementation
|
||||||
|
|
||||||
|
This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
|
||||||
|
message implementation, several stream decorators, and some helpful
|
||||||
|
functionality like query string parsing.
|
||||||
|
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7)
|
||||||
|
|
||||||
|
|
||||||
|
# Stream implementation
|
||||||
|
|
||||||
|
This package comes with a number of stream implementations and stream
|
||||||
|
decorators.
|
||||||
|
|
||||||
|
|
||||||
|
## AppendStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\AppendStream`
|
||||||
|
|
||||||
|
Reads from multiple streams, one after the other.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$a = Psr7\stream_for('abc, ');
|
||||||
|
$b = Psr7\stream_for('123.');
|
||||||
|
$composed = new Psr7\AppendStream([$a, $b]);
|
||||||
|
|
||||||
|
$composed->addStream(Psr7\stream_for(' Above all listen to me'));
|
||||||
|
|
||||||
|
echo $composed; // abc, 123. Above all listen to me.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## BufferStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\BufferStream`
|
||||||
|
|
||||||
|
Provides a buffer stream that can be written to fill a buffer, and read
|
||||||
|
from to remove bytes from the buffer.
|
||||||
|
|
||||||
|
This stream returns a "hwm" metadata value that tells upstream consumers
|
||||||
|
what the configured high water mark of the stream is, or the maximum
|
||||||
|
preferred size of the buffer.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
// When more than 1024 bytes are in the buffer, it will begin returning
|
||||||
|
// false to writes. This is an indication that writers should slow down.
|
||||||
|
$buffer = new Psr7\BufferStream(1024);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## CachingStream
|
||||||
|
|
||||||
|
The CachingStream is used to allow seeking over previously read bytes on
|
||||||
|
non-seekable streams. This can be useful when transferring a non-seekable
|
||||||
|
entity body fails due to needing to rewind the stream (for example, resulting
|
||||||
|
from a redirect). Data that is read from the remote stream will be buffered in
|
||||||
|
a PHP temp stream so that previously read bytes are cached first in memory,
|
||||||
|
then on disk.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
|
||||||
|
$stream = new Psr7\CachingStream($original);
|
||||||
|
|
||||||
|
$stream->read(1024);
|
||||||
|
echo $stream->tell();
|
||||||
|
// 1024
|
||||||
|
|
||||||
|
$stream->seek(0);
|
||||||
|
echo $stream->tell();
|
||||||
|
// 0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## DroppingStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\DroppingStream`
|
||||||
|
|
||||||
|
Stream decorator that begins dropping data once the size of the underlying
|
||||||
|
stream becomes too full.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
// Create an empty stream
|
||||||
|
$stream = Psr7\stream_for();
|
||||||
|
|
||||||
|
// Start dropping data when the stream has more than 10 bytes
|
||||||
|
$dropping = new Psr7\DroppingStream($stream, 10);
|
||||||
|
|
||||||
|
$dropping->write('01234567890123456789');
|
||||||
|
echo $stream; // 0123456789
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## FnStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\FnStream`
|
||||||
|
|
||||||
|
Compose stream implementations based on a hash of functions.
|
||||||
|
|
||||||
|
Allows for easy testing and extension of a provided stream without needing
|
||||||
|
to create a concrete class for a simple extension point.
|
||||||
|
|
||||||
|
```php
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$stream = Psr7\stream_for('hi');
|
||||||
|
$fnStream = Psr7\FnStream::decorate($stream, [
|
||||||
|
'rewind' => function () use ($stream) {
|
||||||
|
echo 'About to rewind - ';
|
||||||
|
$stream->rewind();
|
||||||
|
echo 'rewound!';
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
$fnStream->rewind();
|
||||||
|
// Outputs: About to rewind - rewound!
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## InflateStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\InflateStream`
|
||||||
|
|
||||||
|
Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
|
||||||
|
|
||||||
|
This stream decorator skips the first 10 bytes of the given stream to remove
|
||||||
|
the gzip header, converts the provided stream to a PHP stream resource,
|
||||||
|
then appends the zlib.inflate filter. The stream is then converted back
|
||||||
|
to a Guzzle stream resource to be used as a Guzzle stream.
|
||||||
|
|
||||||
|
|
||||||
|
## LazyOpenStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\LazyOpenStream`
|
||||||
|
|
||||||
|
Lazily reads or writes to a file that is opened only after an IO operation
|
||||||
|
take place on the stream.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
|
||||||
|
// The file has not yet been opened...
|
||||||
|
|
||||||
|
echo $stream->read(10);
|
||||||
|
// The file is opened and read from only when needed.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## LimitStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\LimitStream`
|
||||||
|
|
||||||
|
LimitStream can be used to read a subset or slice of an existing stream object.
|
||||||
|
This can be useful for breaking a large file into smaller pieces to be sent in
|
||||||
|
chunks (e.g. Amazon S3's multipart upload API).
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
|
||||||
|
echo $original->getSize();
|
||||||
|
// >>> 1048576
|
||||||
|
|
||||||
|
// Limit the size of the body to 1024 bytes and start reading from byte 2048
|
||||||
|
$stream = new Psr7\LimitStream($original, 1024, 2048);
|
||||||
|
echo $stream->getSize();
|
||||||
|
// >>> 1024
|
||||||
|
echo $stream->tell();
|
||||||
|
// >>> 0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## MultipartStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\MultipartStream`
|
||||||
|
|
||||||
|
Stream that when read returns bytes for a streaming multipart or
|
||||||
|
multipart/form-data stream.
|
||||||
|
|
||||||
|
|
||||||
|
## NoSeekStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\NoSeekStream`
|
||||||
|
|
||||||
|
NoSeekStream wraps a stream and does not allow seeking.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$original = Psr7\stream_for('foo');
|
||||||
|
$noSeek = new Psr7\NoSeekStream($original);
|
||||||
|
|
||||||
|
echo $noSeek->read(3);
|
||||||
|
// foo
|
||||||
|
var_export($noSeek->isSeekable());
|
||||||
|
// false
|
||||||
|
$noSeek->seek(0);
|
||||||
|
var_export($noSeek->read(3));
|
||||||
|
// NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## PumpStream
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\PumpStream`
|
||||||
|
|
||||||
|
Provides a read only stream that pumps data from a PHP callable.
|
||||||
|
|
||||||
|
When invoking the provided callable, the PumpStream will pass the amount of
|
||||||
|
data requested to read to the callable. The callable can choose to ignore
|
||||||
|
this value and return fewer or more bytes than requested. Any extra data
|
||||||
|
returned by the provided callable is buffered internally until drained using
|
||||||
|
the read() function of the PumpStream. The provided callable MUST return
|
||||||
|
false when there is no more data to read.
|
||||||
|
|
||||||
|
|
||||||
|
## Implementing stream decorators
|
||||||
|
|
||||||
|
Creating a stream decorator is very easy thanks to the
|
||||||
|
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
|
||||||
|
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
|
||||||
|
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
|
||||||
|
methods.
|
||||||
|
|
||||||
|
For example, let's say we wanted to call a specific function each time the last
|
||||||
|
byte is read from a stream. This could be implemented by overriding the
|
||||||
|
`read()` method.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use GuzzleHttp\Psr7\StreamDecoratorTrait;
|
||||||
|
|
||||||
|
class EofCallbackStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
private $callback;
|
||||||
|
|
||||||
|
public function __construct(StreamInterface $stream, callable $cb)
|
||||||
|
{
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->callback = $cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
$result = $this->stream->read($length);
|
||||||
|
|
||||||
|
// Invoke the callback when EOF is hit.
|
||||||
|
if ($this->eof()) {
|
||||||
|
call_user_func($this->callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This decorator could be added to any existing stream and used like so:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
$original = Psr7\stream_for('foo');
|
||||||
|
|
||||||
|
$eofStream = new EofCallbackStream($original, function () {
|
||||||
|
echo 'EOF!';
|
||||||
|
});
|
||||||
|
|
||||||
|
$eofStream->read(2);
|
||||||
|
$eofStream->read(1);
|
||||||
|
// echoes "EOF!"
|
||||||
|
$eofStream->seek(0);
|
||||||
|
$eofStream->read(3);
|
||||||
|
// echoes "EOF!"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## PHP StreamWrapper
|
||||||
|
|
||||||
|
You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
|
||||||
|
PSR-7 stream as a PHP stream resource.
|
||||||
|
|
||||||
|
Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
|
||||||
|
stream from a PSR-7 stream.
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GuzzleHttp\Psr7\StreamWrapper;
|
||||||
|
|
||||||
|
$stream = GuzzleHttp\Psr7\stream_for('hello!');
|
||||||
|
$resource = StreamWrapper::getResource($stream);
|
||||||
|
echo fread($resource, 6); // outputs hello!
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# Function API
|
||||||
|
|
||||||
|
There are various functions available under the `GuzzleHttp\Psr7` namespace.
|
||||||
|
|
||||||
|
|
||||||
|
## `function str`
|
||||||
|
|
||||||
|
`function str(MessageInterface $message)`
|
||||||
|
|
||||||
|
Returns the string representation of an HTTP message.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
|
||||||
|
echo GuzzleHttp\Psr7\str($request);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `function uri_for`
|
||||||
|
|
||||||
|
`function uri_for($uri)`
|
||||||
|
|
||||||
|
This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
|
||||||
|
UriInterface for the given value. If the value is already a `UriInterface`, it
|
||||||
|
is returned as-is.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
|
||||||
|
assert($uri === GuzzleHttp\Psr7\uri_for($uri));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `function stream_for`
|
||||||
|
|
||||||
|
`function stream_for($resource = '', array $options = [])`
|
||||||
|
|
||||||
|
Create a new stream based on the input type.
|
||||||
|
|
||||||
|
Options is an associative array that can contain the following keys:
|
||||||
|
|
||||||
|
* - metadata: Array of custom metadata.
|
||||||
|
* - size: Size of the stream.
|
||||||
|
|
||||||
|
This method accepts the following `$resource` types:
|
||||||
|
|
||||||
|
- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
|
||||||
|
- `string`: Creates a stream object that uses the given string as the contents.
|
||||||
|
- `resource`: Creates a stream object that wraps the given PHP stream resource.
|
||||||
|
- `Iterator`: If the provided value implements `Iterator`, then a read-only
|
||||||
|
stream object will be created that wraps the given iterable. Each time the
|
||||||
|
stream is read from, data from the iterator will fill a buffer and will be
|
||||||
|
continuously called until the buffer is equal to the requested read size.
|
||||||
|
Subsequent read calls will first read from the buffer and then call `next`
|
||||||
|
on the underlying iterator until it is exhausted.
|
||||||
|
- `object` with `__toString()`: If the object has the `__toString()` method,
|
||||||
|
the object will be cast to a string and then a stream will be returned that
|
||||||
|
uses the string value.
|
||||||
|
- `NULL`: When `null` is passed, an empty stream object is returned.
|
||||||
|
- `callable` When a callable is passed, a read-only stream object will be
|
||||||
|
created that invokes the given callable. The callable is invoked with the
|
||||||
|
number of suggested bytes to read. The callable can return any number of
|
||||||
|
bytes, but MUST return `false` when there is no more data to return. The
|
||||||
|
stream object that wraps the callable will invoke the callable until the
|
||||||
|
number of requested bytes are available. Any additional bytes will be
|
||||||
|
buffered and used in subsequent reads.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$stream = GuzzleHttp\Psr7\stream_for('foo');
|
||||||
|
$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
|
||||||
|
|
||||||
|
$generator function ($bytes) {
|
||||||
|
for ($i = 0; $i < $bytes; $i++) {
|
||||||
|
yield ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$stream = GuzzleHttp\Psr7\stream_for($generator(100));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `function parse_header`
|
||||||
|
|
||||||
|
`function parse_header($header)`
|
||||||
|
|
||||||
|
Parse an array of header values containing ";" separated data into an array of
|
||||||
|
associative arrays representing the header key value pair data of the header.
|
||||||
|
When a parameter does not contain a value, but just contains a key, this
|
||||||
|
function will inject a key with a '' string value.
|
||||||
|
|
||||||
|
|
||||||
|
## `function normalize_header`
|
||||||
|
|
||||||
|
`function normalize_header($header)`
|
||||||
|
|
||||||
|
Converts an array of header values that may contain comma separated headers
|
||||||
|
into an array of headers with no comma separated values.
|
||||||
|
|
||||||
|
|
||||||
|
## `function modify_request`
|
||||||
|
|
||||||
|
`function modify_request(RequestInterface $request, array $changes)`
|
||||||
|
|
||||||
|
Clone and modify a request with the given changes. This method is useful for
|
||||||
|
reducing the number of clones needed to mutate a message.
|
||||||
|
|
||||||
|
The changes can be one of:
|
||||||
|
|
||||||
|
- method: (string) Changes the HTTP method.
|
||||||
|
- set_headers: (array) Sets the given headers.
|
||||||
|
- remove_headers: (array) Remove the given headers.
|
||||||
|
- body: (mixed) Sets the given body.
|
||||||
|
- uri: (UriInterface) Set the URI.
|
||||||
|
- query: (string) Set the query string value of the URI.
|
||||||
|
- version: (string) Set the protocol version.
|
||||||
|
|
||||||
|
|
||||||
|
## `function rewind_body`
|
||||||
|
|
||||||
|
`function rewind_body(MessageInterface $message)`
|
||||||
|
|
||||||
|
Attempts to rewind a message body and throws an exception on failure. The body
|
||||||
|
of the message will only be rewound if a call to `tell()` returns a value other
|
||||||
|
than `0`.
|
||||||
|
|
||||||
|
|
||||||
|
## `function try_fopen`
|
||||||
|
|
||||||
|
`function try_fopen($filename, $mode)`
|
||||||
|
|
||||||
|
Safely opens a PHP stream resource using a filename.
|
||||||
|
|
||||||
|
When fopen fails, PHP normally raises a warning. This function adds an error
|
||||||
|
handler that checks for errors and throws an exception instead.
|
||||||
|
|
||||||
|
|
||||||
|
## `function copy_to_string`
|
||||||
|
|
||||||
|
`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
|
||||||
|
|
||||||
|
Copy the contents of a stream into a string until the given number of bytes
|
||||||
|
have been read.
|
||||||
|
|
||||||
|
|
||||||
|
## `function copy_to_stream`
|
||||||
|
|
||||||
|
`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
|
||||||
|
|
||||||
|
Copy the contents of a stream into another stream until the given number of
|
||||||
|
bytes have been read.
|
||||||
|
|
||||||
|
|
||||||
|
## `function hash`
|
||||||
|
|
||||||
|
`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
|
||||||
|
|
||||||
|
Calculate a hash of a Stream. This method reads the entire stream to calculate
|
||||||
|
a rolling hash (based on PHP's hash_init functions).
|
||||||
|
|
||||||
|
|
||||||
|
## `function readline`
|
||||||
|
|
||||||
|
`function readline(StreamInterface $stream, $maxLength = null)`
|
||||||
|
|
||||||
|
Read a line from the stream up to the maximum allowed buffer length.
|
||||||
|
|
||||||
|
|
||||||
|
## `function parse_request`
|
||||||
|
|
||||||
|
`function parse_request($message)`
|
||||||
|
|
||||||
|
Parses a request message string into a request object.
|
||||||
|
|
||||||
|
|
||||||
|
## `function parse_response`
|
||||||
|
|
||||||
|
`function parse_response($message)`
|
||||||
|
|
||||||
|
Parses a response message string into a response object.
|
||||||
|
|
||||||
|
|
||||||
|
## `function parse_query`
|
||||||
|
|
||||||
|
`function parse_query($str, $urlEncoding = true)`
|
||||||
|
|
||||||
|
Parse a query string into an associative array.
|
||||||
|
|
||||||
|
If multiple values are found for the same key, the value of that key value pair
|
||||||
|
will become an array. This function does not parse nested PHP style arrays into
|
||||||
|
an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
|
||||||
|
`['foo[a]' => '1', 'foo[b]' => '2']`).
|
||||||
|
|
||||||
|
|
||||||
|
## `function build_query`
|
||||||
|
|
||||||
|
`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
|
||||||
|
|
||||||
|
Build a query string from an array of key value pairs.
|
||||||
|
|
||||||
|
This function can use the return value of parse_query() to build a query string.
|
||||||
|
This function does not modify the provided keys when an array is encountered
|
||||||
|
(like http_build_query would).
|
||||||
|
|
||||||
|
|
||||||
|
## `function mimetype_from_filename`
|
||||||
|
|
||||||
|
`function mimetype_from_filename($filename)`
|
||||||
|
|
||||||
|
Determines the mimetype of a file by looking at its extension.
|
||||||
|
|
||||||
|
|
||||||
|
## `function mimetype_from_extension`
|
||||||
|
|
||||||
|
`function mimetype_from_extension($extension)`
|
||||||
|
|
||||||
|
Maps a file extensions to a mimetype.
|
||||||
|
|
||||||
|
|
||||||
|
# Additional URI Methods
|
||||||
|
|
||||||
|
Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
|
||||||
|
this library also provides additional functionality when working with URIs as static methods.
|
||||||
|
|
||||||
|
## URI Types
|
||||||
|
|
||||||
|
An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
|
||||||
|
An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
|
||||||
|
the base URI. Relative references can be divided into several forms according to
|
||||||
|
[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
|
||||||
|
|
||||||
|
- network-path references, e.g. `//example.com/path`
|
||||||
|
- absolute-path references, e.g. `/path`
|
||||||
|
- relative-path references, e.g. `subpath`
|
||||||
|
|
||||||
|
The following methods can be used to identify the type of the URI.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::isAbsolute`
|
||||||
|
|
||||||
|
`public static function isAbsolute(UriInterface $uri): bool`
|
||||||
|
|
||||||
|
Whether the URI is absolute, i.e. it has a scheme.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
|
||||||
|
|
||||||
|
`public static function isNetworkPathReference(UriInterface $uri): bool`
|
||||||
|
|
||||||
|
Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
|
||||||
|
termed an network-path reference.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
|
||||||
|
|
||||||
|
`public static function isAbsolutePathReference(UriInterface $uri): bool`
|
||||||
|
|
||||||
|
Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
|
||||||
|
termed an absolute-path reference.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
|
||||||
|
|
||||||
|
`public static function isRelativePathReference(UriInterface $uri): bool`
|
||||||
|
|
||||||
|
Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
|
||||||
|
termed a relative-path reference.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
|
||||||
|
|
||||||
|
`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
|
||||||
|
|
||||||
|
Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
|
||||||
|
fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
|
||||||
|
(apart from its fragment) is considered a same-document reference.
|
||||||
|
|
||||||
|
## URI Components
|
||||||
|
|
||||||
|
Additional methods to work with URI components.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::isDefaultPort`
|
||||||
|
|
||||||
|
`public static function isDefaultPort(UriInterface $uri): bool`
|
||||||
|
|
||||||
|
Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
|
||||||
|
or the standard port. This method can be used independently of the implementation.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::composeComponents`
|
||||||
|
|
||||||
|
`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
|
||||||
|
|
||||||
|
Composes a URI reference string from its various components according to
|
||||||
|
[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
|
||||||
|
manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::fromParts`
|
||||||
|
|
||||||
|
`public static function fromParts(array $parts): UriInterface`
|
||||||
|
|
||||||
|
Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
|
||||||
|
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::withQueryValue`
|
||||||
|
|
||||||
|
`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
|
||||||
|
|
||||||
|
Creates a new URI with a specific query string value. Any existing query string values that exactly match the
|
||||||
|
provided key are removed and replaced with the given key value pair. A value of null will set the query string
|
||||||
|
key without a value, e.g. "key" instead of "key=value".
|
||||||
|
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
|
||||||
|
|
||||||
|
`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
|
||||||
|
|
||||||
|
Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
|
||||||
|
provided key are removed.
|
||||||
|
|
||||||
|
## Reference Resolution
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
|
||||||
|
to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
|
||||||
|
do when resolving a link in a website based on the current request URI.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\UriResolver::resolve`
|
||||||
|
|
||||||
|
`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
|
||||||
|
|
||||||
|
Converts the relative URI into a new URI that is resolved against the base URI.
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
|
||||||
|
|
||||||
|
`public static function removeDotSegments(string $path): string`
|
||||||
|
|
||||||
|
Removes dot segments from a path and returns the new path according to
|
||||||
|
[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\UriResolver::relativize`
|
||||||
|
|
||||||
|
`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
|
||||||
|
|
||||||
|
Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
|
||||||
|
|
||||||
|
```php
|
||||||
|
(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
|
||||||
|
```
|
||||||
|
|
||||||
|
One use-case is to use the current request URI as base URI and then generate relative links in your documents
|
||||||
|
to reduce the document size or offer self-contained downloadable document archives.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$base = new Uri('http://example.com/a/b/');
|
||||||
|
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
|
||||||
|
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
|
||||||
|
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
|
||||||
|
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Normalization and Comparison
|
||||||
|
|
||||||
|
`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
|
||||||
|
[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\UriNormalizer::normalize`
|
||||||
|
|
||||||
|
`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
|
||||||
|
|
||||||
|
Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
|
||||||
|
This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
|
||||||
|
of normalizations to apply. The following normalizations are available:
|
||||||
|
|
||||||
|
- `UriNormalizer::PRESERVING_NORMALIZATIONS`
|
||||||
|
|
||||||
|
Default normalizations which only include the ones that preserve semantics.
|
||||||
|
|
||||||
|
- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
|
||||||
|
|
||||||
|
All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
|
||||||
|
|
||||||
|
Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
|
||||||
|
|
||||||
|
- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
|
||||||
|
|
||||||
|
Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
|
||||||
|
ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
|
||||||
|
not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
|
||||||
|
characters by URI normalizers.
|
||||||
|
|
||||||
|
Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
|
||||||
|
|
||||||
|
- `UriNormalizer::CONVERT_EMPTY_PATH`
|
||||||
|
|
||||||
|
Converts the empty path to "/" for http and https URIs.
|
||||||
|
|
||||||
|
Example: `http://example.org` → `http://example.org/`
|
||||||
|
|
||||||
|
- `UriNormalizer::REMOVE_DEFAULT_HOST`
|
||||||
|
|
||||||
|
Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
|
||||||
|
"localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
|
||||||
|
RFC 3986.
|
||||||
|
|
||||||
|
Example: `file://localhost/myfile` → `file:///myfile`
|
||||||
|
|
||||||
|
- `UriNormalizer::REMOVE_DEFAULT_PORT`
|
||||||
|
|
||||||
|
Removes the default port of the given URI scheme from the URI.
|
||||||
|
|
||||||
|
Example: `http://example.org:80/` → `http://example.org/`
|
||||||
|
|
||||||
|
- `UriNormalizer::REMOVE_DOT_SEGMENTS`
|
||||||
|
|
||||||
|
Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
|
||||||
|
change the semantics of the URI reference.
|
||||||
|
|
||||||
|
Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
|
||||||
|
|
||||||
|
- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
|
||||||
|
|
||||||
|
Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
|
||||||
|
and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
|
||||||
|
may change the semantics. Encoded slashes (%2F) are not removed.
|
||||||
|
|
||||||
|
Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
|
||||||
|
|
||||||
|
- `UriNormalizer::SORT_QUERY_PARAMETERS`
|
||||||
|
|
||||||
|
Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
|
||||||
|
significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
|
||||||
|
of the URI.
|
||||||
|
|
||||||
|
Example: `?lang=en&article=fred` → `?article=fred&lang=en`
|
||||||
|
|
||||||
|
### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
|
||||||
|
|
||||||
|
`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
|
||||||
|
|
||||||
|
Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
|
||||||
|
`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
|
||||||
|
This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
|
||||||
|
equivalence or difference of relative references does not mean anything.
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/psr7",
|
||||||
|
"type": "library",
|
||||||
|
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||||
|
"keywords": ["request", "response", "message", "stream", "http", "uri", "url"],
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Tobias Schultze",
|
||||||
|
"homepage": "https://github.com/Tobion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.4.0",
|
||||||
|
"psr/http-message": "~1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "~4.0"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"psr/http-message-implementation": "1.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\Psr7\\": "src/"
|
||||||
|
},
|
||||||
|
"files": ["src/functions_include.php"]
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.4-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
233
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/AppendStream.php
vendored
Normal file
233
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/AppendStream.php
vendored
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from multiple streams, one after the other.
|
||||||
|
*
|
||||||
|
* This is a read-only stream decorator.
|
||||||
|
*/
|
||||||
|
class AppendStream implements StreamInterface
|
||||||
|
{
|
||||||
|
/** @var StreamInterface[] Streams being decorated */
|
||||||
|
private $streams = [];
|
||||||
|
|
||||||
|
private $seekable = true;
|
||||||
|
private $current = 0;
|
||||||
|
private $pos = 0;
|
||||||
|
private $detached = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StreamInterface[] $streams Streams to decorate. Each stream must
|
||||||
|
* be readable.
|
||||||
|
*/
|
||||||
|
public function __construct(array $streams = [])
|
||||||
|
{
|
||||||
|
foreach ($streams as $stream) {
|
||||||
|
$this->addStream($stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->rewind();
|
||||||
|
return $this->getContents();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a stream to the AppendStream
|
||||||
|
*
|
||||||
|
* @param StreamInterface $stream Stream to append. Must be readable.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if the stream is not readable
|
||||||
|
*/
|
||||||
|
public function addStream(StreamInterface $stream)
|
||||||
|
{
|
||||||
|
if (!$stream->isReadable()) {
|
||||||
|
throw new \InvalidArgumentException('Each stream must be readable');
|
||||||
|
}
|
||||||
|
|
||||||
|
// The stream is only seekable if all streams are seekable
|
||||||
|
if (!$stream->isSeekable()) {
|
||||||
|
$this->seekable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->streams[] = $stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
return copy_to_string($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes each attached stream.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
$this->pos = $this->current = 0;
|
||||||
|
|
||||||
|
foreach ($this->streams as $stream) {
|
||||||
|
$stream->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->streams = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches each attached stream
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function detach()
|
||||||
|
{
|
||||||
|
$this->close();
|
||||||
|
$this->detached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tell()
|
||||||
|
{
|
||||||
|
return $this->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to calculate the size by adding the size of each stream.
|
||||||
|
*
|
||||||
|
* If any of the streams do not return a valid number, then the size of the
|
||||||
|
* append stream cannot be determined and null is returned.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
$size = 0;
|
||||||
|
|
||||||
|
foreach ($this->streams as $stream) {
|
||||||
|
$s = $stream->getSize();
|
||||||
|
if ($s === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$size += $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
return !$this->streams ||
|
||||||
|
($this->current >= count($this->streams) - 1 &&
|
||||||
|
$this->streams[$this->current]->eof());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
$this->seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to seek to the given position. Only supports SEEK_SET.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
if (!$this->seekable) {
|
||||||
|
throw new \RuntimeException('This AppendStream is not seekable');
|
||||||
|
} elseif ($whence !== SEEK_SET) {
|
||||||
|
throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pos = $this->current = 0;
|
||||||
|
|
||||||
|
// Rewind each stream
|
||||||
|
foreach ($this->streams as $i => $stream) {
|
||||||
|
try {
|
||||||
|
$stream->rewind();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \RuntimeException('Unable to seek stream '
|
||||||
|
. $i . ' of the AppendStream', 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek to the actual position by reading from each stream
|
||||||
|
while ($this->pos < $offset && !$this->eof()) {
|
||||||
|
$result = $this->read(min(8096, $offset - $this->pos));
|
||||||
|
if ($result === '') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from all of the appended streams until the length is met or EOF.
|
||||||
|
*
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
$buffer = '';
|
||||||
|
$total = count($this->streams) - 1;
|
||||||
|
$remaining = $length;
|
||||||
|
$progressToNext = false;
|
||||||
|
|
||||||
|
while ($remaining > 0) {
|
||||||
|
|
||||||
|
// Progress to the next stream if needed.
|
||||||
|
if ($progressToNext || $this->streams[$this->current]->eof()) {
|
||||||
|
$progressToNext = false;
|
||||||
|
if ($this->current === $total) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$this->current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->streams[$this->current]->read($remaining);
|
||||||
|
|
||||||
|
// Using a loose comparison here to match on '', false, and null
|
||||||
|
if ($result == null) {
|
||||||
|
$progressToNext = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$buffer .= $result;
|
||||||
|
$remaining = $length - strlen($buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pos += strlen($buffer);
|
||||||
|
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSeekable()
|
||||||
|
{
|
||||||
|
return $this->seekable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Cannot write to an AppendStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetadata($key = null)
|
||||||
|
{
|
||||||
|
return $key ? null : [];
|
||||||
|
}
|
||||||
|
}
|
137
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/BufferStream.php
vendored
Normal file
137
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/BufferStream.php
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a buffer stream that can be written to to fill a buffer, and read
|
||||||
|
* from to remove bytes from the buffer.
|
||||||
|
*
|
||||||
|
* This stream returns a "hwm" metadata value that tells upstream consumers
|
||||||
|
* what the configured high water mark of the stream is, or the maximum
|
||||||
|
* preferred size of the buffer.
|
||||||
|
*/
|
||||||
|
class BufferStream implements StreamInterface
|
||||||
|
{
|
||||||
|
private $hwm;
|
||||||
|
private $buffer = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $hwm High water mark, representing the preferred maximum
|
||||||
|
* buffer size. If the size of the buffer exceeds the high
|
||||||
|
* water mark, then calls to write will continue to succeed
|
||||||
|
* but will return false to inform writers to slow down
|
||||||
|
* until the buffer has been drained by reading from it.
|
||||||
|
*/
|
||||||
|
public function __construct($hwm = 16384)
|
||||||
|
{
|
||||||
|
$this->hwm = $hwm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->getContents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
$buffer = $this->buffer;
|
||||||
|
$this->buffer = '';
|
||||||
|
|
||||||
|
return $buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
$this->buffer = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detach()
|
||||||
|
{
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
return strlen($this->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSeekable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
$this->seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Cannot seek a BufferStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
return strlen($this->buffer) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tell()
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Cannot determine the position of a BufferStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads data from the buffer.
|
||||||
|
*/
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
$currentLength = strlen($this->buffer);
|
||||||
|
|
||||||
|
if ($length >= $currentLength) {
|
||||||
|
// No need to slice the buffer because we don't have enough data.
|
||||||
|
$result = $this->buffer;
|
||||||
|
$this->buffer = '';
|
||||||
|
} else {
|
||||||
|
// Slice up the result to provide a subset of the buffer.
|
||||||
|
$result = substr($this->buffer, 0, $length);
|
||||||
|
$this->buffer = substr($this->buffer, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes data to the buffer.
|
||||||
|
*/
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
$this->buffer .= $string;
|
||||||
|
|
||||||
|
// TODO: What should happen here?
|
||||||
|
if (strlen($this->buffer) >= $this->hwm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strlen($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetadata($key = null)
|
||||||
|
{
|
||||||
|
if ($key == 'hwm') {
|
||||||
|
return $this->hwm;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $key ? null : [];
|
||||||
|
}
|
||||||
|
}
|
138
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/CachingStream.php
vendored
Normal file
138
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/CachingStream.php
vendored
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream decorator that can cache previously read bytes from a sequentially
|
||||||
|
* read stream.
|
||||||
|
*/
|
||||||
|
class CachingStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
/** @var StreamInterface Stream being wrapped */
|
||||||
|
private $remoteStream;
|
||||||
|
|
||||||
|
/** @var int Number of bytes to skip reading due to a write on the buffer */
|
||||||
|
private $skipReadBytes = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We will treat the buffer object as the body of the stream
|
||||||
|
*
|
||||||
|
* @param StreamInterface $stream Stream to cache
|
||||||
|
* @param StreamInterface $target Optionally specify where data is cached
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
StreamInterface $stream,
|
||||||
|
StreamInterface $target = null
|
||||||
|
) {
|
||||||
|
$this->remoteStream = $stream;
|
||||||
|
$this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
return max($this->stream->getSize(), $this->remoteStream->getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
$this->seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
if ($whence == SEEK_SET) {
|
||||||
|
$byte = $offset;
|
||||||
|
} elseif ($whence == SEEK_CUR) {
|
||||||
|
$byte = $offset + $this->tell();
|
||||||
|
} elseif ($whence == SEEK_END) {
|
||||||
|
$size = $this->remoteStream->getSize();
|
||||||
|
if ($size === null) {
|
||||||
|
$size = $this->cacheEntireStream();
|
||||||
|
}
|
||||||
|
$byte = $size + $offset;
|
||||||
|
} else {
|
||||||
|
throw new \InvalidArgumentException('Invalid whence');
|
||||||
|
}
|
||||||
|
|
||||||
|
$diff = $byte - $this->stream->getSize();
|
||||||
|
|
||||||
|
if ($diff > 0) {
|
||||||
|
// Read the remoteStream until we have read in at least the amount
|
||||||
|
// of bytes requested, or we reach the end of the file.
|
||||||
|
while ($diff > 0 && !$this->remoteStream->eof()) {
|
||||||
|
$this->read($diff);
|
||||||
|
$diff = $byte - $this->stream->getSize();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We can just do a normal seek since we've already seen this byte.
|
||||||
|
$this->stream->seek($byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
// Perform a regular read on any previously read data from the buffer
|
||||||
|
$data = $this->stream->read($length);
|
||||||
|
$remaining = $length - strlen($data);
|
||||||
|
|
||||||
|
// More data was requested so read from the remote stream
|
||||||
|
if ($remaining) {
|
||||||
|
// If data was written to the buffer in a position that would have
|
||||||
|
// been filled from the remote stream, then we must skip bytes on
|
||||||
|
// the remote stream to emulate overwriting bytes from that
|
||||||
|
// position. This mimics the behavior of other PHP stream wrappers.
|
||||||
|
$remoteData = $this->remoteStream->read(
|
||||||
|
$remaining + $this->skipReadBytes
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($this->skipReadBytes) {
|
||||||
|
$len = strlen($remoteData);
|
||||||
|
$remoteData = substr($remoteData, $this->skipReadBytes);
|
||||||
|
$this->skipReadBytes = max(0, $this->skipReadBytes - $len);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data .= $remoteData;
|
||||||
|
$this->stream->write($remoteData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
// When appending to the end of the currently read stream, you'll want
|
||||||
|
// to skip bytes from being read from the remote stream to emulate
|
||||||
|
// other stream wrappers. Basically replacing bytes of data of a fixed
|
||||||
|
// length.
|
||||||
|
$overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
|
||||||
|
if ($overflow > 0) {
|
||||||
|
$this->skipReadBytes += $overflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->stream->write($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
return $this->stream->eof() && $this->remoteStream->eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close both the remote stream and buffer stream
|
||||||
|
*/
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
$this->remoteStream->close() && $this->stream->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cacheEntireStream()
|
||||||
|
{
|
||||||
|
$target = new FnStream(['write' => 'strlen']);
|
||||||
|
copy_to_stream($this, $target);
|
||||||
|
|
||||||
|
return $this->tell();
|
||||||
|
}
|
||||||
|
}
|
42
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/DroppingStream.php
vendored
Normal file
42
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/DroppingStream.php
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream decorator that begins dropping data once the size of the underlying
|
||||||
|
* stream becomes too full.
|
||||||
|
*/
|
||||||
|
class DroppingStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
private $maxLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StreamInterface $stream Underlying stream to decorate.
|
||||||
|
* @param int $maxLength Maximum size before dropping data.
|
||||||
|
*/
|
||||||
|
public function __construct(StreamInterface $stream, $maxLength)
|
||||||
|
{
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->maxLength = $maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
$diff = $this->maxLength - $this->stream->getSize();
|
||||||
|
|
||||||
|
// Begin returning 0 when the underlying stream is too large.
|
||||||
|
if ($diff <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the stream or a subset of the stream if needed.
|
||||||
|
if (strlen($string) < $diff) {
|
||||||
|
return $this->stream->write($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->stream->write(substr($string, 0, $diff));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose stream implementations based on a hash of functions.
|
||||||
|
*
|
||||||
|
* Allows for easy testing and extension of a provided stream without needing
|
||||||
|
* to create a concrete class for a simple extension point.
|
||||||
|
*/
|
||||||
|
class FnStream implements StreamInterface
|
||||||
|
{
|
||||||
|
/** @var array */
|
||||||
|
private $methods;
|
||||||
|
|
||||||
|
/** @var array Methods that must be implemented in the given array */
|
||||||
|
private static $slots = ['__toString', 'close', 'detach', 'rewind',
|
||||||
|
'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
|
||||||
|
'isReadable', 'read', 'getContents', 'getMetadata'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $methods Hash of method name to a callable.
|
||||||
|
*/
|
||||||
|
public function __construct(array $methods)
|
||||||
|
{
|
||||||
|
$this->methods = $methods;
|
||||||
|
|
||||||
|
// Create the functions on the class
|
||||||
|
foreach ($methods as $name => $fn) {
|
||||||
|
$this->{'_fn_' . $name} = $fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily determine which methods are not implemented.
|
||||||
|
* @throws \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException(str_replace('_fn_', '', $name)
|
||||||
|
. '() is not implemented in the FnStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The close method is called on the underlying stream only if possible.
|
||||||
|
*/
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
if (isset($this->_fn_close)) {
|
||||||
|
call_user_func($this->_fn_close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds custom functionality to an underlying stream by intercepting
|
||||||
|
* specific method calls.
|
||||||
|
*
|
||||||
|
* @param StreamInterface $stream Stream to decorate
|
||||||
|
* @param array $methods Hash of method name to a closure
|
||||||
|
*
|
||||||
|
* @return FnStream
|
||||||
|
*/
|
||||||
|
public static function decorate(StreamInterface $stream, array $methods)
|
||||||
|
{
|
||||||
|
// If any of the required methods were not provided, then simply
|
||||||
|
// proxy to the decorated stream.
|
||||||
|
foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
|
||||||
|
$methods[$diff] = [$stream, $diff];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new self($methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn___toString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_close);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detach()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_detach);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_getSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tell()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_tell);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_eof);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSeekable()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_isSeekable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
call_user_func($this->_fn_rewind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
call_user_func($this->_fn_seek, $offset, $whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_isWritable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_write, $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_isReadable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_read, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_getContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetadata($key = null)
|
||||||
|
{
|
||||||
|
return call_user_func($this->_fn_getMetadata, $key);
|
||||||
|
}
|
||||||
|
}
|
52
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/InflateStream.php
vendored
Normal file
52
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/InflateStream.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
|
||||||
|
*
|
||||||
|
* This stream decorator skips the first 10 bytes of the given stream to remove
|
||||||
|
* the gzip header, converts the provided stream to a PHP stream resource,
|
||||||
|
* then appends the zlib.inflate filter. The stream is then converted back
|
||||||
|
* to a Guzzle stream resource to be used as a Guzzle stream.
|
||||||
|
*
|
||||||
|
* @link http://tools.ietf.org/html/rfc1952
|
||||||
|
* @link http://php.net/manual/en/filters.compression.php
|
||||||
|
*/
|
||||||
|
class InflateStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
public function __construct(StreamInterface $stream)
|
||||||
|
{
|
||||||
|
// read the first 10 bytes, ie. gzip header
|
||||||
|
$header = $stream->read(10);
|
||||||
|
$filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
|
||||||
|
// Skip the header, that is 10 + length of filename + 1 (nil) bytes
|
||||||
|
$stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
|
||||||
|
$resource = StreamWrapper::getResource($stream);
|
||||||
|
stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
|
||||||
|
$this->stream = new Stream($resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StreamInterface $stream
|
||||||
|
* @param $header
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
|
||||||
|
{
|
||||||
|
$filename_header_length = 0;
|
||||||
|
|
||||||
|
if (substr(bin2hex($header), 6, 2) === '08') {
|
||||||
|
// we have a filename, read until nil
|
||||||
|
$filename_header_length = 1;
|
||||||
|
while ($stream->read(1) !== chr(0)) {
|
||||||
|
$filename_header_length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filename_header_length;
|
||||||
|
}
|
||||||
|
}
|
39
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
vendored
Normal file
39
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily reads or writes to a file that is opened only after an IO operation
|
||||||
|
* take place on the stream.
|
||||||
|
*/
|
||||||
|
class LazyOpenStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
/** @var string File to open */
|
||||||
|
private $filename;
|
||||||
|
|
||||||
|
/** @var string $mode */
|
||||||
|
private $mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $filename File to lazily open
|
||||||
|
* @param string $mode fopen mode to use when opening the stream
|
||||||
|
*/
|
||||||
|
public function __construct($filename, $mode)
|
||||||
|
{
|
||||||
|
$this->filename = $filename;
|
||||||
|
$this->mode = $mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the underlying stream lazily when required.
|
||||||
|
*
|
||||||
|
* @return StreamInterface
|
||||||
|
*/
|
||||||
|
protected function createStream()
|
||||||
|
{
|
||||||
|
return stream_for(try_fopen($this->filename, $this->mode));
|
||||||
|
}
|
||||||
|
}
|
155
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/LimitStream.php
vendored
Normal file
155
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/LimitStream.php
vendored
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorator used to return only a subset of a stream
|
||||||
|
*/
|
||||||
|
class LimitStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
/** @var int Offset to start reading from */
|
||||||
|
private $offset;
|
||||||
|
|
||||||
|
/** @var int Limit the number of bytes that can be read */
|
||||||
|
private $limit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param StreamInterface $stream Stream to wrap
|
||||||
|
* @param int $limit Total number of bytes to allow to be read
|
||||||
|
* from the stream. Pass -1 for no limit.
|
||||||
|
* @param int $offset Position to seek to before reading (only
|
||||||
|
* works on seekable streams).
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
StreamInterface $stream,
|
||||||
|
$limit = -1,
|
||||||
|
$offset = 0
|
||||||
|
) {
|
||||||
|
$this->stream = $stream;
|
||||||
|
$this->setLimit($limit);
|
||||||
|
$this->setOffset($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
// Always return true if the underlying stream is EOF
|
||||||
|
if ($this->stream->eof()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No limit and the underlying stream is not at EOF
|
||||||
|
if ($this->limit == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->stream->tell() >= $this->offset + $this->limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the limited subset of data
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
if (null === ($length = $this->stream->getSize())) {
|
||||||
|
return null;
|
||||||
|
} elseif ($this->limit == -1) {
|
||||||
|
return $length - $this->offset;
|
||||||
|
} else {
|
||||||
|
return min($this->limit, $length - $this->offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow for a bounded seek on the read limited stream
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
if ($whence !== SEEK_SET || $offset < 0) {
|
||||||
|
throw new \RuntimeException(sprintf(
|
||||||
|
'Cannot seek to offset % with whence %s',
|
||||||
|
$offset,
|
||||||
|
$whence
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$offset += $this->offset;
|
||||||
|
|
||||||
|
if ($this->limit !== -1) {
|
||||||
|
if ($offset > $this->offset + $this->limit) {
|
||||||
|
$offset = $this->offset + $this->limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->stream->seek($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give a relative tell()
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function tell()
|
||||||
|
{
|
||||||
|
return $this->stream->tell() - $this->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset to start limiting from
|
||||||
|
*
|
||||||
|
* @param int $offset Offset to seek to and begin byte limiting from
|
||||||
|
*
|
||||||
|
* @throws \RuntimeException if the stream cannot be seeked.
|
||||||
|
*/
|
||||||
|
public function setOffset($offset)
|
||||||
|
{
|
||||||
|
$current = $this->stream->tell();
|
||||||
|
|
||||||
|
if ($current !== $offset) {
|
||||||
|
// If the stream cannot seek to the offset position, then read to it
|
||||||
|
if ($this->stream->isSeekable()) {
|
||||||
|
$this->stream->seek($offset);
|
||||||
|
} elseif ($current > $offset) {
|
||||||
|
throw new \RuntimeException("Could not seek to stream offset $offset");
|
||||||
|
} else {
|
||||||
|
$this->stream->read($offset - $current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->offset = $offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the limit of bytes that the decorator allows to be read from the
|
||||||
|
* stream.
|
||||||
|
*
|
||||||
|
* @param int $limit Number of bytes to allow to be read from the stream.
|
||||||
|
* Use -1 for no limit.
|
||||||
|
*/
|
||||||
|
public function setLimit($limit)
|
||||||
|
{
|
||||||
|
$this->limit = $limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
if ($this->limit == -1) {
|
||||||
|
return $this->stream->read($length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the current position is less than the total allowed
|
||||||
|
// bytes + original offset
|
||||||
|
$remaining = ($this->offset + $this->limit) - $this->stream->tell();
|
||||||
|
if ($remaining > 0) {
|
||||||
|
// Only return the amount of requested data, ensuring that the byte
|
||||||
|
// limit is not exceeded
|
||||||
|
return $this->stream->read(min($remaining, $length));
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
183
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
Normal file
183
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/MessageTrait.php
vendored
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait implementing functionality common to requests and responses.
|
||||||
|
*/
|
||||||
|
trait MessageTrait
|
||||||
|
{
|
||||||
|
/** @var array Map of all registered headers, as original name => array of values */
|
||||||
|
private $headers = [];
|
||||||
|
|
||||||
|
/** @var array Map of lowercase header name => original name at registration */
|
||||||
|
private $headerNames = [];
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $protocol = '1.1';
|
||||||
|
|
||||||
|
/** @var StreamInterface */
|
||||||
|
private $stream;
|
||||||
|
|
||||||
|
public function getProtocolVersion()
|
||||||
|
{
|
||||||
|
return $this->protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withProtocolVersion($version)
|
||||||
|
{
|
||||||
|
if ($this->protocol === $version) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->protocol = $version;
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaders()
|
||||||
|
{
|
||||||
|
return $this->headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasHeader($header)
|
||||||
|
{
|
||||||
|
return isset($this->headerNames[strtolower($header)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeader($header)
|
||||||
|
{
|
||||||
|
$header = strtolower($header);
|
||||||
|
|
||||||
|
if (!isset($this->headerNames[$header])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = $this->headerNames[$header];
|
||||||
|
|
||||||
|
return $this->headers[$header];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaderLine($header)
|
||||||
|
{
|
||||||
|
return implode(', ', $this->getHeader($header));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withHeader($header, $value)
|
||||||
|
{
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$value = [$value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->trimHeaderValues($value);
|
||||||
|
$normalized = strtolower($header);
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
if (isset($new->headerNames[$normalized])) {
|
||||||
|
unset($new->headers[$new->headerNames[$normalized]]);
|
||||||
|
}
|
||||||
|
$new->headerNames[$normalized] = $header;
|
||||||
|
$new->headers[$header] = $value;
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withAddedHeader($header, $value)
|
||||||
|
{
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$value = [$value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->trimHeaderValues($value);
|
||||||
|
$normalized = strtolower($header);
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
if (isset($new->headerNames[$normalized])) {
|
||||||
|
$header = $this->headerNames[$normalized];
|
||||||
|
$new->headers[$header] = array_merge($this->headers[$header], $value);
|
||||||
|
} else {
|
||||||
|
$new->headerNames[$normalized] = $header;
|
||||||
|
$new->headers[$header] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withoutHeader($header)
|
||||||
|
{
|
||||||
|
$normalized = strtolower($header);
|
||||||
|
|
||||||
|
if (!isset($this->headerNames[$normalized])) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = $this->headerNames[$normalized];
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
unset($new->headers[$header], $new->headerNames[$normalized]);
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBody()
|
||||||
|
{
|
||||||
|
if (!$this->stream) {
|
||||||
|
$this->stream = stream_for('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withBody(StreamInterface $body)
|
||||||
|
{
|
||||||
|
if ($body === $this->stream) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->stream = $body;
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setHeaders(array $headers)
|
||||||
|
{
|
||||||
|
$this->headerNames = $this->headers = [];
|
||||||
|
foreach ($headers as $header => $value) {
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$value = [$value];
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = $this->trimHeaderValues($value);
|
||||||
|
$normalized = strtolower($header);
|
||||||
|
if (isset($this->headerNames[$normalized])) {
|
||||||
|
$header = $this->headerNames[$normalized];
|
||||||
|
$this->headers[$header] = array_merge($this->headers[$header], $value);
|
||||||
|
} else {
|
||||||
|
$this->headerNames[$normalized] = $header;
|
||||||
|
$this->headers[$header] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trims whitespace from the header values.
|
||||||
|
*
|
||||||
|
* Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
|
||||||
|
*
|
||||||
|
* header-field = field-name ":" OWS field-value OWS
|
||||||
|
* OWS = *( SP / HTAB )
|
||||||
|
*
|
||||||
|
* @param string[] $values Header values
|
||||||
|
*
|
||||||
|
* @return string[] Trimmed header values
|
||||||
|
*
|
||||||
|
* @see https://tools.ietf.org/html/rfc7230#section-3.2.4
|
||||||
|
*/
|
||||||
|
private function trimHeaderValues(array $values)
|
||||||
|
{
|
||||||
|
return array_map(function ($value) {
|
||||||
|
return trim($value, " \t");
|
||||||
|
}, $values);
|
||||||
|
}
|
||||||
|
}
|
153
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/MultipartStream.php
vendored
Normal file
153
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/MultipartStream.php
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream that when read returns bytes for a streaming multipart or
|
||||||
|
* multipart/form-data stream.
|
||||||
|
*/
|
||||||
|
class MultipartStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
private $boundary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $elements Array of associative arrays, each containing a
|
||||||
|
* required "name" key mapping to the form field,
|
||||||
|
* name, a required "contents" key mapping to a
|
||||||
|
* StreamInterface/resource/string, an optional
|
||||||
|
* "headers" associative array of custom headers,
|
||||||
|
* and an optional "filename" key mapping to a
|
||||||
|
* string to send as the filename in the part.
|
||||||
|
* @param string $boundary You can optionally provide a specific boundary
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function __construct(array $elements = [], $boundary = null)
|
||||||
|
{
|
||||||
|
$this->boundary = $boundary ?: sha1(uniqid('', true));
|
||||||
|
$this->stream = $this->createStream($elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the boundary
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getBoundary()
|
||||||
|
{
|
||||||
|
return $this->boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the headers needed before transferring the content of a POST file
|
||||||
|
*/
|
||||||
|
private function getHeaders(array $headers)
|
||||||
|
{
|
||||||
|
$str = '';
|
||||||
|
foreach ($headers as $key => $value) {
|
||||||
|
$str .= "{$key}: {$value}\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the aggregate stream that will be used to upload the POST data
|
||||||
|
*/
|
||||||
|
protected function createStream(array $elements)
|
||||||
|
{
|
||||||
|
$stream = new AppendStream();
|
||||||
|
|
||||||
|
foreach ($elements as $element) {
|
||||||
|
$this->addElement($stream, $element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the trailing boundary with CRLF
|
||||||
|
$stream->addStream(stream_for("--{$this->boundary}--\r\n"));
|
||||||
|
|
||||||
|
return $stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addElement(AppendStream $stream, array $element)
|
||||||
|
{
|
||||||
|
foreach (['contents', 'name'] as $key) {
|
||||||
|
if (!array_key_exists($key, $element)) {
|
||||||
|
throw new \InvalidArgumentException("A '{$key}' key is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$element['contents'] = stream_for($element['contents']);
|
||||||
|
|
||||||
|
if (empty($element['filename'])) {
|
||||||
|
$uri = $element['contents']->getMetadata('uri');
|
||||||
|
if (substr($uri, 0, 6) !== 'php://') {
|
||||||
|
$element['filename'] = $uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list($body, $headers) = $this->createElement(
|
||||||
|
$element['name'],
|
||||||
|
$element['contents'],
|
||||||
|
isset($element['filename']) ? $element['filename'] : null,
|
||||||
|
isset($element['headers']) ? $element['headers'] : []
|
||||||
|
);
|
||||||
|
|
||||||
|
$stream->addStream(stream_for($this->getHeaders($headers)));
|
||||||
|
$stream->addStream($body);
|
||||||
|
$stream->addStream(stream_for("\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function createElement($name, StreamInterface $stream, $filename, array $headers)
|
||||||
|
{
|
||||||
|
// Set a default content-disposition header if one was no provided
|
||||||
|
$disposition = $this->getHeader($headers, 'content-disposition');
|
||||||
|
if (!$disposition) {
|
||||||
|
$headers['Content-Disposition'] = ($filename === '0' || $filename)
|
||||||
|
? sprintf('form-data; name="%s"; filename="%s"',
|
||||||
|
$name,
|
||||||
|
basename($filename))
|
||||||
|
: "form-data; name=\"{$name}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a default content-length header if one was no provided
|
||||||
|
$length = $this->getHeader($headers, 'content-length');
|
||||||
|
if (!$length) {
|
||||||
|
if ($length = $stream->getSize()) {
|
||||||
|
$headers['Content-Length'] = (string) $length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a default Content-Type if one was not supplied
|
||||||
|
$type = $this->getHeader($headers, 'content-type');
|
||||||
|
if (!$type && ($filename === '0' || $filename)) {
|
||||||
|
if ($type = mimetype_from_filename($filename)) {
|
||||||
|
$headers['Content-Type'] = $type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$stream, $headers];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getHeader(array $headers, $key)
|
||||||
|
{
|
||||||
|
$lowercaseHeader = strtolower($key);
|
||||||
|
foreach ($headers as $k => $v) {
|
||||||
|
if (strtolower($k) === $lowercaseHeader) {
|
||||||
|
return $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
22
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/NoSeekStream.php
vendored
Normal file
22
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/NoSeekStream.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream decorator that prevents a stream from being seeked
|
||||||
|
*/
|
||||||
|
class NoSeekStream implements StreamInterface
|
||||||
|
{
|
||||||
|
use StreamDecoratorTrait;
|
||||||
|
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Cannot seek a NoSeekStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSeekable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a read only stream that pumps data from a PHP callable.
|
||||||
|
*
|
||||||
|
* When invoking the provided callable, the PumpStream will pass the amount of
|
||||||
|
* data requested to read to the callable. The callable can choose to ignore
|
||||||
|
* this value and return fewer or more bytes than requested. Any extra data
|
||||||
|
* returned by the provided callable is buffered internally until drained using
|
||||||
|
* the read() function of the PumpStream. The provided callable MUST return
|
||||||
|
* false when there is no more data to read.
|
||||||
|
*/
|
||||||
|
class PumpStream implements StreamInterface
|
||||||
|
{
|
||||||
|
/** @var callable */
|
||||||
|
private $source;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $size;
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $tellPos = 0;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $metadata;
|
||||||
|
|
||||||
|
/** @var BufferStream */
|
||||||
|
private $buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $source Source of the stream data. The callable MAY
|
||||||
|
* accept an integer argument used to control the
|
||||||
|
* amount of data to return. The callable MUST
|
||||||
|
* return a string when called, or false on error
|
||||||
|
* or EOF.
|
||||||
|
* @param array $options Stream options:
|
||||||
|
* - metadata: Hash of metadata to use with stream.
|
||||||
|
* - size: Size of the stream, if known.
|
||||||
|
*/
|
||||||
|
public function __construct(callable $source, array $options = [])
|
||||||
|
{
|
||||||
|
$this->source = $source;
|
||||||
|
$this->size = isset($options['size']) ? $options['size'] : null;
|
||||||
|
$this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
|
||||||
|
$this->buffer = new BufferStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return copy_to_string($this);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
$this->detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detach()
|
||||||
|
{
|
||||||
|
$this->tellPos = false;
|
||||||
|
$this->source = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSize()
|
||||||
|
{
|
||||||
|
return $this->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tell()
|
||||||
|
{
|
||||||
|
return $this->tellPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eof()
|
||||||
|
{
|
||||||
|
return !$this->source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isSeekable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
$this->seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function seek($offset, $whence = SEEK_SET)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Cannot seek a PumpStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($string)
|
||||||
|
{
|
||||||
|
throw new \RuntimeException('Cannot write to a PumpStream');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($length)
|
||||||
|
{
|
||||||
|
$data = $this->buffer->read($length);
|
||||||
|
$readLen = strlen($data);
|
||||||
|
$this->tellPos += $readLen;
|
||||||
|
$remaining = $length - $readLen;
|
||||||
|
|
||||||
|
if ($remaining) {
|
||||||
|
$this->pump($remaining);
|
||||||
|
$data .= $this->buffer->read($remaining);
|
||||||
|
$this->tellPos += strlen($data) - $readLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
$result = '';
|
||||||
|
while (!$this->eof()) {
|
||||||
|
$result .= $this->read(1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetadata($key = null)
|
||||||
|
{
|
||||||
|
if (!$key) {
|
||||||
|
return $this->metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function pump($length)
|
||||||
|
{
|
||||||
|
if ($this->source) {
|
||||||
|
do {
|
||||||
|
$data = call_user_func($this->source, $length);
|
||||||
|
if ($data === false || $data === null) {
|
||||||
|
$this->source = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->buffer->write($data);
|
||||||
|
$length -= strlen($data);
|
||||||
|
} while ($length > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Http\Message\RequestInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PSR-7 request implementation.
|
||||||
|
*/
|
||||||
|
class Request implements RequestInterface
|
||||||
|
{
|
||||||
|
use MessageTrait;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $method;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
private $requestTarget;
|
||||||
|
|
||||||
|
/** @var UriInterface */
|
||||||
|
private $uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $method HTTP method
|
||||||
|
* @param string|UriInterface $uri URI
|
||||||
|
* @param array $headers Request headers
|
||||||
|
* @param string|null|resource|StreamInterface $body Request body
|
||||||
|
* @param string $version Protocol version
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$method,
|
||||||
|
$uri,
|
||||||
|
array $headers = [],
|
||||||
|
$body = null,
|
||||||
|
$version = '1.1'
|
||||||
|
) {
|
||||||
|
if (!($uri instanceof UriInterface)) {
|
||||||
|
$uri = new Uri($uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->method = strtoupper($method);
|
||||||
|
$this->uri = $uri;
|
||||||
|
$this->setHeaders($headers);
|
||||||
|
$this->protocol = $version;
|
||||||
|
|
||||||
|
if (!$this->hasHeader('Host')) {
|
||||||
|
$this->updateHostFromUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($body !== '' && $body !== null) {
|
||||||
|
$this->stream = stream_for($body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestTarget()
|
||||||
|
{
|
||||||
|
if ($this->requestTarget !== null) {
|
||||||
|
return $this->requestTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $this->uri->getPath();
|
||||||
|
if ($target == '') {
|
||||||
|
$target = '/';
|
||||||
|
}
|
||||||
|
if ($this->uri->getQuery() != '') {
|
||||||
|
$target .= '?' . $this->uri->getQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withRequestTarget($requestTarget)
|
||||||
|
{
|
||||||
|
if (preg_match('#\s#', $requestTarget)) {
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
'Invalid request target provided; cannot contain whitespace'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->requestTarget = $requestTarget;
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMethod()
|
||||||
|
{
|
||||||
|
return $this->method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withMethod($method)
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->method = strtoupper($method);
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUri()
|
||||||
|
{
|
||||||
|
return $this->uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withUri(UriInterface $uri, $preserveHost = false)
|
||||||
|
{
|
||||||
|
if ($uri === $this->uri) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
$new->uri = $uri;
|
||||||
|
|
||||||
|
if (!$preserveHost) {
|
||||||
|
$new->updateHostFromUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateHostFromUri()
|
||||||
|
{
|
||||||
|
$host = $this->uri->getHost();
|
||||||
|
|
||||||
|
if ($host == '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($port = $this->uri->getPort()) !== null) {
|
||||||
|
$host .= ':' . $port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->headerNames['host'])) {
|
||||||
|
$header = $this->headerNames['host'];
|
||||||
|
} else {
|
||||||
|
$header = 'Host';
|
||||||
|
$this->headerNames['host'] = 'Host';
|
||||||
|
}
|
||||||
|
// Ensure Host is the first header.
|
||||||
|
// See: http://tools.ietf.org/html/rfc7230#section-5.4
|
||||||
|
$this->headers = [$header => [$host]] + $this->headers;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PSR-7 response implementation.
|
||||||
|
*/
|
||||||
|
class Response implements ResponseInterface
|
||||||
|
{
|
||||||
|
use MessageTrait;
|
||||||
|
|
||||||
|
/** @var array Map of standard HTTP status code/reason phrases */
|
||||||
|
private static $phrases = [
|
||||||
|
100 => 'Continue',
|
||||||
|
101 => 'Switching Protocols',
|
||||||
|
102 => 'Processing',
|
||||||
|
200 => 'OK',
|
||||||
|
201 => 'Created',
|
||||||
|
202 => 'Accepted',
|
||||||
|
203 => 'Non-Authoritative Information',
|
||||||
|
204 => 'No Content',
|
||||||
|
205 => 'Reset Content',
|
||||||
|
206 => 'Partial Content',
|
||||||
|
207 => 'Multi-status',
|
||||||
|
208 => 'Already Reported',
|
||||||
|
300 => 'Multiple Choices',
|
||||||
|
301 => 'Moved Permanently',
|
||||||
|
302 => 'Found',
|
||||||
|
303 => 'See Other',
|
||||||
|
304 => 'Not Modified',
|
||||||
|
305 => 'Use Proxy',
|
||||||
|
306 => 'Switch Proxy',
|
||||||
|
307 => 'Temporary Redirect',
|
||||||
|
400 => 'Bad Request',
|
||||||
|
401 => 'Unauthorized',
|
||||||
|
402 => 'Payment Required',
|
||||||
|
403 => 'Forbidden',
|
||||||
|
404 => 'Not Found',
|
||||||
|
405 => 'Method Not Allowed',
|
||||||
|
406 => 'Not Acceptable',
|
||||||
|
407 => 'Proxy Authentication Required',
|
||||||
|
408 => 'Request Time-out',
|
||||||
|
409 => 'Conflict',
|
||||||
|
410 => 'Gone',
|
||||||
|
411 => 'Length Required',
|
||||||
|
412 => 'Precondition Failed',
|
||||||
|
413 => 'Request Entity Too Large',
|
||||||
|
414 => 'Request-URI Too Large',
|
||||||
|
415 => 'Unsupported Media Type',
|
||||||
|
416 => 'Requested range not satisfiable',
|
||||||
|
417 => 'Expectation Failed',
|
||||||
|
418 => 'I\'m a teapot',
|
||||||
|
422 => 'Unprocessable Entity',
|
||||||
|
423 => 'Locked',
|
||||||
|
424 => 'Failed Dependency',
|
||||||
|
425 => 'Unordered Collection',
|
||||||
|
426 => 'Upgrade Required',
|
||||||
|
428 => 'Precondition Required',
|
||||||
|
429 => 'Too Many Requests',
|
||||||
|
431 => 'Request Header Fields Too Large',
|
||||||
|
451 => 'Unavailable For Legal Reasons',
|
||||||
|
500 => 'Internal Server Error',
|
||||||
|
501 => 'Not Implemented',
|
||||||
|
502 => 'Bad Gateway',
|
||||||
|
503 => 'Service Unavailable',
|
||||||
|
504 => 'Gateway Time-out',
|
||||||
|
505 => 'HTTP Version not supported',
|
||||||
|
506 => 'Variant Also Negotiates',
|
||||||
|
507 => 'Insufficient Storage',
|
||||||
|
508 => 'Loop Detected',
|
||||||
|
511 => 'Network Authentication Required',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $reasonPhrase = '';
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
private $statusCode = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $status Status code
|
||||||
|
* @param array $headers Response headers
|
||||||
|
* @param string|null|resource|StreamInterface $body Response body
|
||||||
|
* @param string $version Protocol version
|
||||||
|
* @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$status = 200,
|
||||||
|
array $headers = [],
|
||||||
|
$body = null,
|
||||||
|
$version = '1.1',
|
||||||
|
$reason = null
|
||||||
|
) {
|
||||||
|
$this->statusCode = (int) $status;
|
||||||
|
|
||||||
|
if ($body !== '' && $body !== null) {
|
||||||
|
$this->stream = stream_for($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setHeaders($headers);
|
||||||
|
if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
|
||||||
|
$this->reasonPhrase = self::$phrases[$this->statusCode];
|
||||||
|
} else {
|
||||||
|
$this->reasonPhrase = (string) $reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->protocol = $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusCode()
|
||||||
|
{
|
||||||
|
return $this->statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReasonPhrase()
|
||||||
|
{
|
||||||
|
return $this->reasonPhrase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withStatus($code, $reasonPhrase = '')
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->statusCode = (int) $code;
|
||||||
|
if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
|
||||||
|
$reasonPhrase = self::$phrases[$new->statusCode];
|
||||||
|
}
|
||||||
|
$new->reasonPhrase = $reasonPhrase;
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
}
|
358
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/ServerRequest.php
vendored
Normal file
358
server/plugins/manogi/mediathumb/vendor/guzzlehttp/psr7/src/ServerRequest.php
vendored
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace GuzzleHttp\Psr7;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Message\UriInterface;
|
||||||
|
use Psr\Http\Message\StreamInterface;
|
||||||
|
use Psr\Http\Message\UploadedFileInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-side HTTP request
|
||||||
|
*
|
||||||
|
* Extends the Request definition to add methods for accessing incoming data,
|
||||||
|
* specifically server parameters, cookies, matched path parameters, query
|
||||||
|
* string arguments, body parameters, and upload file information.
|
||||||
|
*
|
||||||
|
* "Attributes" are discovered via decomposing the request (and usually
|
||||||
|
* specifically the URI path), and typically will be injected by the application.
|
||||||
|
*
|
||||||
|
* Requests are considered immutable; all methods that might change state are
|
||||||
|
* implemented such that they retain the internal state of the current
|
||||||
|
* message and return a new instance that contains the changed state.
|
||||||
|
*/
|
||||||
|
class ServerRequest extends Request implements ServerRequestInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $attributes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $cookieParams = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var null|array|object
|
||||||
|
*/
|
||||||
|
private $parsedBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $queryParams = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $serverParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $uploadedFiles = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $method HTTP method
|
||||||
|
* @param string|UriInterface $uri URI
|
||||||
|
* @param array $headers Request headers
|
||||||
|
* @param string|null|resource|StreamInterface $body Request body
|
||||||
|
* @param string $version Protocol version
|
||||||
|
* @param array $serverParams Typically the $_SERVER superglobal
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$method,
|
||||||
|
$uri,
|
||||||
|
array $headers = [],
|
||||||
|
$body = null,
|
||||||
|
$version = '1.1',
|
||||||
|
array $serverParams = []
|
||||||
|
) {
|
||||||
|
$this->serverParams = $serverParams;
|
||||||
|
|
||||||
|
parent::__construct($method, $uri, $headers, $body, $version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an UploadedFile instance array.
|
||||||
|
*
|
||||||
|
* @param array $files A array which respect $_FILES structure
|
||||||
|
* @throws InvalidArgumentException for unrecognized values
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function normalizeFiles(array $files)
|
||||||
|
{
|
||||||
|
$normalized = [];
|
||||||
|
|
||||||
|
foreach ($files as $key => $value) {
|
||||||
|
if ($value instanceof UploadedFileInterface) {
|
||||||
|
$normalized[$key] = $value;
|
||||||
|
} elseif (is_array($value) && isset($value['tmp_name'])) {
|
||||||
|
$normalized[$key] = self::createUploadedFileFromSpec($value);
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
$normalized[$key] = self::normalizeFiles($value);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentException('Invalid value in files specification');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and return an UploadedFile instance from a $_FILES specification.
|
||||||
|
*
|
||||||
|
* If the specification represents an array of values, this method will
|
||||||
|
* delegate to normalizeNestedFileSpec() and return that return value.
|
||||||
|
*
|
||||||
|
* @param array $value $_FILES struct
|
||||||
|
* @return array|UploadedFileInterface
|
||||||
|
*/
|
||||||
|
private static function createUploadedFileFromSpec(array $value)
|
||||||
|
{
|
||||||
|
if (is_array($value['tmp_name'])) {
|
||||||
|
return self::normalizeNestedFileSpec($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UploadedFile(
|
||||||
|
$value['tmp_name'],
|
||||||
|
(int) $value['size'],
|
||||||
|
(int) $value['error'],
|
||||||
|
$value['name'],
|
||||||
|
$value['type']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize an array of file specifications.
|
||||||
|
*
|
||||||
|
* Loops through all nested files and returns a normalized array of
|
||||||
|
* UploadedFileInterface instances.
|
||||||
|
*
|
||||||
|
* @param array $files
|
||||||
|
* @return UploadedFileInterface[]
|
||||||
|
*/
|
||||||
|
private static function normalizeNestedFileSpec(array $files = [])
|
||||||
|
{
|
||||||
|
$normalizedFiles = [];
|
||||||
|
|
||||||
|
foreach (array_keys($files['tmp_name']) as $key) {
|
||||||
|
$spec = [
|
||||||
|
'tmp_name' => $files['tmp_name'][$key],
|
||||||
|
'size' => $files['size'][$key],
|
||||||
|
'error' => $files['error'][$key],
|
||||||
|
'name' => $files['name'][$key],
|
||||||
|
'type' => $files['type'][$key],
|
||||||
|
];
|
||||||
|
$normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $normalizedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a ServerRequest populated with superglobals:
|
||||||
|
* $_GET
|
||||||
|
* $_POST
|
||||||
|
* $_COOKIE
|
||||||
|
* $_FILES
|
||||||
|
* $_SERVER
|
||||||
|
*
|
||||||
|
* @return ServerRequestInterface
|
||||||
|
*/
|
||||||
|
public static function fromGlobals()
|
||||||
|
{
|
||||||
|
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
|
||||||
|
$headers = function_exists('getallheaders') ? getallheaders() : [];
|
||||||
|
$uri = self::getUriFromGlobals();
|
||||||
|
$body = new LazyOpenStream('php://input', 'r+');
|
||||||
|
$protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
|
||||||
|
|
||||||
|
$serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
|
||||||
|
|
||||||
|
return $serverRequest
|
||||||
|
->withCookieParams($_COOKIE)
|
||||||
|
->withQueryParams($_GET)
|
||||||
|
->withParsedBody($_POST)
|
||||||
|
->withUploadedFiles(self::normalizeFiles($_FILES));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Uri populated with values from $_SERVER.
|
||||||
|
*
|
||||||
|
* @return UriInterface
|
||||||
|
*/
|
||||||
|
public static function getUriFromGlobals() {
|
||||||
|
$uri = new Uri('');
|
||||||
|
|
||||||
|
$uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
|
||||||
|
|
||||||
|
$hasPort = false;
|
||||||
|
if (isset($_SERVER['HTTP_HOST'])) {
|
||||||
|
$hostHeaderParts = explode(':', $_SERVER['HTTP_HOST']);
|
||||||
|
$uri = $uri->withHost($hostHeaderParts[0]);
|
||||||
|
if (isset($hostHeaderParts[1])) {
|
||||||
|
$hasPort = true;
|
||||||
|
$uri = $uri->withPort($hostHeaderParts[1]);
|
||||||
|
}
|
||||||
|
} elseif (isset($_SERVER['SERVER_NAME'])) {
|
||||||
|
$uri = $uri->withHost($_SERVER['SERVER_NAME']);
|
||||||
|
} elseif (isset($_SERVER['SERVER_ADDR'])) {
|
||||||
|
$uri = $uri->withHost($_SERVER['SERVER_ADDR']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
|
||||||
|
$uri = $uri->withPort($_SERVER['SERVER_PORT']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hasQuery = false;
|
||||||
|
if (isset($_SERVER['REQUEST_URI'])) {
|
||||||
|
$requestUriParts = explode('?', $_SERVER['REQUEST_URI']);
|
||||||
|
$uri = $uri->withPath($requestUriParts[0]);
|
||||||
|
if (isset($requestUriParts[1])) {
|
||||||
|
$hasQuery = true;
|
||||||
|
$uri = $uri->withQuery($requestUriParts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
|
||||||
|
$uri = $uri->withQuery($_SERVER['QUERY_STRING']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getServerParams()
|
||||||
|
{
|
||||||
|
return $this->serverParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getUploadedFiles()
|
||||||
|
{
|
||||||
|
return $this->uploadedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function withUploadedFiles(array $uploadedFiles)
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->uploadedFiles = $uploadedFiles;
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getCookieParams()
|
||||||
|
{
|
||||||
|
return $this->cookieParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function withCookieParams(array $cookies)
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->cookieParams = $cookies;
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getQueryParams()
|
||||||
|
{
|
||||||
|
return $this->queryParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function withQueryParams(array $query)
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->queryParams = $query;
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getParsedBody()
|
||||||
|
{
|
||||||
|
return $this->parsedBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function withParsedBody($data)
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->parsedBody = $data;
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAttributes()
|
||||||
|
{
|
||||||
|
return $this->attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getAttribute($attribute, $default = null)
|
||||||
|
{
|
||||||
|
if (false === array_key_exists($attribute, $this->attributes)) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->attributes[$attribute];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function withAttribute($attribute, $value)
|
||||||
|
{
|
||||||
|
$new = clone $this;
|
||||||
|
$new->attributes[$attribute] = $value;
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function withoutAttribute($attribute)
|
||||||
|
{
|
||||||
|
if (false === array_key_exists($attribute, $this->attributes)) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$new = clone $this;
|
||||||
|
unset($new->attributes[$attribute]);
|
||||||
|
|
||||||
|
return $new;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue