Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+23 -1
View File
@@ -99,7 +99,7 @@ class _Application extends \IPS\Application
\IPS\Request::i()->file = 'medium';
}
$imageSize = ( \IPS\Request::i()->file ?: 'medium' ) . '_file_name';
$imageSize = ( ( \IPS\Request::i()->file == 'small' ) ? 'small' : 'masked' ) . '_file_name';
try
{
@@ -108,6 +108,28 @@ class _Application extends \IPS\Application
catch ( \Exception $e ){}
}
}
/* convert ?app=gallery&module=images&section=viewimage&img=14586 */
if( isset( \IPS\Request::i()->section ) AND \IPS\Request::i()->section == 'viewimage' )
{
$id = ( isset( \IPS\Request::i()->img ) ) ? \IPS\Request::i()->img : \IPS\Request::i()->id;
if( $id )
{
if( \IPS\Request::i()->file == 'med' )
{
\IPS\Request::i()->file = 'medium';
}
$imageSize = ( ( \IPS\Request::i()->file == 'small' ) ? 'small' : 'masked' ) . '_file_name';
try
{
\IPS\Output::i()->redirect( \IPS\gallery\Image::load( $id )->url() );
}
catch ( \Exception $e ){}
}
}
}
/**
+16 -4
View File
@@ -27,11 +27,13 @@ class _albums extends \IPS\Api\Controller
* GET /gallery/albums
* Get list of albums
*
* @note For requests using an OAuth Access Token for a particular member, only albums the authorized user can view will be included
* @apiparam string categories Comma-delimited list of categiry IDs - if provided, only albums in those categories are returned
* @apiparam string owners Comma-delimited list of member IDs - if provided, only albums owned by those members are returned
* @apiparam string sortBy What to sort by. Can be 'name', 'count_images' for number of images, or do not specify for ID
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\gallery\Album>
*/
public function GETindex()
@@ -85,13 +87,22 @@ class _albums extends \IPS\Api\Controller
}
$sortDir = ( isset( \IPS\Request::i()->sortDir ) and in_array( mb_strtolower( \IPS\Request::i()->sortDir ), array( 'asc', 'desc' ) ) ) ? \IPS\Request::i()->sortDir : 'asc';
/* Get results */
if ( $this->member )
{
$joins = array();
$where[] = \IPS\gallery\Album\Item::getItemsWithPermissionWhere( $where, $this->member, $joins );
}
/* Return */
return new \IPS\Api\PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'gallery_albums', $where, "{$sortBy} {$sortDir}" ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
'IPS\gallery\Album',
\IPS\Db::i()->select( 'COUNT(*)', 'gallery_albums', $where )->first()
\IPS\Db::i()->select( 'COUNT(*)', 'gallery_albums', $where )->first(),
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
);
}
@@ -100,14 +111,15 @@ class _albums extends \IPS\Api\Controller
* Get information about a specific album
*
* @param int $id ID Number
* @throws 2G315/1 INVALID_ID The album ID does not exist
* @throws 2G315/1 INVALID_ID The album ID does not exist or the authorized user does not have permisison to view it
* @return \IPS\gallery\Album
*/
public function GETitem( $id )
{
try
{
return new \IPS\Api\Response( 200, \IPS\gallery\Album::load( $id )->apiOutput() );
{
$album = $this->member ? \IPS\gallery\Album::loadAndCheckPerms( $id, 'view', $this->member ) : \IPS\gallery\Album::load( $id );
return new \IPS\Api\Response( 200, $album->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
+60 -18
View File
@@ -32,6 +32,7 @@ class _comments extends \IPS\Content\Api\CommentController
* GET /gallery/comments
* Get list of comments
*
* @note For requests using an OAuth Access Token for a particular member, only comments the authorized user can view will be included
* @apiparam string categories Comma-delimited list of category IDs (will also include images in albums in those categories)
* @apiparam string albums Comma-delimited list of album IDs
* @apiparam string authors Comma-delimited list of member IDs - if provided, only topics started by those members are returned
@@ -41,6 +42,7 @@ class _comments extends \IPS\Content\Api\CommentController
* @apiparam string sortBy What to sort by. Can be 'date', 'title' or leave unspecified for ID
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\gallery\Image\Comment>
*/
public function GETindex()
@@ -62,14 +64,24 @@ class _comments extends \IPS\Content\Api\CommentController
* View information about a specific comment
*
* @param int $id ID Number
* @throws 2L297/1 INVALID_ID The comment ID does not exist
* @throws 2L297/1 INVALID_ID The comment ID does not exist or the authorized user does not have permission to view it
* @return \IPS\gallery\Image\Comment
*/
public function GETitem( $id )
{
try
{
return new \IPS\Api\Response( 200, \IPS\gallery\Image\Comment::load( $id )->apiOutput() );
$class = $this->class;
if ( $this->member )
{
$object = $class::loadAndCheckPerms( $id, $this->member );
}
else
{
$object = $class::load( $id );
}
return new \IPS\Api\Response( 200, $object->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
@@ -81,21 +93,23 @@ class _comments extends \IPS\Content\Api\CommentController
* POST /gallery/comments
* Create a comment
*
* @note For requests using an OAuth Access Token for a particular member, any parameters the user doesn't have permission to use are ignored (for example, hidden will only be honoured if the authentictaed user has permission to hide content).
* @reqapiparam int image The ID number of the image the comment is for
* @reqapiparam int author The ID number of the member making the comment (0 for guest)
* @reqapiparam int author The ID number of the member making the comment (0 for guest). Required for requests made using an API Key or the Client Credentials Grant Type. For requests using an OAuth Access Token for a particular member, that member will always be the author
* @apiparam string author_name If author is 0, the guest name that should be used
* @reqapiparam string content The comment content as HTML (e.g. "<p>This is a comment.</p>")
* @apiparam datetime date The date/time that should be used for the comment date. If not provided, will use the current date/time
* @apiparam string ip_address The IP address that should be stored for the comment. If not provided, will use the IP address from the API request
* @reqapiparam string content The comment content as HTML (e.g. "<p>This is a comment.</p>"). Will be sanatized for requests using an OAuth Access Token for a particular member; will be saved unaltered for requests made using an API Key or the Client Credentials Grant Type.
* @apiparam datetime date The date/time that should be used for the comment date. If not provided, will use the current date/time. Ignored for requests using an OAuth Access Token for a particular member
* @apiparam string ip_address The IP address that should be stored for the comment. If not provided, will use the IP address from the API request. Ignored for requests using an OAuth Access Token for a particular member
* @apiparam int hidden 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
* @throws 2G317/3 INVALID_ID The comment ID does not exist
* @throws 1G317/4 NO_AUTHOR The author ID does not exist
* @throws 1G317/5 NO_CONTENT No content was supplied
* @throws 2G317/9 NO_PERMISSION The authorized user does not have permission to comment on that image
* @return \IPS\gallery\Image\Comment
*/
public function POSTindex()
{
/* Get topic */
/* Get image */
try
{
$image = \IPS\gallery\Image::load( \IPS\Request::i()->image );
@@ -106,18 +120,29 @@ class _comments extends \IPS\Content\Api\CommentController
}
/* Get author */
if ( \IPS\Request::i()->author )
if ( $this->member )
{
$author = \IPS\Member::load( \IPS\Request::i()->author );
if ( !$author->member_id )
if ( !$image->canComment( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_AUTHOR', '1G317/4', 404 );
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G317/9', 403 );
}
$author = $this->member;
}
else
{
$author = new \IPS\Member;
$author->name = \IPS\Request::i()->author_name;
if ( \IPS\Request::i()->author )
{
$author = \IPS\Member::load( \IPS\Request::i()->author );
if ( !$author->member_id )
{
throw new \IPS\Api\Exception( 'NO_AUTHOR', '1G317/4', 404 );
}
}
else
{
$author = new \IPS\Member;
$author->name = \IPS\Request::i()->author_name;
}
}
/* Check we have a post */
@@ -134,13 +159,15 @@ class _comments extends \IPS\Content\Api\CommentController
* POST /gallery/comments/{id}
* Edit a comment
*
* @note For requests using an OAuth Access Token for a particular member, any parameters the user doesn't have permission to use are ignored (for example, hidden will only be honoured if the authentictaed user has permission to hide content).
* @param int $id ID Number
* @apiparam int author The ID number of the member making the comment (0 for guest)
* @apiparam int author The ID number of the member making the comment (0 for guest). Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam string author_name If author is 0, the guest name that should be used
* @apiparam string content The comment content as HTML (e.g. "<p>This is a comment.</p>")
* @apiparam string content The comment content as HTML (e.g. "<p>This is a comment.</p>"). Will be sanatized for requests using an OAuth Access Token for a particular member; will be saved unaltered for requests made using an API Key or the Client Credentials Grant Type.
* @apiparam int hidden 1/0 indicating if the topic should be hidden
* @throws 2G317/6 INVALID_ID The comment ID does not exist
* @throws 2G317/6 INVALID_ID The comment ID does not exist or the authorized user does not have permission to view it
* @throws 1G317/7 NO_AUTHOR The author ID does not exist
* @throws 2G317/A NO_PERMISSION The authorized user does not have permission to edit the comment
* @return \IPS\gallery\Image\Comment
*/
public function POSTitem( $id )
@@ -149,6 +176,14 @@ class _comments extends \IPS\Content\Api\CommentController
{
/* Load */
$comment = \IPS\gallery\Image\Comment::load( $id );
if ( $this->member and !$comment->canView( $this->member ) )
{
throw new \OutOfRangeException;
}
if ( $this->member and !$comment->canEdit( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G317/A', 403 );
}
/* Do it */
try
@@ -171,14 +206,21 @@ class _comments extends \IPS\Content\Api\CommentController
* Deletes a comment
*
* @param int $id ID Number
* @throws 2G317/8 INVALID_ID The comment ID does not exist
* @throws 2G317/8 INVALID_ID The comment ID does not exist
* @throws 2G317/B NO_PERMISSION The authorized user does not have permission to delete the comment
* @return void
*/
public function DELETEitem( $id )
{
try
{
\IPS\gallery\Image\Comment::load( $id )->delete();
$class = $this->class;
$object = $class::load( $id );
if ( $this->member and !$object->canDelete( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G317/B', 403 );
}
$object->delete();
return new \IPS\Api\Response( 200, NULL );
}
+103 -38
View File
@@ -32,6 +32,7 @@ class _images extends \IPS\Content\Api\ItemController
* GET /gallery/images
* Get list of images
*
* @note For requests using an OAuth Access Token for a particular member, only images the authorized user can view will be included
* @apiparam string categories Comma-delimited list of category IDs (will also include images in albums in those categories)
* @apiparam string albums Comma-delimited list of album IDs
* @apiparam string authors Comma-delimited list of member IDs - if provided, only images started by those members are returned
@@ -41,6 +42,7 @@ class _images extends \IPS\Content\Api\ItemController
* @apiparam string sortBy What to sort by. Can be 'date' for creation date, 'title' or leave unspecified for ID
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\gallery\Image>
*/
public function GETindex()
@@ -63,7 +65,7 @@ class _images extends \IPS\Content\Api\ItemController
* View information about a specific image
*
* @param int $id ID Number
* @throws 2G316/1 INVALID_ID The image ID does not exist
* @throws 2G316/1 INVALID_ID The image ID does not exist or the authorized user does not have permission to view it
* @return \IPS\gallery\Image
*/
public function GETitem( $id )
@@ -82,20 +84,21 @@ class _images extends \IPS\Content\Api\ItemController
* POST /gallery/images
* Upload an image
*
* @note For requests using an OAuth Access Token for a particular member, any parameters the user doesn't have permission to use are ignored (for example, locked will only be honoured if the authentictaed user has permission to lock images).
* @reqapiparam int album The ID number of the album the image should be created in - not required if category is provided (only provide one or the other)
* @reqapiparam int category The ID number of the category the image should be created in - not required if album is provided (only provide one or the other)
* @reqapiparam int author The ID number of the member uploading the image (0 for guest)
* @reqapiparam int author The ID number of the member uploading the image (0 for guest). Required for requests made using an API Key or the Client Credentials Grant Type. For requests using an OAuth Access Token for a particular member, that member will always be the author
* @reqapiparam string caption The image caption
* @reqapiparam string filename The image filename (e.g. 'image.png')
* @reqapiparam string image The base64 encoded image contents
* @apiparam string description The description as HTML (e.g. "<p>This is an image.</p>")
* @apiparam string description The description as HTML (e.g. "<p>This is an image.</p>"). Will be sanatized for requests using an OAuth Access Token for a particular member; will be saved unaltered for requests made using an API Key or the Client Credentials Grant Type.
* @apiparam string copyright The copyright
* @apiparam string credit The credit information
* @apiparam int gpsShow If the image contains the location in it's EXIF data, 1/0 indicating if a map should be shown (defaults to 1)
* @apiparam string prefix Prefix tag
* @apiparam string tags Comma-separated list of tags (do not include prefix)
* @apiparam datetime date The date/time that should be used for the image post date. If not provided, will use the current date/time
* @apiparam string ip_address The IP address that should be stored for the image. If not provided, will use the IP address from the API request
* @apiparam datetime date The date/time that should be used for the image post date. If not provided, will use the current date/time. Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam string ip_address The IP address that should be stored for the image. If not provided, will use the IP address from the API request. Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam int locked 1/0 indicating if the image should be locked
* @apiparam int hidden 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
* @apiparam int pinned 1/0 indicating if the image should be locked
@@ -105,6 +108,8 @@ class _images extends \IPS\Content\Api\ItemController
* @throws 1G316/4 NO_CAPTION No caption was supplied
* @throws 1G316/5 NO_FILENAME No filename was supplied
* @throws 1G316/6 NO_IMAGE The image was invalid
* @throws 2G316/D NO_PERMISSION The authorized user does not have permission to create an image in that category/album
* @throws 1G316/E IMAGE_TOO_BIG The image exceeds the filesize the authorized user can upload
* @return \IPS\gallery\Image
*/
public function POSTindex()
@@ -135,17 +140,35 @@ class _images extends \IPS\Content\Api\ItemController
}
/* Get author */
if ( \IPS\Request::i()->author )
if ( $this->member )
{
$author = \IPS\Member::load( \IPS\Request::i()->author );
if ( !$author->member_id )
if ( isset( \IPS\Request::i()->album ) )
{
throw new \IPS\Api\Exception( 'NO_AUTHOR', '1G316/3', 400 );
if ( !$album->can( 'add', $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G316/D', 403 );
}
}
elseif ( !$category->can( 'add', $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G316/D', 403 );
}
$author = $this->member;
}
else
{
$author = new \IPS\Member;
if ( \IPS\Request::i()->author )
{
$author = \IPS\Member::load( \IPS\Request::i()->author );
if ( !$author->member_id )
{
throw new \IPS\Api\Exception( 'NO_AUTHOR', '1G316/3', 400 );
}
}
else
{
$author = new \IPS\Member;
}
}
/* Check we have a caption and a filename */
@@ -160,6 +183,10 @@ class _images extends \IPS\Content\Api\ItemController
/* Check it's a valid image */
$imageContents = base64_decode( \IPS\Request::i()->image );
if ( $this->member and $this->member->group['g_max_upload'] and \strlen( $imageContents ) > $this->member->group['g_max_upload'] * 1024 )
{
throw new \IPS\Api\Exception( 'IMAGE_TOO_BIG', '1G316/E', 400 );
}
try
{
$imageObject = \IPS\Image::create( $imageContents );
@@ -199,32 +226,34 @@ class _images extends \IPS\Content\Api\ItemController
$image->save();
/* Output */
return new \IPS\Api\Response( 201, $image->apiOutput() );
return new \IPS\Api\Response( 201, $image->apiOutput( $this->member ) );
}
/**
* POST /gallery/images/{id}
* Edit an image
*
* @note For requests using an OAuth Access Token for a particular member, any parameters the user doesn't have permission to use are ignored (for example, locked will only be honoured if the authentictaed user has permission to lock topics).
* @apiparam int album The ID number of the album (only provide this or category to move image)
* @apiparam int category The ID number of the category (only provide this or category to move image)
* @apiparam int author The ID number of the member uploading the image (0 for guest)
* @apiparam int author The ID number of the member uploading the image (0 for guest). Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam string caption The image caption
* @apiparam string description The description as HTML (e.g. "<p>This is an image.</p>")
* @apiparam string description The description as HTML (e.g. "<p>This is an image.</p>"). Will be sanatized for requests using an OAuth Access Token for a particular member; will be saved unaltered for requests made using an API Key or the Client Credentials Grant Type.
* @apiparam string copyright The copyright
* @apiparam string credit The credit information
* @apiparam int gpsShow If the image contains the location in it's EXIF data, 1/0 indicating if a map should be shown (defaults to 1)
* @apiparam string prefix Prefix tag
* @apiparam string tags Comma-separated list of tags (do not include prefix)
* @apiparam datetime date The date/time that should be used for the image post date. If not provided, will use the current date/time
* @apiparam string ip_address The IP address that should be stored for the image. If not provided, will use the IP address from the API request
* @apiparam datetime date The date/time that should be used for the image post date. If not provided, will use the current date/time. Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam string ip_address The IP address that should be stored for the image. If not provided, will use the IP address from the API request. Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam int locked 1/0 indicating if the image should be locked
* @apiparam int hidden 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
* @apiparam int pinned 1/0 indicating if the image should be locked
* @apiparam int featured 1/0 indicating if the image should be featured
* @throws 2G316/7 INVALID_ID The image ID is invalid
* @throws 1G316/8 NO_CATEGORY_OR_ALBUM The category or album does not exist
* @throws 2G316/7 INVALID_ID The image ID is invalid or the authorized user does not have permission to view it
* @throws 1G316/8 NO_CATEGORY_OR_ALBUM The category or album does not exist or the authorized user does not have permission to post in it
* @throws 1G316/9 NO_AUTHOR The author ID does not exist
* @throws 2G316/F NO_PERMISSION The authorized user does not have permission to edit the image
* @return \IPS\gallery\Image
*/
public function POSTitem( $id )
@@ -232,28 +261,49 @@ class _images extends \IPS\Content\Api\ItemController
try
{
$image = \IPS\gallery\Image::load( $id );
if ( $this->member and !$image->can( 'read', $this->member ) )
{
throw new \OutOfRangeException;
}
if ( $this->member and !$image->canEdit( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G316/FD', 403 );
}
/* New category or album */
try
if ( !$this->member or $image->canMove( $this->member ) )
{
if ( isset( \IPS\Request::i()->album ) and \IPS\Request::i()->album != $image->album_id )
try
{
$newAlbum = \IPS\gallery\Album::load( \IPS\Request::i()->album );
$image->move( $newAlbum );
if ( isset( \IPS\Request::i()->album ) and \IPS\Request::i()->album != $image->album_id )
{
$newAlbum = \IPS\gallery\Album::load( \IPS\Request::i()->album );
if ( $this->member and !$newAlbum->can( 'add', $this->member ) )
{
throw new \OutOfRangeException;
}
$image->move( $newAlbum );
}
elseif ( isset( \IPS\Request::i()->category ) and \IPS\Request::i()->category != $image->category_id )
{
$newCategory = \IPS\gallery\Category::load( \IPS\Request::i()->category );
if ( $this->member and !$newCategory->can( 'add', $this->member ) )
{
throw new \OutOfRangeException;
}
$image->move( $newCategory );
}
}
elseif ( isset( \IPS\Request::i()->category ) and \IPS\Request::i()->category != $image->category_id )
catch ( \OutOfRangeException $e )
{
$newCategory = \IPS\gallery\Category::load( \IPS\Request::i()->category );
$image->move( $newCategory );
throw new \IPS\Api\Exception( 'NO_CATEGORY_OR_ALBUM', '1G316/8', 400 );
}
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'NO_CATEGORY_OR_ALBUM', '1G316/8', 400 );
}
/* New author */
if ( isset( \IPS\Request::i()->author ) )
if ( !$this->member and isset( \IPS\Request::i()->author ) )
{
try
{
@@ -272,11 +322,11 @@ class _images extends \IPS\Content\Api\ItemController
}
/* Everything else */
$this->_createOrUpdate( $image );
$this->_createOrUpdate( $image, 'edit' );
/* Save and return */
$image->save();
return new \IPS\Api\Response( 200, $image->apiOutput() );
return new \IPS\Api\Response( 200, $image->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
@@ -292,7 +342,8 @@ class _images extends \IPS\Content\Api\ItemController
* @apiparam int hidden If 1, only comments which are hidden are returned, if 0 only not hidden
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @throws 2G316/A INVALID_ID The image ID does not exist
* @apiparam int perPage Number of results per page - defaults to 25
* @throws 2G316/A INVALID_ID The image ID does not exist or the authorized user does not have permission to view it
* @return \IPS\Api\PaginatedResponse<IPS\gallery\Image\Comment>
*/
public function GETitem_comments( $id )
@@ -315,7 +366,8 @@ class _images extends \IPS\Content\Api\ItemController
* @apiparam int hidden If 1, only comments which are hidden are returned, if 0 only not hidden
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @throws 2G316/B INVALID_ID The image ID does not exist
* @apiparam int perPage Number of results per page - defaults to 25
* @throws 2G316/B INVALID_ID The image ID does not exist or the authorized user does not have permission to view it
* @return \IPS\Api\PaginatedResponse<IPS\gallery\Image\Review>
*/
public function GETitem_reviews( $id )
@@ -334,9 +386,10 @@ class _images extends \IPS\Content\Api\ItemController
* Create or update image
*
* @param \IPS\Content\Item $item The item
* @param string $type add or edit
* @return \IPS\Content\Item
*/
protected function _createOrUpdate( \IPS\Content\Item $item )
protected function _createOrUpdate( \IPS\Content\Item $item, $type='add' )
{
/* Album */
if ( isset( \IPS\Request::i()->album ) and !$item->id )
@@ -353,7 +406,12 @@ class _images extends \IPS\Content\Api\ItemController
/* Description */
if ( isset( \IPS\Request::i()->description ) )
{
$item->description = \IPS\Request::i()->description;
$descriptionContents = \IPS\Request::i()->description;
if ( $this->member )
{
$descriptionContents = \IPS\Text\Parser::parseStatic( $descriptionContents, TRUE, NULL, $this->member, 'gallery_Images' );
}
$item->description = $descriptionContents;
}
/* Copyright */
@@ -375,7 +433,7 @@ class _images extends \IPS\Content\Api\ItemController
}
/* Pass up */
return parent::_createOrUpdate( $item );
return parent::_createOrUpdate( $item, $type );
}
/**
@@ -383,14 +441,21 @@ class _images extends \IPS\Content\Api\ItemController
* Delete an image
*
* @param int $id ID Number
* @throws 2G316/C INVALID_ID The image ID does not exist
* @throws 2G316/C INVALID_ID The image ID does not exist
* @throws 2G316/G NO_PERMISSION The authorized user does not have permission to delete the image
* @return void
*/
public function DELETEitem( $id )
{
try
{
\IPS\gallery\Image::load( $id )->delete();
$item = \IPS\gallery\Image::load( $id );
if ( $this->member and !$item->canDelete( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G316/G', 404 );
}
$item->delete();
return new \IPS\Api\Response( 200, NULL );
}
+60 -18
View File
@@ -32,6 +32,7 @@ class _reviews extends \IPS\Content\Api\CommentController
* GET /gallery/reviews
* Get list of reviews
*
* @note For requests using an OAuth Access Token for a particular member, only reviews the authorized user can view will be included
* @apiparam string categories Comma-delimited list of category IDs (will also include images in albums in those categories)
* @apiparam string albums Comma-delimited list of album IDs
* @apiparam string authors Comma-delimited list of member IDs - if provided, only topics started by those members are returned
@@ -41,6 +42,7 @@ class _reviews extends \IPS\Content\Api\CommentController
* @apiparam string sortBy What to sort by. Can be 'date', 'title' or leave unspecified for ID
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\gallery\Image\Review>
*/
public function GETindex()
@@ -62,14 +64,24 @@ class _reviews extends \IPS\Content\Api\CommentController
* View information about a specific review
*
* @param int $id ID Number
* @throws 2G318/1 INVALID_ID The review ID does not exist
* @throws 2G318/1 INVALID_ID The review ID does not exist or the authorized user does not have permission to view it
* @return \IPS\gallery\Image\Review
*/
public function GETitem( $id )
{
try
{
return new \IPS\Api\Response( 200, \IPS\gallery\Image\Review::load( $id )->apiOutput() );
$class = $this->class;
if ( $this->member )
{
$object = $class::loadAndCheckPerms( $id, $this->member );
}
else
{
$object = $class::load( $id );
}
return new \IPS\Api\Response( 200, $object->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
@@ -81,23 +93,25 @@ class _reviews extends \IPS\Content\Api\CommentController
* POST /gallery/reviews
* Create a review
*
* @note For requests using an OAuth Access Token for a particular member, any parameters the user doesn't have permission to use are ignored (for example, hidden will only be honoured if the authentictaed user has permission to hide content).
* @reqapiparam int image The ID number of the image the review is for
* @reqapiparam int author The ID number of the member making the review (0 for guest)
* @reqapiparam int author The ID number of the member making the review (0 for guest). Required for requests made using an API Key or the Client Credentials Grant Type. For requests using an OAuth Access Token for a particular member, that member will always be the author
* @apiparam string author_name If author is 0, the guest name that should be used
* @reqapiparam string content The review content as HTML (e.g. "<p>This is a review.</p>")
* @apiparam datetime date The date/time that should be used for the review date. If not provided, will use the current date/time
* @apiparam string ip_address The IP address that should be stored for the review. If not provided, will use the IP address from the API request
* @reqapiparam string content The review content as HTML (e.g. "<p>This is a review.</p>"). Will be sanatized for requests using an OAuth Access Token for a particular member; will be saved unaltered for requests made using an API Key or the Client Credentials Grant Type.
* @apiparam datetime date The date/time that should be used for the review date. If not provided, will use the current date/time. Ignored for requests using an OAuth Access Token for a particular member
* @apiparam string ip_address The IP address that should be stored for the review. If not provided, will use the IP address from the API request. Ignored for requests using an OAuth Access Token for a particular member
* @apiparam int hidden 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
* @reqapiparam int rating Star rating
* @throws 2G318/8 INVALID_ID The forum ID does not exist
* @throws 1G318/3 NO_AUTHOR The author ID does not exist
* @throws 1L298/4 NO_CONTENT No content was supplied
* @throws 1G318/5 INVALID_RATING The rating is not a valid number up to the maximum rating
* @throws 2G318/D NO_PERMISSION The authorized user does not have permission to review that image
* @return \IPS\gallery\Image\Review
*/
public function POSTindex()
{
/* Get topic */
/* Get image */
try
{
$image = \IPS\gallery\Image::load( \IPS\Request::i()->image );
@@ -108,18 +122,29 @@ class _reviews extends \IPS\Content\Api\CommentController
}
/* Get author */
if ( \IPS\Request::i()->author )
if ( $this->member )
{
$author = \IPS\Member::load( \IPS\Request::i()->author );
if ( !$author->member_id )
if ( !$image->canReview( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_AUTHOR', '1G318/3', 404 );
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G318/D', 403 );
}
$author = $this->member;
}
else
{
$author = new \IPS\Member;
$author->name = \IPS\Request::i()->author_name;
if ( \IPS\Request::i()->author )
{
$author = \IPS\Member::load( \IPS\Request::i()->author );
if ( !$author->member_id )
{
throw new \IPS\Api\Exception( 'NO_AUTHOR', '1G318/3', 404 );
}
}
else
{
$author = new \IPS\Member;
$author->name = \IPS\Request::i()->author_name;
}
}
/* Check we have a post */
@@ -142,15 +167,17 @@ class _reviews extends \IPS\Content\Api\CommentController
* POST /gallery/reviews/{id}
* Edit a review
*
* @note For requests using an OAuth Access Token for a particular member, any parameters the user doesn't have permission to use are ignored (for example, hidden will only be honoured if the authentictaed user has permission to hide content).
* @param int $id ID Number
* @apiparam int author The ID number of the member making the review (0 for guest)
* @apiparam int author The ID number of the member making the review (0 for guest). Ignored for requests using an OAuth Access Token for a particular member.
* @apiparam string author_name If author is 0, the guest name that should be used
* @apiparam string content The review content as HTML (e.g. "<p>This is a review.</p>")
* @apiparam string content The review content as HTML (e.g. "<p>This is a review.</p>"). Will be sanatized for requests using an OAuth Access Token for a particular member; will be saved unaltered for requests made using an API Key or the Client Credentials Grant Type.
* @apiparam int hidden 1/0 indicating if the topic should be hidden
* @apiparam int rating Star rating
* @throws 2L298/5 INVALID_ID The review ID does not exist
* @throws 2L298/5 INVALID_ID The review ID does not exist or the authorized user does not have permission to view it
* @throws 1G318/B NO_AUTHOR The author ID does not exist
* @throws 1G318/A INVALID_RATING The rating is not a valid number up to the maximum rating
* @throws 2G318/E NO_PERMISSION The authorized user does not have permission to edit the review
* @return \IPS\gallery\Image\Review
*/
public function POSTitem( $id )
@@ -159,6 +186,14 @@ class _reviews extends \IPS\Content\Api\CommentController
{
/* Load */
$comment = \IPS\gallery\Image\Review::load( $id );
if ( $this->member and !$comment->canView( $this->member ) )
{
throw new \OutOfRangeException;
}
if ( $this->member and !$comment->canEdit( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G318/E', 403 );
}
/* Check */
if ( isset( \IPS\Request::i()->rating ) and !in_array( (int) \IPS\Request::i()->rating, range( 1, \IPS\Settings::i()->reviews_rating_out_of ) ) )
@@ -186,14 +221,21 @@ class _reviews extends \IPS\Content\Api\CommentController
* Deletes a review
*
* @param int $id ID Number
* @throws 2G318/C INVALID_ID The review ID does not exist
* @throws 2G318/C INVALID_ID The review ID does not exist
* @throws 2G318/F NO_PERMISSION The authorized user does not have permission to delete the review
* @return void
*/
public function DELETEitem( $id )
{
try
{
\IPS\gallery\Image\Review::load( $id )->delete();
$class = $this->class;
$object = $class::load( $id );
if ( $this->member and !$object->canDelete( $this->member ) )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2G318/F', 403 );
}
$object->delete();
return new \IPS\Api\Response( 200, NULL );
}
+4 -4
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<emails><template><template_app>gallery</template_app><template_name>digests__item</template_name><template_content_html><![CDATA[<tr style='border-bottom: 1px solid #eeeeee'>
<td dir='{dir}' width='120' align='center' valign='top'>
<img src='{file="$image->thumb_file_name" extension="gallery_Images"}' style='border: 1px solid #000; vertical-align: middle;' width='100' height='100'>
<img src='{file="$image->small_file_name" extension="gallery_Images"}' style='border: 1px solid #000; vertical-align: middle;' width='100' height='100'>
</td>
<td dir='{dir}' align='left'>
<p style='font-family: "Helvetica Neue", helvetica, sans-serif; margin: 0; font-size: 13px; font-weight: bold'>
@@ -22,9 +22,9 @@
<strong style='font-family: "Helvetica Neue", helvetica, sans-serif; '><img src='{resource="email/comment.png" location="interface" app="core"}' width='13' height='12' style='vertical-align: middle'>&nbsp; {$email->language->addToStack("num_comments", FALSE, array( 'pluralize' => array( $image->comments ) ) )}</strong>
</p>
</td>
</tr>]]></template_content_html><template_data>$image, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("x_added_image_in", FALSE, array( 'sprintf' => array( $image->author()->name, $image->container()->_title ) ) )}: {$image->mapped('name')} ({$image->url()})]]></template_content_plaintext></template><template><template_app>gallery</template_app><template_name>digests__comment</template_name><template_content_html><![CDATA[<tr style='border-bottom: 1px solid #eeeeee'>
</tr>]]></template_content_html><template_data>$image, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("x_added_image_in", FALSE, array( 'sprintf' => array( $image->author()->name, $image->container()->_title ) ) )}: {$image->mapped('name')} ({$image->url()})]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>gallery</template_app><template_name>digests__comment</template_name><template_content_html><![CDATA[<tr style='border-bottom: 1px solid #eeeeee'>
<td dir='{dir}' width='120' align='center' valign='top'>
<img src='{file="$comment->item()->thumb_file_name" extension="gallery_Images"}' style='border: 1px solid #000; vertical-align: middle;' width='100' height='100'>
<img src='{file="$comment->item()->small_file_name" extension="gallery_Images"}' style='border: 1px solid #000; vertical-align: middle;' width='100' height='100'>
</td>
<td dir='{dir}' align='left'>
<p style='font-family: "Helvetica Neue", helvetica, sans-serif; margin: 0; font-size: 13px; font-weight: bold'>
@@ -38,4 +38,4 @@
</div>
</td>
</tr>]]></template_content_html><template_data>$comment, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("x_commented_photo", FALSE, array( 'sprintf' => array( $comment->author()->name ) ) )}: {$comment->item()->name} ({$comment->url()})
]]></template_content_plaintext></template></emails>
]]></template_content_plaintext><template_pinned>0</template_pinned></template></emails>
File diff suppressed because it is too large. Load diff
+92 -34
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<language>
<app key="gallery" version="102002">
<app key="gallery" version="103005">
<word key="__app_gallery" js="0">Gallery</word>
<word key="frontnavigation_gallery" js="0">Gallery</word>
<word key="gallery_title" js="0">Gallery</word>
@@ -12,8 +12,15 @@
<word key="__indefart_gallery_album" js="0">a gallery album</word>
<word key="__defart_gallery_album_comment" js="0">comment on a gallery album</word>
<word key="__defart_gallery_album" js="0">gallery album</word>
<word key="__indefart_gallery_image_review" js="0">a review on a gallery image</word>
<word key="__defart_gallery_image_review" js="0">review on a gallery image</word>
<word key="__indefart_gallery_album_review" js="0">a review on a gallery album</word>
<word key="__defart_gallery_album_review" js="0">review on a gallery album</word>
<word key="module__gallery_stats" js="0">Statistics</word>
<word key="search_contextual_item_gallery_image" js="0">This Image</word>
<word key="album_user_own_activity_item" js="0">%s added images to %s in </word>
<word key="club_node_gallery" js="0">Club Images</word>
<word key="community_image_categories" js="0">Community Images</word>
<word key="menu__gallery_gallery" js="0">Gallery</word>
<word key="menu__gallery_gallery_categories" js="0">Categories</word>
<word key="menu__gallery_gallery_albums" js="0">Albums</word>
@@ -40,6 +47,10 @@
<word key="editor__gallery_Images" js="0">Gallery Images</word>
<word key="editorMedia_gallery_Images" js="0">Gallery Images</word>
<word key="member_gallery_image" js="0">Keep using my Gallery Image</word>
<word key="member_restrictions__gallery_Gallery" js="0">Gallery</word>
<word key="restriction_no_gallery_access" js="0">Restricted from accessing Gallery</word>
<word key="restriction_no_gallery_uploads" js="0">Restricted from uploading Gallery images</word>
<word key="content_stats__gallery_Gallery" js="0">Gallery</word>
<word key="group__gallery_Gallery" js="0">Gallery</word>
<word key="gallery_album_permissions" js="0">Album Restrictions</word>
<word key="g_create_albums" js="0">Can create albums?</word>
@@ -67,7 +78,8 @@
<word key="r__bandwidth" js="0">Bandwidth</word>
<word key="r__bandwidth_manage" js="0">Can view bandwidth statistics?</word>
<word key="gcategory_parent_id" js="0">Parent Category</word>
<word key="category_sort_options" js="0">Default sort order</word>
<word key="category_sort_options" js="0">Album sort order</word>
<word key="category_sort_options_img" js="0">Image sort order</word>
<word key="category_approve_img" js="0">Images must be approved?</word>
<word key="category_comments_and_ratings" js="0">Comments and Ratings</word>
<word key="category_allow_comments" js="0">Allow comments?</word>
@@ -75,7 +87,13 @@
<word key="gcategory_allow_rating" js="0">Allow ratings?</word>
<word key="category_can_tag" js="0">Allow tags?</word>
<word key="category_tag_prefixes" js="0">Allow prefixes?</word>
<word key="category_allow_albums" js="0">Allow albums?</word>
<word key="category_allow_albums" js="0">Albums</word>
<word key="cat_no_allow_albums" js="0">Do not allow albums</word>
<word key="cat_no_allow_albums_desc" js="0">Images can only be submitted directly to the category</word>
<word key="cat_allow_albums" js="0">Allow albums</word>
<word key="cat_allow_albums_desc" js="0">Images can be submitted directly to the catgory or to albums within the category</word>
<word key="cat_require_albums" js="0">Require albums</word>
<word key="cat_require_albums_desc" js="0">Images can only be submitted to albums within the category</word>
<word key="category_watermark" js="0">Watermark images?</word>
<word key="category_preset_tags" js="0">Preset tags</word>
<word key="perm__view" js="0">See Category</word>
@@ -128,8 +146,16 @@
<word key="album_private_mod" js="0">Only the owner can see this album</word>
<word key="album_private_badge" js="0">Private album</word>
<word key="album_friend_only" js="0">I want to choose who can see this album</word>
<word key="album_submit_type" js="0">Submissions</word>
<word key="album_submit_access_members" js="0">Allowed members</word>
<word key="album_submit_access_groups" js="0">Allowed groups</word>
<word key="album_friend_only_badge" js="0">Invite-only album</word>
<word key="album_allowed_access" js="0">Members with access</word>
<word key="album_submittype_owner" js="0">Only I can submit to this album</word>
<word key="album_submittype_public" js="0">Anyone can submit to this album</word>
<word key="album_submittype_groups" js="0">I want to choose which groups can submit to this album</word>
<word key="album_submittype_members" js="0">I want to choose which members can submit to this album</word>
<word key="album_submittype_club" js="0">Anyone in this club can submit to this album</word>
<word key="set_album_owner" js="0">Owner</word>
<word key="set_album_owner_me" js="0">This album is for me</word>
<word key="set_album_owner_other" js="0">This album is for someone else</word>
@@ -142,7 +168,7 @@
<word key="can_feature_gallery_image" js="0">Can feature images?</word>
<word key="can_unfeature_gallery_image" js="0">Can unfeature images?</word>
<word key="can_move_gallery_image" js="0">Can move images?</word>
<word key="can_move_gallery_image_desc" js="0">Will be able to move images only between the categories they have permission to moderate, not to any other category.</word>
<word key="can_move_gallery_image_desc" js="0">Will be able to move images only between the categories they can create new content within.</word>
<word key="can_lock_gallery_image" js="0">Can lock images?</word>
<word key="can_unlock_gallery_image" js="0">Can unlock images?</word>
<word key="can_reply_to_locked_gallery_image" js="0">Can comment on locked images?</word>
@@ -163,7 +189,7 @@
<word key="can_unhide_gallery_album" js="0">Can unhide albums?</word>
<word key="can_view_hidden_gallery_album" js="0">Can view hidden albums?</word>
<word key="can_move_gallery_album" js="0">Can move albums?</word>
<word key="can_move_gallery_album_desc" js="0">Will be able to move albums only between the categories they have permission to moderate, not to any other category.</word>
<word key="can_move_gallery_album_desc" js="0">Will be able to move albums only between the categories they can create new content within.</word>
<word key="can_lock_gallery_album" js="0">Can lock albums?</word>
<word key="can_unlock_gallery_album" js="0">Can unlock albums?</word>
<word key="can_reply_to_locked_gallery_album" js="0">Can reply to locked albums?</word>
@@ -193,21 +219,16 @@
<word key="can_delete_item_message_gallery_image" js="0">Can delete messages on images?</word>
<word key="acplogs__gallery_settings" js="0">Gallery settings updated</word>
<word key="acplogs__gallery_rebuilt" js="0">Images rebuild tool run.</word>
<word key="gallery_dims_explanation" js="0"><![CDATA[Copies of all images uploaded to the gallery are created at different sizes and are then used as appropriate in the interface throughout the gallery. You can control the maximum dimensions of each size below. The settings apply to new images only, unless you <a data-confirm href='{internal.app=gallery&module=gallery&controller=settings&do=rebuildImages}'>rebuild existing images</a>.]]></word>
<word key="gallery_dims_explanation" js="0">Copies of all images uploaded to the gallery are created at different sizes and are then used as appropriate in the interface throughout the gallery. You can control the maximum dimensions of each size below.</word>
<word key="gallery_large_dims" js="0">Large image dimensions</word>
<word key="gallery_medium_dims" js="0">Medium image dimensions</word>
<word key="gallery_small_dims" js="0">Small image dimensions</word>
<word key="gallery_thumb_dims" js="0">Thumbnail dimensions</word>
<word key="gallery_small_dims" js="0">Thumbnail image dimensions</word>
<word key="gallery_use_square_thumbnails" js="0">Use square thumbnails</word>
<word key="gallery_use_square_thumbnails_desc" js="0"><![CDATA[If you change this, you may want to <a data-confirm href='{internal.app=gallery&module=gallery&controller=settings&do=rebuildImages}'>rebuild existing images</a> otherwise the change will only apply to newly uploaded images.]]></word>
<word key="gallery_watermark_path" js="0">Watermark image</word>
<word key="gallery_watermark_path_desc" js="0"><![CDATA[The image provided will be overlaid on all gallery images (for example, to show a copyright message). Be aware that individual categories can disable the watermark on images. For best results, use a small image with a transparent background.<br><br><a data-confirm href='{internal.app=gallery&module=gallery&controller=settings&do=rebuildImages}'>Rebuild Existing Images</a>]]></word>
<word key="gallery_watermark_path_desc" js="0">The image provided will be overlaid on all gallery images (for example, to show a copyright message). Be aware that individual categories can disable the watermark on images. For best results, use a small image with a transparent background.</word>
<word key="gallery_watermark_images" js="0">Images to watermark</word>
<word key="gallery_use_watermarks" js="0">Watermark images?</word>
<word key="gallery_watermark_large" js="0">Large images</word>
<word key="gallery_watermark_medium" js="0">Medium images</word>
<word key="gallery_watermark_small" js="0">Small images</word>
<word key="gallery_watermark_thumb" js="0">Thumbnails</word>
<word key="gallery_watermark_small" js="0">Thumbnail images</word>
<word key="gallery_bandwidth" js="0">Bandwidth Logging</word>
<word key="gallery_detailed_bandwidth" js="0">Store detailed bandwidth logs</word>
<word key="gallery_detailed_bandwidth_desc" js="0">Enabling this setting will cause Gallery image views to tracked. You can then use the bandwidth logs to see how much bandwidth image views consume over periods of time, and you can view bandwidth statistics on a per-member basis when editing members.</word>
@@ -216,10 +237,16 @@
<word key="gallery_rss_enabled" js="0">Enable latest images RSS feed</word>
<word key="gallery_rss_enabled_desc" js="0"><![CDATA[Only content that is available to guests will be shown. For more options, create a <a href='{internal.app=core&module=discovery&controller=rss}'>Custom RSS Feed</a>]]></word>
<word key="gallery_rebuildimages" js="0">Rebuilding images</word>
<word key="rebuilding_gallery_images" js="0">Rebuilding gallery images</word>
<word key="cleaning_up_gallery_images" js="0">Cleaning up Gallery images </word>
<word key="member__gallery_Images" js="0">Gallery</word>
<word key="remove_gallery_access" js="0">Allowed to access gallery?</word>
<word key="remove_gallery_upload" js="0">Allowed to upload images?</word>
<word key="gallery_stats" js="0">View Gallery Statistics</word>
<word key="history_restrictions_remove_gallery_access_1" js="0">disabled ability to access Gallery</word>
<word key="history_restrictions_remove_gallery_access_0" js="0">restored ability to access Gallery</word>
<word key="history_restrictions_remove_gallery_upload_1" js="0">disabled ability to upload Gallery images</word>
<word key="history_restrictions_remove_gallery_upload_0" js="0">restored ability to upload Gallery images</word>
<word key="deleted_image" js="0">Deleted image</word>
<word key="bandwidth" js="0">Bandwidth</word>
<word key="image_id" js="0">Image</word>
@@ -232,10 +259,12 @@
<word key="number_of_views" js="0">Number of Views *</word>
<word key="gallery_bandwidth_used" js="0">Bandwidth Used *</word>
<word key="images_stat_of_total" js="0">%s (%d%% of total)</word>
<word key="images_stat_of_total_link" js="0">%s (%d%% of total) %s</word>
<word key="images_stat_average" js="0">%s (average for all users: %s)</word>
<word key="gallery_stats_disclaimer" js="0">* This statistic comes from the bandwidth log which may be disabled or set to be pruned periodically.</word>
<word key="gallery_graph_disclaimer" js="0">This data comes from the bandwidth log which may be disabled or set to be pruned periodically.</word>
<word key="member_images_chart" js="0">%s's Image Statistics</word>
<word key="ad_image_lightbox" js="0">In the Gallery image lightbox</word>
<word key="embed_gallery_album" js="0">A gallery album by %s</word>
<word key="embed_gallery_image" js="0">A gallery image by %s</word>
<word key="module__gallery_gallery" js="0">Gallery</word>
@@ -243,7 +272,17 @@
<word key="add_gallery_image_movies" js="0">Add Images or Movies</word>
<word key="submit_first_image" js="0">Add the first image</word>
<word key="browse_categories" js="0">Browse categories</word>
<word key="save_submit_images" js="0">Save Images</word>
<word key="save_submit_images" js="0">Save images</word>
<word key="submit_all_images" js="0">Submit all images</word>
<word key="allowed_file_types_link" js="0">Allowed file types</word>
<word key="gal_notification_options" js="0">Notification settings</word>
<word key="submit_help_info" js="0">Select an image to edit its details.</word>
<word key="add_credit" js="0">Add a credit</word>
<word key="add_copyright" js="0">Add a copyright</word>
<word key="submit_go_back" js="0">Save details</word>
<word key="image_info_saved" js="0">Image information saved</word>
<word key="gallery_submit_prefix_album" js="0">Uploading to album &quot;%s&quot;</word>
<word key="gallery_submit_prefix_cat" js="0">Uploading to category &quot;%s&quot;</word>
<word key="loc_gallery_adding_image" js="0">Uploading an image</word>
<word key="upload_more_images" js="0">Upload more images</word>
<word key="select_your_images" js="0">Select your images</word>
@@ -261,9 +300,9 @@
<word key="image_pending_approval" js="0">This image has not yet been approved and is only visible to staff</word>
<word key="image_details" js="0">About this image</word>
<word key="manage_image" js="0">Manage Image</word>
<word key="report_image" js="0">Report Image</word>
<word key="manage_album" js="0">Manage Album</word>
<word key="report_album" js="0">Report Album</word>
<word key="report_image" js="0">Report image</word>
<word key="manage_album" js="0">Manage album</word>
<word key="report_album" js="0">Report album</word>
<word key="image_uploaded_by" js="0">Uploaded by %s</word>
<word key="image_category" js="0">Category</word>
<word key="image_album" js="0">Album</word>
@@ -272,7 +311,6 @@
<word key="image_submit_comment" js="0">Submit Comment</word>
<word key="map_enabled" js="0">Show map?</word>
<word key="map_privacy" js="0">Map privacy</word>
<word key="view_all_sizes" js="0">Other sizes</word>
<word key="set_image_as" js="0">Set as</word>
<word key="loc_gallery_browsing" js="0">Browsing Gallery</word>
<word key="loc_gallery_browsing_categories" js="0">Browsing gallery categories</word>
@@ -283,7 +321,6 @@
<word key="loc_gallery_viewing_image" js="0">Viewing image: %s</word>
<word key="loc_gallery_viewing_category" js="0">Viewing category: %s</word>
<word key="loc_gallery_viewing_album" js="0">Viewing album: %s</word>
<word key="slider_text" js="0"><![CDATA[Other images in <strong>%s</strong>]]></word>
<word key="gallery_rss_title" js="0">Latest Images</word>
<word key="gallery_rss_title_container" js="0">Latest Images: %s</word>
<word key="gallery_rss_offline" js="0">This RSS feed is disabled</word>
@@ -293,6 +330,7 @@
<word key="image_gps_show" js="0">Show map?</word>
<word key="image_gps_show_desc" js="0">You can optionally display a map for this image showing where it was taken</word>
<word key="view_this_image" js="0">View the image %s</word>
<word key="view_this_video" js="0">View the video %s</word>
<word key="view_this_album" js="0">View the album %s</word>
<word key="num_images" js="0">{# [1:image][?:images]}</word>
<word key="num_images_in_album" js="0">{# [1:image][?:images]} in this album</word>
@@ -300,6 +338,7 @@
<word key="rotate_image" js="0">Rotate</word>
<word key="rotate_left" js="0">Rotate Left</word>
<word key="rotate_right" js="0">Rotate Right</word>
<word key="gallery_image_rotated" js="0">The image has been rotated</word>
<word key="add_more_images" js="0">Add more images</word>
<word key="set_cover_photo" js="0">Set cover photo</word>
<word key="set_for_album" js="0">Set for album?</word>
@@ -313,13 +352,14 @@
<word key="set_as_category_done" js="0">This image has been set as the category cover</word>
<word key="set_as_album_done" js="0">This image has been set as the album cover</word>
<word key="set_as_both_done" js="0">This image has been set as the category and album covers</word>
<word key="find_users_images" js="0">Find their other images</word>
<word key="find_users_images" js="0">View %s's images</word>
<word key="change_author" js="0">Change Author</word>
<word key="change_author_ititle" js="0">Change the member who owns this image</word>
<word key="gallery_metadata" js="0">Photo Information for %s</word>
<word key="gallery_no_metadata" js="0">No extra image data</word>
<word key="view_all_photo_info" js="0">View all photo EXIF information</word>
<word key="view_photo_info" js="0">View photo EXIF information</word>
<word key="gallery_images_no_more" js="0">You can't upload any more images to this album.</word>
<word key="gallery_images_too_many" js="0">You can only upload {# [1:more image][?:more images]} to this album.</word>
<word key="gallery_images_too_many_at_once" js="0">You can only upload 100 images at a time.</word>
<word key="gallery_image_or_movie" js="0">You must upload at least one image or one movie</word>
@@ -336,6 +376,7 @@
<word key="viewfilter_thumbnails" js="0">Thumbnails</word>
<word key="viewfilter_large" js="0">Large Images</word>
<word key="set_gallery_image_photo" js="0">Set as my profile photo</word>
<word key="view_fullsize_image" js="0">View Full Image</word>
<word key="no_photo_for_media" js="0">You can only set an image as your profile photo image</word>
<word key="delete_album" js="0">Delete Album</word>
<word key="edit_album" js="0">Edit Album</word>
@@ -372,14 +413,15 @@
<word key="approve_title_album" js="0">Approve this album</word>
<word key="upload_image_too_big" js="0">Images must be smaller than %s.</word>
<word key="upload_movie_too_big" js="0">Movies must be smaller than %s.</word>
<word key="upload_percn_help" js="0">You can use %n in the caption name, and it will be replaced with the numeric position of this image, in the current set of images (e.g. name %n produces name 1, name 2, name 3, etc). This is useful if you wish to copy the details to all other images in the set. You MUST be sure to click the Copy Details button to apply this template to all images.</word>
<word key="image_info_finishcopy" js="0">Copy details to all images</word>
<word key="unread_cat_images" js="0">This category has new images</word>
<word key="image_tools" js="0">Image Tools</word>
<word key="image_share" js="0">Share</word>
<word key="cat_img_count" js="0">{# [1:image][?:images]}</word>
<word key="cat_album_count" js="0">{# [1:album][?:albums]}</word>
<word key="cat_comment_count" js="0">{# [1:comment][?:comments]}</word>
<word key="image_review_count" js="0">{# [1:Review][?:Reviews]}</word>
<word key="image_comment_count" js="0">{# [1:Comment][?:Comments]}</word>
<word key="from_the_album" js="0">From the album</word>
<word key="image_review_count" js="0">{# [1:review][?:reviews]}</word>
<word key="image_comment_count" js="0">{# [1:comment][?:comments]}</word>
<word key="from_the_album" js="0">From the album: </word>
<word key="album_created_by" js="0">Album created by</word>
<word key="album_everything_seen" js="0">You've seen everything in this album</word>
<word key="album_new_content" js="0">This album has unseen content</word>
@@ -417,12 +459,6 @@
<word key="image_locked_cannot_comment" js="0">This image is now closed to further comments.</word>
<word key="album_locked_can_comment" js="0">This album is locked, but your permissions allow you to add new comments.</word>
<word key="album_locked_cannot_comment" js="0">This album is now closed to further comments.</word>
<word key="all_image_sizes" js="0">All Sizes For %s</word>
<word key="back_to_image_page" js="0">Back to image page</word>
<word key="size_thumb" js="0">Thumb</word>
<word key="size_small" js="0">Small</word>
<word key="size_medium" js="0">Medium</word>
<word key="size_large" js="0">Large / Original</word>
<word key="gallery_edit_details" js="0">Edit Details</word>
<word key="gallery_edit_details_title" js="0">Edit this image's details</word>
<word key="image_rating_value" js="0">Your rating</word>
@@ -457,9 +493,14 @@
<word key="upload_images" js="0">Upload Images</word>
<word key="image_information" js="0">Add Information</word>
<word key="process" js="0">Finish</word>
<word key="create_new_album" js="0">Create New Album</word>
<word key="create_new_album" js="0">Create new album</word>
<word key="choose_selected_album" js="0">Use Selected Album</word>
<word key="submit_breadcrumb" js="0">Submit Image</word>
<word key="submit_directly_to" js="0">Post directly to the category %s</word>
<word key="submit_new_album" js="0">Create a brand new album for your images</word>
<word key="choose_existing_album_desc" js="0">Choose one of your existing albums</word>
<word key="use_plain_editor" js="0">Use plain editor</word>
<word key="use_rte_editor" js="0">Use rich text editor</word>
<word key="album_create_owner" js="0">Ownership</word>
<word key="album_create_privacy" js="0">Privacy</word>
<word key="album_create_features" js="0">Album Features</word>
@@ -474,12 +515,13 @@
<word key="create_album" js="0">Create Album</word>
<word key="create_an_album" js="0">Create an album</word>
<word key="add_to_existing_album" js="0">Add to an existing album</word>
<word key="choose_existing_album" js="0">Choose Existing Album</word>
<word key="choose_existing_album" js="0">Use an existing album</word>
<word key="change_album" js="0">change</word>
<word key="set_tags" js="0">Set tags...</word>
<word key="set_copyright" js="0">Set copyright...</word>
<word key="set_copyright_all" js="0">Set copyright on all</word>
<word key="set_credit_all" js="0">Set credit on all</word>
<word key="set_tags_all" js="0">Set tags on all</word>
<word key="notify_me" js="0">Notify me when comments are posted</word>
<word key="album_limit_message" js="0"><![CDATA[Albums can contain up to <strong>{# [1:image or video][?:images or videos]}</strong>, but you've uploaded <strong>%d</strong>. Only the first {# [1:image or video][?:images or videos]} will be added to the album when you save; any remaining will be removed.]]></word>
<word key="too_many_images" js="0">Too many images</word>
@@ -512,7 +554,9 @@
<word key="set_copyright_desc" js="0">Sets the copyright for each image you're uploading except ones you individually add a copyright for.</word>
<word key="credit" js="0">Credit</word>
<word key="credit_desc" js="0">Sets the credit for each image you're uploading except ones you individually add a credit for.</word>
<word key="tags_all_desc" js="0">Sets the tags for each image you're uploading except ones you individually specify tags for.</word>
<word key="gallery_clear_uploads_error" js="0">Could not delete file %s : %s'</word>
<word key="specify_image_tags" js="0">Set tags</word>
<word key="exif_title" js="0">Full EXIF data</word>
<word key="gallery_focal_length_mm" js="0">%s mm</word>
<word key="gallery_shutterspeed" js="0">1/%s second</word>
@@ -743,6 +787,7 @@
<word key="digest_area_gallery_album" js="0">Albums</word>
<word key="digest_area_gallery_category" js="0">Categories</word>
<word key="x_added_image_in" js="0">%s added an image in %s</word>
<word key="x_added_video_in" js="0">%s added a video in %s</word>
<word key="x_added_album_in" js="0">%s created an album in %s</word>
<word key="x_commented_photo" js="0">%s commented on a photo</word>
<word key="x_reviewed_photo" js="0">%s reviewed a photo</word>
@@ -767,5 +812,18 @@
<word key="images_without_data" js="1">You haven't added any information for {{count}}. Are you sure you want to continue?</word>
<word key="images_without_data_desc" js="1">You can edit the images later to update them.</word>
<word key="set_as_photo_confirm" js="1">Are you sure you want to update your profile photo?</word>
<word key="rebuildGalleryThumbnails" js="1">Rebuild existing thumbnails?</word>
<word key="rebuildGalleryThumbnailsBlurb" js="1">Rebuilding existing thumbnails is recommended when you upload a new watermark image or adjust your thumbnail dimensions</word>
<word key="rebuildGalleryThumbnailsYes" js="1">Yes</word>
<word key="rebuildGalleryThumbnailsNo" js="1">No</word>
<word key="uploader_add_images" js="1">Add Images</word>
<word key="confirmSubmitClose" js="1">Are you sure you want to exit? Any images you have uploaded won't be saved.</word>
<word key="addImages" js="1">Add Images</word>
<word key="createAlbum" js="1">Create Album</word>
<word key="existingAlbum" js="1">Choose Existing Album</word>
<word key="chooseCategory" js="1">Choose Category</word>
<word key="chooseAlbum" js="1">Choose Album</word>
<word key="imageUploadErrors" js="1">{# [1:image][?:images]} had errors</word>
<word key="imageUploadErrorsDesc" js="1">Correct the errors on the marked {!# [1:image][?:images]} and submit again.</word>
</app>
</language>
+132 -39
View File
@@ -561,6 +561,34 @@
"zerofill": false,
"auto_increment": false,
"binary": false
},
"album_submit_type": {
"name": "album_submit_type",
"type": "INT",
"length": 10,
"decimals": null,
"values": null,
"allow_null": false,
"default": "0",
"comment": "Submission type. 0=owner only, 1=anyone, 2=specific groups, 3=specific members, 4=club",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
},
"album_submit_access": {
"name": "album_submit_access",
"type": "TEXT",
"length": null,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
},
"indexes": {
@@ -622,7 +650,9 @@
"album_name"
]
}
}
},
"comment": "",
"reporting": "count"
},
"gallery_bandwidth": {
"name": "gallery_bandwidth",
@@ -1177,6 +1207,20 @@
"zerofill": false,
"auto_increment": false,
"binary": false
},
"category_sort_options_img": {
"name": "category_sort_options_img",
"type": "VARCHAR",
"length": 255,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
},
"indexes": {
@@ -1241,7 +1285,9 @@
"category_allow_albums": "1",
"category_show_rules": "0"
}
]
],
"comment": "",
"reporting": "count"
},
"gallery_comments": {
"name": "gallery_comments",
@@ -1464,7 +1510,9 @@
null
]
}
}
},
"comment": "",
"reporting": "count"
},
"gallery_images": {
"name": "gallery_images",
@@ -1567,20 +1615,6 @@
"values": [],
"zerofill": false
},
"image_medium_file_name": {
"allow_null": true,
"auto_increment": false,
"binary": false,
"comment": "",
"decimals": null,
"default": null,
"length": 0,
"name": "image_medium_file_name",
"type": "TEXT",
"unsigned": false,
"values": [],
"zerofill": false
},
"image_original_file_name": {
"allow_null": true,
"auto_increment": false,
@@ -1973,20 +2007,6 @@
"values": [],
"zerofill": false
},
"image_thumb_file_name": {
"allow_null": true,
"auto_increment": false,
"binary": false,
"comment": "",
"decimals": null,
"default": null,
"length": 0,
"name": "image_thumb_file_name",
"type": "TEXT",
"unsigned": false,
"values": [],
"zerofill": false
},
"image_updated": {
"allow_null": true,
"auto_increment": false,
@@ -2315,7 +2335,9 @@
}
},
"collation": "utf8mb4_unicode_ci",
"engine": "InnoDB"
"engine": "InnoDB",
"comment": "",
"reporting": "count"
},
"gallery_images_uploads": {
"name": "gallery_images_uploads",
@@ -2417,6 +2439,20 @@
"unsigned": true,
"values": [],
"zerofill": false
},
"upload_order": {
"name": "upload_order",
"type": "INT",
"length": 10,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": true,
"zerofill": false,
"auto_increment": false,
"binary": false
}
},
"indexes": {
@@ -2443,11 +2479,13 @@
"upload_session": {
"type": "key",
"name": "upload_session",
"length": [
null
],
"columns": [
"upload_session"
"upload_session",
"upload_order"
],
"length": [
null,
null
]
},
"upload_member_id": {
@@ -2676,6 +2714,20 @@
"zerofill": false,
"auto_increment": false,
"binary": false
},
"review_author_response": {
"name": "review_author_response",
"type": "MEDIUMTEXT",
"length": null,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
},
"indexes": {
@@ -2718,10 +2770,23 @@
"columns": [
"review_author"
]
},
"review_image": {
"type": "key",
"name": "review_image",
"columns": [
"review_image_id",
"review_approved"
],
"length": [
null,
null
]
}
},
"collation": "utf8mb4_unicode_ci",
"comment": ""
"comment": "",
"reporting": "count"
},
"gallery_album_comments": {
"name": "gallery_album_comments",
@@ -2946,7 +3011,9 @@
}
},
"collation": "utf8mb4_unicode_ci",
"engine": "InnoDB"
"engine": "InnoDB",
"comment": "",
"reporting": "count"
},
"gallery_album_reviews": {
"name": "gallery_album_reviews",
@@ -3160,6 +3227,20 @@
"unsigned": false,
"values": [],
"zerofill": false
},
"review_author_response": {
"name": "review_author_response",
"type": "MEDIUMTEXT",
"length": null,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
},
"indexes": {
@@ -3202,7 +3283,19 @@
"columns": [
"review_author"
]
},
"review_album_id": {
"type": "key",
"name": "review_album_id",
"columns": [
"review_album_id"
],
"length": [
null
]
}
}
},
"comment": "",
"reporting": "count"
}
}
+18 -17
View File
@@ -1,46 +1,47 @@
[
{
"key": "gallery_bandwidth_period",
"default": "-1"
"default": "-1",
"report": "full"
},
{
"key": "gallery_detailed_bandwidth",
"default": "1"
"default": "1",
"report": "full"
},
{
"key": "gallery_large_dims",
"default": "1600x1200"
},
{
"key": "gallery_medium_dims",
"default": "640x480"
"default": "1600x1200",
"report": "full"
},
{
"key": "gallery_rss_enabled",
"default": "1"
"default": "1",
"report": "full"
},
{
"key": "gallery_small_dims",
"default": "240x240"
},
{
"key": "gallery_thumb_dims",
"default": "100x100"
"default": "600x600",
"report": "full"
},
{
"key": "gallery_use_square_thumbnails",
"default": "1"
"default": "0",
"report": "full"
},
{
"key": "gallery_use_watermarks",
"default": "0"
"default": "0",
"report": "full"
},
{
"key": "gallery_watermark_images",
"default": "large,medium"
"default": "large",
"report": "full"
},
{
"key": "gallery_watermark_path",
"default": ""
"default": "",
"report": "none"
}
]
File diff suppressed because it is too large. Load diff
+52 -1
View File
@@ -197,5 +197,56 @@
"101117": "4.2.0 Beta 8",
"102000": "4.2.0",
"102001": "4.2.0",
"102002": "4.2.0"
"102002": "4.2.0",
"102003": "4.2.0",
"102004": "4.2.1",
"102005": "4.2.2 Beta 1",
"102006": "4.2.2 Beta 2",
"102007": "4.2.2",
"102008": "4.2.2",
"102009": "4.2.2",
"102010": "4.2.3 Beta 1",
"102011": "4.2.3",
"102012": "4.2.3",
"102013": "4.2.3",
"102014": "4.2.4 Beta 1",
"102015": "4.2.4 Beta 2",
"102016": "4.2.4",
"102017": "4.2.4",
"102018": "4.2.4",
"102019": "4.2.4",
"102020": "4.2.4",
"102021": "4.2.5 Beta 1",
"102022": "4.2.5 Beta 2",
"102023": "4.2.5 Beta 3",
"102024": "4.2.5",
"102025": "4.2.5",
"102026": "4.2.5",
"102027": "4.2.5",
"102028": "4.2.5",
"102029": "4.2.5",
"102030": "4.2.6 Beta 1",
"102031": "4.2.6 Beta 1",
"102032": "4.2.6 Beta 2",
"102033": "4.2.6 Beta 3",
"102034": "4.2.6",
"102035": "4.2.6",
"102036": "4.2.6",
"102037": "4.2.7 Beta 1",
"102038": "4.2.7 Beta 2",
"102039": "4.2.7 Beta 3",
"102040": "4.2.7 Beta 4",
"102041": "4.2.7 Beta 5",
"102042": "4.2.7",
"102043": "4.2.7",
"102044": "4.2.7",
"102045": "4.2.7",
"102046": "4.2.7",
"102047": "4.2.8",
"102048": "4.2.8",
"103000": "4.3.0 Beta 1",
"103002": "4.3.0 Beta 2",
"103003": "4.3.0 Beta 3",
"103004": "4.3.0 Beta 4",
"103005": "4.3.0"
}
@@ -0,0 +1,47 @@
<?php
/**
* @brief Advertisement locations extension
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 16 Jan 2018
*/
namespace IPS\gallery\extensions\core\AdvertisementLocations;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Advertisement locations extension
*/
class _ImageLightbox
{
/**
* Get the locations and the additional settings
*
* @param array $settings Current setting values
* @return array Array with two elements: 'locations' which should have keys as the location keys and values as the fields to toggle, and 'settings' which are additional fields to add to the form
*/
public function getSettings( $settings )
{
return array( 'locations' => array( 'ad_image_lightbox' => array() ), 'settings' => array() );
}
/**
* Return an array of setting values to store
*
* @param array $values Values from the form submission
* @return array Array of setting key => value to store
*/
public function parseSettings( $values )
{
return array();
}
}
@@ -68,6 +68,7 @@ class _Gallery
return new \IPS\Helpers\Table\Content( $className, $url, $where );
}
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_browse.js', 'gallery' ) );
\IPS\gallery\Application::outputCss();
$table = new \IPS\gallery\Image\Table( $className, $url, $where );
@@ -30,12 +30,19 @@ class _gallery
*/
public function getItems()
{
if ( \IPS\gallery\Category::canOnAny( 'add' ) )
if ( \IPS\gallery\Category::canOnAny( 'add', NULL, \IPS\Settings::i()->club_nodes_in_apps ? array() : array( array( 'category_club_id IS NULL' ) ) ) )
{
return array(
'gallery_image' => array(
'link' => \IPS\Http\Url::internal( "app=gallery&module=gallery&controller=submit&_new=1", 'front', 'gallery_submit' ),
'extraData' => array( 'data-ipsDialog-size' => "fullscreen", 'data-ipsDialog' => true )
'extraData' => array(
'data-ipsDialog-size' => "medium",
'data-ipsDialog' => 'true',
'data-ipsDialog-destructOnClose' => 'true',
'data-ipsDialog-close' => 'false',
'data-ipsDialog-extraClass' => 'cGalleryDialog_outer',
'data-ipsDialog-remoteSubmit' => 'true',
)
)
);
}
@@ -70,7 +70,7 @@ class _Images
foreach ( \IPS\Db::i()->select( '*', 'gallery_images', $where, 'image_date DESC', array( ( $page - 1 ) * $limit, $limit ) ) as $row )
{
$image = \IPS\gallery\Image::load( $row['image_id'] );
$obj = \IPS\File::get( 'gallery_Images', $row['image_masked_file_name'] );
$obj = \IPS\File::get( 'gallery_Images', $row['image_original_file_name'] );
$obj->contextInfo = $image->caption;
$return[ (string) $image->url() ] = $obj;
}
@@ -47,19 +47,6 @@ class _Images
$image = \IPS\Db::i()->select( '*', 'gallery_images', array(), 'image_id', array( $offset, 1 ) )->first();
$update = array();
if( $image['image_thumb_file_name'] )
{
try
{
$file = \IPS\File::get( $oldConfiguration ?: 'gallery_Images', $image['image_thumb_file_name'] )->move( $storageConfiguration );
$update['image_thumb_file_name'] = (string) $file;
}
catch( \Exception $e )
{
/* Any issues are logged */
}
}
if( $image['image_small_file_name'] )
{
try
@@ -93,26 +80,6 @@ class _Images
}
}
if( $image['image_medium_file_name'] )
{
if( $image['image_medium_file_name'] == $image['image_masked_file_name'] AND isset( $update['image_masked_file_name'] ) )
{
$update['image_medium_file_name'] = $update['image_masked_file_name'];
}
else
{
try
{
$file = \IPS\File::get( $oldConfiguration ?: 'gallery_Images', $image['image_medium_file_name'] )->move( $storageConfiguration );
$update['image_medium_file_name'] = (string) $file;
}
catch( \Exception $e )
{
/* Any issues are logged */
}
}
}
if( $image['image_original_file_name'] )
{
if( $image['image_original_file_name'] == $image['image_masked_file_name'] AND isset( $update['image_masked_file_name'] ) )
@@ -161,7 +128,7 @@ class _Images
$image = \IPS\Db::i()->select( '*', 'gallery_images', array(), 'image_id', array( $offset, 1 ) )->first();
$update = array();
foreach( array( 'image_thumb_file_name', 'image_small_file_name', 'image_masked_file_name', 'image_medium_file_name', 'image_original_file_name' ) as $key )
foreach( array( 'image_small_file_name', 'image_masked_file_name', 'image_original_file_name' ) as $key )
{
if( $image[ $key ] )
{
@@ -188,7 +155,7 @@ class _Images
{
try
{
$record = \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_masked_file_name=? OR image_medium_file_name=? OR image_original_file_name=? OR image_thumb_file_name=? OR image_small_file_name=?', (string) $file, (string) $file, (string) $file, (string) $file, (string) $file ) )->first();
$record = \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_masked_file_name=? OR image_original_file_name=? OR image_small_file_name=?', (string) $file, (string) $file, (string) $file ) )->first();
return TRUE;
}
@@ -207,49 +174,16 @@ class _Images
{
foreach( \IPS\Db::i()->select( '*', 'gallery_images' ) as $image )
{
if( $image['image_masked_file_name'] )
foreach( array( 'image_masked_file_name', 'image_original_file_name', 'image_small_file_name' ) as $size )
{
try
if( $image[ $size ] )
{
\IPS\File::get( 'gallery_Images', $image['image_masked_file_name'] )->delete();
try
{
\IPS\File::get( 'gallery_Images', $image[ $size ] )->delete();
}
catch( \Exception $e ){}
}
catch( \Exception $e ){}
}
if( $image['image_original_file_name'] )
{
try
{
\IPS\File::get( 'gallery_Images', $image['image_original_file_name'] )->delete();
}
catch( \Exception $e ){}
}
if( $image['image_medium_file_name'] )
{
try
{
\IPS\File::get( 'gallery_Images', $image['image_medium_file_name'] )->delete();
}
catch( \Exception $e ){}
}
if( $image['image_thumb_file_name'] )
{
try
{
\IPS\File::get( 'gallery_Images', $image['image_thumb_file_name'] )->delete();
}
catch( \Exception $e ){}
}
if( $image['image_small_file_name'] )
{
try
{
\IPS\File::get( 'gallery_Images', $image['image_small_file_name'] )->delete();
}
catch( \Exception $e ){}
}
}
}
@@ -37,7 +37,7 @@ class _Album
'exclude' => array(),
'lessIsMore' => array(),
'neg1IsBest' => array(),
'zeroIsBest' => array( 'g_album_limit' ),
'zeroIsBest' => array( 'g_album_limit', 'g_img_album_limit', 'g_max_upload', 'g_max_transfer', 'g_max_views' ),
);
}
}
@@ -0,0 +1,85 @@
<?php
/**
* @brief Member ACP Profile - Content Statistics Tab: Gallery
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 30 Nov 2017
*/
namespace IPS\gallery\extensions\core\MemberACPProfileContentTab;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* @brief Member ACP Profile - Content Statistics Tab: Gallery
*/
class _Gallery extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$imageCount = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images', array( 'image_member_id=?', $this->member->member_id ) )->first();
$diskspaceUsed = \IPS\Db::i()->select( 'SUM(image_file_size)', 'gallery_images', array( 'image_member_id=?', $this->member->member_id ) )->first();
$numberOfViews = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_bandwidth', array( 'member_id=?', $this->member->member_id ) )->first();
$bandwidthUsed = \IPS\Db::i()->select( 'SUM(bsize)', 'gallery_bandwidth', array( 'member_id=?', $this->member->member_id ) )->first();
$allImages = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images' )->first();
$totalFilesize = \IPS\Db::i()->select( 'SUM(image_file_size)', 'gallery_images' )->first();
$allBandwidth = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_bandwidth' )->first();
$totalBandwidth = \IPS\Db::i()->select( 'SUM(bsize)', 'gallery_bandwidth' )->first();
return \IPS\Theme::i()->getTemplate( 'stats', 'gallery' )->information( $this->member, \IPS\Theme::i()->getTemplate( 'global', 'core' )->definitionTable( array(
'images_submitted' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Member::loggedIn()->language()->formatNumber( $imageCount ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $allImages ? ( 100 / $allImages ) : 0 ) * $imageCount ), 2 ) ) )
),
'gdiskspace_used' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Output\Plugin\Filesize::humanReadableFilesize( $diskspaceUsed ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $totalFilesize ? ( 100 / $totalFilesize ) : 0 ) * $diskspaceUsed ), 2 ) ) )
),
'gaverage_filesize' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_average' , FALSE, array( 'sprintf' => array(
\IPS\Output\Plugin\Filesize::humanReadableFilesize( \IPS\Db::i()->select( 'AVG(image_file_size)', 'gallery_images', array( 'image_member_id=?', $this->member->member_id ) )->first() ),
\IPS\Output\Plugin\Filesize::humanReadableFilesize( \IPS\Db::i()->select( 'AVG(image_file_size)', 'gallery_images' )->first() ) ) )
),
'number_of_views' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Member::loggedIn()->language()->formatNumber( $numberOfViews ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $allBandwidth ? ( 100 / $allBandwidth ) : 0 ) * $imageCount ), 2 ) ))
),
'gallery_bandwidth_used' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total_link', FALSE, array( 'htmlsprintf' => array(
\IPS\Output\Plugin\Filesize::humanReadableFilesize( $bandwidthUsed ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $totalBandwidth ? ( 100 / $totalBandwidth ) : 0 ) * $bandwidthUsed ), 2 ),
\IPS\Theme::i()->getTemplate( 'stats', 'gallery' )->bandwidthButton( $this->member ) ) )
)
) ) );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
$bandwidthChart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=editBlock&block=IPS\\gallery\\extensions\\core\\MemberACPProfileContentTab\\Gallery&id={$this->member->member_id}&chart=downloads&_graph=1" ), 'gallery_bandwidth', 'bdate', '', array( 'vAxis' => array( 'title' => \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ) ) ), 'LineChart', 'daily' );
$bandwidthChart->groupBy = 'bdate';
$bandwidthChart->where[] = array( 'member_id=?', $this->member->member_id );
$bandwidthChart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('bandwidth_use'), 'number', 'ROUND((SUM(bsize)/1024),2)', FALSE );
return ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->_graph ) ) ? (string) $bandwidthChart : \IPS\Theme::i()->getTemplate( 'stats', 'gallery' )->graphs( (string) $bandwidthChart );
}
}
@@ -1,52 +1,3 @@
<?php
/**
* @brief Admin CP Member Form
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 07 Mar 2014
*/
namespace IPS\gallery\extensions\core\MemberForm;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Admin CP Member Form
*/
class _Images
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
$form->addMessage( "<a href='" . \IPS\Http\Url::internal( 'app=gallery&module=stats&controller=member&do=images&id=' . $member->member_id ) . "'>" . \IPS\Member::loggedIn()->language()->addToStack('gallery_stats') . '</a>', '', FALSE );
$form->add( new \IPS\Helpers\Form\YesNo( 'remove_gallery_access', !$member->members_bitoptions['remove_gallery_access'], NULL, array( 'togglesOn' => array( 'remove_gallery_upload' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'remove_gallery_upload', !$member->members_bitoptions['remove_gallery_upload'], NULL, array(), NULL, NULL, NULL, 'remove_gallery_upload' ) );
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
$member->members_bitoptions['remove_gallery_access'] = !$values['remove_gallery_access'];
$member->members_bitoptions['remove_gallery_upload'] = !$values['remove_gallery_upload'];
}
}
// Removed
@@ -0,0 +1,103 @@
<?php
/**
* @brief Member Restrictions: Gallery
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 30 Nov 2017
*/
namespace IPS\gallery\extensions\core\MemberRestrictions;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* @brief Member Restrictions: Gallery
*/
class _Gallery extends \IPS\core\MemberACPProfile\Restriction
{
/**
* Modify Edit Restrictions form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( \IPS\Helpers\Form $form )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'remove_gallery_access', !$this->member->members_bitoptions['remove_gallery_access'], FALSE, array( 'togglesOn' => array( 'remove_gallery_upload' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'remove_gallery_upload', !$this->member->members_bitoptions['remove_gallery_upload'], FALSE, array(), NULL, NULL, NULL, 'remove_gallery_upload' ) );
}
/**
* Save Form
*
* @param array $values Values from form
* @return array
*/
public function save( $values )
{
$return = array();
if ( $this->member->members_bitoptions['remove_gallery_access'] == $values['remove_gallery_access'] )
{
$return['remove_gallery_access'] = array( 'old' => $this->member->members_bitoptions['remove_gallery_access'], 'new' => !$values['remove_gallery_access'] );
$this->member->members_bitoptions['remove_gallery_access'] = !$values['remove_gallery_access'];
}
if ( $this->member->members_bitoptions['remove_gallery_upload'] == $values['remove_gallery_upload'] )
{
$return['remove_gallery_upload'] = array( 'old' => $this->member->members_bitoptions['remove_gallery_upload'], 'new' => !$values['remove_gallery_upload'] );
$this->member->members_bitoptions['remove_gallery_upload'] = !$values['remove_gallery_upload'];
}
return $return;
}
/**
* What restrictions are active on the account?
*
* @return array
*/
public function activeRestrictions()
{
$return = array();
if ( $this->member->members_bitoptions['remove_gallery_access'] )
{
$return[] = 'restriction_no_gallery_access';
}
elseif ( $this->member->members_bitoptions['remove_gallery_upload'] )
{
$return[] = 'restriction_no_gallery_uploads';
}
return $return;
}
/**
* Get details of a change to show on history
*
* @param array $changes Changes as set in save()
* @return array
*/
public static function changesForHistory( $changes )
{
$return = array();
foreach ( array( 'remove_gallery_access', 'remove_gallery_upload' ) as $k )
{
if ( isset( $changes[ $k ] ) )
{
$return[] = \IPS\Member::loggedIn()->language()->addToStack( 'history_restrictions_' . $k . '_' . intval( $changes[ $k ]['new'] ) );
}
}
return $return;
}
}
@@ -0,0 +1,42 @@
<?php
/**
* @brief Template Plugin - Gallery: Background image formatting
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Content
* @since 12 March 2014
*/
namespace IPS\gallery\extensions\core\OutputPlugins;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Template Plugin - Gallery: Background image formatting
*/
class _Backgroundimage
{
/**
* @brief Can be used when compiling CSS
*/
public static $canBeUsedInCss = TRUE;
/**
* Run the plug-in
*
* @param string $data The initial data from the tag
* @param array $options Array of options
* @return string Code to eval
*/
public static function runPlugin( $data, $options )
{
return "str_replace( array( '(', ')' ), array( '\(', '\)' ), " . $data . " );";
}
}
@@ -0,0 +1,159 @@
<?php
/**
* @brief Background Task
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 06 Mar 2018
*/
namespace IPS\gallery\extensions\core\Queue;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Background Task
*/
class _CleanUpGalleryImages
{
/**
* Parse data before queuing
*
* @param array $data
* @return array
*/
public function preQueueData( $data )
{
try
{
$data['count'] = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images' )->first();
}
catch( \Exception $ex )
{
throw new \OutOfRangeException;
}
if( $data['count'] == 0 )
{
return null;
}
$data['did'] = 0;
return $data;
}
/**
* Run Background Task
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( $data, $offset )
{
if ( !\IPS\Application::appIsEnabled( 'gallery' ) )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
$last = NULL;
foreach( \IPS\Db::i()->select( '*', 'gallery_images', NULL, 'image_id ASC', array( $offset, 100 ) ) as $image )
{
$data['did']++;
/* Update the sizes array to remove the images we no longer support */
$sizes = json_decode( $image['image_data'], true );
if( isset( $sizes['thumb'] ) )
{
unset( $sizes['thumb'] );
}
if( isset( $sizes['medium'] ) )
{
unset( $sizes['medium'] );
}
$update = array( 'image_data' => json_encode( $sizes ) );
/* If we have a medium but no large image, or a thumb but no small image, adapt as appropriate */
$keepMedium = FALSE;
if( !$image['image_masked_file_name'] AND $image['image_medium_file_name'] )
{
$image['image_masked_file_name'] = $image['image_medium_file_name'];
$update['image_masked_file_name'] = $image['image_masked_file_name'];
$keepMedium = TRUE;
}
$keepThumb = FALSE;
if( !$image['image_small_file_name'] AND $image['image_thumb_file_name'] )
{
$image['image_small_file_name'] = $image['image_thumb_file_name'];
$update['image_small_file_name'] = $image['image_small_file_name'];
$keepThumb = TRUE;
}
\IPS\Db::i()->update( 'gallery_images', $update, array( 'image_id=?', $image['image_id'] ) );
/* Delete the images we don't need now */
if( !$keepMedium AND $image['image_medium_file_name'] )
{
try
{
\IPS\File::get( 'gallery_Images', $image['image_medium_file_name'] )->delete();
}
catch( \Exception $e ) {}
}
if( !$keepThumb AND $image['image_thumb_file_name'] )
{
try
{
\IPS\File::get( 'gallery_Images', $image['image_thumb_file_name'] )->delete();
}
catch( \Exception $e ) {}
}
$last = $image['image_id'];
}
if ( $last === NULL )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
return $last;
}
/**
* Get Progress
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function getProgress( $data, $offset )
{
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('cleaning_up_gallery_images'), 'complete' => $data['count'] ? ( round( 100 / $data['count'] * $data['did'], 2 ) ) : 100 );
}
/**
* Perform post-completion processing
*
* @param array $data
* @return void
*/
public function postComplete( $data )
{
\IPS\Db::i()->query( "ALTER TABLE `" . \IPS\Db::i()->prefix . "gallery_images` DROP COLUMN image_thumb_file_name, DROP COLUMN image_medium_file_name" );
}
}
@@ -0,0 +1,112 @@
<?php
/**
* @brief Background Task
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 04 Dec 2017
*/
namespace IPS\gallery\extensions\core\Queue;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Background Task
*/
class _RebuildGalleryImages
{
/**
* @brief Number of content items to rebuild per cycle
*/
public $rebuild = 20;
/**
* Parse data before queuing
*
* @param array $data
* @return array
*/
public function preQueueData( $data )
{
try
{
$data['count'] = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images', array( 'image_media=?', 0 ) )->first();
}
catch( \Exception $ex )
{
throw new \OutOfRangeException;
}
if( $data['count'] == 0 )
{
return null;
}
$data['indexed'] = 0;
return $data;
}
/**
* Run Background Task
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return int New offset
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function run( &$data, $offset )
{
if ( !\IPS\Application::appIsEnabled( 'gallery' ) )
{
throw new \IPS\Task\Queue\OutOfRangeException;
}
$iterator = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'gallery_images', array( array( 'image_media=? AND image_id>?', 0, $offset ) ), 'image_id ASC', array( 0, $this->rebuild ) ), 'IPS\\gallery\\Image' );
$last = NULL;
foreach( $iterator as $image )
{
try
{
$image->deleteThumbnails();
$image->buildThumbnails();
$image->save();
}
catch ( \Exception $e ) {}
$last = $image->id;
$data['indexed']++;
}
if( $last === NULL )
{
\IPS\Session::i()->log( 'acplogs__gallery_rebuilt' );
throw new \IPS\Task\Queue\OutOfRangeException;
}
return $last;
}
/**
* Get Progress
*
* @param mixed $data Data as it was passed to \IPS\Task::queue()
* @param int $offset Offset
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
*/
public function getProgress( $data, $offset )
{
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack('rebuilding_gallery_images' ), 'complete' => $data['count'] ? ( round( 100 / $data['count'] * $data['indexed'], 2 ) ) : 100 );
}
}
@@ -0,0 +1,163 @@
<?php
/**
* @brief Support Images in sitemaps
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 09 Feb 2018
*/
namespace IPS\gallery\extensions\core\Sitemap;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Support Images in sitemaps
*/
class _Images
{
/**
* @brief Recommended Settings
*/
public $recommendedSettings = array();
/**
* Add settings for ACP configuration to the form
*
* @return array
*/
public function settings()
{
return array();
}
/**
* Get the sitemap filename(s)
*
* @return array
*/
public function getFilenames()
{
$count = \IPS\gallery\Image::getItemsWithPermission( array( 'image_media=?', 0 ), NULL, 10, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, FALSE, FALSE, FALSE, TRUE );
$files = array();
$count = ceil( $count / \IPS\Sitemap::MAX_PER_FILE );
for( $i=1; $i <= $count; $i++ )
{
$files[] = 'sitemap_imagesitemap_' . $i;
}
return $files;
}
/**
* Generate the sitemap
*
* @param string $filename The sitemap file to build (should be one returned from getFilenames())
* @param \IPS\Sitemap $sitemap Sitemap object reference
* @return void
*/
public function generateSitemap( $filename, $sitemap )
{
$entries = array();
$lastId = 0;
$settings = \IPS\Settings::i()->sitemap_content_settings ? json_decode( \IPS\Settings::i()->sitemap_content_settings, TRUE ) : array();
$class = 'IPS\\gallery\\Image';
$exploded = explode( '_', $filename );
$block = (int) array_pop( $exploded );
$totalLimit = isset( $settings["sitemap_{$class::$title}_count"] ) AND $settings["sitemap_{$class::$title}_count"] ? $settings["sitemap_{$class::$title}_count"] : \IPS\core\extensions\core\Sitemap\Content::RECOMMENDED_ITEM_LIMIT;
$offset = ( $block - 1 ) * \IPS\Sitemap::MAX_PER_FILE;
$limit = \IPS\Sitemap::MAX_PER_FILE;
if ( $totalLimit > -1 and ( $offset + $limit ) > $totalLimit )
{
$limit = $totalLimit - $offset;
}
/* Create limit clause */
$limitClause = array( $offset, $limit );
$where = $class::sitemapWhere();
$where[] = array( 'image_media=?', 0 );
/* Try to fetch the highest ID built in the last sitemap, if it exists */
try
{
$lastId = \IPS\Db::i()->select( 'last_id', 'core_sitemap', array( array( 'sitemap=?', implode( '_', $exploded ) . '_' . ( $block - 1 ) ) ) )->first();
if( $lastId > 0 )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId . ' > ?', $lastId );
$limitClause = $limit;
}
}
catch( \UnderflowException $e ){}
$idColumn = $class::$databaseColumnId;
foreach ( $class::getItemsWithPermission( $where, $class::$databasePrefix . $class::$databaseColumnId .' ASC', $limitClause, 'read', \IPS\Content\Hideable::FILTER_PUBLIC_ONLY, \IPS\Content\Item::SELECT_IDS_FIRST, new \IPS\Member, TRUE ) as $item )
{
if( !$item->canView( new \IPS\Member ) )
{
continue;
}
$data = array( 'url' => $item->url() );
$lastMod = NULL;
if ( isset( $item::$databaseColumnMap['last_comment'] ) )
{
$lastCommentField = $item::$databaseColumnMap['last_comment'];
if ( is_array( $lastCommentField ) )
{
foreach ( $lastCommentField as $column )
{
$lastMod = \IPS\DateTime::ts( $item->$column );
}
}
else
{
$lastMod = \IPS\DateTime::ts( $item->$lastCommentField );
}
}
if ( $lastMod )
{
$data['lastmod'] = $lastMod;
}
/* Image sitemap data */
$data['image:image'] = array(
'image:loc' => (string) \IPS\File::get( 'gallery_Images', $item->masked_file_name )->url,
'image:caption' => $item->mapped('title'),
'image:title' => $item->mapped('title')
);
if( \IPS\GeoLocation::enabled() AND $item->gps_show )
{
$data['image:image']['image:geo_location'] = $item->loc_short;
}
$priority = ( $item->sitemapPriority() ?: ( intval( isset( $settings["sitemap_{$class::$title}_priority"] ) ? $settings["sitemap_{$class::$title}_priority"] : \IPS\core\extensions\core\Sitemap\Content::RECOMMENDED_ITEM_PRIORITY ) ) );
if ( $priority !== -1 )
{
$data['priority'] = $priority;
$entries[] = $data;
}
$lastId = $item->$idColumn;
}
$sitemap->buildSitemapFile( $filename, $entries, $lastId, array( 'image' => 'http://www.google.com/schemas/sitemap-image/1.1' ) );
return $lastId;
}
}
@@ -0,0 +1,169 @@
<?php
/**
* @brief Support Videos in sitemaps
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 09 Feb 2018
*/
namespace IPS\gallery\extensions\core\Sitemap;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Support Videos in sitemaps
*/
class _Videos
{
/**
* @brief Recommended Settings
*/
public $recommendedSettings = array();
/**
* Add settings for ACP configuration to the form
*
* @return array
*/
public function settings()
{
return array();
}
/**
* Get the sitemap filename(s)
*
* @return array
*/
public function getFilenames()
{
$count = \IPS\gallery\Image::getItemsWithPermission( array( 'image_media=?', 1 ), NULL, 10, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, FALSE, FALSE, FALSE, TRUE );
$files = array();
$count = ceil( $count / \IPS\Sitemap::MAX_PER_FILE );
for( $i=1; $i <= $count; $i++ )
{
$files[] = 'sitemap_videositemap_' . $i;
}
return $files;
}
/**
* Generate the sitemap
*
* @param string $filename The sitemap file to build (should be one returned from getFilenames())
* @param \IPS\Sitemap $sitemap Sitemap object reference
* @return void
*/
public function generateSitemap( $filename, $sitemap )
{
$entries = array();
$lastId = 0;
$settings = \IPS\Settings::i()->sitemap_content_settings ? json_decode( \IPS\Settings::i()->sitemap_content_settings, TRUE ) : array();
$class = 'IPS\\gallery\\Image';
$exploded = explode( '_', $filename );
$block = (int) array_pop( $exploded );
$totalLimit = isset( $settings["sitemap_{$class::$title}_count"] ) AND $settings["sitemap_{$class::$title}_count"] ? $settings["sitemap_{$class::$title}_count"] : \IPS\core\extensions\core\Sitemap\Content::RECOMMENDED_ITEM_LIMIT;
$offset = ( $block - 1 ) * \IPS\Sitemap::MAX_PER_FILE;
$limit = \IPS\Sitemap::MAX_PER_FILE;
if ( $totalLimit > -1 and ( $offset + $limit ) > $totalLimit )
{
$limit = $totalLimit - $offset;
}
/* Create limit clause */
$limitClause = array( $offset, $limit );
$where = $class::sitemapWhere();
$where[] = array( 'image_media=?', 1 );
/* Try to fetch the highest ID built in the last sitemap, if it exists */
try
{
$lastId = \IPS\Db::i()->select( 'last_id', 'core_sitemap', array( array( 'sitemap=?', implode( '_', $exploded ) . '_' . ( $block - 1 ) ) ) )->first();
if( $lastId > 0 )
{
$where[] = array( $class::$databaseTable . '.' . $class::$databasePrefix . $class::$databaseColumnId . ' > ?', $lastId );
$limitClause = $limit;
}
}
catch( \UnderflowException $e ){}
$idColumn = $class::$databaseColumnId;
foreach ( $class::getItemsWithPermission( $where, $class::$databasePrefix . $class::$databaseColumnId .' ASC', $limitClause, 'read', \IPS\Content\Hideable::FILTER_PUBLIC_ONLY, \IPS\Content\Item::SELECT_IDS_FIRST, new \IPS\Member, TRUE ) as $item )
{
if( !$item->canView( new \IPS\Member ) )
{
continue;
}
$data = array( 'url' => $item->url() );
$lastMod = NULL;
if ( isset( $item::$databaseColumnMap['last_comment'] ) )
{
$lastCommentField = $item::$databaseColumnMap['last_comment'];
if ( is_array( $lastCommentField ) )
{
foreach ( $lastCommentField as $column )
{
$lastMod = \IPS\DateTime::ts( $item->$column );
}
}
else
{
$lastMod = \IPS\DateTime::ts( $item->$lastCommentField );
}
}
if ( $lastMod )
{
$data['lastmod'] = $lastMod;
}
/* Video sitemap data */
$data['video:video'] = array(
'video:content_loc' => (string) \IPS\File::get( 'gallery_Images', $item->original_file_name )->url,
'video:description' => $item->mapped('content'),
'video:title' => $item->mapped('title'),
'video:rating' => $item->mapped('rating_average'),
'video:view_count' => $item->mapped('views'),
'video:publication_date' => \IPS\DateTime::ts( $item->date )->rfc3339(),
'video:category' => \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( $item->container()->_titleLanguageKey ),
'video:gallery_loc' => array( 0 => (string) $item->directContainer()->url(), 'title' => $item->directContainer()->_title ),
'video:uploader' => array( 0 => $item->author()->name, 'info' => (string) $item->author()->url() ),
);
if( $item->masked_file_name )
{
$data['video:video']['video:thumbnail_loc'] = (string) \IPS\File::get( 'gallery_Images', $item->masked_file_name )->url;
}
$priority = ( $item->sitemapPriority() ?: ( intval( isset( $settings["sitemap_{$class::$title}_priority"] ) ? $settings["sitemap_{$class::$title}_priority"] : \IPS\core\extensions\core\Sitemap\Content::RECOMMENDED_ITEM_PRIORITY ) ) );
if ( $priority !== -1 )
{
$data['priority'] = $priority;
$entries[] = $data;
}
$lastId = $item->$idColumn;
}
$sitemap->buildSitemapFile( $filename, $entries, $lastId, array( 'video' => 'http://www.google.com/schemas/sitemap-video/1.1' ) );
return $lastId;
}
}
@@ -9,8 +9,9 @@
* @since 30 May 2013
*/
define('REPORT_EXCEPTIONS', TRUE);
require_once str_replace( 'applications/gallery/interface/legacy/image.php', '', str_replace( '\\', '/', __FILE__ ) ) . 'init.php';
\IPS\Session\External::i();
\IPS\Dispatcher\External::i();
try
{
@@ -1,102 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<font id="VideoJS" horiz-adv-x="1792">
<font-face font-family="VideoJS"
units-per-em="1792" ascent="1792"
descent="0" />
<missing-glyph horiz-adv-x="0" />
<glyph glyph-name="play"
unicode="&#xF101;"
horiz-adv-x="1792" d=" M597.3333333333334 1418.6666666666665V373.3333333333333L1418.6666666666667 896z" />
<glyph glyph-name="play-circle"
unicode="&#xF102;"
horiz-adv-x="1792" d=" M746.6666666666667 560L1194.6666666666667 896L746.6666666666667 1232V560zM896 1642.6666666666667C483.4666666666667 1642.6666666666667 149.3333333333334 1308.5333333333333 149.3333333333334 896S483.4666666666667 149.3333333333333 896 149.3333333333333S1642.6666666666667 483.4666666666667 1642.6666666666667 896S1308.5333333333333 1642.6666666666667 896 1642.6666666666667zM896 298.6666666666665C566.72 298.6666666666665 298.6666666666667 566.7199999999998 298.6666666666667 896S566.72 1493.3333333333333 896 1493.3333333333333S1493.3333333333335 1225.28 1493.3333333333335 896S1225.2800000000002 298.6666666666665 896 298.6666666666665z" />
<glyph glyph-name="pause"
unicode="&#xF103;"
horiz-adv-x="1792" d=" M448 373.3333333333333H746.6666666666667V1418.6666666666665H448V373.3333333333333zM1045.3333333333335 1418.6666666666665V373.3333333333333H1344V1418.6666666666665H1045.3333333333335z" />
<glyph glyph-name="volume-mute"
unicode="&#xF104;"
horiz-adv-x="1792" d=" M1232 896C1232 1027.7866666666666 1155.8400000000001 1141.6533333333332 1045.3333333333335 1196.5333333333333V1031.52L1228.6399999999999 848.2133333333334C1230.88 863.8933333333334 1232 879.9466666666667 1232 896.0000000000001zM1418.6666666666667 896C1418.6666666666667 825.8133333333333 1403.3600000000001 759.7333333333333 1378.3466666666668 698.8799999999999L1491.466666666667 585.7599999999998C1540 678.72 1568 783.9999999999999 1568 896C1568 1215.5733333333333 1344.3733333333334 1482.88 1045.3333333333335 1550.8266666666666V1396.6399999999999C1261.1200000000001 1332.4266666666667 1418.6666666666667 1132.6933333333332 1418.6666666666667 896zM319.2000000000001 1568L224 1472.8L576.8 1120H224V672H522.6666666666667L896 298.6666666666665V800.8L1213.7066666666667 483.0933333333332C1163.68 444.6399999999999 1107.3066666666666 413.6533333333332 1045.3333333333335 394.9866666666665V240.7999999999998C1148 264.32 1241.7066666666667 311.3599999999997 1320.48 375.9466666666663L1472.8000000000002 224L1568 319.1999999999998L896 991.1999999999998L319.2000000000001 1568zM896 1493.3333333333333L739.9466666666667 1337.28L896 1181.2266666666667V1493.3333333333333z" />
<glyph glyph-name="volume-low"
unicode="&#xF105;"
horiz-adv-x="1792" d=" M522.6666666666667 1120V672H821.3333333333334L1194.6666666666667 298.6666666666665V1493.3333333333333L821.3333333333334 1120H522.6666666666667z" />
<glyph glyph-name="volume-mid"
unicode="&#xF106;"
horiz-adv-x="1792" d=" M1381.3333333333335 896C1381.3333333333335 1027.7866666666666 1305.1733333333334 1141.6533333333332 1194.6666666666667 1196.5333333333333V595.0933333333332C1305.1733333333334 650.3466666666666 1381.3333333333335 764.2133333333331 1381.3333333333335 896zM373.3333333333334 1120V672H672L1045.3333333333335 298.6666666666665V1493.3333333333333L672 1120H373.3333333333334z" />
<glyph glyph-name="volume-high"
unicode="&#xF107;"
horiz-adv-x="1792" d=" M224 1120V672H522.6666666666667L896 298.6666666666665V1493.3333333333333L522.6666666666667 1120H224zM1232 896C1232 1027.7866666666666 1155.8400000000001 1141.6533333333332 1045.3333333333335 1196.5333333333333V595.0933333333332C1155.8400000000001 650.3466666666666 1232 764.2133333333331 1232 896zM1045.3333333333335 1550.8266666666666V1396.6399999999999C1261.1200000000001 1332.4266666666667 1418.6666666666667 1132.6933333333332 1418.6666666666667 896S1261.1200000000001 459.5733333333333 1045.3333333333335 395.3600000000002V241.1733333333332C1344.3733333333334 309.1199999999999 1568 576.0533333333333 1568 896S1344.3733333333334 1482.88 1045.3333333333335 1550.8266666666666z" />
<glyph glyph-name="fullscreen-enter"
unicode="&#xF108;"
horiz-adv-x="1792" d=" M522.6666666666667 746.6666666666665H373.3333333333334V373.3333333333333H746.6666666666667V522.6666666666665H522.6666666666667V746.6666666666665zM373.3333333333334 1045.3333333333333H522.6666666666667V1269.3333333333333H746.6666666666667V1418.6666666666665H373.3333333333334V1045.3333333333333zM1269.3333333333335 522.6666666666665H1045.3333333333335V373.3333333333333H1418.6666666666667V746.6666666666665H1269.3333333333335V522.6666666666665zM1045.3333333333335 1418.6666666666665V1269.3333333333333H1269.3333333333335V1045.3333333333333H1418.6666666666667V1418.6666666666665H1045.3333333333335z" />
<glyph glyph-name="fullscreen-exit"
unicode="&#xF109;"
horiz-adv-x="1792" d=" M373.3333333333334 597.3333333333333H597.3333333333334V373.3333333333333H746.6666666666667V746.6666666666665H373.3333333333334V597.3333333333333zM597.3333333333334 1194.6666666666665H373.3333333333334V1045.3333333333333H746.6666666666667V1418.6666666666665H597.3333333333334V1194.6666666666665zM1045.3333333333335 373.3333333333333H1194.6666666666667V597.3333333333333H1418.6666666666667V746.6666666666665H1045.3333333333335V373.3333333333333zM1194.6666666666667 1194.6666666666665V1418.6666666666665H1045.3333333333335V1045.3333333333333H1418.6666666666667V1194.6666666666665H1194.6666666666667z" />
<glyph glyph-name="square"
unicode="&#xF10A;"
horiz-adv-x="1792" d=" M1344 1493.3333333333333H448C365.4933333333334 1493.3333333333333 298.6666666666667 1426.5066666666667 298.6666666666667 1344V448C298.6666666666667 365.4933333333331 365.4933333333334 298.6666666666665 448 298.6666666666665H1344C1426.506666666667 298.6666666666665 1493.3333333333335 365.4933333333331 1493.3333333333335 448V1344C1493.3333333333335 1426.5066666666667 1426.506666666667 1493.3333333333333 1344 1493.3333333333333zM1344 448H448V1344H1344V448z" />
<glyph glyph-name="spinner"
unicode="&#xF10B;"
horiz-adv-x="1792" d=" M701.8666666666668 1008L1057.6533333333334 1624.3733333333334C1005.7600000000002 1635.9466666666667 951.6266666666666 1642.6666666666667 896 1642.6666666666667C716.8000000000001 1642.6666666666667 552.9066666666668 1579.5733333333333 424.1066666666667 1474.2933333333333L697.76 1000.5333333333334L701.8666666666666 1008zM1608.32 1120C1539.6266666666666 1338.4 1373.1200000000001 1512.7466666666667 1160.6933333333332 1593.3866666666668L887.4133333333334 1120H1608.32zM1627.7333333333336 1045.3333333333333H1068.48L1090.1333333333334 1008L1445.92 392C1567.6266666666668 524.9066666666668 1642.6666666666667 701.4933333333333 1642.6666666666667 896C1642.6666666666667 947.1466666666666 1637.44 997.1733333333332 1627.7333333333336 1045.3333333333333zM637.2800000000001 896L346.08 1400C224.3733333333333 1267.0933333333332 149.3333333333334 1090.5066666666667 149.3333333333334 896C149.3333333333334 844.8533333333332 154.56 794.8266666666666 164.2666666666667 746.6666666666665H723.5200000000001L637.2800000000002 896zM183.68 672C252.3733333333334 453.5999999999999 418.88 279.2533333333334 631.3066666666667 198.6133333333332L904.5866666666668 672H183.68zM1025.1733333333334 672L733.9733333333334 167.6266666666666C786.24 156.0533333333333 840.3733333333334 149.3333333333333 896 149.3333333333333C1075.2 149.3333333333333 1239.0933333333332 212.4266666666665 1367.8933333333334 317.7066666666665L1094.24 791.4666666666666L1025.1733333333334 672z" />
<glyph glyph-name="subtitles"
unicode="&#xF10C;"
horiz-adv-x="1792" d=" M1493.3333333333335 1493.3333333333333H298.6666666666667C216.16 1493.3333333333333 149.3333333333334 1426.5066666666667 149.3333333333334 1344V448C149.3333333333334 365.4933333333331 216.16 298.6666666666665 298.6666666666667 298.6666666666665H1493.3333333333335C1575.8400000000001 298.6666666666665 1642.6666666666667 365.4933333333331 1642.6666666666667 448V1344C1642.6666666666667 1426.5066666666667 1575.8400000000001 1493.3333333333333 1493.3333333333335 1493.3333333333333zM298.6666666666667 896H597.3333333333334V746.6666666666665H298.6666666666667V896zM1045.3333333333335 448H298.6666666666667V597.3333333333333H1045.3333333333335V448zM1493.3333333333335 448H1194.6666666666667V597.3333333333333H1493.3333333333335V448zM1493.3333333333335 746.6666666666665H746.6666666666667V896H1493.3333333333335V746.6666666666665z" />
<glyph glyph-name="captions"
unicode="&#xF10D;"
horiz-adv-x="1792" d=" M1418.6666666666667 1493.3333333333333H373.3333333333334C290.8266666666667 1493.3333333333333 224 1426.5066666666667 224 1344V448C224 365.4933333333331 290.8266666666667 298.6666666666665 373.3333333333334 298.6666666666665H1418.6666666666667C1501.1733333333334 298.6666666666665 1568 365.4933333333331 1568 448V1344C1568 1426.5066666666667 1501.1733333333334 1493.3333333333333 1418.6666666666667 1493.3333333333333zM821.3333333333334 970.6666666666666H709.3333333333334V1008H560V783.9999999999999H709.3333333333334V821.3333333333333H821.3333333333334V746.6666666666665C821.3333333333334 705.5999999999999 788.1066666666667 672 746.6666666666667 672H522.6666666666667C481.2266666666667 672 448 705.5999999999999 448 746.6666666666665V1045.3333333333333C448 1086.4 481.2266666666667 1120 522.6666666666667 1120H746.6666666666667C788.1066666666667 1120 821.3333333333334 1086.4 821.3333333333334 1045.3333333333333V970.6666666666666zM1344 970.6666666666666H1232V1008H1082.6666666666667V783.9999999999999H1232V821.3333333333333H1344V746.6666666666665C1344 705.5999999999999 1310.7733333333333 672 1269.3333333333335 672H1045.3333333333335C1003.8933333333334 672 970.6666666666669 705.5999999999999 970.6666666666669 746.6666666666665V1045.3333333333333C970.6666666666669 1086.4 1003.8933333333334 1120 1045.3333333333335 1120H1269.3333333333335C1310.7733333333333 1120 1344 1086.4 1344 1045.3333333333333V970.6666666666666z" />
<glyph glyph-name="chapters"
unicode="&#xF10E;"
horiz-adv-x="1792" d=" M224 821.3333333333333H373.3333333333334V970.6666666666666H224V821.3333333333333zM224 522.6666666666665H373.3333333333334V672H224V522.6666666666665zM224 1120H373.3333333333334V1269.3333333333333H224V1120zM522.6666666666667 821.3333333333333H1568V970.6666666666666H522.6666666666667V821.3333333333333zM522.6666666666667 522.6666666666665H1568V672H522.6666666666667V522.6666666666665zM522.6666666666667 1269.3333333333333V1120H1568V1269.3333333333333H522.6666666666667z" />
<glyph glyph-name="share"
unicode="&#xF10F;"
horiz-adv-x="1792" d=" M1344 590.9866666666665C1287.2533333333333 590.9866666666665 1236.1066666666668 568.9599999999998 1197.2800000000002 533.4933333333331L665.2800000000001 843.7333333333333C669.3866666666667 860.5333333333333 672 878.08 672 896S669.3866666666667 931.4666666666666 665.2800000000001 948.2666666666667L1191.68 1255.52C1231.6266666666668 1218.1866666666665 1285.0133333333335 1195.04 1344 1195.04C1467.5733333333335 1195.04 1568 1295.4666666666665 1568 1419.04S1467.5733333333335 1643.04 1344 1643.04S1120 1542.6133333333332 1120 1419.04C1120 1401.12 1122.6133333333335 1383.5733333333333 1126.72 1366.773333333333L600.3199999999999 1059.5199999999998C560.3733333333333 1096.853333333333 506.9866666666666 1119.9999999999998 448 1119.9999999999998C324.4266666666666 1119.9999999999998 224 1019.5733333333332 224 895.9999999999998S324.4266666666666 671.9999999999998 448 671.9999999999998C506.9866666666666 671.9999999999998 560.3733333333333 695.1466666666665 600.3199999999999 732.4799999999998L1132.32 422.2399999999998C1128.5866666666666 406.5599999999997 1126.3466666666666 390.133333333333 1126.3466666666666 373.3333333333331C1126.3466666666666 253.1199999999997 1223.7866666666669 155.6799999999996 1344 155.6799999999996S1561.6533333333334 253.1199999999997 1561.6533333333334 373.3333333333331S1464.2133333333334 590.9866666666662 1344 590.9866666666662z" />
<glyph glyph-name="cog"
unicode="&#xF110;"
horiz-adv-x="1792" d=" M1450.7733333333333 823.1999999999999C1453.76 847.0933333333334 1456 871.3599999999999 1456 896S1453.76 944.9066666666666 1450.7733333333333 968.8L1608.6933333333336 1092.3733333333332C1622.8800000000003 1103.5733333333333 1626.986666666667 1123.7333333333331 1617.6533333333336 1140.1599999999999L1468.3200000000004 1398.8799999999999C1458.986666666667 1414.9333333333334 1439.5733333333335 1421.6533333333332 1422.7733333333338 1414.9333333333334L1236.8533333333337 1339.8933333333332C1198.4000000000003 1369.3866666666668 1156.2133333333338 1394.3999999999999 1110.6666666666672 1413.44L1082.6666666666667 1611.3066666666666C1079.3066666666668 1628.8533333333332 1064 1642.6666666666667 1045.3333333333335 1642.6666666666667H746.6666666666667C728 1642.6666666666667 712.6933333333334 1628.8533333333332 709.7066666666668 1611.3066666666666L681.7066666666668 1413.44C636.1600000000002 1394.4 593.9733333333335 1369.76 555.5200000000001 1339.8933333333332L369.6 1414.9333333333334C352.8000000000001 1421.28 333.3866666666667 1414.9333333333334 324.0533333333334 1398.88L174.72 1140.1599999999999C165.3866666666667 1124.1066666666666 169.4933333333334 1103.9466666666667 183.68 1092.3733333333332L341.2266666666667 968.8C338.2400000000001 944.9066666666666 336 920.64 336 896S338.2400000000001 847.0933333333334 341.2266666666667 823.1999999999999L183.68 699.6266666666668C169.4933333333334 688.4266666666667 165.3866666666667 668.2666666666667 174.72 651.8399999999999L324.0533333333334 393.1199999999999C333.3866666666667 377.0666666666666 352.8 370.3466666666666 369.6 377.0666666666666L555.5200000000001 452.1066666666666C593.9733333333334 422.6133333333333 636.16 397.5999999999999 681.7066666666668 378.56L709.7066666666668 180.6933333333334C712.6933333333334 163.1466666666668 728 149.3333333333333 746.6666666666667 149.3333333333333H1045.3333333333335C1064 149.3333333333333 1079.3066666666668 163.1466666666665 1082.2933333333333 180.6933333333334L1110.2933333333333 378.56C1155.84 397.5999999999999 1198.0266666666666 422.24 1236.48 452.1066666666666L1422.3999999999999 377.0666666666666C1439.2 370.7199999999998 1458.6133333333332 377.0666666666666 1467.9466666666665 393.1199999999999L1617.2799999999997 651.8399999999999C1626.6133333333332 667.8933333333332 1622.5066666666664 688.0533333333333 1608.3199999999997 699.6266666666668L1450.773333333333 823.1999999999999zM896 634.6666666666665C751.52 634.6666666666665 634.6666666666667 751.52 634.6666666666667 896S751.52 1157.3333333333333 896 1157.3333333333333S1157.3333333333335 1040.48 1157.3333333333335 896S1040.48 634.6666666666665 896 634.6666666666665z" />
<glyph glyph-name="circle"
unicode="&#xF111;"
horiz-adv-x="1792" d=" M149.3333333333334 896C149.3333333333334 483.6273867930074 483.6273867930075 149.3333333333333 896 149.3333333333333C1308.3726132069926 149.3333333333333 1642.6666666666667 483.6273867930074 1642.6666666666667 896C1642.6666666666667 1308.3726132069926 1308.3726132069926 1642.6666666666667 896 1642.6666666666667C483.6273867930075 1642.6666666666667 149.3333333333334 1308.3726132069926 149.3333333333334 896z" />
<glyph glyph-name="circle-outline"
unicode="&#xF112;"
horiz-adv-x="1792" d=" M896 1642.6666666666667C483.4666666666667 1642.6666666666667 149.3333333333334 1308.5333333333333 149.3333333333334 896S483.4666666666667 149.3333333333333 896 149.3333333333333S1642.6666666666667 483.4666666666667 1642.6666666666667 896S1308.5333333333333 1642.6666666666667 896 1642.6666666666667zM896 298.6666666666665C566.72 298.6666666666665 298.6666666666667 566.7199999999998 298.6666666666667 896S566.72 1493.3333333333333 896 1493.3333333333333S1493.3333333333335 1225.28 1493.3333333333335 896S1225.2800000000002 298.6666666666665 896 298.6666666666665z" />
<glyph glyph-name="circle-inner-circle"
unicode="&#xF113;"
horiz-adv-x="1792" d=" M896 1642.6666666666667C484.2133333333334 1642.6666666666667 149.3333333333334 1307.7866666666666 149.3333333333334 896S484.2133333333334 149.3333333333333 896 149.3333333333333S1642.6666666666667 484.2133333333331 1642.6666666666667 896S1307.7866666666669 1642.6666666666667 896 1642.6666666666667zM896 298.6666666666665C566.72 298.6666666666665 298.6666666666667 566.7199999999998 298.6666666666667 896S566.72 1493.3333333333333 896 1493.3333333333333S1493.3333333333335 1225.28 1493.3333333333335 896S1225.2800000000002 298.6666666666665 896 298.6666666666665zM1120 896C1120 772.4266666666666 1019.5733333333334 672 896 672S672 772.4266666666666 672 896S772.4266666666667 1120 896 1120S1120 1019.5733333333332 1120 896z" />
<glyph glyph-name="hd"
unicode="&#xF114;"
horiz-adv-x="1792" d=" M1418.6666666666667 1568H373.3333333333334C290.4533333333333 1568 224 1500.8 224 1418.6666666666665V373.3333333333333C224 291.1999999999998 290.4533333333334 224 373.3333333333334 224H1418.6666666666667C1500.8000000000002 224 1568 291.1999999999998 1568 373.3333333333333V1418.6666666666665C1568 1500.8 1500.8000000000002 1568 1418.6666666666667 1568zM821.3333333333334 672H709.3333333333334V821.3333333333333H560V672H448V1120H560V933.3333333333331H709.3333333333334V1120H821.3333333333334V672zM970.6666666666669 1120H1269.3333333333335C1310.4 1120 1344 1086.4 1344 1045.3333333333333V746.6666666666665C1344 705.5999999999999 1310.4 672 1269.3333333333335 672H970.6666666666669V1120zM1082.6666666666667 783.9999999999999H1232V1008H1082.6666666666667V783.9999999999999z" />
<glyph glyph-name="cancel"
unicode="&#xF115;"
horiz-adv-x="1792" d=" M896 1642.6666666666667C483.4666666666667 1642.6666666666667 149.3333333333334 1308.5333333333333 149.3333333333334 896S483.4666666666667 149.3333333333333 896 149.3333333333333S1642.6666666666667 483.4666666666667 1642.6666666666667 896S1308.5333333333333 1642.6666666666667 896 1642.6666666666667zM1269.3333333333335 628.3199999999999L1163.68 522.6666666666665L896 790.3466666666667L628.3199999999999 522.6666666666665L522.6666666666667 628.3199999999999L790.3466666666668 896L522.6666666666667 1163.68L628.3199999999999 1269.3333333333333L896 1001.6533333333332L1163.68 1269.3333333333333L1269.3333333333335 1163.68L1001.6533333333334 896L1269.3333333333335 628.3199999999999z" />
<glyph glyph-name="replay"
unicode="&#xF116;"
horiz-adv-x="1792" d=" M896 1418.6666666666665V1717.3333333333333L522.6666666666667 1344L896 970.6666666666666V1269.3333333333333C1143.52 1269.3333333333333 1344 1068.8533333333332 1344 821.3333333333333S1143.52 373.3333333333333 896 373.3333333333333S448 573.813333333333 448 821.3333333333333H298.6666666666667C298.6666666666667 491.3066666666664 565.9733333333334 224 896 224S1493.3333333333335 491.3066666666664 1493.3333333333335 821.3333333333333S1226.0266666666669 1418.6666666666665 896 1418.6666666666665z" />
<glyph glyph-name="facebook"
unicode="&#xF117;"
horiz-adv-x="1792" d=" M1343 1780V1516H1186Q1100 1516 1070 1480T1040 1372V1183H1333L1294 887H1040V128H734V887H479V1183H734V1401Q734 1587 838 1689.5T1115 1792Q1262 1792 1343 1780z" />
<glyph glyph-name="gplus"
unicode="&#xF118;"
horiz-adv-x="1792" d=" M799 996Q799 960 831 925.5T908.5 857.5T999 784T1076 680T1108 538Q1108 448 1060 365Q988 243 849 185.5T551 128Q419 128 304.5 169.5T133 307Q96 367 96 438Q96 519 140.5 588T259 703Q390 785 663 803Q631 845 615.5 877T600 950Q600 986 621 1035Q575 1031 553 1031Q405 1031 303.5 1127.5T202 1372Q202 1454 238 1531T337 1662Q414 1728 519.5 1760T737 1792H1155L1017 1704H886Q960 1641 998 1571T1036 1411Q1036 1339 1011.5 1281.5T952.5 1188.5T883 1123.5T823.5 1062T799 996zM653 1092Q691 1092 731 1108.5T797 1152Q850 1209 850 1311Q850 1369 833 1436T784.5 1565.5T700 1669T583 1710Q541 1710 500.5 1690.5T435 1638Q388 1579 388 1478Q388 1432 398 1380.5T429.5 1277.5T481.5 1185T556.5 1118T653 1092zM655 219Q713 219 766.5 232T865.5 271T938.5 344T966 453Q966 478 959 502T944.5 544T917.5 585.5T888 620.5T849.5 655T813 684T771.5 714T735 740Q719 742 687 742Q634 742 582 735T474.5 710T377.5 664T309 589.5T282 484Q282 414 317 360.5T408.5 277.5T527.5 233.5T655 219zM1465 1095H1678V987H1465V768H1360V987H1148V1095H1360V1312H1465V1095z" />
<glyph glyph-name="linkedin"
unicode="&#xF119;"
horiz-adv-x="1792" d=" M477 1167V176H147V1167H477zM498 1473Q499 1400 447.5 1351T312 1302H310Q228 1302 178 1351T128 1473Q128 1547 179.5 1595.5T314 1644T447 1595.5T498 1473zM1664 744V176H1335V706Q1335 811 1294.5 870.5T1168 930Q1105 930 1062.5 895.5T999 810Q988 780 988 729V176H659Q661 575 661 823T660 1119L659 1167H988V1023H986Q1006 1055 1027 1079T1083.5 1131T1170.5 1174.5T1285 1190Q1456 1190 1560 1076.5T1664 744z" />
<glyph glyph-name="twitter"
unicode="&#xF11A;"
horiz-adv-x="1792" d=" M1684 1384Q1617 1286 1522 1217Q1523 1203 1523 1175Q1523 1045 1485 915.5T1369.5 667T1185 456.5T927 310.5T604 256Q333 256 108 401Q143 397 186 397Q411 397 587 535Q482 537 399 599.5T285 759Q318 754 346 754Q389 754 431 765Q319 788 245.5 876.5T172 1082V1086Q240 1048 318 1045Q252 1089 213 1160T174 1314Q174 1402 218 1477Q339 1328 512.5 1238.5T884 1139Q876 1177 876 1213Q876 1347 970.5 1441.5T1199 1536Q1339 1536 1435 1434Q1544 1455 1640 1512Q1603 1397 1498 1334Q1591 1344 1684 1384z" />
<glyph glyph-name="tumblr"
unicode="&#xF11B;"
horiz-adv-x="1792" d=" M1328 463L1408 226Q1385 191 1297 160T1120 128Q1016 126 929.5 154T787 228T692 334T636.5 454T620 572V1116H452V1331Q524 1357 581 1400.5T672 1490.5T730 1592.5T764 1691.5T779 1780Q780 1785 783.5 1788.5T791 1792H1035V1368H1368V1116H1034V598Q1034 568 1040.5 542T1063 489.5T1112.5 448T1194 434Q1272 436 1328 463z" />
<glyph glyph-name="pinterest"
unicode="&#xF11C;"
horiz-adv-x="1792" d=" M1664 896Q1664 687 1561 510.5T1281.5 231T896 128Q785 128 678 160Q737 253 756 324Q765 358 810 535Q830 496 883 467.5T997 439Q1118 439 1213 507.5T1360 696T1412 966Q1412 1080 1352.5 1180T1180 1343T925 1406Q820 1406 729 1377T574.5 1300T465.5 1189.5T398.5 1060T377 926Q377 822 417 743T534 632Q564 620 572 652Q574 659 580 683T588 713Q594 736 577 756Q526 817 526 907Q526 1058 630.5 1166.5T904 1275Q1055 1275 1139.5 1193T1224 980Q1224 810 1155.5 691T980 572Q919 572 882 615.5T859 720Q867 755 885.5 813.5T915.5 916.5T927 992Q927 1042 900 1075T823 1108Q761 1108 718 1051T675 909Q675 836 700 787L601 369Q584 299 588 192Q382 283 255 473T128 896Q128 1105 231 1281.5T510.5 1561T896 1664T1281.5 1561T1561 1281.5T1664 896z" />
<glyph glyph-name="audio-description"
unicode="&#xF11D;"
horiz-adv-x="1792" d=" M795.5138904615 457.270933L795.5138904615 1221.5248286325C971.84576475 1225.085121904 1107.39330415 1232.12360523 1207.223857 1161.5835220499998C1303.033991 1093.8857027 1377.7922305 962.20560625 1364.3373135 792.9476205000001C1350.102593 613.9029365000001 1219.6655764999998 463.4600215 1050.12389545 448.2843645000001C965.8259268 440.7398275000001 798.21890505 448.2843645000001 798.21890505 448.2843645000001C798.21890505 448.2843645000001 795.2791410655 453.016494 795.5138904615 457.270933M966.1564647 649.0863960000001C1076.16084135 644.6767075 1152.385591 707.3020429999999 1163.8910079999998 807.9351875C1179.2994744999999 942.71878505 1089.73043585 1030.3691748 960.74508635 1020.7227954L960.74508635 658.08043C960.6196169500002 652.9482330000001 962.7606933 650.3134680000001 966.1564647 649.0863960000001 M1343.2299685 457.3517725000002C1389.9059734 444.3690160000001 1404.0840274999998 496.0596970000001 1424.48294065 532.2791494999999C1469.0084255 611.2788500000001 1502.5101322 712.8584189999999 1503.0416912 828.9881705C1503.8147453000001 995.5680973 1438.8404296 1117.7973688000002 1378.4383305 1200.62456881045L1348.652139905 1200.62456881045C1346.6001063899998 1187.06858424 1356.44474056 1175.024791325 1362.18395859 1164.6588891000001C1408.2649952 1081.49431985 1450.96645015 966.7230041 1451.57490975 834.9817034999999C1452.27106325 683.8655425000002 1402.00636065 557.5072264999999 1343.2299685 457.3517725000002 M1488.0379675 457.3517725000002C1534.7139723999999 444.3690160000001 1548.8825828 496.0671625 1569.29093965 532.2791494999999C1613.8164245 611.2788500000001 1647.3113856500001 712.8584189999999 1647.8496902000002 828.9881705C1648.6227442999998 995.5680973 1583.6484286 1117.7973688000002 1523.2463295 1200.62456881045L1493.460138905 1200.62456881045C1491.40810539 1187.06858424 1501.250041305 1175.021805755 1506.9919575899999 1164.6588891000001C1553.0729942 1081.49431985 1595.7757984 966.7230041 1596.3829087499998 834.9817034999999C1597.07906225 683.8655425000002 1546.8143596500001 557.5072264999999 1488.0379675 457.3517725000002 M1631.9130380000001 457.3517725000002C1678.5890429 444.3690160000001 1692.7576533 496.0671625 1713.1660101500001 532.2791494999999C1757.691495 611.2788500000001 1791.1864561500001 712.8584189999999 1791.7247607000002 828.9881705C1792.4978148 995.5680973 1727.5234991000002 1117.7973688000002 1667.1214 1200.62456881045L1637.3352094050001 1200.62456881045C1635.28317589 1187.06858424 1645.1251118050002 1175.02329854 1650.86702809 1164.6588891000001C1696.9480647 1081.49431985 1739.64951965 966.7230041 1740.25797925 834.9817034999999C1740.95413275 683.8655425000002 1690.6894301500001 557.5072264999999 1631.9130380000001 457.3517725000002 M15.66796875 451.481947L254.03034755 451.481947L319.0356932 551.1747990000001L543.6261075 551.6487970000001C543.6261075 551.6487970000001 543.8541115 483.7032095 543.8541115 451.481947L714.4993835 451.481947L714.4993835 1230.9210795L508.643051 1230.9210795C488.8579955 1197.5411595 15.66796875 451.481947 15.66796875 451.481947L15.66796875 451.481947zM550.0048155000001 959.9708615L550.0048155000001 710.916297L408.4199 711.8642895L550.0048155000001 959.9708615L550.0048155000001 959.9708615z" />
<glyph glyph-name="audio"
unicode="&#xF11E;"
horiz-adv-x="1792" d=" M896 1717.3333333333333C524.9066666666668 1717.3333333333333 224 1416.4266666666667 224 1045.3333333333333V522.6666666666665C224 399.0933333333333 324.4266666666667 298.6666666666665 448 298.6666666666665H672V896H373.3333333333334V1045.3333333333333C373.3333333333334 1333.92 607.4133333333334 1568 896 1568S1418.6666666666667 1333.92 1418.6666666666667 1045.3333333333333V896H1120V298.6666666666665H1344C1467.5733333333335 298.6666666666665 1568 399.0933333333333 1568 522.6666666666665V1045.3333333333333C1568 1416.4266666666667 1267.0933333333332 1717.3333333333333 896 1717.3333333333333z" />
</font>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 26 KiB

Whitespace-only changes.
@@ -1,34 +0,0 @@
videojs.addLanguage("ar",{
"Play": "تشغيل",
"Pause": "ايقاف",
"Current Time": "الوقت الحالي",
"Duration Time": "Dauer",
"Remaining Time": "الوقت المتبقي",
"Stream Type": "نوع التيار",
"LIVE": "مباشر",
"Loaded": "تم التحميل",
"Progress": "التقدم",
"Fullscreen": "ملء الشاشة",
"Non-Fullscreen": "غير ملء الشاشة",
"Mute": "صامت",
"Unmute": "غير الصامت",
"Playback Rate": "معدل التشغيل",
"Subtitles": "الترجمة",
"subtitles off": "ايقاف الترجمة",
"Captions": "التعليقات",
"captions off": "ايقاف التعليقات",
"Chapters": "فصول",
"You aborted the media playback": "لقد ألغيت تشغيل الفيديو",
"A network error caused the media download to fail part-way.": "تسبب خطأ في الشبكة بفشل تحميل الفيديو بالكامل.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "لا يمكن تحميل الفيديو بسبب فشل في الخادم أو الشبكة ، أو فشل بسبب عدم امكانية قراءة تنسيق الفيديو.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "تم ايقاف تشغيل الفيديو بسبب مشكلة فساد أو لأن الفيديو المستخدم يستخدم ميزات غير مدعومة من متصفحك.",
"No compatible source was found for this media.": "فشل العثور على أي مصدر متوافق مع هذا الفيديو.",
"Play Video": "تشغيل الفيديو",
"Close": "أغلق",
"Modal Window": "نافذة مشروطة",
"This is a modal window": "هذه نافذة مشروطة",
"This modal can be closed by pressing the Escape key or activating the close button.": "يمكن غلق هذه النافذة المشروطة عن طريق الضغط على زر الخروج أو تفعيل زر الإغلاق",
", opens captions settings dialog": ", تفتح نافذة خيارات التعليقات",
", opens subtitles settings dialog": ", تفتح نافذة خيارات الترجمة",
", selected": ", مختار"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("ba",{
"Play": "Pusti",
"Pause": "Pauza",
"Current Time": "Trenutno vrijeme",
"Duration Time": "Vrijeme trajanja",
"Remaining Time": "Preostalo vrijeme",
"Stream Type": "Način strimovanja",
"LIVE": "UŽIVO",
"Loaded": "Učitan",
"Progress": "Progres",
"Fullscreen": "Puni ekran",
"Non-Fullscreen": "Mali ekran",
"Mute": "Prigušen",
"Unmute": "Ne-prigušen",
"Playback Rate": "Stopa reprodukcije",
"Subtitles": "Podnaslov",
"subtitles off": "Podnaslov deaktiviran",
"Captions": "Titlovi",
"captions off": "Titlovi deaktivirani",
"Chapters": "Poglavlja",
"You aborted the media playback": "Isključili ste reprodukciju videa.",
"A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.",
"No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("bg",{
"Play": "Възпроизвеждане",
"Pause": "Пауза",
"Current Time": "Текущо време",
"Duration Time": "Продължителност",
"Remaining Time": "Оставащо време",
"Stream Type": "Тип на потока",
"LIVE": "НА ЖИВО",
"Loaded": "Заредено",
"Progress": "Прогрес",
"Fullscreen": "Цял екран",
"Non-Fullscreen": "Спиране на цял екран",
"Mute": "Без звук",
"Unmute": "Със звук",
"Playback Rate": "Скорост на възпроизвеждане",
"Subtitles": "Субтитри",
"subtitles off": "Спряни субтитри",
"Captions": "Аудио надписи",
"captions off": "Спряни аудио надписи",
"Chapters": "Глави",
"You aborted the media playback": "Спряхте възпроизвеждането на видеото",
"A network error caused the media download to fail part-way.": "Грешка в мрежата провали изтеглянето на видеото.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Видеото не може да бъде заредено заради проблем със сървъра или мрежата или защото този формат не е поддържан.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Възпроизвеждането на видеото беше прекъснато заради проблем с файла или защото видеото използва опции които браузърът Ви не поддържа.",
"No compatible source was found for this media.": "Не беше намерен съвместим източник за това видео."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("ca",{
"Play": "Reproducció",
"Pause": "Pausa",
"Current Time": "Temps reproduït",
"Duration Time": "Durada total",
"Remaining Time": "Temps restant",
"Stream Type": "Tipus de seqüència",
"LIVE": "EN DIRECTE",
"Loaded": "Carregat",
"Progress": "Progrés",
"Fullscreen": "Pantalla completa",
"Non-Fullscreen": "Pantalla no completa",
"Mute": "Silencia",
"Unmute": "Amb so",
"Playback Rate": "Velocitat de reproducció",
"Subtitles": "Subtítols",
"subtitles off": "Subtítols desactivats",
"Captions": "Llegendes",
"captions off": "Llegendes desactivades",
"Chapters": "Capítols",
"You aborted the media playback": "Heu interromput la reproducció del vídeo.",
"A network error caused the media download to fail part-way.": "Un error de la xarxa ha interromput la baixada del vídeo.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "No s'ha pogut carregar el vídeo perquè el servidor o la xarxa han fallat, o bé perquè el seu format no és compatible.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La reproducció de vídeo s'ha interrumput per un problema de corrupció de dades o bé perquè el vídeo demanava funcions que el vostre navegador no ofereix.",
"No compatible source was found for this media.": "No s'ha trobat cap font compatible amb el vídeo."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("cs",{
"Play": "Přehrát",
"Pause": "Pauza",
"Current Time": "Aktuální čas",
"Duration Time": "Doba trvání",
"Remaining Time": "Zbývající čas",
"Stream Type": "Stream Type",
"LIVE": "ŽIVĚ",
"Loaded": "Načteno",
"Progress": "Stav",
"Fullscreen": "Celá obrazovka",
"Non-Fullscreen": "Zmenšená obrazovka",
"Mute": "Ztlumit zvuk",
"Unmute": "Přehrát zvuk",
"Playback Rate": "Rychlost přehrávání",
"Subtitles": "Titulky",
"subtitles off": "Titulky vypnuty",
"Captions": "Popisky",
"captions off": "Popisky vypnuty",
"Chapters": "Kapitoly",
"You aborted the media playback": "Přehrávání videa je přerušeno.",
"A network error caused the media download to fail part-way.": "Video nemohlo být načteno, kvůli chybě v síti.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video nemohlo být načteno, buď kvůli chybě serveru nebo sítě nebo proto, že daný formát není podporován.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Váš prohlížeč nepodporuje formát videa.",
"No compatible source was found for this media.": "Špatně zadaný zdroj videa."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("da",{
"Play": "Afspil",
"Pause": "Pause",
"Current Time": "Aktuel tid",
"Duration Time": "Varighed",
"Remaining Time": "Resterende tid",
"Stream Type": "Stream-type",
"LIVE": "LIVE",
"Loaded": "Indlæst",
"Progress": "Status",
"Fullscreen": "Fuldskærm",
"Non-Fullscreen": "Luk fuldskærm",
"Mute": "Uden lyd",
"Unmute": "Med lyd",
"Playback Rate": "Afspilningsrate",
"Subtitles": "Undertekster",
"subtitles off": "Uden undertekster",
"Captions": "Undertekster for hørehæmmede",
"captions off": "Uden undertekster for hørehæmmede",
"Chapters": "Kapitler",
"You aborted the media playback": "Du afbrød videoafspilningen.",
"A network error caused the media download to fail part-way.": "En netværksfejl fik download af videoen til at fejle.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikke indlæses, enten fordi serveren eller netværket fejlede, eller fordi formatet ikke er understøttet.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoafspilningen blev afbrudt på grund af ødelagte data eller fordi videoen benyttede faciliteter som din browser ikke understøtter.",
"No compatible source was found for this media.": "Fandt ikke en kompatibel kilde for denne media."
});
@@ -1,40 +0,0 @@
videojs.addLanguage("de",{
"Play": "Wiedergabe",
"Pause": "Pause",
"Current Time": "Aktueller Zeitpunkt",
"Duration Time": "Dauer",
"Remaining Time": "Verbleibende Zeit",
"Stream Type": "Streamtyp",
"LIVE": "LIVE",
"Loaded": "Geladen",
"Progress": "Status",
"Fullscreen": "Vollbild",
"Non-Fullscreen": "Kein Vollbild",
"Mute": "Ton aus",
"Unmute": "Ton ein",
"Playback Rate": "Wiedergabegeschwindigkeit",
"Subtitles": "Untertitel",
"subtitles off": "Untertitel aus",
"Captions": "Untertitel",
"captions off": "Untertitel aus",
"Chapters": "Kapitel",
"You aborted the media playback": "Sie haben die Videowiedergabe abgebrochen.",
"A network error caused the media download to fail part-way.": "Der Videodownload ist aufgrund eines Netzwerkfehlers fehlgeschlagen.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Die Videowiedergabe wurde entweder wegen eines Problems mit einem beschädigten Video oder wegen verwendeten Funktionen, die vom Browser nicht unterstützt werden, abgebrochen.",
"No compatible source was found for this media.": "Für dieses Video wurde keine kompatible Quelle gefunden.",
"Play Video": "Video abspielen",
"Close": "Schließen",
"Modal Window": "Modales Fenster",
"This is a modal window": "Dies ist ein modales Fenster",
"This modal can be closed by pressing the Escape key or activating the close button.": "Durch Drücken der Esc-Taste bzw. Betätigung der Schaltfläche \"Schließen\" wird dieses modale Fenster geschlossen.",
", opens captions settings dialog": ", öffnet Einstellungen für Untertitel",
", opens subtitles settings dialog": ", öffnet Einstellungen für Untertitel",
", selected": ", ausgewählt",
"Close Modal Dialog": "Modales Fenster schließen",
"Descriptions": "Beschreibungen",
"descriptions off": "Beschreibungen aus",
"The media is encrypted and we do not have the keys to decrypt it.": "Die Entschlüsselungsschlüssel für den verschlüsselten Medieninhalt sind nicht verfügbar.",
", opens descriptions settings dialog": ", öffnet Einstellungen für Beschreibungen",
"Audio Track": "Tonspur"
});
@@ -1,40 +0,0 @@
videojs.addLanguage("el",{
"Play": "Aναπαραγωγή",
"Pause": "Παύση",
"Current Time": "Τρέχων χρόνος",
"Duration Time": "Συνολικός χρόνος",
"Remaining Time": "Υπολοιπόμενος χρόνος",
"Stream Type": "Τύπος ροής",
"LIVE": "ΖΩΝΤΑΝΑ",
"Loaded": "Φόρτωση επιτυχής",
"Progress": "Πρόοδος",
"Fullscreen": "Πλήρης οθόνη",
"Non-Fullscreen": "Έξοδος από πλήρη οθόνη",
"Mute": "Σίγαση",
"Unmute": "Kατάργηση σίγασης",
"Playback Rate": "Ρυθμός αναπαραγωγής",
"Subtitles": "Υπότιτλοι",
"subtitles off": "απόκρυψη υπότιτλων",
"Captions": "Λεζάντες",
"captions off": "απόκρυψη λεζάντων",
"Chapters": "Κεφάλαια",
"Close Modal Dialog": "Κλείσιμο παραθύρου",
"Descriptions": "Περιγραφές",
"descriptions off": "απόκρυψη περιγραφών",
"Audio Track": "Ροή ήχου",
"You aborted the media playback": "Ακυρώσατε την αναπαραγωγή",
"A network error caused the media download to fail part-way.": "Ένα σφάλμα δικτύου προκάλεσε την αποτυχία μεταφόρτωσης του αρχείου προς αναπαραγωγή.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Το αρχείο προς αναπαραγωγή δεν ήταν δυνατό να φορτωθεί είτε γιατί υπήρξε σφάλμα στον διακομιστή ή το δίκτυο, είτε γιατί ο τύπος του αρχείου δεν υποστηρίζεται.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Η αναπαραγωγή ακυρώθηκε είτε λόγω κατεστραμμένου αρχείου, είτε γιατί το αρχείο απαιτεί λειτουργίες που δεν υποστηρίζονται από το πρόγραμμα περιήγησης που χρησιμοποιείτε.",
"No compatible source was found for this media.": "Δεν βρέθηκε συμβατή πηγή αναπαραγωγής για το συγκεκριμένο αρχείο.",
"The media is encrypted and we do not have the keys to decrypt it.": "Το αρχείο προς αναπαραγωγή είναι κρυπτογραφημένo και δεν υπάρχουν τα απαραίτητα κλειδιά αποκρυπτογράφησης.",
"Play Video": "Αναπαραγωγή βίντεο",
"Close": "Κλείσιμο",
"Modal Window": "Aναδυόμενο παράθυρο",
"This is a modal window": "Το παρών είναι ένα αναδυόμενο παράθυρο",
"This modal can be closed by pressing the Escape key or activating the close button.": "Αυτό το παράθυρο μπορεί να εξαφανιστεί πατώντας το πλήκτρο Escape ή πατώντας το κουμπί κλεισίματος.",
", opens captions settings dialog": ", εμφανίζει τις ρυθμίσεις για τις λεζάντες",
", opens subtitles settings dialog": ", εμφανίζει τις ρυθμίσεις για τους υπότιτλους",
", opens descriptions settings dialog": ", εμφανίζει τις ρυθμίσεις για τις περιγραφές",
", selected": ", επιλεγμένο"
});
@@ -1,40 +0,0 @@
videojs.addLanguage("en",{
"Play": "Play",
"Pause": "Pause",
"Current Time": "Current Time",
"Duration Time": "Duration Time",
"Remaining Time": "Remaining Time",
"Stream Type": "Stream Type",
"LIVE": "LIVE",
"Loaded": "Loaded",
"Progress": "Progress",
"Fullscreen": "Fullscreen",
"Non-Fullscreen": "Non-Fullscreen",
"Mute": "Mute",
"Unmute": "Unmute",
"Playback Rate": "Playback Rate",
"Subtitles": "Subtitles",
"subtitles off": "subtitles off",
"Captions": "Captions",
"captions off": "captions off",
"Chapters": "Chapters",
"Close Modal Dialog": "Close Modal Dialog",
"Descriptions": "Descriptions",
"descriptions off": "descriptions off",
"Audio Track": "Audio Track",
"You aborted the media playback": "You aborted the media playback",
"A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.",
"No compatible source was found for this media.": "No compatible source was found for this media.",
"The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.",
"Play Video": "Play Video",
"Close": "Close",
"Modal Window": "Modal Window",
"This is a modal window": "This is a modal window",
"This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.",
", opens captions settings dialog": ", opens captions settings dialog",
", opens subtitles settings dialog": ", opens subtitles settings dialog",
", opens descriptions settings dialog": ", opens descriptions settings dialog",
", selected": ", selected"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("es",{
"Play": "Reproducción",
"Pause": "Pausa",
"Current Time": "Tiempo reproducido",
"Duration Time": "Duración total",
"Remaining Time": "Tiempo restante",
"Stream Type": "Tipo de secuencia",
"LIVE": "DIRECTO",
"Loaded": "Cargado",
"Progress": "Progreso",
"Fullscreen": "Pantalla completa",
"Non-Fullscreen": "Pantalla no completa",
"Mute": "Silenciar",
"Unmute": "No silenciado",
"Playback Rate": "Velocidad de reproducción",
"Subtitles": "Subtítulos",
"subtitles off": "Subtítulos desactivados",
"Captions": "Subtítulos especiales",
"captions off": "Subtítulos especiales desactivados",
"Chapters": "Capítulos",
"You aborted the media playback": "Ha interrumpido la reproducción del vídeo.",
"A network error caused the media download to fail part-way.": "Un error de red ha interrumpido la descarga del vídeo.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "No se ha podido cargar el vídeo debido a un fallo de red o del servidor o porque el formato es incompatible.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La reproducción de vídeo se ha interrumpido por un problema de corrupción de datos o porque el vídeo precisa funciones que su navegador no ofrece.",
"No compatible source was found for this media.": "No se ha encontrado ninguna fuente compatible con este vídeo."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("fa",{
"Play": "پخش",
"Pause": "وقفه",
"Current Time": "زمان کنونی",
"Duration Time": "مدت زمان",
"Remaining Time": "زمان باقیمانده",
"Stream Type": "نوع استریم",
"LIVE": "زنده",
"Loaded": "فراخوانی شده",
"Progress": "پیشرفت",
"Fullscreen": "تمام صفحه",
"Non-Fullscreen": "نمایش عادی",
"Mute": "بی صدا",
"Unmute": "بهمراه صدا",
"Playback Rate": "سرعت پخش",
"Subtitles": "زیرنویس",
"subtitles off": "بدون زیرنویس",
"Captions": "عنوان",
"captions off": "بدون عنوان",
"Chapters": "فصل",
"You aborted the media playback": "شما پخش را متوقف کردید.",
"A network error caused the media download to fail part-way.": "مشکل در دریافت ویدئو ...",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "فرمت پشتیبانی نمیشود یا خطایی روی داده است.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "مشکل در دریافت ویدئو ...",
"No compatible source was found for this media.": "هیچ ورودی ای برای این رسانه شناسایی نشد."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("fi",{
"Play": "Toisto",
"Pause": "Tauko",
"Current Time": "Tämänhetkinen aika",
"Duration Time": "Kokonaisaika",
"Remaining Time": "Jäljellä oleva aika",
"Stream Type": "Lähetystyyppi",
"LIVE": "LIVE",
"Loaded": "Ladattu",
"Progress": "Edistyminen",
"Fullscreen": "Koko näyttö",
"Non-Fullscreen": "Koko näyttö pois",
"Mute": "Ääni pois",
"Unmute": "Ääni päällä",
"Playback Rate": "Toistonopeus",
"Subtitles": "Tekstitys",
"subtitles off": "Tekstitys pois",
"Captions": "Tekstitys",
"captions off": "Tekstitys pois",
"Chapters": "Kappaleet",
"You aborted the media playback": "Olet keskeyttänyt videotoiston.",
"A network error caused the media download to fail part-way.": "Verkkovirhe keskeytti videon latauksen.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videon lataus ei onnistunut joko palvelin- tai verkkovirheestä tai väärästä formaatista johtuen.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videon toisto keskeytyi, koska media on vaurioitunut tai käyttää käyttää toimintoja, joita selaimesi ei tue.",
"No compatible source was found for this media.": "Tälle videolle ei löytynyt yhteensopivaa lähdettä."
});
@@ -1,40 +0,0 @@
videojs.addLanguage("fr",{
"Play": "Lecture",
"Pause": "Pause",
"Current Time": "Temps actuel",
"Duration Time": "Durée",
"Remaining Time": "Temps restant",
"Stream Type": "Type de flux",
"LIVE": "EN DIRECT",
"Loaded": "Chargé",
"Progress": "Progression",
"Fullscreen": "Plein écran",
"Non-Fullscreen": "Fenêtré",
"Mute": "Sourdine",
"Unmute": "Son activé",
"Playback Rate": "Vitesse de lecture",
"Subtitles": "Sous-titres",
"subtitles off": "Sous-titres désactivés",
"Captions": "Sous-titres transcrits",
"captions off": "Sous-titres transcrits désactivés",
"Chapters": "Chapitres",
"Close Modal Dialog": "Fermer la boîte de dialogue modale",
"Descriptions": "Descriptions",
"descriptions off": "descriptions désactivées",
"Audio Track": "Piste audio",
"You aborted the media playback": "Vous avez interrompu la lecture de la vidéo.",
"A network error caused the media download to fail part-way.": "Une erreur de réseau a interrompu le téléchargement de la vidéo.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Cette vidéo n'a pas pu être chargée, soit parce que le serveur ou le réseau a échoué ou parce que le format n'est pas reconnu.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La lecture de la vidéo a été interrompue à cause d'un problème de corruption ou parce que la vidéo utilise des fonctionnalités non prises en charge par votre navigateur.",
"No compatible source was found for this media.": "Aucune source compatible n'a été trouvée pour cette vidéo.",
"The media is encrypted and we do not have the keys to decrypt it.": "Le média est chiffré et nous n'avons pas les clés pour le déchiffrer.",
"Play Video": "Lire la vidéo",
"Close": "Fermer",
"Modal Window": "Fenêtre modale",
"This is a modal window": "Ceci est une fenêtre modale",
"This modal can be closed by pressing the Escape key or activating the close button.": "Ce modal peut être fermé en appuyant sur la touche Échap ou activer le bouton de fermeture.",
", opens captions settings dialog": ", ouvrir les paramètres des sous-titres transcrits",
", opens subtitles settings dialog": ", ouvrir les paramètres des sous-titres",
", opens descriptions settings dialog": ", ouvrir les paramètres des descriptions",
", selected": ", sélectionné"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("hr",{
"Play": "Pusti",
"Pause": "Pauza",
"Current Time": "Trenutno vrijeme",
"Duration Time": "Vrijeme trajanja",
"Remaining Time": "Preostalo vrijeme",
"Stream Type": "Način strimovanja",
"LIVE": "UŽIVO",
"Loaded": "Učitan",
"Progress": "Progres",
"Fullscreen": "Puni ekran",
"Non-Fullscreen": "Mali ekran",
"Mute": "Prigušen",
"Unmute": "Ne-prigušen",
"Playback Rate": "Stopa reprodukcije",
"Subtitles": "Podnaslov",
"subtitles off": "Podnaslov deaktiviran",
"Captions": "Titlovi",
"captions off": "Titlovi deaktivirani",
"Chapters": "Poglavlja",
"You aborted the media playback": "Isključili ste reprodukciju videa.",
"A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.",
"No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("hu",{
"Play": "Lejátszás",
"Pause": "Szünet",
"Current Time": "Aktuális időpont",
"Duration Time": "Hossz",
"Remaining Time": "Hátralévő idő",
"Stream Type": "Adatfolyam típusa",
"LIVE": "ÉLŐ",
"Loaded": "Betöltve",
"Progress": "Állapot",
"Fullscreen": "Teljes képernyő",
"Non-Fullscreen": "Normál méret",
"Mute": "Némítás",
"Unmute": "Némítás kikapcsolva",
"Playback Rate": "Lejátszási sebesség",
"Subtitles": "Feliratok",
"subtitles off": "Feliratok kikapcsolva",
"Captions": "Magyarázó szöveg",
"captions off": "Magyarázó szöveg kikapcsolva",
"Chapters": "Fejezetek",
"You aborted the media playback": "Leállította a lejátszást",
"A network error caused the media download to fail part-way.": "Hálózati hiba miatt a videó részlegesen töltődött le.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "A videó nem tölthető be hálózati vagy kiszolgálói hiba miatt, vagy a formátuma nem támogatott.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A lejátszás adatsérülés miatt leállt, vagy a videó egyes tulajdonságait a böngészője nem támogatja.",
"No compatible source was found for this media.": "Nincs kompatibilis forrás ehhez a videóhoz."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("it",{
"Play": "Play",
"Pause": "Pausa",
"Current Time": "Orario attuale",
"Duration Time": "Durata",
"Remaining Time": "Tempo rimanente",
"Stream Type": "Tipo del Streaming",
"LIVE": "LIVE",
"Loaded": "Caricato",
"Progress": "Stato",
"Fullscreen": "Schermo intero",
"Non-Fullscreen": "Chiudi schermo intero",
"Mute": "Muto",
"Unmute": "Audio",
"Playback Rate": "Tasso di riproduzione",
"Subtitles": "Sottotitoli",
"subtitles off": "Senza sottotitoli",
"Captions": "Sottotitoli non udenti",
"captions off": "Senza sottotitoli non udenti",
"Chapters": "Capitolo",
"You aborted the media playback": "La riproduzione del filmato è stata interrotta.",
"A network error caused the media download to fail part-way.": "Il download del filmato è stato interrotto a causa di un problema rete.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Il filmato non può essere caricato a causa di un errore nel server o nella rete o perché il formato non viene supportato.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "La riproduzione del filmato è stata interrotta a causa di un file danneggiato o per l’utilizzo di impostazioni non supportate dal browser.",
"No compatible source was found for this media.": "Non ci sono fonti compatibili per questo filmato."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("ja",{
"Play": "再生",
"Pause": "一時停止",
"Current Time": "現在の時間",
"Duration Time": "長さ",
"Remaining Time": "残りの時間",
"Stream Type": "ストリームの種類",
"LIVE": "ライブ",
"Loaded": "ロード済み",
"Progress": "進行状況",
"Fullscreen": "フルスクリーン",
"Non-Fullscreen": "フルスクリーン以外",
"Mute": "ミュート",
"Unmute": "ミュート解除",
"Playback Rate": "再生レート",
"Subtitles": "サブタイトル",
"subtitles off": "サブタイトル オフ",
"Captions": "キャプション",
"captions off": "キャプション オフ",
"Chapters": "チャプター",
"You aborted the media playback": "動画再生を中止しました",
"A network error caused the media download to fail part-way.": "ネットワーク エラーにより動画のダウンロードが途中で失敗しました",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "サーバーまたはネットワークのエラー、またはフォーマットがサポートされていないため、動画をロードできませんでした",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "破損の問題、またはお使いのブラウザがサポートしていない機能が動画に使用されていたため、動画の再生が中止されました",
"No compatible source was found for this media.": "この動画に対して互換性のあるソースが見つかりませんでした"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("ko",{
"Play": "재생",
"Pause": "일시중지",
"Current Time": "현재 시간",
"Duration Time": "지정 기간",
"Remaining Time": "남은 시간",
"Stream Type": "스트리밍 유형",
"LIVE": "라이브",
"Loaded": "로드됨",
"Progress": "진행",
"Fullscreen": "전체 화면",
"Non-Fullscreen": "전체 화면 해제",
"Mute": "음소거",
"Unmute": "음소거 해제",
"Playback Rate": "재생 비율",
"Subtitles": "서브타이틀",
"subtitles off": "서브타이틀 끄기",
"Captions": "자막",
"captions off": "자막 끄기",
"Chapters": "챕터",
"You aborted the media playback": "비디오 재생을 취소했습니다.",
"A network error caused the media download to fail part-way.": "네트워크 오류로 인하여 비디오 일부를 다운로드하지 못 했습니다.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "비디오를 로드할 수 없습니다. 서버 혹은 네트워크 오류 때문이거나 지원되지 않는 형식 때문일 수 있습니다.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "비디오 재생이 취소됐습니다. 비디오가 손상되었거나 비디오가 사용하는 기능을 브라우저에서 지원하지 않는 것 같습니다.",
"No compatible source was found for this media.": "비디오에 호환되지 않는 소스가 있습니다."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("nb",{
"Play": "Spill",
"Pause": "Pause",
"Current Time": "Aktuell tid",
"Duration Time": "Varighet",
"Remaining Time": "Gjenstående tid",
"Stream Type": "Type strøm",
"LIVE": "DIREKTE",
"Loaded": "Lastet inn",
"Progress": "Status",
"Fullscreen": "Fullskjerm",
"Non-Fullscreen": "Lukk fullskjerm",
"Mute": "Lyd av",
"Unmute": "Lyd på",
"Playback Rate": "Avspillingsrate",
"Subtitles": "Undertekst på",
"subtitles off": "Undertekst av",
"Captions": "Undertekst for hørselshemmede på",
"captions off": "Undertekst for hørselshemmede av",
"Chapters": "Kapitler",
"You aborted the media playback": "Du avbrøt avspillingen.",
"A network error caused the media download to fail part-way.": "En nettverksfeil avbrøt nedlasting av videoen.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikke lastes ned, på grunn av nettverksfeil eller serverfeil, eller fordi formatet ikke er støttet.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoavspillingen ble avbrudt på grunn av ødelagte data eller fordi videoen ville gjøre noe som nettleseren din ikke har støtte for.",
"No compatible source was found for this media.": "Fant ikke en kompatibel kilde for dette mediainnholdet."
});
@@ -1,37 +0,0 @@
videojs.addLanguage("nl",{
"Play": "Afspelen",
"Pause": "Pauze",
"Current Time": "Huidige tijd",
"Duration Time": "Looptijd",
"Remaining Time": "Resterende tijd",
"Stream Type": "Streamtype",
"LIVE": "LIVE",
"Loaded": "Geladen",
"Progress": "Status",
"Fullscreen": "Volledig scherm",
"Non-Fullscreen": "Geen volledig scherm",
"Mute": "Geluid uit",
"Unmute": "Geluid aan",
"Playback Rate": "Weergavesnelheid",
"Subtitles": "Ondertiteling",
"subtitles off": "ondertiteling uit",
"Captions": "Bijschriften",
"captions off": "bijschriften uit",
"Chapters": "Hoofdstukken",
"Descriptions": "Beschrijvingen",
"descriptions off": "beschrijvingen off",
"You aborted the media playback": "U hebt de mediaweergave afgebroken.",
"A network error caused the media download to fail part-way.": "De mediadownload is mislukt door een netwerkfout.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "De media kon niet worden geladen, vanwege een server- of netwerkfout of doordat het formaat niet wordt ondersteund.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "De mediaweergave is afgebroken vanwege beschadigde data of het mediabestand gebruikt functies die niet door uw browser worden ondersteund.",
"No compatible source was found for this media.": "Voor deze media is geen ondersteunde bron gevonden.",
"Play Video": "Video Afspelen",
"Close": "Sluiten",
"Modal Window": "Modal Venster",
"This is a modal window": "Dit is een modaal venster",
"This modal can be closed by pressing the Escape key or activating the close button.": "Dit modaal venster kan gesloten worden door op Escape te drukken of de 'sluiten' knop te activeren.",
", opens captions settings dialog": ", opent bijschriften instellingen venster",
", opens subtitles settings dialog": ", opent ondertiteling instellingen venster",
", opens descriptions settings dialog": ", opent beschrijvingen instellingen venster",
", selected": ", selected"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("nn",{
"Play": "Spel",
"Pause": "Pause",
"Current Time": "Aktuell tid",
"Duration Time": "Varigheit",
"Remaining Time": "Tid attende",
"Stream Type": "Type straum",
"LIVE": "DIREKTE",
"Loaded": "Lasta inn",
"Progress": "Status",
"Fullscreen": "Fullskjerm",
"Non-Fullscreen": "Stenga fullskjerm",
"Mute": "Ljod av",
"Unmute": "Ljod på",
"Playback Rate": "Avspelingsrate",
"Subtitles": "Teksting på",
"subtitles off": "Teksting av",
"Captions": "Teksting for høyrselshemma på",
"captions off": "Teksting for høyrselshemma av",
"Chapters": "Kapitel",
"You aborted the media playback": "Du avbraut avspelinga.",
"A network error caused the media download to fail part-way.": "Ein nettverksfeil avbraut nedlasting av videoen.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Videoen kunne ikkje lastas ned, på grunn av ein nettverksfeil eller serverfeil, eller av di formatet ikkje er stoda.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Videoavspelinga blei broten på grunn av øydelagde data eller av di videoen ville gjera noe som nettlesaren din ikkje stodar.",
"No compatible source was found for this media.": "Fant ikke en kompatibel kilde for dette mediainnholdet."
});
@@ -1,34 +0,0 @@
videojs.addLanguage("pl",{
"Play": "Odtwarzaj",
"Pause": "Pauza",
"Current Time": "Aktualny czas",
"Duration Time": "Czas trwania",
"Remaining Time": "Pozostały czas",
"Stream Type": "Typ strumienia",
"LIVE": "NA ŻYWO",
"Loaded": "Załadowany",
"Progress": "Status",
"Fullscreen": "Pełny ekran",
"Non-Fullscreen": "Pełny ekran niedostępny",
"Mute": "Wyłącz dźwięk",
"Unmute": "Włącz dźwięk",
"Playback Rate": "Szybkość odtwarzania",
"Subtitles": "Napisy",
"subtitles off": "Napisy wyłączone",
"Captions": "Transkrypcja",
"captions off": "Transkrypcja wyłączona",
"Chapters": "Rozdziały",
"You aborted the media playback": "Odtwarzanie zostało przerwane",
"A network error caused the media download to fail part-way.": "Problemy z siecią spowodowały błąd przy pobieraniu materiału wideo.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Materiał wideo nie może być załadowany, ponieważ wystąpił problem z siecią lub format nie jest obsługiwany",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Odtwarzanie materiału wideo zostało przerwane z powodu uszkodzonego pliku wideo lub z powodu błędu funkcji, które nie są wspierane przez przeglądarkę.",
"No compatible source was found for this media.": "Dla tego materiału wideo nie znaleziono kompatybilnego źródła.",
"Play video": "Odtwarzaj wideo",
"Close": "Zamknij",
"Modal Window": "Okno Modala",
"This is a modal window": "To jest okno modala",
"This modal can be closed by pressing the Escape key or activating the close button.": "Ten modal możesz zamknąć naciskając przycisk Escape albo wybierając przycisk Zamknij.",
", opens captions settings dialog": ", otwiera okno dialogowe ustawień transkrypcji",
", opens subtitles settings dialog": ", otwiera okno dialogowe napisów",
", selected": ", zaznaczone"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("pt-BR",{
"Play": "Tocar",
"Pause": "Pausar",
"Current Time": "Tempo",
"Duration Time": "Duração",
"Remaining Time": "Tempo Restante",
"Stream Type": "Tipo de Stream",
"LIVE": "AO VIVO",
"Loaded": "Carregado",
"Progress": "Progresso",
"Fullscreen": "Tela Cheia",
"Non-Fullscreen": "Tela Normal",
"Mute": "Mudo",
"Unmute": "Habilitar Som",
"Playback Rate": "Velocidade",
"Subtitles": "Legendas",
"subtitles off": "Sem Legendas",
"Captions": "Anotações",
"captions off": "Sem Anotações",
"Chapters": "Capítulos",
"You aborted the media playback": "Você parou a execução do vídeo.",
"A network error caused the media download to fail part-way.": "Um erro na rede fez o vídeo parar parcialmente.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "O vídeo não pode ser carregado, ou porque houve um problema com sua rede ou pelo formato do vídeo não ser suportado.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "A execução foi interrompida por um problema com o vídeo ou por seu navegador não dar suporte ao seu formato.",
"No compatible source was found for this media.": "Não foi encontrada fonte de vídeo compatível."
});
@@ -1,40 +0,0 @@
videojs.addLanguage("ru",{
"Play": "Воспроизвести",
"Pause": "Приостановить",
"Current Time": "Текущее время",
"Duration Time": "Продолжительность",
"Remaining Time": "Оставшееся время",
"Stream Type": "Тип потока",
"LIVE": "ОНЛАЙН",
"Loaded": "Загрузка",
"Progress": "Прогресс",
"Fullscreen": "Полноэкранный режим",
"Non-Fullscreen": "Неполноэкранный режим",
"Mute": "Без звука",
"Unmute": "Со звуком",
"Playback Rate": "Скорость воспроизведения",
"Subtitles": "Субтитры",
"subtitles off": "Субтитры выкл.",
"Captions": "Подписи",
"captions off": "Подписи выкл.",
"Chapters": "Главы",
"Close Modal Dialog": "Закрыть модальное окно",
"Descriptions": "Описания",
"descriptions off": "описания выкл.",
"Audio Track": "Звуковая дорожка",
"You aborted the media playback": "Вы прервали воспроизведение видео",
"A network error caused the media download to fail part-way.": "Ошибка сети вызвала сбой во время загрузки видео.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Невозможно загрузить видео из-за сетевого или серверного сбоя либо формат не поддерживается.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Воспроизведение видео было приостановлено из-за повреждения либо в связи с тем, что видео использует функции, неподдерживаемые вашим браузером.",
"No compatible source was found for this media.": "Совместимые источники для этого видео отсутствуют.",
"The media is encrypted and we do not have the keys to decrypt it.": "Видео в зашифрованном виде, и у нас нет ключей для расшифровки.",
"Play Video": "Воспроизвести видео",
"Close": "Закрыть",
"Modal Window": "Модальное окно",
"This is a modal window": "Это модальное окно.",
"This modal can be closed by pressing the Escape key or activating the close button.": "Модальное окно можно закрыть нажав Esc или кнопку закрытия окна.",
", opens captions settings dialog": ", откроется диалог настройки подписей",
", opens subtitles settings dialog": ", откроется диалог настройки субтитров",
", opens descriptions settings dialog": ", откроется диалог настройки описаний",
", selected": ", выбрано"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("sr",{
"Play": "Pusti",
"Pause": "Pauza",
"Current Time": "Trenutno vrijeme",
"Duration Time": "Vrijeme trajanja",
"Remaining Time": "Preostalo vrijeme",
"Stream Type": "Način strimovanja",
"LIVE": "UŽIVO",
"Loaded": "Učitan",
"Progress": "Progres",
"Fullscreen": "Puni ekran",
"Non-Fullscreen": "Mali ekran",
"Mute": "Prigušen",
"Unmute": "Ne-prigušen",
"Playback Rate": "Stopa reprodukcije",
"Subtitles": "Podnaslov",
"subtitles off": "Podnaslov deaktiviran",
"Captions": "Titlovi",
"captions off": "Titlovi deaktivirani",
"Chapters": "Poglavlja",
"You aborted the media playback": "Isključili ste reprodukciju videa.",
"A network error caused the media download to fail part-way.": "Video se prestao preuzimati zbog greške na mreži.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video se ne može reproducirati zbog servera, greške u mreži ili je format ne podržan.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Reprodukcija videa je zaustavljenja zbog greške u formatu ili zbog verzije vašeg pretraživača.",
"No compatible source was found for this media.": "Nije nađen nijedan kompatibilan izvor ovog videa."
});
@@ -1,26 +0,0 @@
videojs.addLanguage("sv",{
"Play": "Spela",
"Pause": "Pausa",
"Current Time": "Aktuell tid",
"Duration Time": "Total tid",
"Remaining Time": "Återstående tid",
"Stream Type": "Strömningstyp",
"LIVE": "LIVE",
"Loaded": "Laddad",
"Progress": "Förlopp",
"Fullscreen": "Fullskärm",
"Non-Fullscreen": "Ej fullskärm",
"Mute": "Ljud av",
"Unmute": "Ljud på",
"Playback Rate": "Uppspelningshastighet",
"Subtitles": "Text på",
"subtitles off": "Text av",
"Captions": "Text på",
"captions off": "Text av",
"Chapters": "Kapitel",
"You aborted the media playback": "Du har avbrutit videouppspelningen.",
"A network error caused the media download to fail part-way.": "Ett nätverksfel gjorde att nedladdningen av videon avbröts.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Det gick inte att ladda videon, antingen på grund av ett server- eller nätverksfel, eller för att formatet inte stöds.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Uppspelningen avbröts på grund av att videon är skadad, eller också för att videon använder funktioner som din webbläsare inte stöder.",
"No compatible source was found for this media.": "Det gick inte att hitta någon kompatibel källa för den här videon."
});
@@ -1,34 +0,0 @@
videojs.addLanguage("tr",{
"Play": "Oynat",
"Pause": "Duraklat",
"Current Time": "Süre",
"Duration Time": "Toplam Süre",
"Remaining Time": "Kalan Süre",
"Stream Type": "Yayın Tipi",
"LIVE": "CANLI",
"Loaded": "Yüklendi",
"Progress": "Yükleniyor",
"Fullscreen": "Tam Ekran",
"Non-Fullscreen": "Küçük Ekran",
"Mute": "Ses Kapa",
"Unmute": "Ses Aç",
"Playback Rate": "Oynatma Hızı",
"Subtitles": "Altyazı",
"subtitles off": "Altyazı Kapalı",
"Captions": "Ek Açıklamalar",
"captions off": "Ek Açıklamalar Kapalı",
"Chapters": "Bölümler",
"You aborted the media playback": "Video oynatmayı iptal ettiniz",
"A network error caused the media download to fail part-way.": "Video indirilirken bağlantı sorunu oluştu.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video oynatılamadı, ağ ya da sunucu hatası veya belirtilen format desteklenmiyor.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Tarayıcınız desteklemediği için videoda hata oluştu.",
"No compatible source was found for this media.": "Video için kaynak bulunamadı.",
"Play Video": "Videoyu Oynat",
"Close": "Kapat",
"Modal Window": "Modal Penceresi",
"This is a modal window": "Bu bir modal penceresidir",
"This modal can be closed by pressing the Escape key or activating the close button.": "Bu modal ESC tuşuna basarak ya da kapata tıklanarak kapatılabilir.",
", opens captions settings dialog": ", ek açıklama ayarları menüsünü açar",
", opens subtitles settings dialog": ", altyazı ayarları menüsünü açar",
", selected": ", seçildi"
});
@@ -1,40 +0,0 @@
videojs.addLanguage("uk",{
"Play": "Відтворити",
"Pause": "Призупинити",
"Current Time": "Поточний час",
"Duration Time": "Тривалість",
"Remaining Time": "Час, що залишився",
"Stream Type": "Тип потоку",
"LIVE": "НАЖИВО",
"Loaded": "Завантаження",
"Progress": "Прогрес",
"Fullscreen": "Повноекранний режим",
"Non-Fullscreen": "Неповноекранний режим",
"Mute": "Без звуку",
"Unmute": "Зі звуком",
"Playback Rate": "Швидкість відтворення",
"Subtitles": "Субтитри",
"subtitles off": "Без субтитрів",
"Captions": "Підписи",
"captions off": "Без підписів",
"Chapters": "Розділи",
"Close Modal Dialog": "Закрити модальний діалог",
"Descriptions": "Описи",
"descriptions off": "Без описів",
"Audio Track": "Аудіодоріжка",
"You aborted the media playback": "Ви припинили відтворення відео",
"A network error caused the media download to fail part-way.": "Помилка мережі викликала збій під час завантаження відео.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Неможливо завантажити відео через мережевий чи серверний збій або формат не підтримується.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Відтворення відео було припинено через пошкодження або у зв'язку з тим, що відео використовує функції, які не підтримуються вашим браузером.",
"No compatible source was found for this media.": "Сумісні джерела для цього відео відсутні.",
"The media is encrypted and we do not have the keys to decrypt it.": "Відео в зашифрованому вигляді, і ми не маємо ключі для розшифровки.",
"Play Video": "Відтворити відео",
"Close": "Закрити",
"Modal Window": "Модальне вікно",
"This is a modal window": "Це модальне вікно.",
"This modal can be closed by pressing the Escape key or activating the close button.": "Модальне вікно можна закрити, натиснувши клавішу Esc або кнопку закриття вікна.",
", opens captions settings dialog": ", відкриється діалогове вікно налаштування підписів",
", opens subtitles settings dialog": ", відкриється діалогове вікно налаштування субтитрів",
", opens descriptions settings dialog": ", відкриється діалогове вікно налаштування описів",
", selected": ", обраний"
});
@@ -1,26 +0,0 @@
videojs.addLanguage("vi",{
"Play": "Phát",
"Pause": "Tạm dừng",
"Current Time": "Thời gian hiện tại",
"Duration Time": "Độ dài",
"Remaining Time": "Thời gian còn lại",
"Stream Type": "Kiểu Stream",
"LIVE": "TRỰC TIẾP",
"Loaded": "Đã tải",
"Progress": "Tiến trình",
"Fullscreen": "Toàn màn hình",
"Non-Fullscreen": "Thoát toàn màn hình",
"Mute": "Tắt tiếng",
"Unmute": "Bật âm thanh",
"Playback Rate": "Tốc độ phát",
"Subtitles": "Phụ đề",
"subtitles off": "Tắt phụ đề",
"Captions": "Chú thích",
"captions off": "Tắt chú thích",
"Chapters": "Chương",
"You aborted the media playback": "Bạn đã hủy việc phát media.",
"A network error caused the media download to fail part-way.": "Một lỗi mạng dẫn đến việc tải media bị lỗi.",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "Video không tải được, mạng hay server có lỗi hoặc định dạng không được hỗ trợ.",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "Phát media đã bị hủy do một sai lỗi hoặc media sử dụng những tính năng trình duyệt không hỗ trợ.",
"No compatible source was found for this media.": "Không có nguồn tương thích cho media này."
});
@@ -1,27 +0,0 @@
videojs.addLanguage("zh-CN",{
"Play": "播放",
"Pause": "暂停",
"Current Time": "当前时间",
"Duration Time": "时长",
"Remaining Time": "剩余时间",
"Stream Type": "媒体流类型",
"LIVE": "直播",
"Loaded": "加载完毕",
"Progress": "进度",
"Fullscreen": "全屏",
"Non-Fullscreen": "退出全屏",
"Mute": "静音",
"Unmute": "取消静音",
"Playback Rate": "播放码率",
"Subtitles": "字幕",
"subtitles off": "字幕关闭",
"Captions": "内嵌字幕",
"captions off": "内嵌字幕关闭",
"Chapters": "节目段落",
"You aborted the media playback": "视频播放被终止",
"A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
"No compatible source was found for this media.": "无法找到此视频兼容的源。",
"The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。"
});
@@ -1,40 +0,0 @@
videojs.addLanguage("zh-TW",{
"Play": "播放",
"Pause": "暫停",
"Current Time": "目前時間",
"Duration Time": "總共時間",
"Remaining Time": "剩餘時間",
"Stream Type": "串流類型",
"LIVE": "直播",
"Loaded": "載入完畢",
"Progress": "進度",
"Fullscreen": "全螢幕",
"Non-Fullscreen": "退出全螢幕",
"Mute": "靜音",
"Unmute": "取消靜音",
"Playback Rate": " 播放速率",
"Subtitles": "字幕",
"subtitles off": "關閉字幕",
"Captions": "內嵌字幕",
"captions off": "關閉內嵌字幕",
"Chapters": "章節",
"Close Modal Dialog": "關閉對話框",
"Descriptions": "描述",
"descriptions off": "關閉描述",
"Audio Track": "音軌",
"You aborted the media playback": "影片播放已終止",
"A network error caused the media download to fail part-way.": "網路錯誤導致影片下載失敗。",
"The media could not be loaded, either because the server or network failed or because the format is not supported.": "影片因格式不支援或者伺服器或網路的問題無法載入。",
"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由於影片檔案損毀或是該影片使用了您的瀏覽器不支援的功能,播放終止。",
"No compatible source was found for this media.": "無法找到相容此影片的來源。",
"The media is encrypted and we do not have the keys to decrypt it.": "影片已加密,無法解密。",
"Play Video": "播放影片",
"Close": "關閉",
"Modal Window": "對話框",
"This is a modal window": "這是一個對話框",
"This modal can be closed by pressing the Escape key or activating the close button.": "可以按ESC按鍵或啟用關閉按鈕來關閉此對話框。",
", opens captions settings dialog": ", 開啟標題設定對話框",
", opens subtitles settings dialog": ", 開啟字幕設定對話框",
", opens descriptions settings dialog": ", 開啟描述設定對話框",
", selected": ", 選擇"
});
File diff suppressed because it is too large. Load diff
@@ -1 +0,0 @@
.video-js .vjs-audio-button,.video-js .vjs-big-play-button,.video-js .vjs-captions-button,.video-js .vjs-chapters-button,.video-js .vjs-control.vjs-close-button,.video-js .vjs-descriptions-button,.video-js .vjs-fullscreen-control,.video-js .vjs-mouse-display,.video-js .vjs-mute-control,.video-js .vjs-mute-control.vjs-vol-0,.video-js .vjs-mute-control.vjs-vol-1,.video-js .vjs-mute-control.vjs-vol-2,.video-js .vjs-play-control,.video-js .vjs-play-control.vjs-playing,.video-js .vjs-play-progress,.video-js .vjs-subtitles-button,.video-js .vjs-volume-level,.video-js .vjs-volume-menu-button,.video-js .vjs-volume-menu-button.vjs-vol-0,.video-js .vjs-volume-menu-button.vjs-vol-1,.video-js .vjs-volume-menu-button.vjs-vol-2,.video-js.vjs-fullscreen .vjs-fullscreen-control,.vjs-icon-audio,.vjs-icon-audio-description,.vjs-icon-cancel,.vjs-icon-captions,.vjs-icon-chapters,.vjs-icon-circle,.vjs-icon-circle-inner-circle,.vjs-icon-circle-outline,.vjs-icon-cog,.vjs-icon-facebook,.vjs-icon-fullscreen-enter,.vjs-icon-fullscreen-exit,.vjs-icon-gplus,.vjs-icon-hd,.vjs-icon-linkedin,.vjs-icon-pause,.vjs-icon-pinterest,.vjs-icon-play,.vjs-icon-play-circle,.vjs-icon-replay,.vjs-icon-spinner,.vjs-icon-square,.vjs-icon-subtitles,.vjs-icon-tumblr,.vjs-icon-twitter,.vjs-icon-volume-high,.vjs-icon-volume-low,.vjs-icon-volume-mid,.vjs-icon-volume-mute{font-family:VideoJS;font-weight:400;font-style:normal}.video-js,.vjs-no-js{color:#fff;background-color:#000}.video-js .vjs-big-play-button:before,.video-js .vjs-control,.video-js .vjs-control:before,.vjs-menu li,.vjs-no-js{text-align:center}.video-js .vjs-big-play-button:before,.video-js .vjs-control:before,.video-js .vjs-modal-dialog,.vjs-modal-dialog .vjs-modal-dialog-content{position:absolute;top:0;left:0;width:100%;height:100%}@font-face{font-family:VideoJS;src:url(font/VideoJS.eot?#iefix) format("eot")}@font-face{font-family:VideoJS;src:url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAA54AAoAAAAAFmgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAD4AAABWUZFeBWNtYXAAAAE0AAAAOgAAAUriMBC2Z2x5ZgAAAXAAAAouAAAPUFvx6AdoZWFkAAALoAAAACsAAAA2DIPpX2hoZWEAAAvMAAAAGAAAACQOogcgaG10eAAAC+QAAAAPAAAAfNkAAABsb2NhAAAL9AAAAEAAAABAMMg06m1heHAAAAw0AAAAHwAAACABMAB5bmFtZQAADFQAAAElAAACCtXH9aBwb3N0AAANfAAAAPwAAAGBZkSN43icY2BkZ2CcwMDKwMFSyPKMgYHhF4RmjmEIZzzHwMDEwMrMgBUEpLmmMDh8ZPwoxw7iLmSHCDOCCADvEAo+AAB4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGD7K/f8PUvCREUTzM0DVAwEjG8OIBwCPdwbVAAB4nI1Xe1CU1xX/zv1eLItLln0JwrIfC7sJGET2hRJ2N1GUoBJE8AESQEEhmBHjaB7UuBMTO4GMaSu7aY3RNlOdRPNqO2pqRmuTaSZtR6JJILUZk00a/4imjpmiecB303O/XUgMJOPufvd+99xzzz33nN855y4HHH7EfrGfIxwHRiANvF/sH71I9BzHszmpW+rGOQOXxXE6YhI4PoMT8zkT4cDFuf1cwMrZJI5cglM0HKVv0MaUFDgIFfg9mJJCG+kbKn1JkqBOVaFOkuhLpARq8fu0Nnc9/zdvfY9PxXW4PdH0C6N+PCejhorxFjAqRjgFRXSINEARbBGsoxcFK7IJmr4OycFJnInL59zIXwxui80fkGRbEHyosMWaATJKUfCskmwJQsAWANkmnIGOhlf514h7U8HNIv3owoHB0WMt0Eb3sx0guLi5pq/8Ny1q6969fKR9X9GBV6dPv6dp04K99SOwtmyPl47ApRa6n4ZpP1yjr5fn7MmYP/vXLUJs715UguklHBaHOZHZmG1N9FAIW2mf0MqWCIdo/8RZ1yGfxKUldDcGIbFA7ICO+vqOMSPTh/ZrSqgHi/bB/O8E8Mnzp+M+acxfpsTShBwej26TiGxBn7m4eEIO+Rueu6Hj+IFBnh88cAEUEQ//nVLx5C7kf+yIR47QEe+eMlhz9SqsGbe3hh2R03NGzoY6O42Kz8l7fB6fAk6LYnTyFo/FYyT6GGyNx2Jx2sdH4rA1Fo/HyCXaFyOp8dhYBCfJb2NIn1ImE6CYNGmgSTb52DawJR6jfXEmDU4xyTEmpgHHOIStoxfjSGdkbsK2w2jbdMQG4sgAstEONgURYCwGHhEhhscioQaAhhCf7McifEQc0l6+mxj9nI+gmSdiQ0Zbm7gZnIO7GSMEXG6UDAVocxAV8GcEXCKg1a02RcTtwANWRGIAyElor6n/+ZU2yOB3+T77Hb1MLqhn4KHVnQBjJnqe9QZSon6Kc5DxAD2vMdPL/BXSmQGwspa67z9wLUjdi9TN7QC7lyyBr9rpt7uXVC1CMpyjKRoXnGPHTuiaPLsNdc2dbAFQLAooPkXEh33FodHl4XpC6sPCIa0ftUIhHSYXVSu5iME+DIXsbZJ51BeidCgajcai43jU9nVzoSn2dPqcFvSoxSzJzgRKAx47WMRxOrIj3Wf0+hndxhJTiOkSEqxar3b3RKM9hY64oxBA64ieURLvCfpkDb8siBdUJ1bgT+urJ5PGfewQrmm5R5+0HmfyIPySD7OYkT0WxRePah8oEiyjlxIP74thVoRTURpmL6QhGuWS+QDjdANXjIM8SQa/1w128ODx0Qp4aLMNg9+JL3joUn8AMxW+aLNiuKjarn4uyyTdXjOzZTsh21uwldUvJoYza+zELALfu3p1L8/3krtyZ0Ag058J3hxHghvbGZn0dHZy6Mim/7Blre4lpHd1c28yVqRViO153F2oIWoXCIKbL4Z0cM1iaQn9mI5KuV2SzEvWXJDMNtkANpMdQoDDhIdD4A/YrP6Aye9ysxyE+uOEAcTDorgvVZJjcua043PnZ/PmdDqcbibZlXOOT8uSo7Kof0YUn9GL+Jo17ficymxiTofC6znUso0DhAxs1Fo+kF+d36vLmgZ8mk5cdGv2mwYj5k3Dm9m3LhJ1aVRNm6HrTbLgYAoWXDhDd/u4PGy5CT+xGMdiaBovewUCF/1BiWNljI9MLn7jeScpg+WyH6mfU62eVDql7hsrmvx1ezp/YldE2LhjbkiDnAn8tGy/MW3IXRMYJduvq9HpmIcKuFt+JCtgdGEGKAcF6UacVwIYbVPGfw/+YuNBS4cx/CUHcnyfc+wRDMtTr72mMSBjT/yn/GKSdeDWQUCH6Xoqq5R10RE60gV6erUL0iCti16d0hZjxut4QI/rEpgSh6WjnJXdBXRg1GKCucGJPtFqM27aD1tOqqKonsQ2KsFSSmEpmvRlsR+TcD9OFwrqXxIclL4sJTnGMSuG8KpkZvKdeVIOKDyWSyPLV16/p1QMPbP8NihwUzr47bdnXtwtjdCvqqpO0H+pOvIl3Pzv46e5CT/tQjklXCXXym1AaWY7bzHLkuDMc7ldKCvgxzLn8wYkJLBhEDyK7MT8bTbwbkxbfp+3mKAGsmTBpabSIEECzMIcQlzOPAMKsxMs7uhsnxPLuofPDTc1hkuq6MX9j16YU7CqegcYHbmWYuvAP6tCS97tgWf7dlQvnl25YPavXLVZvrzQPeHCpZmzzEUVq/xzu5sChnSTPTW7oOYmh69z4zL/gk3b+O6hoa733uviP82vnFcbqWlc9tDmZa23LVzaV1yXURi+JX+28NeBuj3+O8IrQ080Vm1eWB4OKjPmrJu7c1udWynvKF6/vs479lSW9+5gZkn+dKfellNGDPllzeULustz+A0bPvhgw7lkvEUwn/N4Ty7U7nhGsEpFkOfy+kutbOh1JQxhVDJumoW11hnkPThznh6FFlhfT+ra1x9sF56kx5YuDzVY9PQYAYA7iblw4frQ4TPCk2MK/xGU3rlmze62trHz6lsko+v+So/do74PT8KLine truncated
Binary file not shown.
File diff suppressed because it is too large. Load diff
-19
View File
@@ -1,19 +0,0 @@
/**
* @license
* Video.js 5.19.2 <http://videojs.com/>
* Copyright Brightcove, Inc. <https://www.brightcove.com/>
* Available under Apache License Version 2.0
* <https://github.com/videojs/video.js/blob/master/LICENSE>
*
* Includes vtt.js <https://github.com/mozilla/vtt.js>
* Available under Apache License Version 2.0
* <https://github.com/mozilla/vtt.js/blob/master/LICENSE>
*/
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.videojs=a()}}(function(){var a;return function b(a,c,d){function e(g,h){if(!c[g]){if(!a[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};a[g][0].call(k.exports,function(b){var c=a[g][1][b];return e(c?c:b)},k,k.exports,b,a,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function f(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function g(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var h=a(2),i=d(h),j=a(5),k=d(j),l=function(a){function b(){return e(this,b),f(this,a.apply(this,arguments))}return g(b,a),b.prototype.buildCSSClass=function(){return"vjs-big-play-button"},b.prototype.handleClick=function(a){this.player_.play();var b=this.player_.getChild("controlBar"),c=b&&b.getChild("playToggle");if(!c)return void this.player_.focus();this.setTimeout(function(){c.focus()},1)},b}(i["default"]);l.prototype.controlText_="Play Video",k["default"].registerComponent("BigPlayButton",l),c["default"]=l},{2:2,5:5}],2:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function f(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function g(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var h=a(3),i=d(h),j=a(5),k=d(j),l=a(86),m=d(l),n=a(88),o=function(a){function b(){return e(this,b),f(this,a.apply(this,arguments))}return g(b,a),b.prototype.createEl=function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"button",b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};b=(0,n.assign)({className:this.buildCSSClass()},b),"button"!==a&&(m["default"].warn("Creating a Button with an HTML element of "+a+" is deprecated; use ClickableComponent instead."),b=(0,n.assign)({tabIndex:0},b),c=(0,n.assign)({role:"button"},c)),c=(0,n.assign)({type:"button","aria-live":"polite"},c);var d=k["default"].prototype.createEl.call(this,a,b,c);return this.createControlTextEl(d),d},b.prototype.addChild=function(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},c=this.constructor.name;return m["default"].warn("Adding an actionable (user controllable) child to a Button ("+c+") is not supported; use a ClickableComponent instead."),k["default"].prototype.addChild.call(this,a,b)},b.prototype.enable=function(){a.prototype.enable.call(this),this.el_.removeAttribute("disabled")},b.prototype.disable=function(){a.prototype.disable.call(this),this.el_.setAttribute("disabled","disabled")},b.prototype.handleKeyPress=function(b){32!==b.which&&13!==b.which&&a.prototype.handleKeyPress.call(this,b)},b}(i["default"]);k["default"].registerComponent("Button",o),c["default"]=o},{3:3,5:5,86:86,88:88}],3:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(81),l=d(k),m=a(82),n=d(m),o=a(83),p=d(o)Line truncated
;if(b)for(var c=b.length-1;c>=0;c--)a.addChild(new n["default"](this.player(),{rate:b[c]+"x"}));return a},b.prototype.updateARIAAttributes=function(){this.el().setAttribute("aria-valuenow",this.player().playbackRate())},b.prototype.handleClick=function(a){for(var b=this.player().playbackRate(),c=this.playbackRates(),d=c[0],e=0;e<c.length;e++)if(c[e]>b){d=c[e];break}this.player().playbackRate(d)},b.prototype.playbackRates=function(){return this.options_.playbackRates||this.options_.playerOptions&&this.options_.playerOptions.playbackRates},b.prototype.playbackRateSupported=function(){return this.player().tech_&&this.player().tech_.featuresPlaybackRate&&this.playbackRates()&&this.playbackRates().length>0},b.prototype.updateVisibility=function(a){this.playbackRateSupported()?this.removeClass("vjs-hidden"):this.addClass("vjs-hidden")},b.prototype.updateLabel=function(a){this.playbackRateSupported()&&(this.labelEl_.innerHTML=this.player().playbackRate()+"x")},b}(j["default"]);s.prototype.controlText_="Playback Rate",p["default"].registerComponent("PlaybackRateMenuButton",s),c["default"]=s},{14:14,47:47,49:49,5:5,81:81}],14:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function f(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function g(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var h=a(48),i=d(h),j=a(5),k=d(j),l=function(a){function b(c,d){e(this,b);var g=d.rate,h=parseFloat(g,10);d.label=g,d.selected=1===h,d.selectable=!0;var i=f(this,a.call(this,c,d));return i.label=g,i.rate=h,i.on(c,"ratechange",i.update),i}return g(b,a),b.prototype.handleClick=function(b){a.prototype.handleClick.call(this),this.player().playbackRate(this.rate)},b.prototype.update=function(a){this.selected(this.player().playbackRate()===this.rate)},b}(i["default"]);l.prototype.contentElType="button",k["default"].registerComponent("PlaybackRateMenuItem",l),c["default"]=l},{48:48,5:5}],15:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(81),l=d(k),m=function(a){function b(c,d){f(this,b);var e=g(this,a.call(this,c,d));return e.partEls_=[],e.on(c,"progress",e.update),e}return h(b,a),b.prototype.createEl=function(){return a.prototype.createEl.call(this,"div",{className:"vjs-load-progress",innerHTML:'<span class="vjs-control-text"><span>'+this.localize("Loaded")+"</span>: 0%</span>"})},b.prototype.update=function(a){var b=this.player_.buffered(),c=this.player_.duration(),d=this.player_.bufferedEnd(),e=this.partEls_,f=function(a,b){var c=a/b||0;return 100*(c>=1?1:c)+"%"};this.el_.style.width=f(d,c);for(var g=0;g<b.length;g++){var h=b.start(g),i=b.end(g),j=e[g];j||(j=this.el_.appendChild(l.createEl()),e[g]=j),j.style.left=f(h,d),j.style.width=f(i-h,d)}for(var k=e.length;k>b.length;k--)this.el_.removeChild(e[k-1]);e.length=b.length},b}(j["default"]);j["default"].registerComponent("LoadProgressBar",m),c["default"]=m},{5:5,81:81}],16:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=aLine truncated
constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(81),l=d(k),m=a(84),n=e(m),o=function(a){function b(c,d){f(this,b);var e=g(this,a.call(this,c,d));return e.on(c,"timeupdate",e.updateContent),e}return h(b,a),b.prototype.createEl=function(){var b=a.prototype.createEl.call(this,"div",{className:"vjs-current-time vjs-time-control vjs-control"});return this.contentEl_=l.createEl("div",{className:"vjs-current-time-display",innerHTML:'<span class="vjs-control-text">Current Time </span>0:00'},{"aria-live":"off"}),b.appendChild(this.contentEl_),b},b.prototype.updateContent=function(a){var b=this.player_.scrubbing()?this.player_.getCache().currentTime:this.player_.currentTime(),c=this.localize("Current Time"),d=(0,n["default"])(b,this.player_.duration());d!==this.formattedTime_&&(this.formattedTime_=d,this.contentEl_.innerHTML='<span class="vjs-control-text">'+c+"</span> "+d)},b}(j["default"]);j["default"].registerComponent("CurrentTimeDisplay",o),c["default"]=o},{5:5,81:81,84:84}],33:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(81),l=d(k),m=a(84),n=e(m),o=function(a){function b(c,d){f(this,b);var e=g(this,a.call(this,c,d));return e.on(c,"durationchange",e.updateContent),e.on(c,"timeupdate",e.updateContent),e.on(c,"loadedmetadata",e.updateContent),e}return h(b,a),b.prototype.createEl=function(){var b=a.prototype.createEl.call(this,"div",{className:"vjs-duration vjs-time-control vjs-control"});return this.contentEl_=l.createEl("div",{className:"vjs-duration-display",innerHTML:'<span class="vjs-control-text">'+this.localize("Duration Time")+"</span> 0:00"},{"aria-live":"off"}),b.appendChild(this.contentEl_),b},b.prototype.updateContent=function(a){var b=this.player_.duration();if(b&&this.duration_!==b){this.duration_=b;var c=this.localize("Duration Time"),d=(0,n["default"])(b);this.contentEl_.innerHTML='<span class="vjs-control-text">'+c+"</span> "+d}},b}(j["default"]);j["default"].registerComponent("DurationDisplay",o),c["default"]=o},{5:5,81:81,84:84}],34:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(81),l=d(k),m=a(84),n=e(m),o=function(a){function b(c,d){f(this,b);var e=g(this,a.call(this,c,d));return e.on(c,"timeupdate",e.updateContent),e.on(c,"durationchange",e.updateContent),e}return h(b,a),b.prototype.createEl=function(){var b=a.prototype.createEl.call(this,"div",{className:"vjs-remaining-time vjs-time-control vjs-control"});return this.contentEl_=l.createEl("div",{className:"vjs-remaining-time-display",innerHTML:'<span class="vjs-control-text">'+this.localize("Remaining Time")+"</span> -0:00"},{"aria-live":"off"}),b.appendChild(this.contentEl_),b},b.prototype.updateContent=function(a){if(this.player_.duration()){var b=this.localize("Remaining Time"),c=(0,n["default"])(this.player_.remainingTime());c!==this.formattedTime_&&(this.formattedTime_=c,this.contentEl_.innerHTML='<span class="vjs-control-text">'+b+"</span> -"+c)}},b}(j["default"]);j["default"].registerComponent("RemainingTimeDisplay",o),c["default"]=o},{5:5,81:81,84:84}],35:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function f(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");retLine truncated
var b=this.contentEl(),c=b.parentNode,d=b.nextSibling;return this.trigger("beforemodalfill"),this.hasBeenFilled_=!0,c.removeChild(b),this.empty(),j.insertContent(b,a),this.trigger("modalfill"),d?c.insertBefore(b,d):c.appendChild(b),this},b.prototype.empty=function(){return this.trigger("beforemodalempty"),j.emptyEl(this.contentEl()),this.trigger("modalempty"),this},b.prototype.content=function(a){return void 0!==a&&(this.content_=a),this.content_},b}(n["default"]);o.prototype.options_={pauseOnOpen:!0,temporary:!0},n["default"].registerComponent("ModalDialog",o),c["default"]=o},{5:5,81:81,83:83}],51:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(94),l=e(k),m=a(95),n=e(m),o=a(82),p=d(o),q=a(81),r=d(q),s=a(83),t=d(s),u=a(85),v=d(u),w=a(78),x=d(w),y=a(86),z=e(y),A=a(91),B=e(A),C=a(90),D=a(79),E=a(89),F=d(E),G=a(44),H=e(G),I=a(46),J=e(I),K=a(97),L=e(K),M=a(88),N=a(87),O=e(N),P=a(69),Q=e(P),R=a(50),S=e(R),T=a(62),U=e(T),V=a(63),W=e(V),X=a(76),Y=e(X);a(61),a(59),a(55),a(68),a(45),a(1),a(4),a(8),a(41),a(71),a(60);var Z=["progress","abort","suspend","emptied","stalled","loadedmetadata","loadeddata","timeupdate","ratechange","volumechange","texttrackchange"],$=function(a){function b(c,d,e){if(f(this,b),c.id=c.id||"vjs_video_"+v.newGUID(),d=(0,M.assign)(b.getTagSettings(c),d),d.initChildren=!1,d.createEl=!1,d.reportTouchActivity=!1,!d.language)if("function"==typeof c.closest){var h=c.closest("[lang]");h&&(d.language=h.getAttribute("lang"))}else for(var i=c;i&&1===i.nodeType;){if(r.getElAttributes(i).hasOwnProperty("lang")){d.language=i.getAttribute("lang");break}i=i.parentNode}var j=g(this,a.call(this,null,d,e));if(!j.options_||!j.options_.techOrder||!j.options_.techOrder.length)throw new Error("No techOrder specified. Did you overwrite videojs.options instead of just changing the properties you want to override?");if(j.tag=c,j.tagAttributes=c&&r.getElAttributes(c),j.language(j.options_.language),d.languages){var k={};Object.getOwnPropertyNames(d.languages).forEach(function(a){k[a.toLowerCase()]=d.languages[a]}),j.languages_=k}else j.languages_=b.prototype.options_.languages;j.cache_={},j.poster_=d.poster||"",j.controls_=!!d.controls,c.controls=!1,j.scrubbing_=!1,j.el_=j.createEl();var l=(0,O["default"])(j.options_);if(d.plugins){var m=d.plugins;Object.getOwnPropertyNames(m).forEach(function(a){"function"==typeof this[a]?this[a](m[a]):z["default"].error("Unable to find plugin:",a)},j)}return j.options_.playerOptions=l,j.initChildren(),j.isAudio("audio"===c.nodeName.toLowerCase()),j.controls()?j.addClass("vjs-controls-enabled"):j.addClass("vjs-controls-disabled"),j.el_.setAttribute("role","region"),j.isAudio()?j.el_.setAttribute("aria-label","audio player"):j.el_.setAttribute("aria-label","video player"),j.isAudio()&&j.addClass("vjs-audio"),j.flexNotSupported_()&&j.addClass("vjs-no-flex"),x.IS_IOS||j.addClass("vjs-workinghover"),b.players[j.id_]=j,j.userActive(!0),j.reportUserActivity(),j.listenForUserActivity_(),j.on("fullscreenchange",j.handleFullscreenChange_),j.on("stageclick",j.handleStageClick_),j}return h(b,a),b.prototype.dispose=function(){this.trigger("dispose"),this.off("dispose"),this.styleEl_&&this.styleEl_.parentNode&&this.styleEl_.parentNode.removeChild(this.styleEl_),b.players[this.id_]=null,this.tag&&this.tag.player&&(this.tag.player=null),this.el_&&this.el_.player&&(this.el_.player=null),this.tech_&&this.tech_.dispose(),a.prototype.dispose.call(this)},b.prototype.createEl=function(){var b=this.tag,c=void 0,d=this.playerElIngest_=b.parentNode&&b.parentNode.hasAttribute&&b.parentNode.hasAttribute("data-vjs-player");c=this.el_=d?b.parentNode:a.prototype.createEl.call(this,"div"),b.setAttribute("tabindex","-1"),b.removeAttribute("width"),b.removeAttribute("height");var e=r.getElAttributes(b);if(Object.getOwnPropertyNames(e).forEach(function(a){"class"===a?c.className+=" "+e[a]:c.setAttribute(a,e[a])}),b.playerId=b.id,b.id+="_html5_api",b.className="vjs-tech",b.player=c.player=this,this.addClass("vjs-paused"),n["default"].VIDEOJS_NO_DYNAMIC_STYLE!==!0){this.styleEl_=F.createStyleElement("vjs-styles-dimensions");var f=r.$(".vjs-styles-defaults"),g=r.$("head");g.insertBefore(this.styleEl_,f?f.nextSibling:g.firstChild)}thiLine truncated
;return e.update(),c.on("posterchange",n.bind(e,e.update)),e}return h(b,a),b.prototype.dispose=function(){this.player().off("posterchange",this.update),a.prototype.dispose.call(this)},b.prototype.createEl=function(){var a=p.createEl("div",{className:"vjs-poster",tabIndex:-1});return r.BACKGROUND_SIZE_SUPPORTED||(this.fallbackImg_=p.createEl("img"),a.appendChild(this.fallbackImg_)),a},b.prototype.update=function(a){var b=this.player().poster();this.setSrc(b),b?this.show():this.hide()},b.prototype.setSrc=function(a){if(this.fallbackImg_)this.fallbackImg_.src=a;else{var b="";a&&(b='url("'+a+'")'),this.el_.style.backgroundImage=b}},b.prototype.handleClick=function(a){this.player_.controls()&&(this.player_.paused()?this.player_.play():this.player_.pause())},b}(j["default"]);l["default"].registerComponent("PosterImage",s),c["default"]=s},{3:3,5:5,78:78,81:81,83:83}],56:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function f(a,b){b&&(p=b),n["default"].setTimeout(q,a)}c.__esModule=!0,c.hasLoaded=c.autoSetupTimeout=c.autoSetup=void 0;var g=a(81),h=e(g),i=a(82),j=e(i),k=a(94),l=d(k),m=a(95),n=d(m),o=!1,p=void 0,q=function(){if(h.isReal()){var a=l["default"].getElementsByTagName("video"),b=l["default"].getElementsByTagName("audio"),c=[];if(a&&a.length>0)for(var d=0,e=a.length;d<e;d++)c.push(a[d]);if(b&&b.length>0)for(var g=0,i=b.length;g<i;g++)c.push(b[g]);if(c&&c.length>0)for(var j=0,k=c.length;j<k;j++){var m=c[j];if(!m||!m.getAttribute){f(1);break}if(void 0===m.player){var n=m.getAttribute("data-setup");null!==n&&p(m)}}else o||f(1)}};h.isReal()&&"complete"===l["default"].readyState?o=!0:j.one(n["default"],"load",function(){o=!0});var r=function(){return o};c.autoSetup=q,c.autoSetupTimeout=f,c.hasLoaded=r},{81:81,82:82,94:94,95:95}],57:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(5),j=e(i),k=a(81),l=d(k),m=a(88),n=function(a){function b(c,d){f(this,b);var e=g(this,a.call(this,c,d));return e.bar=e.getChild(e.options_.barName),e.vertical(!!e.options_.vertical),e.on("mousedown",e.handleMouseDown),e.on("touchstart",e.handleMouseDown),e.on("focus",e.handleFocus),e.on("blur",e.handleBlur),e.on("click",e.handleClick),e.on(c,"controlsvisible",e.update),e.on(c,e.playerEvent,e.update),e}return h(b,a),b.prototype.createEl=function(b){var c=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},d=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return c.className=c.className+" vjs-slider",c=(0,m.assign)({tabIndex:0},c),d=(0,m.assign)({role:"slider","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,tabIndex:0},d),a.prototype.createEl.call(this,b,c,d)},b.prototype.handleMouseDown=function(a){var b=this.bar.el_.ownerDocument;a.preventDefault(),l.blockTextSelection(),this.addClass("vjs-sliding"),this.trigger("slideractive"),this.on(b,"mousemove",this.handleMouseMove),this.on(b,"mouseup",this.handleMouseUp),this.on(b,"touchmove",this.handleMouseMove),this.on(b,"touchend",this.handleMouseUp),this.handleMouseMove(a)},b.prototype.handleMouseMove=function(a){},b.prototype.handleMouseUp=function(){var a=this.bar.el_.ownerDocument;l.unblockTextSelection(),this.removeClass("vjs-sliding"),this.trigger("sliderinactive"),this.off(a,"mousemove",this.handleMouseMove),this.off(a,"mouseup",this.handleMouseUp),this.off(a,"touchmove",this.handleMouseMove),this.off(a,"touchend",this.handleMouseUp),this.update()},b.prototype.update=function(){if(this.el_){var a=this.getPercent(),b=this.bar;if(b){("number"!=typeof a||a!==a||a<0||a===1/0)&&(a=0);var c=(100*a).toFixed(2)+"%";this.vertical()?b.el().style.height=c:b.el().style.width=c}}},b.prototype.calculateDistance=function(a){var b=l.getPointerPosition(this.el_,a);return this.vertical()?b.y:b.x},b.prototype.handleFocus=function(){this.on(this.bar.el_.ownerDocument,"keydown",this.handleKeyPress)},b.prototype.handleKeyPress=function(a){37===a.which||40===a.which?(a.preventDefault(),this.stepBack()):38!==a.which&&39!==a.which||(a.preventDefault(),this.stepForward())},b.prototype.handleBlur=function(){this.off(this.bar.el_.Line truncated
d.addEventListener("addtrack",c),a.on("dispose",function(){d.removeEventListener("removetrack",c),d.removeEventListener("addtrack",c)})})},c.prototype.addWebVttScript_=function(){var b=this;if(!I["default"].WebVTT)if(K["default"].body.contains(this.el())){var c=a(105);if(!this.options_["vtt.js"]&&(0,L.isPlain)(c)&&Object.keys(c).length>0)return void this.trigger("vttjsloaded");var d=K["default"].createElement("script");d.src=this.options_["vtt.js"]||"https://vjs.zencdn.net/vttjs/0.12.3/vtt.min.js",d.onload=function(){b.trigger("vttjsloaded")},d.onerror=function(){b.trigger("vttjserror")},this.on("dispose",function(){d.onload=null,d.onerror=null}),I["default"].WebVTT=!0,this.el().parentNode.appendChild(d)}else this.ready(this.addWebVttScript_)},c.prototype.emulateTextTracks=function(){var a=this,b=this.textTracks();if(b){var c=this.remoteTextTracks(),d=function(a){return b.addTrack_(a.track)},e=function(a){return b.removeTrack_(a.track)};c.on("addtrack",d),c.on("removetrack",e),this.addWebVttScript_();var f=function(){return a.trigger("texttrackchange")},g=function(){f();for(var a=0;a<b.length;a++){var c=b[a];c.removeEventListener("cuechange",f),"showing"===c.mode&&c.addEventListener("cuechange",f)}};g(),b.addEventListener("change",g),b.addEventListener("addtrack",g),b.addEventListener("removetrack",g),this.on("dispose",function(){c.off("addtrack",d),c.off("removetrack",e),b.removeEventListener("change",g),b.removeEventListener("addtrack",g),b.removeEventListener("removetrack",g);for(var a=0;a<b.length;a++){b[a].removeEventListener("cuechange",f)}})}},c.prototype.videoTracks=function(){return this.videoTracks_=this.videoTracks_||new w["default"],this.videoTracks_},c.prototype.audioTracks=function(){return this.audioTracks_=this.audioTracks_||new y["default"],this.audioTracks_},c.prototype.textTracks=function(){return this.textTracks_=this.textTracks_||new u["default"],this.textTracks_},c.prototype.remoteTextTracks=function(){return this.remoteTextTracks_=this.remoteTextTracks_||new u["default"],this.remoteTextTracks_},c.prototype.remoteTextTrackEls=function(){return this.remoteTextTrackEls_=this.remoteTextTrackEls_||new o["default"],this.remoteTextTrackEls_},c.prototype.addTextTrack=function(a,b,c){if(!a)throw new Error("TextTrack kind is required but was not provided");return i(this,a,b,c)},c.prototype.createRemoteTextTrack=function(a){var b=(0,q["default"])(a,{tech:this});return new m["default"](b)},c.prototype.addRemoteTextTrack=function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},b=arguments[1],c=this.createRemoteTextTrack(a);return b!==!0&&b!==!1&&(C["default"].warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'),b=!0),this.remoteTextTrackEls().addTrackElement_(c),this.remoteTextTracks().addTrack_(c.track),b!==!0&&this.autoRemoteTextTracks_.addTrack_(c.track),c},c.prototype.removeRemoteTextTrack=function(a){var b=this.remoteTextTrackEls().getTrackElementByTrack_(a);this.remoteTextTrackEls().removeTrackElement_(b),this.remoteTextTracks().removeTrack_(a),this.autoRemoteTextTracks_.removeTrack_(a)},c.prototype.setPoster=function(){},c.prototype.canPlayType=function(){return""},c.isTech=function(a){return a.prototype instanceof c||a instanceof c||a===c},c.registerTech=function(a,b){if(c.techs_||(c.techs_={}),!c.isTech(b))throw new Error("Tech "+a+" must be a Tech");return c.techs_[a]=b,b},c.getTech=function(a){return c.techs_&&c.techs_[a]?c.techs_[a]:I["default"]&&I["default"].videojs&&I["default"].videojs[a]?(C["default"].warn("The "+a+" tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)"),I["default"].videojs[a]):void 0},c}(k["default"]);M.prototype.textTracks_,M.prototype.audioTracks_,M.prototype.videoTracks_,M.prototype.featuresVolumeControl=!0,M.prototype.featuresFullscreenResize=!1,M.prototype.featuresPlaybackRate=!1,M.prototype.featuresProgressEvents=!1,M.prototype.featuresTimeupdateEvents=!1,M.prototype.featuresNativeTextTracks=!1,M.withSourceHandlers=function(a){a.registerSourceHandler=function(b,c){var d=a.sourceHandlers;d||(d=a.sourceHandlers=[]),void 0===c&&(c=d.length),d.splice(c,0,b)},a.canPlayType=function(b){for(var c=a.sourceHandlers||[],d=void 0,e=0;e<c.length;e++)if(d=c[e].canPlayType(b))return d;return""},a.selectSourceHandler=function(b,c){for(var d=a.sourceHandlers||[],e=0;e<d.length;e++)if(d[e].canHandleSource(b,c))return d[e];return null},a.canPlaySource=function(b,c){var d=a.selectSourceHandler(b,c);return d?d.canHandleSource(b,c):""},["seekable","duration"].forEach(function(a){var b=this[a];"function"==typeof b&&(this[a]=function(){return this.sourceHandler_&&this.sourceHandler_[a]?this.sourceHandler_[a].apply(this.sourceHandler_,arguments):b.apply(this,arguments)})},a.prototype),a.prototype.setSource=function(b){var c=a.selectSourceHandler(b,this.options_);return c||(a.nativeSourceHandler?c=Line truncated
var e=this.cues[c];e.startTime<=a&&e.endTime>=a?b.push(e):e.startTime===e.endTime&&e.startTime<=a&&e.startTime+.5>=a&&b.push(e)}if(r=!1,b.length!==this.activeCues_.length)r=!0;else for(var f=0;f<b.length;f++)this.activeCues_.indexOf(b[f])===-1&&(r=!0);return this.activeCues_=b,q.setCues_(this.activeCues_),q},set:function(){}}),h.src?(n.src=h.src,B(h.src,n)):n.loaded_=!0,d=n,g(c,d)}return h(b,a),b.prototype.addCue=function(a){var b=a;if(q["default"].vttjs&&!(a instanceof q["default"].vttjs.VTTCue)){b=new q["default"].vttjs.VTTCue(a.startTime,a.endTime,a.text);for(var c in a)c in b||(b[c]=a[c]);b.id=a.id,b.originalCue_=a}var d=this.tech_.textTracks();if(d)for(var e=0;e<d.length;e++)d[e]!==this&&d[e].removeCue(b);this.cues_.push(b),this.cues.setCues_(this.cues_)},b.prototype.removeCue=function(a){for(var b=this.cues_.length;b--;){var c=this.cues_[b];if(c===a||c.originalCue_&&c.originalCue_===a){this.cues_.splice(b,1),this.cues.setCues_(this.cues_);break}}},b}(s["default"]);C.prototype.allowedEvents_={cuechange:"cuechange"},c["default"]=C},{67:67,73:73,75:75,78:78,83:83,86:86,87:87,92:92,95:95,99:99}],73:[function(a,b,c){"use strict";c.__esModule=!0;c.VideoTrackKind={alternative:"alternative",captions:"captions",main:"main",sign:"sign",subtitles:"subtitles",commentary:"commentary"},c.AudioTrackKind={alternative:"alternative",descriptions:"descriptions",main:"main","main-desc":"main-desc",translation:"translation",commentary:"commentary"},c.TextTrackKind={subtitles:"subtitles",captions:"captions",descriptions:"descriptions",chapters:"chapters",metadata:"metadata"},c.TextTrackMode={disabled:"disabled",hidden:"hidden",showing:"showing"}},{}],74:[function(a,b,c){"use strict";function d(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function e(a){return a&&a.__esModule?a:{"default":a}}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(42),j=e(i),k=a(78),l=d(k),m=a(94),n=e(m),o=function(a){function b(){var c,d=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;f(this,b);var h=g(this,a.call(this));if(!e&&(e=h,l.IS_IE8)){e=n["default"].createElement("custom");for(var i in b.prototype)"constructor"!==i&&(e[i]=b.prototype[i])}e.tracks_=[],Object.defineProperty(e,"length",{get:function(){return this.tracks_.length}});for(var j=0;j<d.length;j++)e.addTrack_(d[j]);return c=e,g(h,c)}return h(b,a),b.prototype.addTrack_=function(a){var b=this.tracks_.length;""+b in this||Object.defineProperty(this,b,{get:function(){return this.tracks_[b]}}),this.tracks_.indexOf(a)===-1&&(this.tracks_.push(a),this.trigger({track:a,type:"addtrack"}))},b.prototype.removeTrack_=function(a){for(var b=void 0,c=0,d=this.length;c<d;c++)if(this[c]===a){b=this[c],b.off&&b.off(),this.tracks_.splice(c,1);break}b&&this.trigger({track:b,type:"removetrack"})},b.prototype.getTrackById=function(a){for(var b=null,c=0,d=this.length;c<d;c++){var e=this[c];if(e.id===a){b=e;break}}return b},b}(j["default"]);o.prototype.allowedEvents_={change:"change",addtrack:"addtrack",removetrack:"removetrack"};for(var p in o.prototype.allowedEvents_)o.prototype["on"+p]=null;c["default"]=o},{42:42,78:78,94:94}],75:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{"default":a}}function e(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b}function f(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function g(a,b){if(!a)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!b||"object"!=typeof b&&"function"!=typeof b?a:b}function h(a,b){if("function"!=typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}c.__esModule=!0;var i=a(78),j=e(i),k=a(94),l=d(k),m=a(85),n=e(m),o=a(42),p=d(o),q=function(a){function b(){var c,d=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};f(this,b);var e=g(this,a.call(this)),h=e;if(j.IS_IE8){h=l["default"].createElement("custom");for(var i in b.prototype)"constructor"!==i&&(h[i]=b.prototype[i])}var k={id:d.id||"vjs_track_"+n.newGUID(),kind:dLine truncated
if("undefined"==typeof a.callback)throw new Error("callback argument missing");var h=!1,j=function(b,c,d){h||(h=!0,a.callback(b,c,d))},k=a.xhr||null;k||(k=a.cors||a.useXDR?new g.XDomainRequest:new g.XMLHttpRequest);var l,n,o,p=k.url=a.uri||a.url,q=k.method=a.method||"GET",r=a.body||a.data,s=k.headers=a.headers||{},t=!!a.sync,u=!1,v={body:void 0,headers:{},statusCode:0,method:q,url:p,rawRequest:k};if("json"in a&&a.json!==!1&&(u=!0,s.accept||s.Accept||(s.Accept="application/json"),"GET"!==q&&"HEAD"!==q&&(s["content-type"]||s["Content-Type"]||(s["Content-Type"]="application/json"),r=JSON.stringify(a.json===!0?r:a.json))),k.onreadystatechange=b,k.onload=f,k.onerror=d,k.onprogress=function(){},k.onabort=function(){n=!0},k.ontimeout=d,k.open(q,p,!t,a.username,a.password),t||(k.withCredentials=!!a.withCredentials),!t&&a.timeout>0&&(o=setTimeout(function(){if(!n){n=!0,k.abort("timeout");var a=new Error("XMLHttpRequest timeout");a.code="ETIMEDOUT",d(a)}},a.timeout)),k.setRequestHeader)for(l in s)s.hasOwnProperty(l)&&k.setRequestHeader(l,s[l]);else if(a.headers&&!e(a.headers))throw new Error("Headers cannot be set on an XDomainRequest object");return"responseType"in a&&(k.responseType=a.responseType),"beforeSend"in a&&"function"==typeof a.beforeSend&&a.beforeSend(k),k.send(r||null),k}function i(a){if("document"===a.responseType)return a.responseXML;var b=a.responseXML&&"parsererror"===a.responseXML.documentElement.nodeName;return""!==a.responseType||b?null:a.responseXML}function j(){}var k=a(95),l=a(100),m=a(103),n=a(104);b.exports=g,g.XMLHttpRequest=k.XMLHttpRequest||j,g.XDomainRequest="withCredentials"in new g.XMLHttpRequest?g.XMLHttpRequest:k.XDomainRequest,d(["get","put","post","patch","head","delete"],function(a){g["delete"===a?"del":a]=function(b,c,d){return c=f(b,c,d),c.method=a.toUpperCase(),h(c)}})},{100:100,103:103,104:104,95:95}],100:[function(a,b,c){function d(a){var b=e.call(a);return"[object Function]"===b||"function"==typeof a&&"[object RegExp]"!==b||"undefined"!=typeof window&&(a===window.setTimeout||a===window.alert||a===window.confirm||a===window.prompt)}b.exports=d;var e=Object.prototype.toString},{}],101:[function(a,b,c){function d(a,b,c){if(!h(b))throw new TypeError("iterator must be a function");arguments.length<3&&(c=this),"[object Array]"===i.call(a)?e(a,b,c):"string"==typeof a?f(a,b,c):g(a,b,c)}function e(a,b,c){for(var d=0,e=a.length;d<e;d++)j.call(a,d)&&b.call(c,a[d],d,a)}function f(a,b,c){for(var d=0,e=a.length;d<e;d++)b.call(c,a.charAt(d),d,a)}function g(a,b,c){for(var d in a)j.call(a,d)&&b.call(c,a[d],d,a)}var h=a(100);b.exports=d;var i=Object.prototype.toString,j=Object.prototype.hasOwnProperty},{100:100}],102:[function(a,b,c){function d(a){return a.replace(/^\s*|\s*$/g,"")}c=b.exports=d,c.left=function(a){return a.replace(/^\s*/,"")},c.right=function(a){return a.replace(/\s*$/,"")}},{}],103:[function(a,b,c){var d=a(102),e=a(101),f=function(a){return"[object Array]"===Object.prototype.toString.call(a)};b.exports=function(a){if(!a)return{};var b={};return e(d(a).split("\n"),function(a){var c=a.indexOf(":"),e=d(a.slice(0,c)).toLowerCase(),g=d(a.slice(c+1));"undefined"==typeof b[e]?b[e]=g:f(b[e])?b[e].push(g):b[e]=[b[e],g]}),b}},{101:101,102:102}],104:[function(a,b,c){function d(){for(var a={},b=0;b<arguments.length;b++){var c=arguments[b];for(var d in c)e.call(c,d)&&(a[d]=c[d])}return a}b.exports=d;var e=Object.prototype.hasOwnProperty},{}],105:[function(a,b,c){var d=a(109),e=b.exports={WebVTT:a(106).WebVTT,VTTCue:a(107).VTTCue,VTTRegion:a(108).VTTRegion};d.vttjs=e,d.WebVTT=e.WebVTT;var f=e.VTTCue,g=e.VTTRegion,h=d.VTTCue,i=d.VTTRegion;e.shim=function(){d.VTTCue=f,d.VTTRegion=g},e.restore=function(){d.VTTCue=h,d.VTTRegion=i},d.VTTCue||e.shim()},{106:106,107:107,108:108,109:109}],106:[function(a,b,c){!function(a){function b(a,b){this.name="ParsingError",this.code=a.code,this.message=b||a.message}function c(a){function b(a,b,c,d){return 3600*(0|a)+60*(0|b)+(0|c)+(0|d)/1e3}var c=a.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);return c?c[3]?b(c[1],c[2],c[3].replace(":",""),c[4]):c[1]>59?b(c[1],c[2],0,c[4]):b(0,c[1],c[2],c[4]):null}function d(){this.values=p(null)}function e(a,b,c,d){var e=d?a.split(d):[a];for(var f in e)if("string"==typeof e[f]){var g=e[f].split(c);if(2===g.length){var h=g[0],i=g[1];b(h,i)}}}function f(a,f,g){function h(){var d=c(a);if(null===d)throw new b(b.Errors.BadTimeStamp,"Malformed timestamp: "+k);return a=a.replace(/^[^\sa-zA-Z-]+/,""),d}function i(a,b){var c=new d;e(a,function(a,b){switch(a){case"region":for(var d=g.length-1;d>=0;d--)if(g[d].id===b){c.set(a,g[d].region);break}break;case"vertical":c.alt(a,b,["rl","lr"]);break;case"line":var e=b.split(","),f=e[0];c.integer(a,f),c.percent(a,f)&&c.set("snapToLines",!1),c.alt(a,f,["auto"]),2===e.length&&c.alt("lineAlign",e[1],["start","middle","end"]);break;case"position":e=b.split(","),c.percent(a,e[0]),2===e.length&&c.alt("positionAlign",e[1],["start","middle","end"]);break;case"size":c.percent(a,b);break;case"align":c.altLine truncated
@@ -43,16 +43,16 @@ class _settings extends \IPS\Dispatcher\Controller
{
$form = new \IPS\Helpers\Form;
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_settings.js', 'gallery', 'admin' ) );
$form->attributes['data-controller'] = 'gallery.admin.settings.settings';
$form->hiddenValues['rebuildWatermarkScreenshots'] = \IPS\Request::i()->rebuildWatermarkScreenshots ?: 0;
$form->addHeader( 'gallery_images' );
$form->addMessage( \IPS\Member::loggedIn()->language()->addToStack('gallery_dims_explanation'), '', FALSE );
$large = ( isset( \IPS\Settings::i()->gallery_large_dims ) ) ? explode( 'x', \IPS\Settings::i()->gallery_large_dims ) : array( 1600, 1200 );
$medium = ( isset( \IPS\Settings::i()->gallery_medium_dims ) ) ? explode( 'x', \IPS\Settings::i()->gallery_medium_dims ) : array( 640, 480 );
$small = ( isset( \IPS\Settings::i()->gallery_small_dims ) ) ? explode( 'x', \IPS\Settings::i()->gallery_small_dims ) : array( 240, 240 );
$thumb = ( isset( \IPS\Settings::i()->gallery_thumb_dims ) ) ? explode( 'x', \IPS\Settings::i()->gallery_thumb_dims ) : array( 100, 100 );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'gallery_large_dims', $large, TRUE, array( 'resizableDiv' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'gallery_medium_dims', $medium, TRUE, array( 'resizableDiv' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'gallery_small_dims', $small, TRUE, array( 'resizableDiv' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'gallery_thumb_dims', $thumb, TRUE, array( 'resizableDiv' => FALSE ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'gallery_use_square_thumbnails', \IPS\Settings::i()->gallery_use_square_thumbnails ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'gallery_use_watermarks', \IPS\Settings::i()->gallery_use_watermarks, FALSE, array( 'togglesOn' => array( 'gallery_watermark_path', 'gallery_watermark_images' ) ) ) );
$form->add( new \IPS\Helpers\Form\Upload( 'gallery_watermark_path', \IPS\Settings::i()->gallery_watermark_path ? \IPS\File::get( 'core_Theme', \IPS\Settings::i()->gallery_watermark_path ) : NULL, FALSE, array( 'image' => TRUE, 'storageExtension' => 'core_Theme' ), NULL, NULL, NULL, 'gallery_watermark_path' ) );
@@ -61,7 +61,7 @@ class _settings extends \IPS\Dispatcher\Controller
FALSE,
array(
'multiple' => TRUE,
'options' => array( 'large' => 'gallery_watermark_large', 'medium' => 'gallery_watermark_medium', 'small' => 'gallery_watermark_small', 'thumbnail' => 'gallery_watermark_thumb' ),
'options' => array( 'large' => 'gallery_watermark_large', 'small' => 'gallery_watermark_small' ),
),
NULL,
NULL,
@@ -80,11 +80,9 @@ class _settings extends \IPS\Dispatcher\Controller
{
$form->saveAsSettings( array(
'gallery_large_dims' => implode( 'x', $values['gallery_large_dims'] ),
'gallery_medium_dims' => implode( 'x', $values['gallery_medium_dims'] ),
'gallery_small_dims' => implode( 'x', $values['gallery_small_dims'] ),
'gallery_thumb_dims' => implode( 'x', $values['gallery_thumb_dims'] ),
'gallery_use_square_thumbnails' => $values['gallery_use_square_thumbnails'],
'gallery_watermark_path' => $values['gallery_watermark_path'],
'gallery_watermark_path' => (string) $values['gallery_watermark_path'],
'gallery_detailed_bandwidth' => $values['gallery_detailed_bandwidth'],
'gallery_bandwidth_period' => $values['gallery_bandwidth_period'],
'gallery_rss_enabled' => $values['gallery_rss_enabled'],
@@ -92,53 +90,15 @@ class _settings extends \IPS\Dispatcher\Controller
'gallery_use_watermarks' => $values['gallery_use_watermarks'],
) );
\IPS\Session::i()->log( 'acplogs__gallery_settings' );
if( $values['rebuildWatermarkScreenshots'] )
{
\IPS\Db::i()->delete( 'core_queue', array( '`app`=? OR `key`=?', 'gallery', 'RebuildGalleryImages' ) );
\IPS\Task::queue( 'gallery', 'RebuildGalleryImages', array( ), 2 );
}
}
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('settings');
\IPS\Output::i()->output = $form;
}
/**
* Rebuild gallery images
*
* @return void
*/
protected function rebuildImages()
{
$perGo = 5;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('gallery_rebuildimages');
\IPS\Output::i()->output = new \IPS\Helpers\MultipleRedirect( \IPS\Http\Url::internal( 'app=gallery&module=gallery&controller=settings&do=rebuildImages' ), function( $doneSoFar ) use( $perGo )
{
$doneSoFar = intval( $doneSoFar );
$select = \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_media=?', 0 ), 'image_id', array( $doneSoFar, $perGo ), NULL, NULL, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS );
$total = $select->count( TRUE );
if ( !$select->count() )
{
return NULL;
}
foreach ( $select as $row )
{
try
{
$image = \IPS\gallery\Image::constructFromData( $row );
$image->deleteThumbnails();
$image->buildThumbnails();
$image->save();
}
catch ( \Exception $e ) {}
}
$doneSoFar += $perGo;
return array( $doneSoFar, \IPS\Member::loggedIn()->language()->addToStack('rebuilding'), ( 100 * $doneSoFar ) / $total );
}, function()
{
\IPS\Session::i()->log( 'acplogs__gallery_rebuilt' );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=gallery&module=gallery&controller=settings' ), 'completed' );
} );
}
}
@@ -58,12 +58,14 @@ class _bandwidth extends \IPS\Dispatcher\Controller
$chart->tableParsers = array(
'member_id' => function( $val )
{
try
$member = \IPS\Member::load( $val );
if( $member->member_id )
{
$member = \IPS\Member::load( $val );
return "<a href='" . \IPS\Http\Url::internal( "app=gallery&module=stats&controller=member&do=images&id={$member->member_id}" ) . "'>{$member->name}</a>";
$url = \IPS\Http\Url::internal( "app=gallery&module=stats&controller=member&do=images&id={$member->member_id}" );
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $url, FALSE, $member->name );
}
catch ( \OutOfRangeException $e )
else
{
return \IPS\Member::loggedIn()->language()->addToStack('deleted_member');
}
@@ -73,7 +75,7 @@ class _bandwidth extends \IPS\Dispatcher\Controller
try
{
$image = \IPS\gallery\Image::load( $val );
return "<a href='{$image->url()}' target='_blank'>{$image->caption}</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $image->url(), TRUE, $image->caption );
}
catch ( \OutOfRangeException $e )
{
@@ -104,8 +104,8 @@ class _diskspace extends \IPS\Dispatcher\Controller
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('stats_date_range') )
)
);
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('stats')->uploadersTable( $select, $pagination, $members );
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate( 'global', 'core' )->message( \IPS\Member::loggedIn()->language()->addToStack( 'stats_include_hidden_content' ), 'info' );
\IPS\Output::i()->output .= \IPS\Theme::i()->getTemplate('stats')->uploadersTable( $select, $pagination, $members );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('menu__gallery_stats_diskspace');
}
}
@@ -30,80 +30,6 @@ class _member extends \IPS\Dispatcher\Controller
*/
protected function images()
{
/* Get member */
try
{
$member = \IPS\Member::load( \IPS\Request::i()->id );
}
catch ( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2G192/1', 404, '' );
}
/* Build chart */
$tabs = array( 'information' => 'information', 'bandwidth_use' => 'bandwidth_use' );
$activeTab = \IPS\Request::i()->tab ?: 'information';
switch ( $activeTab )
{
case 'information':
$imageCount = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images', array( 'image_member_id=?', $member->member_id ) )->first();
$diskspaceUsed = \IPS\Db::i()->select( 'SUM(image_file_size)', 'gallery_images', array( 'image_member_id=?', $member->member_id ) )->first();
$numberOfViews = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_bandwidth', array( 'member_id=?', $member->member_id ) )->first();
$bandwidthUsed = \IPS\Db::i()->select( 'SUM(bsize)', 'gallery_bandwidth', array( 'member_id=?', $member->member_id ) )->first();
$allImages = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images' )->first();
$totalFilesize = \IPS\Db::i()->select( 'SUM(image_file_size)', 'gallery_images' )->first();
$allBandwidth = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_bandwidth' )->first();
$totalBandwidth = \IPS\Db::i()->select( 'SUM(bsize)', 'gallery_bandwidth' )->first();
$activeTabContents = \IPS\Theme::i()->getTemplate( 'stats' )->information( \IPS\Theme::i()->getTemplate( 'global', 'core' )->definitionTable( array(
'images_submitted' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Member::loggedIn()->language()->formatNumber( $imageCount ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $allImages ? ( 100 / $allImages ) : 0 ) * $imageCount ), 2 ) ) )
),
'gdiskspace_used' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Output\Plugin\Filesize::humanReadableFilesize( $diskspaceUsed ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $totalFilesize ? ( 100 / $totalFilesize ) : 0 ) * $diskspaceUsed ), 2 ) ) )
),
'gaverage_filesize' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_average' , FALSE, array( 'sprintf' => array(
\IPS\Output\Plugin\Filesize::humanReadableFilesize( \IPS\Db::i()->select( 'AVG(image_file_size)', 'gallery_images', array( 'image_member_id=?', $member->member_id ) )->first() ),
\IPS\Output\Plugin\Filesize::humanReadableFilesize( \IPS\Db::i()->select( 'AVG(image_file_size)', 'gallery_images' )->first() ) ) )
),
'number_of_views' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Member::loggedIn()->language()->formatNumber( $numberOfViews ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $allBandwidth ? ( 100 / $allBandwidth ) : 0 ) * $imageCount ), 2 ) ))
),
'gallery_bandwidth_used' =>
\IPS\Member::loggedIn()->language()->addToStack( 'images_stat_of_total', FALSE, array( 'sprintf' => array(
\IPS\Output\Plugin\Filesize::humanReadableFilesize( $bandwidthUsed ),
\IPS\Member::loggedIn()->language()->formatNumber( ( ( $totalBandwidth ? ( 100 / $totalBandwidth ) : 0 ) * $bandwidthUsed ), 2 ) ) )
)
) ) );
break;
case 'bandwidth_use':
$bandwidthChart = new \IPS\Helpers\Chart\Database( \IPS\Http\Url::internal( "app=gallery&module=stats&controller=member&do=images&id={$member->member_id}&tab=bandwidth_use&_graph=1" ), 'gallery_bandwidth', 'bdate', '', array( 'vAxis' => array( 'title' => \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ) ) ), 'LineChart', 'daily' );
$bandwidthChart->groupBy = 'bdate';
$bandwidthChart->where[] = array( 'member_id=?', $member->member_id );
$bandwidthChart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('bandwidth_use'), 'number', 'ROUND((SUM(bsize)/1024),2)', FALSE );
$activeTabContents = ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->_graph ) ) ? (string) $bandwidthChart : \IPS\Theme::i()->getTemplate( 'stats' )->graphs( (string) $bandwidthChart );
break;
}
/* Display */
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('member_images_chart', FALSE, array( 'sprintf' => array( $member->name ) ) );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->output = $activeTabContents;
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $activeTab, $activeTabContents, \IPS\Http\Url::internal( "app=gallery&module=stats&controller=member&do=images&id={$member->member_id}" ) );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal("app=core&module=members&controller=members&do=view&id=" . \IPS\Request::i()->id . "&blockKey=core_ContentStatistics&block[core_ContentStatistics]=gallery_Gallery") );
}
}
@@ -132,8 +132,8 @@ class _browse extends \IPS\Content\Controller
protected function _index()
{
/* Get stuff */
$featured = iterator_to_array( \IPS\gallery\Image::featured( 20, '_rand' ) );
$new = iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( array(), NULL, 30 ) );
$featured = iterator_to_array( \IPS\gallery\Image::featured( 20, NULL ) );
$new = iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( ( \IPS\Settings::i()->club_nodes_in_apps ? array() : array( array( 'category_club_id IS NULL' ) ) ), NULL, 30, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, !\IPS\Settings::i()->club_nodes_in_apps ) );
/* Online User Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=gallery', 'front', 'gallery' ), array(), 'loc_gallery_browsing' );
@@ -153,7 +153,7 @@ class _browse extends \IPS\Content\Controller
/* Online User Location */
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=gallery&module=gallery&controller=browse&do=categories', 'front', 'gallery_categories' ), array(), 'loc_gallery_browsing_categories' );
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('__app_gallery');
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('gallery_title');
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'browse' )->categories();
}
@@ -209,6 +209,14 @@ class _browse extends \IPS\Content\Controller
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'browse' ), $this->getTableRowsTemplate() );
$table->title = \IPS\Member::loggedIn()->language()->pluralize( \IPS\Member::loggedIn()->language()->get('num_images'), array( $category->count_imgs ) );
$table->sortBy = \IPS\Request::i()->sortby ? $table->sortBy : $category->sort_options_img;
/* Make sure captions are sorted in the right order */
if ( $table->sortBy == "title" )
{
$table->sortDirection = "asc";
}
if( !$category->allow_comments )
{
unset( $table->sortOptions['num_comments'] );
@@ -267,7 +275,7 @@ class _browse extends \IPS\Content\Controller
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'browse' ), 'imageTable' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'browse' ), $this->getTableRowsTemplate() );
$table->title = \IPS\Member::loggedIn()->language()->pluralize( \IPS\Member::loggedIn()->language()->get('num_images'), array( $album->count_imgs ) );
$table->title = \IPS\Member::loggedIn()->language()->addToStack( 'num_images', FALSE, array( 'pluralize' => array( $album->count_imgs ) ) );
if( !$album->allow_comments )
{
@@ -284,6 +292,11 @@ class _browse extends \IPS\Content\Controller
/* Load the item */
$asItem = $album->asItem();
/* Mark the album as read - we don't bother tracking page number because albums are sorted in all different manners so the
page parameter is unreliable for determining if the album is read. By default, typically, new images show at the beginning
of the album anyways */
$asItem->markRead();
/* Sort out comments and reviews */
$tabs = $asItem->commentReviewTabs();
$_tabs = array_keys( $tabs );
@@ -300,7 +313,7 @@ class _browse extends \IPS\Content\Controller
}
/* Fetching comments/reviews */
if( \IPS\Request::i()->isAjax() )
if( \IPS\Request::i()->isAjax() AND !isset( \IPS\Request::i()->listResort ) )
{
\IPS\Output::i()->output = $activeTabContents;
return;
@@ -341,10 +354,18 @@ class _browse extends \IPS\Content\Controller
\IPS\Output::i()->breadcrumb[] = array( $album->category()->url(), $album->category()->_title );
}
if( $album->coverPhoto('medium') )
if( $album->coverPhoto('masked') )
{
\IPS\Output::i()->metaTags['og:image'] = $album->coverPhoto('medium');
\IPS\Output::i()->metaTags['og:image'] = $album->coverPhoto('masked');
}
$albumItem = $album->asItem();
\IPS\core\Facebook\Pixel::i()->PageView = array(
'item_id' => $album->id,
'item_name' => $albumItem->mapped('title'),
'item_type' => isset( $albumItem::$contentType ) ? $albumItem::$contentType : $albumItem::$title,
'category_name' => $albumItem->container()->_title
);
\IPS\Output::i()->breadcrumb[] = array( NULL, $album->_title );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'browse' )->album( $album, (string) $table, $commentsAndReviews );
@@ -371,51 +392,6 @@ class _browse extends \IPS\Content\Controller
}
}
/**
* Retrieve some images, used for the slider on the image view page
*
* @return void
*/
protected function getImages()
{
try
{
$startingPoint = \IPS\gallery\Image::load( \IPS\Request::i()->image );
}
catch ( \OutOfRangeException $e)
{
\IPS\Output::i()->error( 'node_error', '2G189/C', 404, '' );
}
$count = intval( \IPS\Request::i()->count );
if( \IPS\Request::i()->direction == 'next' )
{
$results = $startingPoint->nextImages( $count );
}
else
{
$results = $startingPoint->previousImages( $count );
}
$toSend = array();
foreach( $results as $result )
{
$toSend[] = \IPS\Theme::i()->getTemplate( 'view' )->imageThumbnail( $result );
}
/* Respond or redirect */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( $toSend );
}
else
{
\IPS\Output::i()->redirect( $startingPoint->url() );
}
}
/**
* Latest images RSS
*
@@ -484,7 +460,7 @@ class _browse extends \IPS\Content\Controller
\IPS\Output::i()->error( 'node_error', '2G189/5', 404, '' );
}
if( !$album->canEdit() )
if( !$album->asItem()->canEdit() )
{
\IPS\Output::i()->error( 'node_error', '2G189/4', 403, '' );
}
@@ -597,7 +573,7 @@ class _browse extends \IPS\Content\Controller
$category->save();
/* Hide the album */
$album->type = 4;
$album->type = \IPS\gallery\Album::AUTH_TYPE_DELETED;
$album->save();
/* Are we moving the images? */
File diff suppressed because it is too large. Load diff
@@ -109,9 +109,9 @@ class _view extends \IPS\Content\Controller
\IPS\Output::i()->metaTags['og:video:tag'] = $this->image->tags();
}
if( $this->image->medium_file_name )
if( $this->image->masked_file_name )
{
\IPS\Output::i()->metaTags['og:image'] = \IPS\File::get( 'gallery_Images', $this->image->medium_file_name )->url;
\IPS\Output::i()->metaTags['og:image'] = \IPS\File::get( 'gallery_Images', $this->image->masked_file_name )->url;
}
}
else
@@ -125,15 +125,22 @@ class _view extends \IPS\Content\Controller
}
}
\IPS\Output::i()->linkTags[] = array(
'rel' => 'preload',
'href' => (string) \IPS\File::get( 'gallery_Images', $this->image->masked_file_name )->url,
'as' => $this->image->media ? 'video' : 'image',
'type' => $this->image->file_type
);
/* Sort out comments and reviews */
$tabs = $this->image->commentReviewTabs();
$_tabs = array_keys( $tabs );
$tab = isset( \IPS\Request::i()->tab ) ? \IPS\Request::i()->tab : array_shift( $_tabs );
$activeTabContents = $this->image->commentReviews( $tab );
$activeTabContents = $this->image->commentReviews( $tab, \IPS\Request::i()->lightbox );
if ( count( $tabs ) > 1 )
{
$commentsAndReviews = count( $tabs ) ? \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $tab, $activeTabContents, $this->image->url(), 'tab', FALSE, TRUE ) : NULL;
$commentsAndReviews = count( $tabs ) ? \IPS\Theme::i()->getTemplate( 'global', 'core' )->tabs( $tabs, $tab, $activeTabContents, \IPS\Request::i()->lightbox ? $this->image->url()->setQueryString( 'lightbox', 1 ) : $this->image->url(), 'tab', FALSE, TRUE, \IPS\Request::i()->lightbox ? 'ipsTabs_small ipsTabs_stretch' : '' ) : NULL;
}
else
{
@@ -143,46 +150,6 @@ class _view extends \IPS\Content\Controller
/* Set the session location */
\IPS\Session::i()->setLocation( $this->image->url(), $this->image->onlineListPermissions(), 'loc_gallery_viewing_image', array( $this->image->caption => FALSE ) );
/* Get next 2 and previous 2 images in the container for slider */
$slider = array(
'previous' => $this->image->previousImages( 4 ),
'next' => $this->image->nextImages( 4 ),
);
$hasNext = TRUE;
$hasPrev = TRUE;
if( count( $slider['previous'] ) < 4 )
{
$hasPrev = FALSE;
}
else
{
$slider['previous'] = array_slice( $slider['previous'], 0, -1 );
}
if( count( $slider['next'] ) < 4 )
{
$hasNext = FALSE;
}
else
{
$slider['next'] = array_slice( $slider['next'], 0, -1 );
}
/* If this is a video, grab the necessary javascript */
if( $this->image->media )
{
if( \IPS\IN_DEV )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'videojs/video.js', 'gallery', 'interface' ) );
}
else
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'videojs/video.min.js', 'gallery', 'interface' ) );
}
}
/* Store bandwidth log */
if( \IPS\Settings::i()->gallery_detailed_bandwidth )
{
@@ -192,30 +159,25 @@ class _view extends \IPS\Content\Controller
$displayedImage = \IPS\File::get( 'gallery_Images', $this->image->original_file_name );
}
/* Otherwise, fetch the thumbnails */
elseif( ( \IPS\Request::i()->imageSize == 'medium' OR !\IPS\Request::i()->imageSize ) AND $this->image->medium_file_name )
{
$displayedImage = \IPS\File::get( 'gallery_Images', $this->image->medium_file_name );
}
elseif( \IPS\Request::i()->imageSize == 'large' )
else
{
$displayedImage = \IPS\File::get( 'gallery_Images', $this->image->masked_file_name );
}
/* We hide the sidebar when displaying the large image */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
}
elseif( \IPS\Request::i()->imageSize == 'small' )
/* Get filesize, but don't error out if there is a problem fetching it at this point */
try
{
$displayedImage = \IPS\File::get( 'gallery_Images', $this->image->small_file_name );
$filesize = ( ( isset( $displayedImage ) AND $displayedImage->filesize() ) ? $displayedImage->filesize() : $this->image->file_size );
}
elseif( \IPS\Request::i()->imageSize == 'thumb' )
catch( \Exception $e )
{
$displayedImage = \IPS\File::get( 'gallery_Images', $this->image->thumb_file_name );
$filesize = $this->image->file_size;
}
\IPS\Db::i()->insert( 'gallery_bandwidth', array(
'member_id' => (int) \IPS\Member::loggedIn()->member_id,
'bdate' => time(),
'bsize' => (int) ( ( isset( $displayedImage ) AND $displayedImage->filesize() ) ? $displayedImage->filesize() : $this->image->file_size ),
'bsize' => (int) $filesize,
'image_id' => $this->image->id
) );
}
@@ -226,15 +188,14 @@ class _view extends \IPS\Content\Controller
'@type' => $this->image->media ? "VideoObject" : "VisualArtwork",
'@id' => (string) $this->image->url(),
'url' => (string) $this->image->url(),
'name' => htmlspecialchars( $this->image->mapped('title'), ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ),
'name' => $this->image->mapped('title'),
'description' => strip_tags( $this->image->truncated() ),
'dateCreated' => \IPS\DateTime::ts( $this->image->date )->format( \IPS\DateTime::ISO8601 ),
'fileFormat' => $this->image->file_type,
'keywords' => $this->image->tags(),
'author' => array(
'@type' => 'Person',
'name' => htmlspecialchars( \IPS\Member::load( $this->image->member_id )->name, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ),
'url' => (string) \IPS\Member::load( $this->image->member_id )->url(),
'name' => \IPS\Member::load( $this->image->member_id )->name,
'image' => \IPS\Member::load( $this->image->member_id )->get_photo()
),
'interactionStatistic' => array(
@@ -246,6 +207,12 @@ class _view extends \IPS\Content\Controller
)
);
/* Do we have a real author? */
if( $this->image->member_id )
{
\IPS\Output::i()->jsonLd['gallery']['author']['url'] = (string) \IPS\Member::load( $this->image->member_id )->url();
}
if ( $this->image->container()->allow_comments AND $this->image->directContainer()->allow_comments )
{
\IPS\Output::i()->jsonLd['gallery']['interactionStatistic'][] = array(
@@ -275,61 +242,59 @@ class _view extends \IPS\Content\Controller
if( $this->image->media )
{
if( $this->image->medium_file_name )
if( $this->image->masked_file_name )
{
\IPS\Output::i()->jsonLd['gallery']['thumbnail'] = (string) \IPS\File::get( 'gallery_Images', $this->image->medium_file_name )->url;
\IPS\Output::i()->jsonLd['gallery']['thumbnailUrl'] = (string) \IPS\File::get( 'gallery_Images', $this->image->medium_file_name )->url;
\IPS\Output::i()->jsonLd['gallery']['thumbnail'] = (string) \IPS\File::get( 'gallery_Images', $this->image->masked_file_name )->url;
\IPS\Output::i()->jsonLd['gallery']['thumbnailUrl'] = (string) \IPS\File::get( 'gallery_Images', $this->image->masked_file_name )->url;
}
\IPS\Output::i()->jsonLd['gallery']['contentSize'] = (string) \IPS\File::get( 'gallery_Images', $this->image->original_file_name )->filesize();
\IPS\Output::i()->jsonLd['gallery']['contentSize'] = htmlspecialchars( $this->image->mapped('title'), ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
else
{
$mediumFile = \IPS\File::get( 'gallery_Images', $this->image->medium_file_name );
$dimensions = $mediumFile->getImageDimensions();
\IPS\Output::i()->jsonLd['gallery']['artMedium'] = 'Digital';
\IPS\Output::i()->jsonLd['gallery']['width'] = $dimensions[0];
\IPS\Output::i()->jsonLd['gallery']['height'] = $dimensions[1];
\IPS\Output::i()->jsonLd['gallery']['image'] = array(
'@type' => 'ImageObject',
'url' => (string) $mediumFile->url,
'caption' => htmlspecialchars( $this->image->mapped('title'), ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ),
'thumbnail' => (string) \IPS\File::get( 'gallery_Images', $this->image->thumb_file_name )->url,
'width' => $dimensions[0],
'height' => $dimensions[1],
);
if( count( $this->image->metadata ) )
try
{
\IPS\Output::i()->jsonLd['gallery']['image']['exifData'] = array();
$largeFile = \IPS\File::get( 'gallery_Images', $this->image->masked_file_name );
$dimensions = $this->image->_dimensions;
foreach( $this->image->metadata as $k => $v )
\IPS\Output::i()->jsonLd['gallery']['artMedium'] = 'Digital';
\IPS\Output::i()->jsonLd['gallery']['width'] = $dimensions['large'][0];
\IPS\Output::i()->jsonLd['gallery']['height'] = $dimensions['large'][1];
\IPS\Output::i()->jsonLd['gallery']['image'] = array(
'@type' => 'ImageObject',
'url' => (string) $largeFile->url,
'caption' => $this->image->mapped('title'),
'thumbnail' => (string) \IPS\File::get( 'gallery_Images', $this->image->small_file_name )->url,
'width' => $dimensions['large'][0],
'height' => $dimensions['large'][1],
);
if( count( $this->image->metadata ) )
{
\IPS\Output::i()->jsonLd['gallery']['image']['exifData'][] = array(
'@type' => 'PropertyValue',
'name' => $k,
'value' => $v
);
\IPS\Output::i()->jsonLd['gallery']['image']['exifData'] = array();
foreach( $this->image->metadata as $k => $v )
{
\IPS\Output::i()->jsonLd['gallery']['image']['exifData'][] = array(
'@type' => 'PropertyValue',
'name' => $k,
'value' => $v
);
}
}
\IPS\Output::i()->jsonLd['gallery']['thumbnailUrl'] = (string) \IPS\File::get( 'gallery_Images', $this->image->small_file_name )->url;
}
\IPS\Output::i()->jsonLd['gallery']['thumbnailUrl'] = (string) \IPS\File::get( 'gallery_Images', $this->image->thumb_file_name )->url;
/* File doesn't exist */
catch ( \RuntimeException $e ){}
}
/* Display */
if( isset( \IPS\Request::i()->imageSize ) )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_view.js', 'gallery' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'view' )->imageSizes( $this->image, $this->image->sizes() );
}
elseif( \IPS\Request::i()->isAjax() && isset( \IPS\Request::i()->browse ) )
if( \IPS\Request::i()->isAjax() && isset( \IPS\Request::i()->browse ) )
{
$return = array(
'title' => htmlspecialchars( $this->image->mapped('title'), ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ),
'image' => \IPS\Theme::i()->getTemplate( 'view' )->imageFrame( $this->image, $slider, $this->image->nextItem(), $this->image->prevItem() ),
'info' => \IPS\Theme::i()->getTemplate( 'view' )->imageInfo( $this->image, $slider, $this->image->nextItem(), $this->image->prevItem() ),
'slider' => \IPS\Theme::i()->getTemplate( 'view' )->imageSlider( $this->image, $slider, $this->image->nextItem(), $this->image->prevItem(), $hasNext, $hasPrev )
'title' => htmlspecialchars( $this->image->mapped('title'), ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ),
'image' => \IPS\Request::i()->lightbox ? \IPS\Theme::i()->getTemplate( 'view' )->imageLightboxFrame( $this->image ) : \IPS\Theme::i()->getTemplate( 'view' )->imageFrame( $this->image ),
'info' => \IPS\Request::i()->lightbox ? \IPS\Theme::i()->getTemplate( 'view' )->imageLightboxInfo( $this->image, $commentsAndReviews ) : \IPS\Theme::i()->getTemplate( 'view' )->imageInfo( $this->image )
);
if( $this->image->directContainer()->allow_comments )
@@ -340,16 +305,26 @@ class _view extends \IPS\Content\Controller
\IPS\Output::i()->json( $return );
}
/* Switching comments only */
elseif( \IPS\Request::i()->isAjax() AND !isset( \IPS\Request::i()->rating_submitted ) )
elseif( \IPS\Request::i()->isAjax() AND !isset( \IPS\Request::i()->rating_submitted ) AND \IPS\Request::i()->tab )
{
\IPS\Output::i()->output = $activeTabContents;
return;
}
else
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, array( \IPS\Http\Url::internal( 'applications/gallery/interface/videojs/video-js.min.css', 'none' ) ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_view.js', 'gallery' ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'view' )->image( $this->image, $slider, $this->image->nextItem(), $this->image->prevItem(), $hasNext, $hasPrev, $commentsAndReviews );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_browse.js', 'gallery' ) );
if( \IPS\Request::i()->lightbox )
{
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'view' )->imageLightbox( $this->image, $commentsAndReviews );
return;
}
else
{
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'view' )->image( $this->image, $commentsAndReviews );
}
}
}
@@ -363,10 +338,8 @@ class _view extends \IPS\Content\Controller
try
{
/* Get file and data */
$size = ( isset( \IPS\Request::i()->imageSize ) AND \IPS\Request::i()->imageSize AND \IPS\Request::i()->imageSize != 'large' ) ? \IPS\Request::i()->imageSize . "_file_name" : "masked_file_name";
$file = \IPS\File::get( 'gallery_Images', $this->image->$size );
$headers = array_merge( \IPS\Output::getCacheHeaders( time(), 360 ), array( "Content-Disposition" => \IPS\Output::getContentDisposition( 'download', $file->originalFilename ), "X-Content-Type-Options" => "nosniff" ) );
$file = \IPS\File::get( 'gallery_Images', $this->image->original_file_name ?: $this->image->masked_file_name );
$headers = array_merge( \IPS\Output::getCacheHeaders( time(), 360 ), array( "Content-Disposition" => \IPS\Output::getContentDisposition( 'attachment', $file->originalFilename ), "X-Content-Type-Options" => "nosniff" ) );
/* Send headers and print file */
\IPS\Output::i()->sendStatusCodeHeader( 200 );
@@ -376,6 +349,7 @@ class _view extends \IPS\Content\Controller
{
\IPS\Output::i()->sendHeader( $key . ': ' . $header );
}
\IPS\Output::i()->sendHeader( "Content-Length: " . $file->filesize() );
$file->printFile();
@@ -408,7 +382,22 @@ class _view extends \IPS\Content\Controller
*/
protected function cover()
{
if( ( !$this->image->album_id OR !$this->image->canEdit() ) AND !\IPS\gallery\Image::modPermission( 'edit', NULL, $this->image->container() ) )
switch( \IPS\Request::i()->set )
{
case 'album':
$check = $this->image->canSetAsAlbumCover();
break;
case 'category':
$check = $this->image->canSetAsCategoryCover();
break;
case 'both':
$check = ( $this->image->canSetAsAlbumCover() AND $this->image->canSetAsCategoryCover() );
break;
}
if ( !$check )
{
\IPS\Output::i()->error( 'node_error', '2G188/5', 403, '' );
}
@@ -416,7 +405,7 @@ class _view extends \IPS\Content\Controller
\IPS\Session::i()->csrfCheck();
$lang = '';
if( ( $this->image->album_id AND $this->image->canEdit() ) && ( \IPS\Request::i()->set == 'album' or \IPS\Request::i()->set == 'both' ) )
if( $this->image->canSetAsAlbumCover() && ( \IPS\Request::i()->set == 'album' or \IPS\Request::i()->set == 'both' ) )
{
$this->image->directContainer()->cover_img_id = $this->image->id;
$this->image->directContainer()->save();
@@ -424,7 +413,7 @@ class _view extends \IPS\Content\Controller
$lang = \IPS\Member::loggedIn()->language()->addToStack('set_as_album_done');
}
if( ( \IPS\gallery\Image::modPermission( 'edit', NULL, $this->image->container() ) ) && ( \IPS\Request::i()->set == 'category' or \IPS\Request::i()->set == 'both' ) )
if( $this->image->canSetAsCategoryCover() && ( \IPS\Request::i()->set == 'category' or \IPS\Request::i()->set == 'both' ) )
{
$this->image->container()->cover_img_id = $this->image->id;
$this->image->container()->save();
@@ -468,11 +457,11 @@ class _view extends \IPS\Content\Controller
/* Determine angle to rotate */
if( \IPS\Request::i()->direction == 'right' )
{
$angle = 90;
$angle = ( \IPS\Settings::i()->image_suite == 'imagemagick' and class_exists( 'Imagick', FALSE ) ) ? 90 : -90;
}
else
{
$angle = 270;
$angle = ( \IPS\Settings::i()->image_suite == 'imagemagick' and class_exists( 'Imagick', FALSE ) ) ? -90 : 90;
}
/* Rotate the image and rebuild thumbnails */
@@ -487,7 +476,12 @@ class _view extends \IPS\Content\Controller
/* Respond or redirect */
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( 'OK' );
\IPS\Output::i()->json( array(
'src' => (string) \IPS\File::get( 'gallery_Images', $this->image->masked_file_name )->url,
'message' => \IPS\Member::loggedIn()->language()->addToStack('gallery_image_rotated'),
'width' => $this->image->_dimensions['large'][0],
'height' => $this->image->_dimensions['large'][1],
) );
}
else
{
@@ -516,7 +510,7 @@ class _view extends \IPS\Content\Controller
/* Handle submissions */
if ( $values = $form->values() )
{
$this->image->member_id = $values['author']->member_id;
$this->image->changeAuthor( $values['author'] );
$this->image->save();
\IPS\Output::i()->redirect( $this->image->url() );
@@ -548,15 +542,16 @@ class _view extends \IPS\Content\Controller
\IPS\Session::i()->csrfCheck();
/* Update profile photo */
$file = \IPS\File::get( 'gallery_Images', $this->image->medium_file_name );
$image = \IPS\Image::create( $file->contents() );
$photo = \IPS\File::create( 'core_Profile', $file->filename, (string) $image );
$file = \IPS\File::get( 'gallery_Images', $this->image->masked_file_name );
$image = \IPS\Image::create( $file->contents() );
$photo = \IPS\File::create( 'core_Profile', $file->filename, (string) $image );
\IPS\Member::loggedIn()->pp_main_photo = (string) $photo;
\IPS\Member::loggedIn()->pp_thumb_photo = (string) $photo->thumbnail( 'core_Profile', \IPS\PHOTO_THUMBNAIL_SIZE, \IPS\PHOTO_THUMBNAIL_SIZE );
\IPS\Member::loggedIn()->pp_photo_type = "custom";
\IPS\Member::loggedIn()->photo_last_update = time();
\IPS\Member::loggedIn()->save();
\IPS\Member::loggedIn()->logHistory( 'core', 'photo', array( 'action' => 'new', 'type' => 'gallery', 'id' => $this->image->id ) );
/* Redirect back to image */
if( \IPS\Request::i()->isAjax() )
@@ -586,68 +581,8 @@ class _view extends \IPS\Content\Controller
throw new \DomainException;
}
$form = new \IPS\Helpers\Form( 'form', 'move' );
$form->class = 'ipsForm_vertical';
if ( \IPS\gallery\Category::canOnAny('add') and \IPS\gallery\Album::canOnAny('add') )
{
$options = array( 'category' => 'image_category', 'album' => 'image_album' );
$toggles = array( 'category' => array( 'move_to_category' ), 'album' => array( 'move_to_album' ) );
$extraFields = array();
if ( \IPS\gallery\Image::modPermission( 'edit', NULL, NULL ) and \IPS\Db::i()->select( 'COUNT(*)', 'gallery_categories', array( array( 'category_allow_albums=1' ), array( '(' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . \IPS\gallery\Category::$permissionMap['add'], \IPS\Member::loggedIn()->groups ) . ' OR ' . 'core_permission_index.perm_' . \IPS\gallery\Category::$permissionMap['add'] . '=? )', '*' ) ) )->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . \IPS\gallery\Category::$databaseTable . "." . \IPS\gallery\Category::$databasePrefix . \IPS\gallery\Category::$databaseColumnId, \IPS\gallery\Category::$permApp, \IPS\gallery\Category::$permType ) )->first() )
{
$options['new_album'] = 'move_to_new_album';
$form = \IPS\gallery\Image\Table::buildMoveForm( $item->container(), 'IPS\\gallery\\Image', array( 'forceOwner' => $item->author() ) );
foreach ( \IPS\gallery\Album::formFields( NULL, TRUE, \IPS\Request::i()->move_to === 'new_album' ) as $field )
{
if ( !$field->htmlId )
{
$field->htmlId = $field->name . '_id';
}
$toggles['new_album'][] = $field->htmlId;
$extraFields[] = $field;
}
}
$form->add( new \IPS\Helpers\Form\Radio( 'move_to', NULL, TRUE, array( 'options' => $options, 'toggles' => $toggles ) ) );
foreach ( $extraFields as $field )
{
$form->add( $field );
}
}
$form->add( new \IPS\Helpers\Form\Node( 'move_to_category', NULL, NULL, array( 'clubs' => true, 'class' => 'IPS\\gallery\\Category', 'permissionCheck' => 'add' ), function( $val ) {
if ( !$val and isset( \IPS\Request::i()->move_to ) and \IPS\Request::i()->move_to == 'category' )
{
throw new \DomainException('form_required');
}
}, NULL, NULL, 'move_to_category' ) );
$form->add( new \IPS\Helpers\Form\Node( 'move_to_album', NULL, NULL, array(
'class' => 'IPS\\gallery\\Album',
'permissionCheck' => function( $node )
{
/* Do we have permission to add? */
if( !$node->can( 'add' ) )
{
return false;
}
/* Have we hit an images per album limit? */
if( $node->owner()->group['g_img_album_limit'] AND ( $node->count_imgs + $node->count_imgs_hidden ) >= $node->owner()->group['g_img_album_limit'] )
{
return false;
}
return true;
},
'forceOwner' => $item->author()
), function( $val ) {
if ( !$val and isset( \IPS\Request::i()->move_to ) and \IPS\Request::i()->move_to == 'album' )
{
throw new \DomainException('form_required');
}
}, NULL, NULL, 'move_to_album' ) );
if ( $values = $form->values() )
{
if ( isset( $values['move_to'] ) )
@@ -0,0 +1,15 @@
{
"1": {
"method": "update",
"params": [
"gallery_categories",
{
"category_sort_options": "album_name"
},
[
"category_sort_options=?",
"title"
]
]
}
}
@@ -0,0 +1,37 @@
<?php
/**
* @brief 4.2.2 Beta 1 Upgrade Code
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 26 Jul 2017
*/
namespace IPS\gallery\setup\upg_102005;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* 4.2.2 Beta 1 Upgrade Code
*/
class _Upgrade
{
/**
* Recount containers
*
* @return array If returns TRUE, upgrader will proceed to next step. If it returns any other value, it will set this as the value of the 'extra' GET parameter and rerun this step (useful for loops)
*/
public function finish()
{
\IPS\Task::queue( 'core', 'RebuildContainerCounts', array( 'class' => 'IPS\gallery\Album', 'count' => 0 ), 5, array( 'class' ) );
return TRUE;
}
}
@@ -0,0 +1,175 @@
{
"1": {
"method": "addColumn",
"params": [
"gallery_albums",
{
"name": "album_submit_type",
"type": "INT",
"length": 10,
"decimals": null,
"values": null,
"allow_null": false,
"default": "0",
"comment": "Submission type. 0=owner only, 1=anyone, 2=specific groups, 3=specific members, 4=club",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
]
},
"2": {
"method": "addColumn",
"params": [
"gallery_albums",
{
"name": "album_submit_access",
"type": "TEXT",
"length": null,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
]
},
"3": {
"method": "addColumn",
"params": [
"gallery_album_reviews",
{
"name": "review_author_response",
"type": "MEDIUMTEXT",
"length": null,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
]
},
"4": {
"method": "addColumn",
"params": [
"gallery_images_uploads",
{
"name": "upload_order",
"type": "INT",
"length": 10,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": true,
"zerofill": false,
"auto_increment": false,
"binary": false
}
]
},
"5": {
"method": "addColumn",
"params": [
"gallery_reviews",
{
"name": "review_author_response",
"type": "MEDIUMTEXT",
"length": null,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
]
},
"6": {
"method": "changeIndex",
"params": [
"gallery_images_uploads",
"upload_session",
{
"type": "key",
"name": "upload_session",
"columns": [
"upload_session",
"upload_order"
],
"length": [
null,
null
]
}
]
},
"7": {
"method": "addColumn",
"params": [
"gallery_categories",
{
"name": "category_sort_options_img",
"type": "VARCHAR",
"length": 255,
"decimals": null,
"values": null,
"allow_null": true,
"default": null,
"comment": "",
"unsigned": false,
"zerofill": false,
"auto_increment": false,
"binary": false
}
]
},
"8": {
"method": "addIndex",
"params": [
"gallery_album_reviews",
{
"type": "key",
"name": "review_album_id",
"columns": [
"review_album_id"
],
"length": [
null
]
}
]
},
"9": {
"method": "addIndex",
"params": [
"gallery_reviews",
{
"type": "key",
"name": "review_image",
"columns": [
"review_image_id",
"review_approved"
],
"length": [
null,
null
]
}
]
}
}
@@ -0,0 +1,36 @@
<?php
/**
* @brief 4.3.0 Beta 1 Upgrade Code
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 03 Nov 2017
*/
namespace IPS\gallery\setup\upg_103000;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* 4.3.0 Beta 1 Upgrade Code
*/
class _Upgrade
{
/**
* Delete copies of images we no longer need, and rebuild the sizes array
*
* @return bool
*/
public function finish()
{
\IPS\Task::queue( 'gallery', 'CleanUpGalleryImages', array(), 5 );
return TRUE;
}
}
@@ -0,0 +1,40 @@
<?php
/**
* @brief 4.3.0 Beta 4 Upgrade Code
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 09 Apr 2018
*/
namespace IPS\gallery\setup\upg_103004;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* 4.3.0 Beta 4 Upgrade Code
*/
class _Upgrade
{
/**
* Initialize a search index rebuild for gallery images
*
* @return array If returns TRUE, upgrader will proceed to next step. If it returns any other value, it will set this as the value of the 'extra' GET parameter and rerun this step (useful for loops)
*/
public function step1()
{
\IPS\Task::queue( 'core', 'RebuildSearchIndex', array( 'class' => 'IPS\gallery\Image' ), 5 );
return TRUE;
}
// You can create as many additional methods (step2, step3, etc.) as is necessary.
// Each step will be executed in a new HTTP request
}
@@ -1,19 +1,12 @@
{
"1": {
"method": "delete",
"params": [
"custom_bbcode",
"bbcode_tag='gallery' AND bbcode_php_plugin='gallery.php'"
]
},
"2": {
"method": "delete",
"params": [
"core_sys_conf_settings",
"conf_key='gallery_create_thumbs'"
]
},
"3": {
"2": {
"method": "changeColumn",
"params": [
"gallery_comments",
@@ -27,7 +20,7 @@
}
]
},
"4": {
"3": {
"method": "changeColumn",
"params": [
"gallery_images",
@@ -41,7 +34,7 @@
}
]
},
"5": {
"4": {
"method": "dropColumn",
"params": [
"gallery_comments",
@@ -53,7 +46,7 @@
]
]
},
"6": {
"5": {
"method": "addColumn",
"params": [
"gallery_albums_main",
@@ -0,0 +1,48 @@
<?php
/**
* @brief 4.1.0 Beta 1 Upgrade Code
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Gallery
* @since 17 Oct 2017
*/
namespace IPS\gallery\setup\upg_41000;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* 4.1.0 Beta 1 Upgrade Code
*/
class _Upgrade
{
/**
* ...
*
* @return array If returns TRUE, upgrader will proceed to next step. If it returns any other value, it will set this as the value of the 'extra' GET parameter and rerun this step (useful for loops)
*/
public function step1()
{
try
{
\IPS\Db::i()->delete( 'custom_bbcode', array( 'bbcode_tag=? AND bbcode_php_plugin=?', 'gallery', 'gallery.php' ) );
}
catch ( \IPS\Db\Exception $e )
{
/* This table may not exist, depending on the version they are upgrading from */
if( $e->getCode() != 1146 )
{
throw $e;
}
}
return TRUE;
}
}
@@ -17,6 +17,9 @@ if( \IPS\Db::i()->checkForTable( 'gallery_albums_main' ) )
{
$_options = iterator_to_array( \IPS\Db::i()->select( 'album_id, album_name', 'gallery_albums_main', 'album_is_global=1 AND album_node_level=0' )->setKeyField('album_id')->setValueField('album_name') );
$options[] = new \IPS\Helpers\Form\Select( '42000_members_album', 0, TRUE, array( 'options' => $_options ) );
$options[] = new \IPS\Helpers\Form\Checkbox( '42000_new_members_album', 1, FALSE );
if( !empty( $_options ) )
{
$options[] = new \IPS\Helpers\Form\Select( '42000_members_album', 0, TRUE, array( 'options' => $_options ) );
$options[] = new \IPS\Helpers\Form\Checkbox( '42000_new_members_album', 1, FALSE );
}
}
@@ -502,7 +502,7 @@ class _Upgrade
\IPS\Settings::i()->gallery_members_album = 0;
}
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->clearCache();
//-----------------------------------------
// Get global albums and loop
@@ -678,8 +678,7 @@ class _Upgrade
\IPS\Db::i()->delete( 'gallery_albums_main', 'album_is_global=1' );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => \IPS\Settings::i()->gallery_members_album ), array( 'conf_key=?', 'gallery_members_album' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'gallery_members_album' => \IPS\Settings::i()->gallery_members_album ) );
return TRUE;
}
@@ -304,11 +304,11 @@ class _Upgrade
{
$url = \IPS\File::create( 'gallery_Images', $fileName, file_get_contents( $settings['gallery_watermark_path'] ), NULL, TRUE, NULL, FALSE );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $url ), array( 'conf_key=?', 'gallery_watermark_path' ) );
\IPS\Settings::i()->changeValues( array( 'gallery_watermark_path' => $url ) );
}
catch( \Exception $e )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => '' ), array( 'conf_key=?', 'gallery_watermark_path' ) );
\IPS\Settings::i()->changeValues( array( 'gallery_watermark_path' => '' ) );
}
}
@@ -316,7 +316,8 @@ class _Upgrade
\IPS\Db::i()->delete( 'core_sys_conf_settings', "conf_key IN ( 'gallery_watermark_opacity', 'gallery_images_url', 'gallery_images_path' )" );
unset( \IPS\Data\Store::i()->settings, $_SESSION['_step3Count'] );
unset( $_SESSION['_step3Count'] );
\IPS\Settings::i()->clearCache();
return TRUE;
}
+200 -59
View File
@@ -24,12 +24,21 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\Embeddable
{
/**
* @brief Define access levels
* @brief Define view access levels
*/
const AUTH_TYPE_PUBLIC = 1;
const AUTH_TYPE_PRIVATE = 2;
const AUTH_TYPE_RESTRICTED = 3;
const AUTH_TYPE_DELETED = 4;
/**
* @brief Define submit access levels
*/
const AUTH_SUBMIT_OWNER = 0;
const AUTH_SUBMIT_PUBLIC = 1;
const AUTH_SUBMIT_GROUPS = 2;
const AUTH_SUBMIT_MEMBERS = 3;
const AUTH_SUBMIT_CLUB = 4;
/**
* @brief [ActiveRecord] Multiton Store
@@ -124,16 +133,19 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
/**
* [Node] Load and check permissions
*
* @param mixed $id ID
* @param string $perm Permission Key
* @param mixed $id ID
* @param string $perm Permission Key
* @param \IPS\Member|NULL $member Member, or NULL for logged in member
* @return static
* @throws \OutOfRangeException
* @note Album 'add' permissions are properly checked via the can() method
*/
public static function loadAndCheckPerms( $id, $perm='view' )
public static function loadAndCheckPerms( $id, $perm='view', \IPS\Member $member = NULL )
{
$obj = parent::loadAndCheckPerms( $id );
$member = $member ?: \IPS\Member::loggedIn();
if ( !$obj->category()->can( $perm ) )
if ( !$obj->can( $perm ) )
{
throw new \OutOfRangeException;
}
@@ -147,23 +159,23 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
if( !\IPS\gallery\Image::modPermission( 'edit', NULL, $obj->category() ) )
{
/* Throw exception if this is a private album we can't access */
if( $obj->type == static::AUTH_TYPE_PRIVATE AND $obj->owner() != \IPS\Member::loggedIn() )
if( $obj->type == static::AUTH_TYPE_PRIVATE AND $obj->owner() != $member )
{
throw new \OutOfRangeException;
}
/* Throw exception if this is a restricted album we can't access */
if( $obj->type == static::AUTH_TYPE_RESTRICTED AND $obj->owner() != \IPS\Member::loggedIn() )
if( $obj->type == static::AUTH_TYPE_RESTRICTED AND $obj->owner() != $member )
{
/* This will throw an exception if the row does not exist... but we need it to throw an OutOfRangeException */
try
{
if( !\IPS\Member::loggedIn()->member_id )
if( !$member->member_id )
{
throw new \OutOfRangeException;
}
$member = \IPS\Db::i()->select( '*', 'core_sys_social_group_members', array( 'group_id=? AND member_id=?', $obj->allowed_access, \IPS\Member::loggedIn()->member_id ) )->first();
$member = \IPS\Db::i()->select( '*', 'core_sys_social_group_members', array( 'group_id=? AND member_id=?', $obj->allowed_access, $member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
@@ -174,6 +186,27 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
return $obj;
}
/**
* Fetch all albums we can submit to
*
* @param \IPS\gallery\Category $category Category we are submitting in
* @return array
* @throws \RuntimeException
*/
public static function loadForSubmit( $category )
{
$ownedAlbums = static::loadByOwner( NULL, array( array( 'album_category_id=?', $category->id ) ) );
$permittedAlbums = \IPS\gallery\Album\Item::getItemsWithPermission( array( array( 'album_category_id=?', $category->id ) ), NULL, NULL, 'add' );
$finalAlbums = $ownedAlbums;
foreach( $permittedAlbums as $album )
{
$finalAlbums[ $album->id ] = $album->asNode();
}
return $finalAlbums;
}
/**
* Fetch all nodes owned by a given user
@@ -196,7 +229,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
protected $_approvedMembers = FALSE;
/**
* Get members with access to restricted album
* Get members with access to view restricted album
*
* @return array|null
* @note This list will NOT include the album owner, who also inherently has access
@@ -288,6 +321,17 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
return $this->count_imgs;
}
/**
* Get items count language string
*
* @return string
* @throws \BadMethodCallException
*/
public function get__countLanguageString()
{
return 'num_images';
}
/**
* [Node] Get number of content comments
*
@@ -442,7 +486,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
$return[] = new \IPS\Helpers\Form\Text( 'album_name', $album ? $album->name : '', $required ?: NULL );
$return[] = new \IPS\Helpers\Form\Editor( 'album_description', $album ? $album->description : '', FALSE, array( 'app' => 'gallery', 'key' => 'Albums', 'autoSaveKey' => ( $album ? "gallery_album_{$album->id}_desc" : "gallery-new-album" ), 'attachIds' => $album ? array( $album->id, NULL, 'description' ) : NULL, 'minimize' => 'cdesc_placeholder' ) );
$return[] = new \IPS\Helpers\Form\Editor( 'album_description', $album ? $album->description : '', FALSE, array( 'app' => 'gallery', 'key' => 'Albums', 'autoSaveKey' => ( $album ? "gallery_album_{$album->id}_desc" : "gallery-new-album" ), 'attachIds' => $album ? array( $album->id, NULL, 'description' ) : NULL ) );
$return[] = new \IPS\Helpers\Form\Node( 'album_category', ( $album and $album->category_id ) ? \IPS\gallery\Category::load( $album->category_id ) : NULL, $required ?: NULL, array(
'class' => 'IPS\gallery\Category',
@@ -463,7 +507,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
}
) );
if( $forOther or \IPS\gallery\Image::modPermission( 'edit', NULL, ( $album ? $album->category() : NULL ) ) )
if( $forOther or \IPS\gallery\Album\Item::modPermission( 'edit', NULL, ( $album ? $album->category() : NULL ) ) )
{
if ( !$forOther )
{
@@ -497,8 +541,37 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
{
$return[] = new \IPS\Helpers\Form\SocialGroup( 'album_allowed_access', $album ? $album->allowed_access : NULL, FALSE, array( 'owner' => $album ? \IPS\Member::load( $album->owner_id ) : ( \IPS\Request::i()->album_owner ? \IPS\Member::load( \IPS\Request::i()->album_owner, 'name' ) : \IPS\Member::loggedIn() ) ), NULL, NULL, NULL, 'album_allowed_access' );
}
$submitTypes = array(
static::AUTH_SUBMIT_OWNER => 'album_submittype_owner',
static::AUTH_SUBMIT_PUBLIC => 'album_submittype_public',
static::AUTH_SUBMIT_GROUPS => 'album_submittype_groups',
static::AUTH_SUBMIT_MEMBERS => 'album_submittype_members'
);
/* Is the category in a club */
if( $album AND $album->category_id )
{
if( $album->category()->club() )
{
$submitTypes[ static::AUTH_SUBMIT_CLUB ] = 'album_submittype_club';
}
}
elseif( !$album AND ( isset( \IPS\Request::i()->chosenCategory ) or isset( \IPS\Request::i()->category ) ) )
{
if( \IPS\gallery\Category::load( (int) \IPS\Request::i()->chosenCategory ?: \IPS\Request::i()->category )->club() )
{
$submitTypes[ static::AUTH_SUBMIT_CLUB ] = 'album_submittype_club';
}
}
$return[] = new \IPS\Helpers\Form\Radio( 'album_submit_type', ( $album and $album->submit_type ) ? $album->submit_type : static::AUTH_SUBMIT_OWNER, FALSE, array( 'options' => $submitTypes, 'toggles' => array( static::AUTH_SUBMIT_MEMBERS => array( 'album_submit_access_members' ), static::AUTH_SUBMIT_GROUPS => array( 'album_submit_access_groups' ) ) ), NULL, NULL, NULL, 'album_submit_type' );
$return[] = new \IPS\Helpers\Form\SocialGroup( 'album_submit_access_members', ( $album AND $album->submit_type == static::AUTH_SUBMIT_MEMBERS ) ? $album->submit_access : NULL, FALSE, array( 'owner' => $album ? \IPS\Member::load( $album->owner_id ) : ( \IPS\Request::i()->album_owner ? \IPS\Member::load( \IPS\Request::i()->album_owner, 'name' ) : \IPS\Member::loggedIn() ) ), NULL, NULL, NULL, 'album_submit_access_members' );
$groups = array_combine( array_keys( \IPS\Member\Group::groups( TRUE, FALSE ) ), array_map( function( $_group ) { return (string) $_group; }, \IPS\Member\Group::groups( TRUE, FALSE ) ) );
$return[] = new \IPS\Helpers\Form\Select( 'album_submit_access_groups', ( $album and $album->submit_type == static::AUTH_SUBMIT_GROUPS ) ? explode( ',', $album->submit_access ) : NULL, FALSE, array( 'options' => $groups, 'multiple' => true ), NULL, NULL, NULL, 'album_submit_access_groups' );
$return[] = new \IPS\Helpers\Form\Select( 'album_sort_options', ( $album and $album->sort_options ) ? $album->sort_options : 'updated', FALSE, array( 'options' => array( 'updated' => 'sort_updated', 'last_comment' => 'sort_last_comment', 'title' => 'sort_title', 'rating' => 'sort_rating', 'date' => 'sort_date', 'num_comments' => 'sort_num_comments', 'num_reviews' => 'sort_num_reviews', 'views' => 'sort_views' ) ), NULL, NULL, NULL, 'album_sort_options' );
$return[] = new \IPS\Helpers\Form\Select( 'album_sort_options', ( $album and $album->sort_options ) ? $album->sort_options : 'updated', FALSE, array( 'options' => array( 'updated' => 'sort_updated', 'last_comment' => 'sort_last_comment', 'title' => 'album_sort_caption', 'rating' => 'sort_rating', 'date' => 'sort_date', 'num_comments' => 'sort_num_comments', 'num_reviews' => 'sort_num_reviews', 'views' => 'sort_views' ) ), NULL, NULL, NULL, 'album_sort_options' );
$return[] = new \IPS\Helpers\Form\YesNo( 'album_allow_rating', $album ? $album->allow_rating : TRUE, FALSE );
@@ -624,11 +697,25 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
$values['owner_id'] = ( $values['album_owner'] instanceof \IPS\Member ) ? $values['album_owner']->member_id : $values['album_owner'];
unset( $values['album_owner'] );
}
else
else if ( !$this->postSaveIsEdit )
{
$values['owner_id'] = \IPS\Member::loggedIn()->member_id;
}
switch( $values['album_submit_type'] )
{
case static::AUTH_SUBMIT_GROUPS:
$values['submit_access'] = implode( ',', $values['album_submit_access_groups'] );
break;
case static::AUTH_SUBMIT_MEMBERS:
$values['submit_access'] = $values['album_submit_access_members'];
break;
}
unset( $values['album_submit_access_members'] );
unset( $values['album_submit_access_groups'] );
/* Send to parent */
return $values;
}
@@ -656,6 +743,8 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
*/
public function postSaveForm( $values )
{
\IPS\File::claimAttachments( 'gallery-new-album', $this->id );
/* Update counts in categories if we move the album */
if( $this->postSaveIsEdit and $this->postSaveCategory != $values['category_id'] )
{
@@ -732,6 +821,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
/* Update caches */
$asItem->expireWidgetCaches();
$asItem->adjustSessions();
}
/**
@@ -815,7 +905,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
$this->last_img_date = 0;
$this->last_img_id = 0;
foreach( \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_album_id=? AND image_approved=1', $this->id ), $sortBy, array( 0, 20 ) ) as $image )
foreach( \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_album_id=? AND image_approved=1', $this->id ), $sortBy, array( 0, 20 ), NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER ) as $image )
{
if( $image['image_date'] > $this->last_img_date )
{
@@ -829,23 +919,23 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
$this->last_x_images = json_encode( $_latestImages );
/* Now get the counts and set them for our images */
$this->_items = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_images', array( 'image_album_id=? AND image_approved=1', $this->id ) )->first();
$this->_unapprovedItems = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_images', array( 'image_album_id=? AND image_approved=0', $this->id ) )->first();
$this->_items = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_images', array( 'image_album_id=? AND image_approved=1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->_unapprovedItems = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_images', array( 'image_album_id=? AND image_approved=0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->_comments = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_comments', array( 'gallery_images.image_album_id=? AND comment_approved=1', $this->id ) )->join( 'gallery_images', 'image_id=comment_img_id' )->first();
$this->_unapprovedComments = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_comments', array( 'gallery_images.image_album_id=? AND comment_approved=0', $this->id ) )->join( 'gallery_images', 'image_id=comment_img_id' )->first();
$this->_comments = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_comments', array( 'gallery_images.image_album_id=? AND comment_approved=1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->join( 'gallery_images', 'image_id=comment_img_id' )->first();
$this->_unapprovedComments = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_comments', array( 'gallery_images.image_album_id=? AND comment_approved=0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->join( 'gallery_images', 'image_id=comment_img_id' )->first();
$this->_reviews = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_reviews', array( 'gallery_images.image_album_id=? AND review_approved=1', $this->id ) )->join( 'gallery_images', 'image_id=review_image_id' )->first();
$this->_unapprovedReviews = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_reviews', array( 'gallery_images.image_album_id=? AND review_approved=0', $this->id ) )->join( 'gallery_images', 'image_id=review_image_id' )->first();
$this->_reviews = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_reviews', array( 'gallery_images.image_album_id=? AND review_approved=1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->join( 'gallery_images', 'image_id=review_image_id' )->first();
$this->_unapprovedReviews = \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_reviews', array( 'gallery_images.image_album_id=? AND review_approved=0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->join( 'gallery_images', 'image_id=review_image_id' )->first();
/* And then get counts/latest data for the direct comments/reviews */
$this->comments = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_comments', array( 'comment_album_id=? AND comment_approved=1', $this->id ) )->first();
$this->comments_unapproved = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_comments', array( 'comment_album_id=? AND comment_approved=0', $this->id ) )->first();
$this->comments_hidden = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_comments', array( 'comment_album_id=? AND comment_approved=-1', $this->id ) )->first();
$this->comments = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_comments', array( 'comment_album_id=? AND comment_approved=1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->comments_unapproved = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_comments', array( 'comment_album_id=? AND comment_approved=0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->comments_hidden = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_comments', array( 'comment_album_id=? AND comment_approved=-1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->reviews = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_reviews', array( 'review_album_id=? AND review_approved=1', $this->id ) )->first();
$this->reviews_unapproved = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_reviews', array( 'review_album_id=? AND review_approved=0', $this->id ) )->first();
$this->reviews_hidden = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_reviews', array( 'review_album_id=? AND review_approved=-1', $this->id ) )->first();
$this->reviews = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_reviews', array( 'review_album_id=? AND review_approved=1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->reviews_unapproved = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_reviews', array( 'review_album_id=? AND review_approved=0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->reviews_hidden = (int) \IPS\Db::i()->select( 'COUNT(*) as total', 'gallery_album_reviews', array( 'review_album_id=? AND review_approved=-1', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
/* Save and then make sure search index is updated */
\IPS\Content\Search\Index::i()->index( $this->asItem() );
@@ -861,14 +951,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
*/
public function contentImages( $limit = NULL )
{
$images = array();
foreach( \IPS\gallery\Image::getItemsWithPermission( array( array( 'image_album_id=?', $this->id ) ), NULL, $limit ? $limit : 10 ) as $image )
{
$images[] = array( 'gallery_Images' => $image->medium_file_name );
}
return count( $images ) ? $images : NULL;
$this->asItem()->contentImages( $limit );
}
/**
@@ -1047,45 +1130,53 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
/**
* Retrieve a cover photo
*
* @param string $size One of full, medium, small or thumb
* @param string $size Masked or small
* @return string|null
*/
public function coverPhoto( $size='thumb' )
public function coverPhoto( $size='small' )
{
/* Can we view the images in this album? */
if ( !$this->can( 'read' ) )
{
return NULL;
}
if( $size == 'full' )
/* Make sure it's a valid size */
if( !in_array( $size, array( 'masked', 'small' ) ) )
{
$size = 'masked';
throw new \InvalidArgumentException;
}
$property = $size . "_file_name";
if( !$this->cover_img_id )
/* If we have an explicit cover photo set, make sure it's valid and load/cache it */
if( $this->cover_img_id )
{
if( $lastImage = $this->lastImage() )
if( $this->coverPhoto === NULL )
{
return (string) \IPS\File::get( 'gallery_Images', $lastImage->$property )->url;
try
{
$this->coverPhoto = \IPS\gallery\Image::load( $this->cover_img_id );
}
catch( \OutOfRangeException $e )
{
/* Cover photo isn't valid, reset album automatically */
$this->cover_img_id = 0;
$this->save();
}
}
return NULL;
if( $this->coverPhoto !== NULL )
{
return (string) \IPS\File::get( 'gallery_Images', $this->coverPhoto->$property )->url;
}
}
if( !in_array( $size, array( 'masked', 'medium', 'small', 'thumb' ) ) )
if( $lastImage = $this->lastImage() AND !$lastImage->media )
{
throw new \InvalidArgumentException;
return (string) \IPS\File::get( 'gallery_Images', $lastImage->$property )->url;
}
if( $this->coverPhoto === NULL )
{
$this->coverPhoto = \IPS\gallery\Image::load( $this->cover_img_id );
}
return (string) \IPS\File::get( 'gallery_Images', $this->coverPhoto->$property )->url;
return NULL;
}
/**
@@ -1132,7 +1223,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
return TRUE;
}
return FALSE;
return $this->asItem()->canDelete();
}
/**
@@ -1149,6 +1240,55 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
/* Load the item */
$asItem = $this->asItem();
/* Figure out member */
$member = $member ?: \IPS\Member::loggedIn();
/* If we are checking 'add' permission, verify if we can add */
if( $permission == 'add' )
{
/* First check we can submit to this album based on the "Submissions" setting */
switch ( $this->submit_type )
{
/* Owner */
case static::AUTH_SUBMIT_OWNER:
if ( $this->owner_id != $member->member_id )
{
return FALSE;
}
break;
/* Chosen groups */
case static::AUTH_SUBMIT_GROUPS:
if ( !$member->inGroup( explode( ',', $this->submit_access ) ) )
{
return FALSE;
}
break;
/* Chosen members */
case static::AUTH_SUBMIT_MEMBERS:
if ( !in_array( $this->submit_access, $member->socialGroups() ) )
{
return FALSE;
}
break;
/* Club */
case static::AUTH_SUBMIT_CLUB:
if ( $club = $this->category()->club() and !in_array( $club->id, $member->clubs() ) )
{
return FALSE;
}
break;
}
/* Verify if we can add any more files to this album */
if( \IPS\Member::loggedIn()->group['g_img_album_limit'] AND \IPS\Member::loggedIn()->group['g_img_album_limit'] - ( $this->count_imgs + $this->count_imgs_hidden ) < 1 )
{
return FALSE;
}
}
/* Albums can be hidden, so make sure to test permissions */
if( $asItem->hidden() )
{
@@ -1190,7 +1330,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
*/
public function searchIndexPermissions()
{
$return = parent::searchIndexPermissions();
$return = $this->category()->searchIndexPermissions();
if ( $this->type != static::AUTH_TYPE_PUBLIC )
{
@@ -1250,6 +1390,7 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse int id ID number
* @apiresponse string name Name
@@ -1261,17 +1402,17 @@ class _Album extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\
* @apiresponse int images Number of images
* @apiresponse string url URL
*/
public function apiOutput()
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'category' => $this->category()->apiOutput(),
'owner' => $this->owner()->apiOutput(),
'category' => $this->category()->apiOutput( $authorizedMember ),
'owner' => $this->owner()->apiOutput( $authorizedMember ),
'privacy' => ( $this->type == static::AUTH_TYPE_PUBLIC ) ? 'public' : ( $this->type == static::AUTH_TYPE_PRIVATE ? 'private' : ( $this->type == static::AUTH_TYPE_RESTRICTED ? 'restricted' : null ) ),
'approvedMembers' => $this->approvedMembers ? array_map( function( $val ) {
return $val->apiOutput();
'approvedMembers' => $this->approvedMembers ? array_map( function( $val ) use ( $authorizedMember ) {
return $val->apiOutput( $authorizedMember );
}, $this->approvedMembers ) : null,
'images' => $this->count_imgs,
'url' => (string) $this->url()
+15 -3
View File
@@ -21,9 +21,9 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Album Comment Model
*/
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief [ActiveRecord] Multiton Store
@@ -123,7 +123,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
{
parent::postCreate();
$this->item()->asNode()->setLastComment();
$this->item()->asNode()->setLastComment( $this );
$this->item()->asNode()->save();
}
@@ -173,4 +173,16 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'gallery', 'front' ) );
return \IPS\Theme::i()->getTemplate( 'global', 'gallery' )->embedAlbumComment( $this, $this->item(), $this->url()->setQueryString( $params ) );
}
/**
* Get the container item class to use for mod permission checks
*
* @return string|NULL
* @note By default we will return NULL and the container check will execute against Node::$contentItemClass, however
* in some situations we may need to override this (i.e. for Gallery Albums)
*/
protected static function getContainerModPermissionClass()
{
return 'IPS\gallery\Album\Item';
}
}
+102 -23
View File
@@ -31,10 +31,9 @@ class _Item extends \IPS\Content\Item implements
\IPS\Content\Hideable,
\IPS\Content\Featurable,
\IPS\Content\Lockable,
\IPS\Content\ReportCenter,
\IPS\Content\MetaData
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief Application
@@ -230,7 +229,7 @@ class _Item extends \IPS\Content\Item implements
{
if( $this->cover_img_id OR $this->last_img_id )
{
return \IPS\File::get( 'gallery_Images', \IPS\gallery\Image::load( $this->cover_img_id ?: $this->last_img_id )->thumb_file_name );
return \IPS\File::get( 'gallery_Images', \IPS\gallery\Image::load( $this->cover_img_id ?: $this->last_img_id )->small_file_name );
}
else
{
@@ -279,10 +278,48 @@ class _Item extends \IPS\Content\Item implements
*/
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=\IPS\Content\Hideable::FILTER_AUTOMATIC, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL, $skipPermission=FALSE, $joinTags=TRUE, $joinAuthor=TRUE, $joinLastCommenter=TRUE, $showMovedLinks=FALSE )
{
if( $permissionKey == 'add' )
{
$where[] = static::submitRestrictionWhere( $member, \IPS\gallery\Category::load( $where[0][1] ) );
}
$where[] = static::getItemsWithPermissionWhere( $where, $member, $joins );
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter, $showMovedLinks );
}
/**
* Additional WHERE clauses for finding albums the user can submit to
*
* @param \IPS\Member|NULL $member Member to check
* @param \IPS\gallery\Category $category Category we are submitted in
* @return string
*/
public static function submitRestrictionWhere( $member, $category )
{
$member = $member ?: \IPS\Member::loggedIn();
/* For starters, allow us to submit to public albums and our own albums */
$wheres = array(
'(album_submit_type=' . \IPS\gallery\Album::AUTH_SUBMIT_OWNER . ' and album_owner_id=' . $member->member_id . ')',
'(album_submit_type=' . \IPS\gallery\Album::AUTH_SUBMIT_PUBLIC . ')'
);
/* Now allow us to submit to albums that allow group submissions */
$wheres[] = '(album_submit_type=' . \IPS\gallery\Album::AUTH_SUBMIT_GROUPS . ' AND ' . \IPS\Db::i()->findInSet( 'album_submit_access', $member->groups ) . ')';
/* And where we as an individual member are allowed to submit */
$wheres[] = '(album_submit_type=' . \IPS\gallery\Album::AUTH_SUBMIT_MEMBERS . ' AND ' . \IPS\Db::i()->findInSet( 'album_submit_access', $member->socialGroups() ) . ')';
/* And finally, if we're in a club and we allow anyone in the club to submit, handle that */
if( $category->club() AND in_array( $category->club()->id, $member->clubs() ) )
{
$wheres[] = '(album_submit_type=' . \IPS\gallery\Album::AUTH_SUBMIT_CLUB . ')';
}
return '(album_owner_id=' . $member->member_id . ' OR ' . implode( ' OR ', $wheres ) . ')';
}
/**
* @brief Cached groups the member can access
*/
@@ -368,24 +405,6 @@ class _Item extends \IPS\Content\Item implements
return $this->asNode()->searchIndexPermissions();
}
/**
* Can edit?
*
* @param \IPS\Member|NULL $member The member to check for (NULL for currently logged in member)
* @return bool
*/
public function canEdit( $member=NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
if( $member->member_id == $this->mapped('author') )
{
return TRUE;
}
return parent::canEdit( $member );
}
/**
* Columns needed to query for search result / stream view
*
@@ -433,8 +452,13 @@ class _Item extends \IPS\Content\Item implements
$return = array();
foreach ( \IPS\gallery\Image::getItemsWithPermission( array( array( 'image_id IN(' . implode( ',', $imageIds ) . ')' ) ), NULL, 20 ) as $image )
foreach ( \IPS\gallery\Image::getItemsWithPermission( array( array( 'image_id IN(' . implode( ',', $imageIds ) . ')' ) ), NULL, NULL ) as $image )
{
if( isset( $return[ $image->album_id] ) AND count( $return[ $image->album_id ] ) > 19 )
{
continue;
}
$return[ $image->album_id ][] = $image;
}
@@ -464,6 +488,29 @@ class _Item extends \IPS\Content\Item implements
return \IPS\Theme::i()->getTemplate( 'global', 'gallery', 'front' )->searchResultAlbumSnippet( $indexData, $itemData, $images, $url, $view == 'condensed' );
}
/**
* Return the language string key to use in search results
*
* @note Normally we show "(user) posted a (thing) in (area)" but sometimes this may not be accurate, so this is abstracted to allow
* content classes the ability to override
* @param array $authorData Author data
* @param array $articles Articles language strings
* @param array $indexData Search index data
* @param array $itemData Data about the item
* @return string
*/
public static function searchResultSummaryLanguage( $authorData, $articles, $indexData, $itemData )
{
if( !in_array( 'IPS\Content\Comment', class_parents( $indexData['index_class'] ) ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( "album_user_own_activity_item", FALSE, array( 'sprintf' => array( $authorData['name'], $articles['indefinite'] ) ) );
}
else
{
return parent::searchResultSummaryLanguage( $authorData, $articles, $indexData, $itemData );
}
}
/**
* @brief A classname applied to the search result block
*/
@@ -516,7 +563,7 @@ class _Item extends \IPS\Content\Item implements
{
\IPS\gallery\Application::outputCss();
return array( \IPS\Theme::i()->getTemplate( 'browse', 'gallery' ), 'albums' );
return array( \IPS\Theme::i()->getTemplate( 'browse', 'gallery', 'front' ), 'albums' );
}
/**
@@ -581,4 +628,36 @@ class _Item extends \IPS\Content\Item implements
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'gallery', 'front' ) );
return \IPS\Theme::i()->getTemplate( 'global', 'gallery' )->embedAlbums( $this, $this->asNode(), $this->url()->setQueryString( $params ) );
}
/**
* Get the container item class to use for mod permission checks
*
* @return string|NULL
* @note By default we will return NULL and the container check will execute against Node::$contentItemClass, however
* in some situations we may need to override this (i.e. for Gallery Albums)
*/
protected static function getContainerModPermissionClass()
{
return 'IPS\gallery\Album\Item';
}
/**
* Returns the content images
*
* @param int|null $limit Number of attachments to fetch, or NULL for all
*
* @return array|NULL If array, then array( 'core_Attachment' => 'month_x/foo.gif', ... );
* @throws \BadMethodCallException
*/
public function contentImages( $limit = NULL )
{
$images = array();
foreach( \IPS\gallery\Image::getItemsWithPermission( array( array( 'image_album_id=?', $this->id ) ), NULL, $limit ? $limit : 10 ) as $image )
{
$images[] = array( 'gallery_Images' => $image->masked_file_name );
}
return count( $images ) ? $images : NULL;
}
}
+16 -3
View File
@@ -21,9 +21,9 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Album Review Model
*/
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief [ActiveRecord] Multiton Store
@@ -63,6 +63,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
'votes_helpful' => 'votes_helpful',
'votes_data' => 'votes_data',
'approved' => 'approved',
'author_response' => 'author_response',
);
/**
@@ -105,7 +106,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
{
parent::postCreate();
$this->item()->asNode()->setLastComment();
$this->item()->asNode()->setLastReview( $this );
$this->item()->asNode()->save();
}
@@ -152,4 +153,16 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'gallery', 'front' ) );
return \IPS\Theme::i()->getTemplate( 'global', 'gallery' )->embedAlbumReview( $this, $this->item(), $this->url()->setQueryString( $params ) );
}
/**
* Get the container item class to use for mod permission checks
*
* @return string|NULL
* @note By default we will return NULL and the container check will execute against Node::$contentItemClass, however
* in some situations we may need to override this (i.e. for Gallery Albums)
*/
protected static function getContainerModPermissionClass()
{
return 'IPS\gallery\Album\Item';
}
}
+20 -3
View File
@@ -62,7 +62,7 @@ class _Table extends \IPS\Helpers\Table\Content
{
if ( !$this->sortBy and $container->_sortBy )
{
$this->sortBy = '`' . \IPS\gallery\Album::$databaseTable . '`.`' . \IPS\gallery\Album::$databasePrefix . $container->_sortBy . '`';
$this->sortBy = \IPS\gallery\Album::$databasePrefix . $container->_sortBy;
$this->sortDirection = $container->_sortOrder;
}
@@ -71,7 +71,7 @@ class _Table extends \IPS\Helpers\Table\Content
$this->filter = $container->_filter;
}
if ( $this->sortBy === 'name' )
if ( $this->sortBy === 'album_name' )
{
$this->sortDirection = 'asc';
}
@@ -107,7 +107,7 @@ class _Table extends \IPS\Helpers\Table\Content
continue;
}
$this->sortOptions[ 'album_' . $k ] = $k;
$this->sortOptions[ 'album_' . $k ] = 'album_' . $k;
}
unset( $this->sortOptions['date'] );
@@ -151,4 +151,21 @@ class _Table extends \IPS\Helpers\Table\Content
{
return FALSE;
}
/**
* Return the sort direction to use for links
*
* @note Abstracted so other table helper instances can adjust as needed
* @param string $column Sort by string
* @return string [asc|desc]
*/
public function getSortDirection( $column )
{
if( $column == 'album_name' )
{
return 'asc';
}
return 'desc';
}
}
@@ -287,6 +287,7 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
return !isset( \IPS\Request::i()->id ) or ( $node->id != \IPS\Request::i()->id and !$node->isChildOf( $node::load( \IPS\Request::i()->id ) ) );
}
) ) );
$sortoptions = array(
'album_last_img_date' => 'sort_updated',
'album_last_comment' => 'sort_last_comment',
@@ -298,7 +299,10 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
'album_count_imgs' => 'sort_album_count_imgs'
);
$form->add( new \IPS\Helpers\Form\Select( 'category_sort_options', $this->sort_options ?: 'updated', FALSE, array( 'options' => $sortoptions ), NULL, NULL, NULL, 'category_sort_options' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'category_allow_albums', $this->id ? $this->allow_albums : TRUE, FALSE ) );
$form->add( new \IPS\Helpers\Form\Select( 'category_sort_options_img', $this->sort_options_img ?: 'updated', FALSE, array( 'options' => array( 'updated' => 'sort_updated', 'last_comment' => 'sort_last_comment', 'title' => 'album_sort_caption', 'rating' => 'sort_rating', 'date' => 'sort_date', 'num_comments' => 'sort_num_comments', 'num_reviews' => 'sort_num_reviews', 'views' => 'sort_views' ) ), NULL, NULL, NULL, 'category_sort_options_img' ) );
$form->add( new \IPS\Helpers\Form\Radio( 'category_allow_albums', $this->id ? $this->allow_albums : 1, TRUE, array( 'options' => array( 0 => 'cat_no_allow_albums', 1 => 'cat_allow_albums', 2 => 'cat_require_albums' ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'category_approve_img', $this->approve_img, FALSE ) );
if( \IPS\Settings::i()->gallery_watermark_path )
@@ -374,7 +378,7 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
/* If watermarks are disabled, enable them at the category level so that if you later enable watermarks they work in your existing categories */
if( !\IPS\Settings::i()->gallery_watermark_path )
{
$values['category_watermark']= 1;
$values['category_watermark'] = 1;
}
/* Claim attachments */
@@ -565,7 +569,7 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
{
try
{
$image = \IPS\gallery\Image::constructFromData( \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_category_id=? AND image_approved=1 AND ( image_album_id = 0 OR album_type NOT IN ( 2, 3 ) )', $this->id ), 'image_date DESC', 1 )->join(
$image = \IPS\gallery\Image::constructFromData( \IPS\Db::i()->select( '*', 'gallery_images', array( 'image_category_id=? AND image_approved=1 AND ( image_album_id = 0 OR album_type NOT IN ( 2, 3 ) )', $this->id ), 'image_date DESC', 1, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->join(
'gallery_albums',
"image_album_id=album_id"
)->first() );
@@ -590,6 +594,29 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
$album->save();
}
}
/**
* Set the comment/approved/hidden counts
*
* @return void
*/
public function resetCommentCounts()
{
parent::resetCommentCounts();
/* If we allow albums, add album comment/review counts too */
if( $this->allow_albums )
{
try
{
$consolidated = \IPS\Db::i()->select( 'SUM(album_comments) as comments, SUM(album_comments_unapproved) as unapproved_comments', 'gallery_albums', array( 'album_category_id=?', $this->_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
$this->_comments = $this->count_comments + $consolidated['comments'];
$this->_unapprovedComments = $this->count_comments_hidden + $consolidated['unapproved_comments'];
}
catch( \UnderflowException $e ){}
}
}
/**
* Get last comment time
@@ -610,44 +637,54 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
/**
* Retrieve a cover photo
*
* @param string $size One of full, medium, small or thumb
* @param string $size Masked or small
* @return string|null
* @throws \InvalidArgumentException
*/
public function coverPhoto( $size='thumb' )
public function coverPhoto( $size='small' )
{
if ( !$this->can( 'read' ) )
{
return NULL;
}
if( $size == 'full' )
/* Make sure it's a valid size */
if( !in_array( $size, array( 'masked', 'small' ) ) )
{
$size = 'masked';
throw new \InvalidArgumentException;
}
$property = $size . "_file_name";
if( !$this->cover_img_id )
/* If we have an explicit cover photo set, make sure it's valid and load/cache it */
if( $this->cover_img_id )
{
if( $lastImage = $this->lastImage() )
if( $this->coverPhoto === NULL )
{
return (string) \IPS\File::get( 'gallery_Images', $lastImage->$property )->url;
try
{
$this->coverPhoto = \IPS\gallery\Image::load( $this->cover_img_id );
}
catch( \OutOfRangeException $e )
{
/* Cover photo isn't valid, reset category automatically */
$this->cover_img_id = 0;
$this->save();
}
}
return NULL;
if( $this->coverPhoto !== NULL )
{
return (string) \IPS\File::get( 'gallery_Images', $this->coverPhoto->$property )->url;
}
}
if( !in_array( $size, array( 'masked', 'medium', 'small', 'thumb' ) ) )
if( $lastImage = $this->lastImage() )
{
throw new \InvalidArgumentException;
return (string) \IPS\File::get( 'gallery_Images', $lastImage->$property )->url;
}
if( $this->coverPhoto === NULL )
{
$this->coverPhoto = \IPS\gallery\Image::load( $this->cover_img_id );
}
return (string) \IPS\File::get( 'gallery_Images', $this->coverPhoto->$property )->url;
return NULL;
}
/**
@@ -778,10 +815,11 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
*
* @param mixed $permission A key which has a value in static::$permissionMap['view'] matching a column ID in core_permission_index
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
* @param array $where Additional WHERE clause
* @return bool
* @throws \OutOfBoundsException If $permission does not exist in static::$permissionMap
*/
public static function canOnAny( $permission, $member = NULL )
public static function canOnAny( $permission, $member=NULL, $where = array() )
{
/* Load member */
if ( $member === NULL )
@@ -794,7 +832,7 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
return FALSE;
}
return parent::canOnAny( $permission, $member );
return parent::canOnAny( $permission, $member, $where );
}
/**
@@ -835,7 +873,6 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
return 'x';
}
/* !Clubs */
/**
@@ -884,5 +921,11 @@ class _Category extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$this->name_seo = \IPS\Http\Url\Friendly::seoTitle( $values['club_node_name'] );
}
if ( !$this->_id )
{
$this->save();
\IPS\File::claimAttachments( 'gallery-new-cat', $this->id, NULL, 'description' );
}
}
}
+14 -5
View File
@@ -21,10 +21,20 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Image Comment Model
*/
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief [Content\Comment] Form Template
*/
public static $formTemplate = array( array( 'forms', 'gallery', 'front' ), 'commentTemplate' );
/**
* @brief [Content\Comment] Comment Template
*/
public static $commentTemplate = array( array( 'global', 'gallery', 'front' ), 'commentContainer' );
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -146,10 +156,9 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
*/
public static function searchResultSnippet( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $view )
{
$image = $itemData['image_medium_file_name'] ?: $itemData['image_masked_file_name'];
$url = \IPS\Http\Url::internal( \IPS\gallery\Image::$urlBase . $indexData['index_item_id'], 'front', \IPS\gallery\Image::$urlTemplate, \IPS\Http\Url\Friendly::seoTitle( $indexData['index_title'] ?: $itemData[ \IPS\gallery\Image::$databasePrefix . \IPS\gallery\Image::$databaseColumnMap['title'] ] ) );
return \IPS\Theme::i()->getTemplate( 'global', 'gallery', 'front' )->searchResultCommentSnippet( $indexData, $itemData, $image, $url, $reviewRating, $view == 'condensed' );
return \IPS\Theme::i()->getTemplate( 'global', 'gallery', 'front' )->searchResultCommentSnippet( $indexData, $itemData, $itemData['image_small_file_name'], $url, $reviewRating, $view == 'condensed' );
}
/**
+246 -189
View File
@@ -25,7 +25,6 @@ class _Image extends \IPS\Content\Item implements
\IPS\Content\Permissions,
\IPS\Content\Tags,
\IPS\Content\Followable,
\IPS\Content\ReportCenter,
\IPS\Content\ReadMarkers,
\IPS\Content\Views,
\IPS\Content\Hideable, \IPS\Content\Featurable, \IPS\Content\Pinnable, \IPS\Content\Lockable,
@@ -35,7 +34,7 @@ class _Image extends \IPS\Content\Item implements
\IPS\Content\Embeddable,
\IPS\Content\MetaData
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief Application
@@ -142,9 +141,9 @@ class _Image extends \IPS\Content\Item implements
public static function basicDataColumns()
{
$return = parent::basicDataColumns();
$return[] = 'image_medium_file_name';
$return[] = 'image_masked_file_name';
$return[] = 'image_original_file_name';
$return[] = 'image_small_file_name';
$return[] = 'image_album_id';
$return[] = 'image_copyright';
return $return;
@@ -239,7 +238,7 @@ class _Image extends \IPS\Content\Item implements
{
if ( ! empty( $v['NOTE'] ) )
{
$v['NOTE'] = htmlspecialchars( $v['NOTE'], \IPS\HTMLENTITIES, 'UTF-8', TRUE );
$v['NOTE'] = htmlspecialchars( $v['NOTE'], ENT_DISALLOWED, 'UTF-8', TRUE );
}
} );
}
@@ -315,17 +314,7 @@ class _Image extends \IPS\Content\Item implements
return $this->_data['caption_seo'] ?: \IPS\Http\Url\Friendly::seoTitle( $this->caption );
}
/**
* Get Medium File Name
*
* @return string
*/
public function get_medium_file_name()
{
return $this->_data['medium_file_name'] ?: $this->_data['masked_file_name'];
}
/**
* Get Small File Name
*
@@ -333,17 +322,7 @@ class _Image extends \IPS\Content\Item implements
*/
public function get_small_file_name()
{
return $this->_data['small_file_name'] ?: $this->_data['medium_file_name'];
}
/**
* Get Thumbnail File Name
*
* @return string
*/
public function get_thumb_file_name()
{
return $this->_data['thumb_file_name'] ?: $this->_data['small_file_name'];
return $this->_data['small_file_name'] ?: $this->_data['masked_file_name'];
}
/**
@@ -369,9 +348,10 @@ class _Image extends \IPS\Content\Item implements
/**
* Return selection of image data as a JSON-encoded string (used for patchwork)
*
* @param array $parameters Optional key => value array of additional query string parameters to use with the image URL
* @return string
*/
public function json()
public function json( $parameters=array() )
{
$imageSizes = json_decode( $this->_data['data'], true );
$state = array();
@@ -453,6 +433,11 @@ class _Image extends \IPS\Content\Item implements
{
$modActions[] = 'approve';
}
if( $this->canHide() )
{
$modActions[] = 'hide';
}
$state['pending'] = TRUE;
$modStates[] = 'unapproved';
@@ -485,26 +470,26 @@ class _Image extends \IPS\Content\Item implements
return json_encode( array(
'filenames' => array(
'small' => array( $this->_data['small_file_name'] ? (string) \IPS\File::get( 'gallery_Images', $this->_data['small_file_name'] )->url : null, $imageSizes['small'][0], $imageSizes['small'][1] ),
'medium' => array( $this->_data['medium_file_name'] ? (string) \IPS\File::get( 'gallery_Images', $this->_data['medium_file_name'] )->url : null, $imageSizes['medium'][0], $imageSizes['medium'][1] )
'large' => array( $this->_data['masked_file_name'] ? (string) \IPS\File::get( 'gallery_Images', $this->_data['masked_file_name'] )->url : null, $imageSizes['large'][0], $imageSizes['large'][1] )
),
/* We do not use ENT_QUOTES as this replaces " to &quot; which browsers turn back into " again which breaks the JSON string as it needs to be \", single quotes break the data-attribute='' boundaries */
'caption' => str_replace( "'", "&apos;", htmlspecialchars( $this->_data['caption'], \IPS\HTMLENTITIES, 'UTF-8', FALSE ) ),
'caption' => $this->_data['caption'],
'date' => \IPS\DateTime::ts( $this->mapped('date') )->relative(),
'hasState' => count( $state ) ? TRUE : FALSE,
'state' => $state,
'container' => ( $this->directContainer() instanceof \IPS\gallery\Category ) ? \IPS\Member::loggedIn()->language()->addToStack( "gallery_category_{$this->directContainer()->_id}", FALSE, array( 'json' => TRUE, 'escape' => TRUE ) ) : mb_substr( json_encode( str_replace( "'", "&apos;", htmlspecialchars( str_replace( '&quot;', '"', $this->directContainer()->_title ), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) ), JSON_UNESCAPED_UNICODE ), 1, -1 ),
'container' => ( $this->directContainer() instanceof \IPS\gallery\Category ) ? \IPS\Member::loggedIn()->language()->addToStack( "gallery_category_{$this->directContainer()->_id}", false, array( 'json' => true, 'jsonEscape' => true ) ) : $this->directContainer()->_title,
'id' => $this->_data['id'],
'url' => (string) $this->url(),
'url' => (string) $this->url()->setQueryString( $parameters ),
'author' => array(
'photo' => str_replace( "'", "&apos;", htmlspecialchars( (string) $this->author()->photo, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) ),
'name' => str_replace( "'", "&apos;", htmlspecialchars( $this->author()->name, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) )
'photo' => (string) $this->author()->photo,
'name' => $this->author()->name
),
'modActions' => $modActions,
'modStates' => $modStates,
'allowComments' => (boolean) $this->directContainer()->allow_comments,
'comments' => ( $this->directContainer()->allow_comments ) ? $this->_data['comments'] : 0,
'views' => $this->_data['views']
) );
), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS );
}
/**
@@ -541,7 +526,7 @@ class _Image extends \IPS\Content\Item implements
{
\IPS\gallery\Application::outputCss();
return array( \IPS\Theme::i()->getTemplate( 'browse', 'gallery' ), 'tableRowsRows' );
return array( \IPS\Theme::i()->getTemplate( 'browse', 'gallery', 'front' ), 'tableRowsRows' );
}
/**
@@ -578,18 +563,19 @@ class _Image extends \IPS\Content\Item implements
/**
* Get comment/review output
*
* @param string $tab Active tab
* @param string $tab Active tab
* @param bool $condensed Use condensed style
* @return string
*/
public function commentReviews( $tab )
public function commentReviews( $tab, $condensed=FALSE )
{
if ( $tab === 'reviews' AND $this->container()->allow_reviews AND $this->directContainer()->allow_reviews )
{
return \IPS\Theme::i()->getTemplate('view')->reviews( $this );
return \IPS\Theme::i()->getTemplate('view')->reviews( $this, $condensed );
}
elseif( $tab === 'comments' AND $this->container()->allow_comments AND $this->directContainer()->allow_comments )
{
return \IPS\Theme::i()->getTemplate('view')->comments( $this );
return \IPS\Theme::i()->getTemplate('view')->comments( $this, $condensed );
}
return '';
@@ -771,7 +757,7 @@ class _Image extends \IPS\Content\Item implements
$queueData['album_id'] = $album->$albumIdColumn;
}
\IPS\Task::queue( 'gallery', 'Follow', $queueData );
\IPS\Task::queue( 'gallery', 'Follow', $queueData, 2 );
}
else
{
@@ -874,14 +860,16 @@ class _Image extends \IPS\Content\Item implements
protected $imagesSameDate = NULL;
/**
* Get the next 5 images in the container
* Get the next or previous 5 images in the container
*
* @param int $count (Maximum) number of images to return
* @param int $count (Maximum) number of images to return
* @param string $direction DESC or ASC
* @return array
*/
public function nextImages( $count )
public function fetchNextOrPreviousImages( $count, $direction = 'DESC' )
{
$where = array();
$dir = $direction == 'DESC' ? '<' : '>';
if( $this->album_id )
{
@@ -905,8 +893,8 @@ class _Image extends \IPS\Content\Item implements
if( !$this->imagesSameDate )
{
$where['date'] = array( static::$databasePrefix . $column . ' >= ?', $this->$column );
return iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' ASC', $count ) );
$where['date'] = array( static::$databasePrefix . $column . " {$dir}= ?", $this->$column );
return iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' ' . $direction, $count ) );
}
else
{
@@ -916,7 +904,7 @@ class _Image extends \IPS\Content\Item implements
$results = array();
$seen = FALSE;
foreach( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' ASC', NULL ) as $image )
foreach( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' ' . $direction, NULL ) as $image )
{
if( !$seen )
{
@@ -938,81 +926,8 @@ class _Image extends \IPS\Content\Item implements
if( count( $results ) < $count )
{
$where['date'] = array( static::$databasePrefix . $column . ' > ?', $this->$column );
$results = array_merge( $results, iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' ASC', $count - count( $results ) ) ) );
}
return $results;
}
}
/**
* Get the previous 5 images in the container
*
* @param int $count (Maximum) number of images to return
* @return array
*/
public function previousImages( $count )
{
$where = array();
if( $this->album_id )
{
$where[] = array( 'image_album_id=?', $this->album_id );
}
else
{
$where[] = array( 'image_category_id=?', $this->category_id );
$where[] = array( 'image_album_id=?', 0 );
}
$where['id'] = array( 'image_id<>?', $this->id );
$column = $this->getDateColumn();
if( $this->imagesSameDate === NULL )
{
$where['date'] = array( static::$databasePrefix . $column . '= ?', $this->$column );
$this->imagesSameDate = \IPS\Db::i()->select( 'COUNT(*)', 'gallery_images', $where )->first();
}
if( !$this->imagesSameDate )
{
$where['date'] = array( static::$databasePrefix . $column . ' <= ?', $this->$column );
return iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' DESC', $count ) );
}
else
{
$where['date'] = array( static::$databasePrefix . $column . '= ?', $this->$column );
unset( $where['id'] );
$results = array();
$seen = FALSE;
foreach( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' DESC', NULL ) as $image )
{
if( !$seen )
{
if( $image->id == $this->id )
{
$seen = TRUE;
}
continue;
}
$results[] = $image;
if( count( $results ) == $count )
{
break;
}
}
if( count( $results ) < $count )
{
$where['date'] = array( static::$databasePrefix . $column . ' < ?', $this->$column );
$results = array_merge( $results, iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' DESC', $count - count( $results ) ) ) );
$where['date'] = array( static::$databasePrefix . $column . " {$dir} ?", $this->$column );
$results = array_merge( $results, iterator_to_array( \IPS\gallery\Image::getItemsWithPermission( $where, static::$databasePrefix . $column . ' ' . $direction, $count - count( $results ) ) ) );
}
return $results;
@@ -1022,11 +937,46 @@ class _Image extends \IPS\Content\Item implements
/**
* Get Next Item
*
* @param string|NULL $context Context to consider next/previous from
* @return \IPS\Content\Item|NULL
*/
public function nextItem()
public function nextItem( $context=NULL )
{
$result = $this->nextImages( 1 );
if( $context !== NULL )
{
$results = NULL;
switch( $context )
{
case 'featured':
$results = iterator_to_array( static::featured( 20, NULL ) );
break;
case 'new':
$results = iterator_to_array( static::getItemsWithPermission( ( \IPS\Settings::i()->club_nodes_in_apps ? array() : array( array( 'category_club_id IS NULL' ) ) ), NULL, 30, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, !\IPS\Settings::i()->club_nodes_in_apps ) );
break;
}
if( $results !== NULL )
{
$returnNext = FALSE;
foreach( $results as $imageResult )
{
if( $returnNext === TRUE )
{
return $imageResult;
}
if( $imageResult->id == $this->id )
{
$returnNext = TRUE;
}
}
}
return NULL;
}
$result = $this->fetchNextOrPreviousImages( 1, 'DESC' );
if( is_array( $result ) )
{
@@ -1037,11 +987,43 @@ class _Image extends \IPS\Content\Item implements
/**
* Get Previous Item
*
* @param string|NULL $context Context to consider next/previous from
* @return \IPS\Content\Item|NULL
*/
public function prevItem()
public function prevItem( $context=NULL )
{
$result = $this->previousImages( 1 );
if( $context !== NULL )
{
$results = NULL;
switch( $context )
{
case 'featured':
$results = iterator_to_array( static::featured( 20, NULL ) );
break;
case 'new':
$results = iterator_to_array( static::getItemsWithPermission( ( \IPS\Settings::i()->club_nodes_in_apps ? array() : array( array( 'category_club_id IS NULL' ) ) ), NULL, 30, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, !\IPS\Settings::i()->club_nodes_in_apps ) );
break;
}
if( $results !== NULL )
{
$previousResult = NULL;
foreach( $results as $imageResult )
{
if( $imageResult->id == $this->id )
{
return $previousResult;
}
$previousResult = $imageResult;
}
}
return NULL;
}
$result = $this->fetchNextOrPreviousImages( 1, 'ASC' );
if( is_array( $result ) )
{
@@ -1060,7 +1042,7 @@ class _Image extends \IPS\Content\Item implements
{
if ( $container and $container->approve_img and !$member->group['g_avoid_q'] )
{
return TRUE;
return !static::modPermission( 'approve', $member, $container );
}
return parent::moderateNewItems( $member, $container );
@@ -1124,7 +1106,7 @@ class _Image extends \IPS\Content\Item implements
'attachIds' => ( $item === NULL ? NULL : array( $item->id ) ),
'editorId' => "filedata_{$currentlyEditing}_image_description"
) );
$return['credit'] = new \IPS\Helpers\Form\TextArea( 'image_credit_info', $item ? $item->credit_info : NULL, FALSE );
$return['credit_info'] = new \IPS\Helpers\Form\TextArea( 'image_credit_info', $item ? $item->credit_info : NULL, FALSE );
$return['copyright'] = new \IPS\Helpers\Form\Text( 'image_copyright', $item ? $item->copyright : NULL, FALSE, array( 'maxLength' => 255 ) );
/* If we are editing, return the appropriate fields */
@@ -1177,7 +1159,7 @@ class _Image extends \IPS\Content\Item implements
* @return void
*/
public function processForm( $values )
{
{
parent::processForm( $values );
/* Set a few details */
@@ -1242,7 +1224,7 @@ class _Image extends \IPS\Content\Item implements
{
$val = utf8_encode( $val );
} );
//$this->metadata = array_map( 'utf8_encode', $this->metadata );
$this->metadata = json_encode( $metadata );
}
@@ -1266,12 +1248,11 @@ class _Image extends \IPS\Content\Item implements
/* Or was the thumbnail removed? */
elseif( !$this->_new AND $this->masked_file_name )
{
foreach( array( 'masked_file_name', 'medium_file_name', 'small_file_name', 'thumb_file_name' ) as $key )
foreach( array( 'masked_file_name', 'small_file_name' ) as $key )
{
if( $this->$key )
{
$file = \IPS\File::get( 'gallery_Images', $this->$key );
$file->delete();
\IPS\File::get( 'gallery_Images', $this->$key )->delete();
$this->$key = NULL;
}
@@ -1390,27 +1371,13 @@ class _Image extends \IPS\Content\Item implements
*/
public function deleteThumbnails()
{
$possibleImagesToDelete = array( 'masked_file_name', 'medium_file_name', 'small_file_name', 'thumb_file_name' );
$imagesToDelete = array();
/* Usually the masked_file_name and the original_file_name are different, however with many upgrades they are not, and if we delete
the original file (which would be mapped in both original_file_name AND masked_file_name) then we cannot rebuild thumbnails after
and the image will be broken */
foreach( $possibleImagesToDelete as $image )
foreach( array( 'masked_file_name', 'small_file_name' ) as $size )
{
if( $this->$image != $this->original_file_name )
{
$imagesToDelete[] = $image;
}
}
foreach ( $imagesToDelete as $k )
{
if ( $this->$k )
if( isset( $this->_data[ $size ] ) AND $this->$size AND $this->$size != $this->original_file_name )
{
try
{
\IPS\File::get( 'gallery_Images', $this->$k )->delete();
\IPS\File::get( 'gallery_Images', $this->$size )->delete();
}
catch( \Exception $e ){}
}
@@ -1425,6 +1392,8 @@ class _Image extends \IPS\Content\Item implements
*/
public function buildThumbnails( $file=NULL )
{
$this->deleteThumbnails();
if( $file === NULL )
{
$file = \IPS\File::get( 'gallery_Images', $this->original_file_name );
@@ -1439,21 +1408,11 @@ class _Image extends \IPS\Content\Item implements
$thumbnailDimensions['large'] = $largeImage->getImageDimensions();
$mediumImage = \IPS\File::create( 'gallery_Images', 'medium.' . $file->originalFilename, $this->_createImage( $file, explode( 'x', \IPS\Settings::i()->gallery_medium_dims ), FALSE, in_array( 'medium', $watermarks ) ) );
$this->medium_file_name = (string) $mediumImage;
$thumbnailDimensions['medium'] = $mediumImage->getImageDimensions();
$smallImage = \IPS\File::create( 'gallery_Images', 'small.' . $file->originalFilename, $this->_createImage( $file, explode( 'x', \IPS\Settings::i()->gallery_small_dims ), FALSE, in_array( 'small', $watermarks ) ) );
$smallImage = \IPS\File::create( 'gallery_Images', 'small.' . $file->originalFilename, $this->_createImage( $file, explode( 'x', \IPS\Settings::i()->gallery_small_dims ), \IPS\Settings::i()->gallery_use_square_thumbnails, in_array( 'small', $watermarks ) ) );
$this->small_file_name = (string) $smallImage;
$thumbnailDimensions['small'] = $smallImage->getImageDimensions();
$thumbImage = \IPS\File::create( 'gallery_Images', 'thumb.' . $file->originalFilename, $this->_createImage( $file, explode( 'x', \IPS\Settings::i()->gallery_thumb_dims ), \IPS\Settings::i()->gallery_use_square_thumbnails, in_array( 'thumbnail', $watermarks ) ) );
$this->thumb_file_name = (string) $thumbImage;
$thumbnailDimensions['thumb'] = $thumbImage->getImageDimensions();
$this->_dimensions = $thumbnailDimensions;
}
@@ -1472,7 +1431,8 @@ class _Image extends \IPS\Content\Item implements
if( $crop )
{
$image->crop( $dimensions[0], $dimensions[1] );
//$image->crop( $dimensions[0], $dimensions[1] );
$image->resizeToMax( $dimensions[0], $dimensions[0] );
}
else
{
@@ -1549,8 +1509,9 @@ class _Image extends \IPS\Content\Item implements
*/
public function sizes()
{
$return = array();
$data = json_decode( $this->data, TRUE );
$return = array();
$data = json_decode( $this->data, TRUE );
if( !empty( $data ) )
{
foreach ( $data as $k => $v )
@@ -1561,15 +1522,29 @@ class _Image extends \IPS\Content\Item implements
}
}
}
if ( isset( $return['large'] ) and isset( $return['thumb'] ) and $return['large'][0] < $return['thumb'][0] and $return['large'][1] < $return['thumb'][1] )
{
unset( $return['thumb'] );
}
return $return;
}
/**
* Log for deletion later
*
* \IPS\Member|NULL $member The member or NULL for currently logged in
* @return void
*/
public function logDelete( $member = NULL )
{
parent::logDelete( $member );
/* Now we need to update "last image" info */
if( $this->album_id )
{
$album = \IPS\gallery\Album::load( $this->album_id );
$album->setLastImage();
$album->save();
}
}
/**
* Delete Record
*
@@ -1624,12 +1599,9 @@ class _Image extends \IPS\Content\Item implements
*/
public static function searchResultSnippet( array $indexData, array $authorData, array $itemData, array $containerData = NULL, array $reputationData, $reviewRating, $view )
{
$image = $itemData['image_medium_file_name'] ?: $itemData['image_masked_file_name'];
$url = \IPS\Http\Url::internal( static::$urlBase . $indexData['index_item_id'], 'front', static::$urlTemplate, \IPS\Http\Url\Friendly::seoTitle( $indexData['index_title'] ?: $itemData[ static::$databasePrefix . static::$databaseColumnMap['title'] ] ) );
$albumData = isset( $itemData['extra'] ) ? $itemData['extra'] : NULL;
return \IPS\Theme::i()->getTemplate( 'global', 'gallery', 'front' )->searchResultImageSnippet( $indexData, $itemData, $albumData, $image, $url, $view == 'condensed' );
return \IPS\Theme::i()->getTemplate( 'global', 'gallery', 'front' )->searchResultImageSnippet( $indexData, $itemData, ( isset( $itemData['extra'] ) ? $itemData['extra'] : NULL ), $itemData['image_small_file_name'], $url, $view == 'condensed' );
}
/**
@@ -1673,13 +1645,14 @@ class _Image extends \IPS\Content\Item implements
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse int id ID number
* @apiresponse string caption Caption
* @apiresponse string description Description
* @apiresponse string filename Original file name (e.g. 'image.png')
* @apiresponse int filesize Original file size, in bytes
* @apiresponse object images URLs to where the images are stored. Keys are 'original', 'large', 'medium', 'small' and 'thumb' and values are URLs to the corresponding images
* @apiresponse object images URLs to where the images are stored. Keys are 'original', 'large', and 'small', and values are URLs to the corresponding images
* @apiresponse \IPS\gallery\Album album The album, if in one
* @apiresponse \IPS\gallery\Category category The category (if in an album, this will be the category that the album is in)
* @apiresponse \IPS\Member author The author
@@ -1700,7 +1673,7 @@ class _Image extends \IPS\Content\Item implements
* @apiresponse string url URL
* @apiresponse float rating Average Rating
*/
public function apiOutput()
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
return array(
'id' => $this->id,
@@ -1711,9 +1684,7 @@ class _Image extends \IPS\Content\Item implements
'images' => array(
'original' => (string) \IPS\File::get( 'gallery_Images', $this->original_file_name )->url,
'large' => (string) \IPS\File::get( 'gallery_Images', $this->masked_file_name )->url,
'medium' => (string) \IPS\File::get( 'gallery_Images', $this->medium_file_name )->url,
'small' => (string) \IPS\File::get( 'gallery_Images', $this->small_file_name )->url,
'thumb' => (string) \IPS\File::get( 'gallery_Images', $this->thumb_file_name )->url,
),
'album' => $this->album_id ? $this->directContainer()->apiOutput() : null,
'category' => $this->container()->apiOutput(),
@@ -1850,6 +1821,38 @@ class _Image extends \IPS\Content\Item implements
return FALSE;
}
/**
* Check permissions
*
* @param mixed $permission A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
* @return bool
* @throws \OutOfBoundsException If $permission does not exist in map
*/
public function can( $permission, $member=NULL )
{
if ( !parent::can( $permission, $member ) )
{
return FALSE;
}
try
{
if ( !$this->directContainer()->can( $permission, $member ) )
{
return FALSE;
}
}
catch( \OutOfRangeException $e )
{
/* If the direct container is lost, assume we can do nothing. @see \IPS\Content\Item::can() */
return FALSE;
}
/* Still here? It must be okay */
return TRUE;
}
/**
* Can view?
@@ -1871,13 +1874,13 @@ class _Image extends \IPS\Content\Item implements
$member = $member ?: \IPS\Member::loggedIn();
/* Is this a private album we can't access? */
if( $this->directContainer()->type == 2 AND $this->directContainer()->owner() != $member )
if( $this->directContainer()->type == \IPS\gallery\Album::AUTH_TYPE_PRIVATE AND $this->directContainer()->owner() != $member )
{
return FALSE;
}
/* Is this a restricted album we can't access? */
if( $this->directContainer()->type == 3 AND $this->directContainer()->owner() != $member )
if( $this->directContainer()->type == \IPS\gallery\Album::AUTH_TYPE_RESTRICTED AND $this->directContainer()->owner() != $member )
{
/* This will throw an exception of the row does not exist */
try
@@ -1887,7 +1890,7 @@ class _Image extends \IPS\Content\Item implements
throw new \OutOfRangeException;
}
$member = \IPS\Db::i()->select( '*', 'core_sys_social_group_members', array( 'group_id=? AND member_id=?', $this->directContainer()->allowed_access, $member->member_id ) )->first();
$member = \IPS\Member::constructFromData( \IPS\Db::i()->select( '*', 'core_sys_social_group_members', array( 'group_id=? AND member_id=?', $this->directContainer()->allowed_access, $member->member_id ) )->first() );
}
catch( \OutOfRangeException $e )
{
@@ -1913,6 +1916,36 @@ class _Image extends \IPS\Content\Item implements
return TRUE;
}
/**
* Can set as album coverphoto?
*
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
* @return bool
*/
public function canSetAsAlbumCover( $member=NULL )
{
if( ( $this->album_id AND $this->canEdit() ) OR static::modPermission( 'edit', $member, $this->container() ) )
{
return TRUE;
}
return FALSE;
}
/**
* Can set as category coverphoto?
*
* @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member
* @return bool
*/
public function canSetAsCategoryCover( $member=NULL )
{
if( static::modPermission( 'edit', $member, $this->container() ) )
{
return TRUE;
}
return FALSE;
}
/**
* @brief Cached groups the member can access
*/
@@ -1955,7 +1988,7 @@ class _Image extends \IPS\Content\Item implements
/* If you can edit images in a category you can see images in private albums in that category. We can only really check globally at this stage, however. */
if( \IPS\gallery\Image::modPermission( 'edit', $member ) )
{
$result = array( "( gallery_albums.album_id IS NULL OR gallery_albums.album_type IN(1,2) OR ( gallery_albums.album_type=3 AND ( gallery_albums.album_owner_id=? OR gallery_albums.album_allowed_access IN (" . implode( ',', $restricted ) . ") ) ) )", $member->member_id );
$result = array( "( gallery_albums.album_id IS NULL OR gallery_albums.album_type IN(1,2,3) )" );
}
else
{
@@ -2011,7 +2044,7 @@ class _Image extends \IPS\Content\Item implements
/* We have to fix order by for images */
$orders = explode( ',', $order );
$newOrder = array();
$newOrders = array();
foreach( $orders as $_order )
{
@@ -2030,7 +2063,7 @@ class _Image extends \IPS\Content\Item implements
$order = implode( ', ', $newOrders );
$where[] = static::getItemsWithPermissionWhere( $where, $member, $joins );
$where[] = static::getItemsWithPermissionWhere( $where, $member, $joins );
return parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter, $showMovedLinks );
}
@@ -2065,10 +2098,12 @@ class _Image extends \IPS\Content\Item implements
return parent::contentCount( $container, $includeItems, $includeComments, $includeReviews, $depth );
}
$count = static::getItemsWithPermission( array( array( 'gallery_images.image_category_id=?', $container->_id ) ), NULL, 1, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, FALSE, FALSE, FALSE, TRUE );
$_key = md5( get_class( $container ) . $container->_id );
static::$itemCounts[ $_key ][ $container->_id ] = $count;
if( !isset( static::$itemCounts[ $_key ][ $container->_id ] ) )
{
static::$itemCounts[ $_key ][ $container->_id ] = static::getItemsWithPermission( array( array( 'gallery_images.image_category_id=?', $container->_id ) ), NULL, 1, 'read', \IPS\Content\Hideable::FILTER_AUTOMATIC, 0, NULL, FALSE, FALSE, FALSE, TRUE );
}
return parent::contentCount( $container, $includeItems, $includeComments, $includeReviews, $depth );
}
@@ -2082,7 +2117,7 @@ class _Image extends \IPS\Content\Item implements
*/
public function embedImage()
{
return \IPS\File::get( 'gallery_Images', $this->thumb_file_name );
return \IPS\File::get( 'gallery_Images', $this->small_file_name );
}
/**
@@ -2108,7 +2143,7 @@ class _Image extends \IPS\Content\Item implements
public function searchIndexPermissions()
{
/* If this is a private album, only the author can view in search */
if ( $this->directContainer() instanceof \IPS\gallery\Album and $this->directContainer()->type != 1 )
if ( $this->directContainer() instanceof \IPS\gallery\Album and $this->directContainer()->type != \IPS\gallery\Album::AUTH_TYPE_PUBLIC )
{
if ( $this->member_id )
{
@@ -2219,4 +2254,26 @@ class _Image extends \IPS\Content\Item implements
return $sortOptions;
}
/**
* Returns the content images
*
* @param int|null $limit Number of attachments to fetch, or NULL for all
*
* @return array|NULL If array, then array( 'core_Attachment' => 'month_x/foo.gif', ... );
* @throws \BadMethodCallException
*/
public function contentImages( $limit = NULL )
{
$result = parent::contentImages( $limit );
if( $result === NULL )
{
$result = array();
}
$result[] = array( 'gallery_Images' => $this->masked_file_name );
return $result;
}
}
+14 -3
View File
@@ -21,10 +21,20 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
/**
* Image Review Model
*/
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\ReportCenter, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \IPS\Content\Hideable, \IPS\Content\Searchable, \IPS\Content\Embeddable
{
use \IPS\Content\Reactable;
use \IPS\Content\Reactable, \IPS\Content\Reportable;
/**
* @brief [Content\Comment] Form Template
*/
public static $formTemplate = array( array( 'forms', 'gallery', 'front' ), 'reviewTemplate' );
/**
* @brief [Content\Comment] Comment Template
*/
public static $commentTemplate = array( array( 'global', 'gallery', 'front' ), 'reviewContainer' );
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -63,6 +73,7 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
'votes_helpful' => 'votes_helpful',
'votes_data' => 'votes_data',
'approved' => 'approved',
'author_response' => 'author_response',
);
/**
+168 -56
View File
@@ -51,83 +51,195 @@ class _Table extends \IPS\Helpers\Table\Content
}
/**
* Multimod
* Build the form to move images
*
* @return void
* @param \IPS\Node\Model $currentContainer Current image container
* @param string $class Class to use
* @param array $extraAlbumOptions Additional options to use for the album helper (e.g. to force owner)
* @return \IPS\Helpers\Form
*/
protected function multimod()
static public function buildMoveForm( $currentContainer, $class, $extraAlbumOptions=array() )
{
\IPS\Session::i()->csrfCheck();
$form = new \IPS\Helpers\Form( 'form', 'move' );
$form->class = 'ipsForm_vertical';
if ( \IPS\gallery\Category::canOnAny('add') and \IPS\gallery\Album::canOnAny('add') )
{
$options = array( 'category' => 'image_category', 'album' => 'image_album' );
$toggles = array( 'category' => array( 'move_to_category' ), 'album' => array( 'move_to_album' ) );
$extraFields = array();
if ( \IPS\gallery\Image::modPermission( 'edit', NULL, NULL ) and \IPS\Db::i()->select( 'COUNT(*)', 'gallery_categories', array( array( 'category_allow_albums>0' ), array( '(' . \IPS\Db::i()->findInSet( 'core_permission_index.perm_' . \IPS\gallery\Category::$permissionMap['add'], \IPS\Member::loggedIn()->groups ) . ' OR ' . 'core_permission_index.perm_' . \IPS\gallery\Category::$permissionMap['add'] . '=? )', '*' ) ) )->join( 'core_permission_index', array( "core_permission_index.app=? AND core_permission_index.perm_type=? AND core_permission_index.perm_type_id=" . \IPS\gallery\Category::$databaseTable . "." . \IPS\gallery\Category::$databasePrefix . \IPS\gallery\Category::$databaseColumnId, \IPS\gallery\Category::$permApp, \IPS\gallery\Category::$permType ) )->first() )
{
$options['new_album'] = 'move_to_new_album';
/* Basic check that we selected something */
if( !isset( \IPS\Request::i()->moderate ) OR empty( \IPS\Request::i()->moderate ) )
{
\IPS\Output::i()->error( 'nothing_mm_selected', '1G329/1', 403, '' );
}
$class = $this->class;
$params = array();
if ( \IPS\Request::i()->modaction == 'move' )
{
$form = new \IPS\Helpers\Form( 'form', 'move' );
$form->add( new \IPS\Helpers\Form\Node( 'move_to_category', NULL, FALSE, array( 'clubs' => true, 'class' => 'IPS\\gallery\\Category', 'url' => \IPS\Request::i()->url()->setQueryString( 'modaction', 'move' ) ) ) );
$form->add( new \IPS\Helpers\Form\Node( 'move_to_album', NULL, FALSE, array(
'class' => 'IPS\\gallery\\Album',
'forceOwner' => FALSE,
'url' => \IPS\Request::i()->url()->setQueryString( 'modaction', 'move' ),
'permissionCheck' => function( $node )
foreach ( \IPS\gallery\Album::formFields( NULL, TRUE, \IPS\Request::i()->move_to === 'new_album' ) as $field )
{
/* Have we hit an images per album limit? */
if( $node->owner()->group['g_img_album_limit'] AND ( $node->count_imgs + $node->count_imgs_hidden ) >= $node->owner()->group['g_img_album_limit'] )
if ( !$field->htmlId )
{
return false;
$field->htmlId = $field->name . '_id';
}
$toggles['new_album'][] = $field->htmlId;
$extraFields[] = $field;
}
}
$form->add( new \IPS\Helpers\Form\Radio( 'move_to', NULL, TRUE, array( 'options' => $options, 'toggles' => $toggles ) ) );
foreach ( $extraFields as $field )
{
$form->add( $field );
}
}
$form->add( new \IPS\Helpers\Form\Node( 'move_to_category', NULL, NULL, array(
'clubs' => true,
'class' => 'IPS\\gallery\\Category',
'permissionCheck' => function( $node ) use ( $currentContainer, $class )
{
/* If the image is in the same category already, we can't move it there */
if( $currentContainer instanceof \IPS\gallery\Category and $currentContainer->id == $node->id )
{
return false;
}
/* If the category requires albums, we cannot move images directly to it */
if( $node->allow_albums == 2 )
{
return false;
}
/* If the category is a club, check mod permissions appropriately */
try
{
/* If the item is in a club, only allow moving to other clubs that you moderate */
if ( $currentContainer and \IPS\IPS::classUsesTrait( $currentContainer, 'IPS\Content\ClubContainer' ) and $currentContainer->club() )
{
return $class::modPermission( 'move', \IPS\Member::loggedIn(), $node ) and $node->can( 'add' ) ;
}
}
catch( \OutOfBoundsException $e ) { }
/* Can we add in this category? */
if ( $node->can( 'add' ) )
{
return true;
}
) ) );
if ( $values = $form->values() )
return false;
}
), function( $val ) {
if ( !$val and isset( \IPS\Request::i()->move_to ) and \IPS\Request::i()->move_to == 'category' )
{
if( ( !isset( $values['move_to_category'] ) OR !( $values['move_to_category'] instanceof \IPS\Node\Model ) ) AND
( !isset( $values['move_to_album'] ) OR !( $values['move_to_album'] instanceof \IPS\Node\Model ) ) )
{
$form->error = \IPS\Member::loggedIn()->language()->addToStack('gallery_cat_or_album');
throw new \DomainException('form_required');
}
}, NULL, NULL, 'move_to_category' ) );
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
return;
$form->add( new \IPS\Helpers\Form\Node( 'move_to_album', NULL, NULL, array_merge( array(
'class' => 'IPS\\gallery\\Album',
'permissionCheck' => function( $node ) use ( $currentContainer )
{
/* If the image is in the same album already, we can't move it there */
if( $currentContainer instanceof \IPS\gallery\Album and $currentContainer->id == $node->id )
{
return false;
}
$params[] = ( isset( $values['move_to_category'] ) AND $values['move_to_category'] ) ? $values['move_to_category'] : $values['move_to_album'];
$params[] = FALSE;
/* Do we have permission to add? */
if( !$node->can( 'add' ) )
{
return false;
}
/* Have we hit an images per album limit? */
if( $node->owner()->group['g_img_album_limit'] AND ( $node->count_imgs + $node->count_imgs_hidden ) >= $node->owner()->group['g_img_album_limit'] )
{
return false;
}
return true;
}
), $extraAlbumOptions ), function( $val ) {
if ( !$val and isset( \IPS\Request::i()->move_to ) and \IPS\Request::i()->move_to == 'album' )
{
throw new \DomainException('form_required');
}
}, NULL, NULL, 'move_to_album' ) );
return $form;
}
/**
* Get the form to move items
*
* @return string|array
*/
protected function getMoveForm()
{
$class = $this->class;
$params = array();
$currentContainer = $this->container;
$form = static::buildMoveForm( $currentContainer, $class );
if ( $values = $form->values() )
{
if ( isset( $values['move_to'] ) )
{
if ( $values['move_to'] == 'new_album' )
{
$albumValues = $values;
unset( $albumValues['move_to'] );
unset( $albumValues['move_to_category'] );
unset( $albumValues['move_to_album'] );
$target = new \IPS\gallery\Album;
$target->saveForm( $target->formatFormValues( $albumValues ) );
$target->save();
}
else
{
$target = ( \IPS\Request::i()->move_to == 'category' ) ? $values['move_to_category'] : $values['move_to_album'];
}
}
else
{
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
return;
$target = isset( $values['move_to_category'] ) ? $values['move_to_category'] : $values['move_to_album'];
}
$params[] = $target;
$params[] = FALSE;
return $params;
}
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
foreach ( array_keys( \IPS\Request::i()->moderate ) as $id )
if ( \IPS\Request::i()->isAjax() )
{
try
{
$object = $class::loadAndCheckPerms( $id );
$object->modAction( \IPS\Request::i()->modaction, \IPS\Member::loggedIn(), $params );
}
catch ( \Exception $e ) {}
\IPS\Output::i()->sendOutput( \IPS\Output::i()->output );
}
\IPS\Output::i()->redirect( $this->baseUrl );
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
return;
}
/**
* Return the sort direction to use for links
*
* @note Abstracted so other table helper instances can adjust as needed
* @param string $column Sort by string
* @return string [asc|desc]
*/
public function getSortDirection( $column )
{
if( $column == 'image_caption' )
{
return 'asc';
}
return 'desc';
}
}
@@ -51,6 +51,13 @@ class _imageFeed extends \IPS\Content\Widget
public function init()
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'widgets.css', 'gallery', 'front' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'gallery.css', 'gallery', 'front' ) );
if ( \IPS\Theme::i()->settings['responsive'] )
{
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'gallery_responsive.css', 'gallery', 'front' ) );
}
parent::init();
}
}