Current Path :
/
home
/
develito
/
www
/
tmp
/
install_611c36ae9165f
/
Site
/
opt
/
fields
/
Or
Select Your Path :
Upload File :
New :
File
Dir
//home/develito/www/tmp/install_611c36ae9165f/Site/opt/fields/image.php
<?php /** * @package: SobiPro Component for Joomla! * * @author * Name: Sigrid Suski & Radek Suski, Sigsiu.NET GmbH * Email: sobi[at]sigsiu.net * Url: https://www.Sigsiu.NET * * @copyright Copyright (C) 2006 - 2021 Sigsiu.NET GmbH (https://www.sigsiu.net). All rights reserved. * @license GNU/GPL Version 3 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation, and under the additional terms according section 7 of GPL v3. * See https://www.gnu.org/licenses/gpl.html and https://www.sigsiu.net/licenses. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * @created 28-Nov-2009 by Radek Suski * @modified 14 April 2021 by Sigrid Suski */ defined( 'SOBIPRO' ) || exit( 'Restricted access' ); use Sobi\FileSystem\File; use Sobi\FileSystem\FileSystem; use Sobi\FileSystem\Image; use Sobi\Input\Input; use Sobi\Utils\StringUtils; SPLoader::loadClass( 'opt.fields.inbox' ); /** * Class SPField_Image */ class SPField_Image extends SPField_Inbox implements SPFieldInterface { /** * @var bool */ protected $keepOrg = true; /** * @var bool */ protected $resize = true; /** * @var bool */ protected $convert = false; /** * @var bool */ protected $crop = false; /** * @var double */ protected $maxSize = 2097152; /** * @var int */ protected $resizeWidth = 500; /** * @var int */ protected $resizeHeight = 500; /** * @var string */ protected $imageName = 'img_{orgname}'; /** * @var string */ protected $imageFloat = ''; /** * @var bool */ protected $generateThumb = true; /** * @var string */ protected $thumbFloat = ''; /** * @var string */ protected $float = ''; /** * @var string */ protected $thumbName = 'thumb_{orgname}'; /** @var int */ protected $thumbWidth = 400; /** @var int */ protected $thumbHeight = 400; /** * @var string */ protected $inVcard = 'thumb'; /** * @var string */ protected $inDetails = 'image'; /** * @var string */ protected $inCategory = 'image'; /** * @var string */ protected $savePath = 'images/sobipro/entries/{id}/'; /** * @var string */ protected $cssClass = 'spClassImage'; /** * @var string */ protected $cssClassView = 'spClassViewImage'; /** * @var string */ protected $cssClassEdit = 'spClassEditImage'; /** * @var string */ protected $dType = 'special'; /** * @var int */ protected $bsWidth = 10; /** @var bool */ static private $CAT_FIELD = true; /** @var bool */ protected $detectTransparency = true; /*** @var bool */ protected $suggesting = false; /** @var bool */ protected $phpsettings = false; /** @var bool */ protected $phpwarnings = false; /** * Returns the parameter list. * * @return array */ protected function getAttr() { return [ 'width', 'savePath', 'inDetails', 'inVcard', 'thumbHeight', 'thumbWidth', 'thumbName', 'keepOrg', 'resize', 'maxSize', 'resizeWidth', 'resizeHeight', 'imageName', 'generateThumb', 'thumbFloat', 'imageFloat', 'itemprop', 'crop', 'cssClassView', 'cssClass', 'cssClassEdit', 'showEditLabel', 'inCategory', 'float', 'detectTransparency', 'bsWidth', 'phpwarnings', 'phpsettings', 'convert' ]; } /** * @param $revision * @param $current * * @return array */ public function compareRevisions( $revision, $current ) { if ( isset( $revision[ 'image' ] ) ) { $rev = basename( $revision[ 'image' ] ); } if ( isset( $current[ 'image' ] ) ) { $cur = basename( $current[ 'image' ] ); } return [ 'current' => $cur, 'revision' => $rev ]; } /** * Shows the field in the entry form. * * @param bool $return return or display directly * * @return bool|string * @throws SPException */ public function field( $return = false ) { if ( !( $this->enabled ) ) { return false; } $this->suffix = ''; //clear if any $class = $this->required ? $this->cssClass . ' required' : $this->cssClass; $params = [ 'id' => $this->nid, 'class' => $class ]; $show = $oldImage = null; static $js = false; $files = $this->getExistingFiles(); if ( is_array( $files ) && count( $files ) ) { $oldImage = array_key_exists( 'original', $files ) ? $files[ 'original' ] : $files[ 'image' ]; if ( isset( $files[ 'ico' ] ) ) { $show = $files[ 'ico' ]; } else { if ( isset( $files[ 'thumb' ] ) ) { $show = $files[ 'thumb' ]; } } } if ( $show ) { $img = Sobi::Cfg( 'live_site' ) . $show; } $field = null; if ( $this->phpsettings ) { $field .= $this->phpSettings(); } if ( $this->phpwarnings ) { $field .= $this->phpWarnings(); } $field .= '<div class="spImageField">'; $field .= "<div id=\"{$this->nid}_img_preview\" class=\"spEditImagePreview\">"; if ( $show ) { $field .= "<img src=\"{$img}\" alt=\"{$this->name}\" />"; } $field .= '</div>'; $field .= '<div class="spImageUpDelete">'; if ( $oldImage ) { if ( !defined( 'SOBIPRO_ADM' ) ) { $pos = strrpos( $oldImage, '/' ); $oldImage = $pos === false ? $oldImage : substr( $oldImage, $pos + 1 ); $oldImage = preg_replace( '/^img_/', '', $oldImage ); } $ext = pathinfo( $oldImage, PATHINFO_EXTENSION ); // if ( $ext == 'webp' && $this->convert ) { if ( $ext == 'webp' ) { $oldImage = preg_replace( '/.webp$/', '', $oldImage ); if ( defined( 'SOBIPRO_ADM' ) ) { $oldImage = $oldImage . ' as webp'; } } $delText = Sobi::Txt( 'FD.IMG_DELETE_CURRENT_IMAGE' ) . '<small> (' . $oldImage . ')</small>'; } else { $delText = Sobi::Txt( 'FD.IMG_DELETE_CURRENT_IMAGE' ); } if ( $show ) { $field .= SPHtml_Input::checkbox( $this->nid . '_delete', 1, $delText, $this->nid . '_delete', false, [ 'class' => $this->cssClass ] ); } $field .= SPHtml_Input::fileUpload( $this->nid, 'image/*', null, 'spImageUpload', preg_replace( '/field_/', 'field.', $this->nid, 1 ). '.upload' ); $field .= '</div></div>'; if ( !( $js ) ) { SPFactory::header() ->addJsFile( 'opt.field_image_edit' ) ->addJsCode( 'SobiPro.jQuery( document ).ready( function () { SobiPro.jQuery( ".spImageUpload" ).SPFileUploader(); } );' ); $js = true; } if ( $this->crop ) { $modalclass = 'modal hide'; SPFactory::header() ->addJsFile( 'cropper' ) ->addCssFile( 'cropper' ); $field .= SPHtml_Input::modalWindow( Sobi::Txt( 'IMAGE_CROP_HEADER' ), $this->nid . '_modal', null, $modalclass, 'CLOSE', 'SAVE' ); } // avoiding multiple roots // $field = "<div>{$field}</div>"; if ( !$return ) { echo $field; } else { return $field; } } /** * @param $entry * @param $name * @param $pattern * @param bool $addExt * * @return string|string[]|null * @throws \Sobi\Error\Exception */ protected function parseName( $entry, $name, $pattern, $addExt = false ) { $nameArray = explode( '.', $name ); $ext = strtolower( array_pop( $nameArray ) ); $name = implode( '.', $nameArray ); $user = SPUser::getBaseData( ( int ) $entry->get( 'owner' ) ); if ( $entry->get( 'oType' ) == 'category' ) { $id = $entry->get( 'parent' ); $category = SPLang::translateObject( [ $id ], [ 'name', 'alias' ] ); $category = $category[ $id ][ 'nid' ]; } else { if ( method_exists( $entry, 'getPlaceholderCategory' ) ) { $category = $entry->getPlaceholderCategory(); // exists only for Imex import, export } else { $category = $entry->getPrimary(); } $category = isset( $category ) ? $category[ 'alias' ] : null; } // @todo change to the global method $placeHolders = [ '/{id}/', '/{orgname}/', '/{entryname}/', '/{oid}/', '/{ownername}/', '/{uid}/', '/{username}/', '/{nid}/', '/{category}/' ]; $replacements = [ $entry->get( 'id' ), $name, $entry->get( 'nid' ), ( isset( $user->id ) ? $user->id : null ), ( isset( $user->name ) ? SPLang::nid( $user->name ) : 'guest' ), Sobi::My( 'id' ), SPLang::nid( Sobi::My( 'name' ) ), $this->nid, $category ]; $fileName = preg_replace( $placeHolders, $replacements, $pattern ); return $addExt ? $fileName . '.' . $ext : $fileName; } /** * @param $data * * @return string */ public function getRawData( &$data ) { if ( is_string( $data ) ) { try { $data = SPConfig::unserialize( $data ); } catch ( SPException $x ) { $data = null; } } // legacy for ImEx - have you learned a lesson Radek? if ( isset( $data[ 'data' ] ) && defined( 'SOBIPRO_ADM' ) ) { unset( $data[ 'data' ] ); } return SPConfig::serialize( $data ); } /** * Gets the data for a field, verifies it and pre-saves it. * * @param SPEntry $entry * @param null $tsId * @param string $request * * @return array|mixed|void * @throws SPException * @throws \Sobi\Error\Exception */ public function submit( &$entry, $tsId = null, $request = 'POST' ) { $save = []; if ( $this->verify( $entry, $request ) ) { // check if we are using the ajax upload - then we don't need to play with temp data $check = Input::String( $this->nid ); if ( !( $check ) ) { /* save the file to temporary folder */ $data = Input::File( $this->nid, 'tmp_name' ); if ( $data ) { $temp = str_replace( '.', '-', $tsId ); $path = SPLoader::dirPath( "tmp.edit.{$temp}.images", 'front', false ); $path .= '/' . Input::File( $this->nid, 'name' ); $file = new File(); $file->upload( $data, $path ); $save[ $this->nid ] = $path; } } else { $save[ $this->nid ] = $check; } $save[ $this->nid . '_delete' ] = Input::Bool( $this->nid . '_delete' ); } return $save; } /** * @param SPEntry $entry * @param string $request * * @return bool * @throws SPException */ private function verify( $entry, $request ) { static $store = null; $directory = Input::String( $this->nid ); if ( strtolower( $request ) == 'post' || strtolower( $request ) == 'get' ) { $data = Input::File( $this->nid, 'tmp_name' ); } else { $data = Input::File( $this->nid, 'tmp_name', $request ); } if ( $store == null ) { $store = SPFactory::registry()->get( 'requestcache_stored' ); } if ( is_array( $store ) && isset( $store[ $this->nid ] ) ) { if ( !( strstr( $store[ $this->nid ], 'file://' ) ) && !( strstr( $store[ $this->nid ], 'directory://' ) ) ) { $data = $store[ $this->nid ]; } else { $directory = $store[ $this->nid ]; } } $fileSize = Input::File( $this->nid, 'size' ); if ( $directory && strstr( $directory, 'directory://' ) ) { // list( $data, $dirName, $files ) = $this->getAjaxFiles( $directory ); // below PHP 7.1 [ $data, $dirName, $files ] = $this->getAjaxFiles( $directory ); // works only since PHP 7.1 which is the minimum requirement for SobiPro 1.5!! if ( count( $files ) ) { foreach ( $files as $file ) { if ( $file == '.' ) { continue; } if ( $file == '..' ) { continue; } if ( strpos( $file, 'icon_' ) !== false ) { continue; } if ( strpos( $file, 'resized_' ) !== false ) { continue; } if ( strpos( $file, 'cropped_' ) !== false ) { continue; } if ( strpos( $file, '.var' ) !== false ) { continue; } $fileSize = filesize( $dirName . $file ); } } } else { // $fileSize = SPRequest::file( $this->nid, 'size' ); } $del = Input::Bool( $this->nid . '_delete' ); //if image should be deleted (checkbox ticked) $task = Input::String( 'task' ); $imex = ( $task == 'imex.doImport' || $task == 'imex.doCImport' ); if ( !$imex ) { // do the checks only if no data import $dexs = strlen( $data ); if ( $this->required && !( $dexs ) ) { $files = $this->getRaw(); if ( (is_array( $files ) && !( count( $files ) )) || !$files ) { throw new SPException( SPLang::e( 'FIELD_REQUIRED_ERR', $this->name ) ); } } if ( $fileSize > $this->maxSize ) { throw new SPException( SPLang::e( 'FIELD_IMG_TOO_LARGE', $this->name, $fileSize, $this->maxSize ) ); } /* check if there was an adminField */ if ( $this->adminField && ( $dexs || $del ) ) { if ( !( Sobi:: Can( 'entry.adm_fields.edit' ) ) ) { throw new SPException( SPLang::e( 'FIELD_NOT_AUTH', $this->name ) ); } } /* check if it was free */ if ( !( $this->isFree ) && $this->fee && $dexs ) { SPFactory::payment()->add( $this->fee, $this->name, $entry->get( 'id' ), $this->fid ); } /* check if it was editLimit */ if ( $this->editLimit == 0 && !( Sobi::Can( 'entry.adm_fields.edit' ) ) && $dexs ) { throw new SPException( SPLang::e( 'FIELD_NOT_AUTH_EXP', $this->name ) ); } /* check if it was editable */ if ( !( $this->editable ) && !( Sobi::Can( 'entry.adm_fields.edit' ) ) && $dexs && $entry->get( 'version' ) > 1 ) { throw new SPException( SPLang::e( 'FIELD_NOT_AUTH_NOT_ED', $this->name ) ); } } return true; } /** * Gets the data for a field and save it in the database. * * @param SPEntry $entry * @param string $request * @param bool $clone * * @return bool * @throws SPException * @throws \Sobi\Error\Exception */ public function saveData( &$entry, $request = 'POST', $clone = false ) { if ( !( $this->enabled ) ) { return false; } $del = Input::Bool( $this->nid . '_delete' ); if ( $clone ) { // if the entry should be duplicated $orgSid = Input::Sid(); $this->loadData( $orgSid ); $files = $this->getExistingFiles(); $cloneFiles = []; if ( isset( $files[ 'image' ] ) && file_exists( SOBI_ROOT . '/' . $files[ 'image' ] ) ) { return $this->cloneFiles( $entry, $request, $files, $cloneFiles ); } } //initializations $fileSize = Input::File( $this->nid, 'size' ); $data = null; $cropped = null; static $store = null; $cache = false; if ( $store == null ) { $store = SPFactory::registry()->get( 'requestcache_stored' ); } if ( is_array( $store ) && isset( $store[ $this->nid ] ) ) { if ( !( strstr( $store[ $this->nid ], 'file://' ) ) && !( strstr( $store[ $this->nid ], 'directory://' ) ) ) { $data = $store[ $this->nid ]; $cache = true; $orgName = Input::File( $this->nid, 'name', $request ); } else { Input::Set( $this->nid, $store[ $this->nid ] ); $orgName = Input::File( $this->nid, 'name' ); $data = Input::File( $this->nid, 'tmp_name' ); } } else { $data = Input::File( $this->nid, 'tmp_name' ); $orgName = Input::File( $this->nid, 'name' ); } $sPath = $this->parseName( $entry, $orgName, $this->savePath ); $path = SPLoader::dirPath( $sPath, 'root', false ); /** Wed, Oct 15, 2014 13:51:03 * Implemented a cropper with Ajax checker. * This is the actual method to get those files * Other methods left for BC * */ if ( !( $data ) ) { $directory = Input::String( $this->nid ); if ( strlen( $directory ) ) { $files = $this->getExistingFiles(); if ( isset ( $files ) ) { // if there is already an old image, delete it $this->delImgs( $entry->get( 'id' ) ); } [ $data, $dirName, $files, $coordinates ] = $this->getAjaxFiles( $directory ); if ( count( $files ) ) { // check all temporary generated files foreach ( $files as $file ) { if ( $file == '.' ) { continue; } if ( $file == '..' ) { continue; } if ( strpos( $file, 'icon_' ) !== false ) { continue; } if ( strpos( $file, 'resized_' ) !== false ) { continue; } // upload the cropped file (will be deleted later) if ( strpos( $file, 'cropped_' ) !== false ) { $cropped = $dirName . $file; FileSystem::Upload( $cropped, $path . basename( $cropped ) ); // upload cropped image continue; } if ( strpos( $file, '.var' ) !== false ) { continue; } $fileSize = filesize( $dirName . $file ); $orgName = $file; } } if ( strlen( $coordinates ) ) { // if the user changed the cropped area then crop again $coordinates = json_decode( SPLang::clean( $coordinates ), true ); $croppedImage = new Image( $dirName . $orgName ); $croppedImage->crop( $coordinates[ 'width' ], $coordinates[ 'height' ], 'top-left', $coordinates[ 'x' ], $coordinates[ 'y' ] ); $cropped = 'cropped_' . $orgName; $croppedImage->saveAs( $path . $cropped ); } $data = strlen( $cropped ) ? $cropped : $dirName . $file; } } $files = []; $task = Input::String( 'task' ); $imex = ( $task == 'imex.doImport' || $task == 'imex.doCImport' ); /* if we have an image to process */ if ( $data && $orgName ) { $imageType = $this->convert ? 'WEBP' : null; $nameArray = explode( '.', $orgName ); $ext = strtolower( array_pop( $nameArray ) ); $nameArray[] = $ext; $orgName = implode( '.', $nameArray ); if ( ( $fileSize > $this->maxSize ) && !$imex ) { throw new SPException( SPLang::e( 'FIELD_IMG_TOO_LARGE', $this->name, $fileSize, $this->maxSize ) ); } if ( $cropped && $this->keepOrg ) { // if we have a cropped image, but should keep the original ... FileSystem::Upload( $dirName . $orgName, $path . $orgName ); // ... upload the original image } if ( $cache ) { $orgImage = new Image( $data ); $orgImage->move( $path . $orgName ); } else { $orgImage = new Image(); $nameArray = explode( '.', $orgName ); $ext = strtolower( array_pop( $nameArray ) ); $nameArray[] = $ext; $orgName = implode( '.', $nameArray ); if ( $cropped ) { $orgImage->setFile( $path . basename( $data ) ); // original image is now the cropped image } else { $orgImage->upload( $dirName . $orgName, $path . $orgName ); } } $files[ 'data' ][ 'exif' ] = $orgImage->exif(); $this->cleanExif( $files[ 'data' ][ 'exif' ] ); if ( Sobi::Cfg( 'image_field.fix_rotation', true ) ) { if ( $orgImage->fixRotation() ) { $orgImage->save(); } } $image = clone $orgImage; $image->setTransparency( $this->detectTransparency ); if ( $this->resize ) { try { $image->resample( $this->resizeWidth, $this->resizeHeight ); $files[ 'image' ] = $this->parseName( $entry, $orgName, $this->imageName, true ); $image->saveAs( $path . $files[ 'image' ] ); if ( $imageType == 'WEBP' ) { $fname = pathinfo( $files[ 'image' ], PATHINFO_BASENAME ) . '.webp'; $image->saveAs( $path . $fname, $imageType ); $files[ 'image' ] = $fname; } } catch ( Exception $x ) { Sobi::Error( $this->name(), SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ), SPC::WARNING, 0, __LINE__, __FILE__ ); $image->delete(); throw new SPException( SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ) ); } } else { // the large image always exists even if not resized try { $image->read(); $files[ 'image' ] = $this->parseName( $entry, $orgName, $this->imageName, true ); $image->saveAs( $path . $files[ 'image' ] ); if ( $imageType == 'WEBP' ) { $fname = pathinfo( $files[ 'image' ], PATHINFO_BASENAME ) . '.webp'; $image->saveAs( $path . $fname, $imageType ); $files[ 'image' ] = $fname; } } catch ( Exception $x ) { Sobi::Error( $this->name(), SPLang::e( 'FIELD_IMG_CANNOT_SAVE', $x->getMessage() ), SPC::WARNING, 0, __LINE__, __FILE__ ); $image->delete(); throw new SPException( SPLang::e( 'FIELD_IMG_CANNOT_SAVE', $x->getMessage() ) ); } } if ( $this->generateThumb ) { // if cropping is set, thumbnail will be generated from the cropped image $thumb = clone $orgImage; $thumb->setTransparency( $this->detectTransparency ); try { $thumb->resample( $this->thumbWidth, $this->thumbHeight ); $files[ 'thumb' ] = $this->parseName( $entry, $orgName, $this->thumbName, true ); $thumb->saveAs( $path . $files[ 'thumb' ] ); if ( $imageType == 'WEBP' ) { $fname = pathinfo( $files[ 'thumb' ], PATHINFO_BASENAME ) . '.webp'; $thumb->saveAs( $path . $fname, $imageType ); $files[ 'thumb' ] = $fname; } } catch ( Exception $x ) { Sobi::Error( $this->name(), SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ), SPC::WARNING, 0, __LINE__, __FILE__ ); $thumb->delete(); throw new SPException( SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ) ); } } $ico = clone $orgImage; try { $icoSize = explode( ':', Sobi::Cfg( 'image.ico_size', '100:100' ) ); $ico->setTransparency( $this->detectTransparency ); $ico->resample( $icoSize[ 0 ], $icoSize[ 1 ] ); // resize $files[ 'ico' ] = $this->parseName( $entry, strtolower( $orgName ), 'ico_{orgname}_' . $this->nid, true ); $ico->saveAs( $path . $files[ 'ico' ] ); // save ico always as original image type } catch ( Exception $x ) { Sobi::Error( $this->name(), SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ), SPC::WARNING, 0, __LINE__, __FILE__ ); $ico->delete(); throw new SPException( SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ) ); } if ( !$this->keepOrg ) { $orgImage->delete(); } else { $files[ 'original' ] = $this->parseName( $entry, $orgName, '{orgname}', true ); if ( $imageType == 'WEBP' ) { $image = clone $orgImage; $fname = pathinfo( $files[ 'original' ], PATHINFO_BASENAME ) . '.webp'; $image->saveAs( $path . $fname, $imageType ); $files[ 'original' ] = $fname; } } foreach ( $files as $i => $file ) { if ( $i == 'data' ) { // exif data continue; } $files[ $i ] = $sPath . $file; } } // if no image to process else { if ( $del ) { // should we delete the existing ones (checkbox ticked)? $this->delImgs( $entry->get( 'id' ) ); $files = []; } else { return true; // we have nothing to do here } } if ( $cropped ) { // delete the cropped image as it is a temporary file $this->delImage( $path . basename( $cropped ) ); } if ( FileSystem::Exists( $dirName ) && $dirName != SOBI_ROOT ) { /* remove the temporary folder */ FileSystem::Rmdir( $dirName ); } if ( !$imex ) { // Imex is doing that by itself $this->storeData( $entry, $request, $files ); // store the general field's data } } /** * @param $data */ protected function cleanExif( &$data ) { // Wed, Feb 19, 2014 17:17:20 // we need to remove junk from indexes too // it appears to be the easies method $data = json_encode( $data ); $data = preg_replace( '/\p{Cc}+/u', null, $data ); $data = str_replace( 'UndefinedTag:', null, $data ); $data = json_decode( $data, true ); if ( is_array( $data ) && count( $data ) ) { foreach ( $data as $index => $row ) { if ( is_array( $row ) ) { $this->cleanExif( $row ); } else { $data[ $index ] = preg_replace( '/\p{Cc}+/u', null, $row ); } } } } /** * (non-PHPdoc) * @see Site/opt/fields/SPFieldType#deleteData($sid) * * @param $sid * * @throws SPException * @throws \Sobi\Error\Exception */ public function deleteData( $sid ) { parent::deleteData( $sid ); $this->delImgs( $sid ); } /** * Deletes all existing images on one entry. * * @param $sid * * @throws SPException * @throws \Sobi\Error\Exception */ private function delImgs( $sid ) { $files = SPConfig::unserialize( $this->getRaw() ); if ( is_array( $files ) && count( $files ) ) { // SPLoader::loadClass( 'cms.base.fs' ); foreach ( $files as $file ) { if ( !( strlen( $file ) ) ) { continue; } $file = FileSystem::FixPath( SOBI_ROOT . "/{$file}" ); // should never happen but who knows .... if ( $file == SOBI_ROOT ) { continue; } if ( FileSystem::Exists( $file ) ) { FileSystem::Delete( $file ); $ext = pathinfo( $file, PATHINFO_EXTENSION ); if ( $ext == 'webp' ) { $f = preg_replace( '/.webp$/', '', $file ); if ( $f == SOBI_ROOT ) { continue; } if ( FileSystem::Exists( $f ) ) { FileSystem::Delete( $f ); } } } } // delete them also from the database $db =& SPFactory::db(); $db->delete( 'spdb_field_data', [ 'fid' => $this->fid, 'sid' => $sid ] ); } } /** * Deletes one image on one entry. * * @param $image * * @throws \Sobi\Error\Exception */ private function delImage( $image ) { // SPLoader::loadClass( 'cms.base.fs' ); if ( strlen( $image ) ) { // should never happen but who knows .... if ( $image == SOBI_ROOT ) { return; } if ( FileSystem::Exists( $image ) ) { FileSystem::Delete( $image ); } } } /** * @param $deg * @param $min * @param $sec * @param $hem * * @return float|int */ protected function convertGPS( $deg, $min, $sec, $hem ) { $d = (float) $deg + ( ( ( (float) $min / 60 ) + ( (float) $sec / 3600 ) / 100 ) ); return ( $hem == 'S' || $hem == 'W' ) ? $d *= -1 : $d; } /** * @return array * @throws SPException */ public function struct() { $files = $this->getRaw(); if ( is_string( $files ) ) { try { $files = SPConfig::unserialize( $files ); } catch ( SPException $x ) { $files = null; } } $exifToPass = []; if ( isset( $files[ 'original' ] ) ) { $files[ 'orginal' ] = $files[ 'original' ]; } if ( isset( $files[ 'data' ][ 'exif' ] ) && Sobi::Cfg( 'image_field.pass_exif', true ) ) { $exif = json_encode( $files[ 'data' ][ 'exif' ] ); $exif = str_replace( 'UndefinedTag:', null, $exif ); $exif = preg_replace( '/\p{Cc}+/u', null, $exif ); $exif = json_decode( preg_replace( '/[^a-zA-Z0-9\{\}\:\.\,\(\)\"\'\/\\\\!\?\[\]\@\#\$\%\^\&\*\+\-\_]/', '', $exif ), true ); if ( isset( $exif[ 'EXIF' ] ) ) { $tags = Sobi::Cfg( 'image_field.exif_data', [] ); if ( count( $tags ) ) { foreach ( $tags as $tag ) { $exifToPass[ 'BASE' ][ $tag ] = isset( $exif[ 'EXIF' ][ $tag ] ) ? $exif[ 'EXIF' ][ $tag ] : 'unknown'; } } } if ( isset( $exif[ 'FILE' ] ) ) { $exifToPass[ 'FILE' ] = $exif[ 'FILE' ]; } if ( isset( $exif[ 'FILE' ] ) ) { $exifToPass[ 'FILE' ] = $exif[ 'FILE' ]; } if ( isset( $exif[ 'IFD0' ] ) ) { $tags = Sobi::Cfg( 'image_field.exif_id_data', [] ); if ( count( $tags ) ) { foreach ( $tags as $tag ) { $exifToPass[ 'IFD0' ][ $tag ] = isset( $exif[ 'IFD0' ][ $tag ] ) ? $exif[ 'IFD0' ][ $tag ] : 'unknown'; } } } if ( isset( $files[ 'data' ][ 'exif' ][ 'GPS' ] ) && ( $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSStatus' ] != 'V' ) ) { //and if not 'void' $exifToPass[ 'GPS' ][ 'coordinates' ][ 'latitude' ] = $this->convertGPS( $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLatitude' ][ 0 ], $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLatitude' ][ 1 ], $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLatitude' ][ 2 ], $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLatitudeRef' ] ); $exifToPass[ 'GPS' ][ 'coordinates' ][ 'longitude' ] = $this->convertGPS( $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLongitude' ][ 0 ], $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLongitude' ][ 1 ], $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLongitude' ][ 2 ], $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLongitudeRef' ] ); $exifToPass[ 'GPS' ][ 'coordinates' ][ 'latitude-ref' ] = isset( $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLatitudeRef' ] ) ? $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLatitudeRef' ] : 'unknown'; $exifToPass[ 'GPS' ][ 'coordinates' ][ 'longitude-ref' ] = isset( $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLongitudeRef' ] ) ? $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPSLongitudeRef' ] : 'unknown'; $tags = Sobi::Cfg( 'image_field.exif_gps_data', [] ); if ( count( $tags ) ) { foreach ( $tags as $tag ) { $exifToPass[ 'GPS' ][ $tag ] = isset( $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPS' . $tag ] ) ? $files[ 'data' ][ 'exif' ][ 'GPS' ][ 'GPS' . $tag ] : 'unknown'; } } } } $float = null; if ( is_array( $files ) && count( $files ) ) { $this->cssClass = strlen( $this->cssClass ) ? $this->cssClass : 'spFieldsData'; $this->cssClass = $this->cssClass . ' ' . $this->nid; $this->cleanCss(); switch ( $this->currentView ) { default: case 'vcard': $img = $this->inVcard; break; case 'details': $img = $this->inDetails; break; case 'category': $img = $this->inCategory; } $prefix = 'img_'; if ( isset( $files[ $img ] ) ) { $show = $files[ $img ]; $prefix = 'img_'; } else { if ( isset( $files[ 'thumb' ] ) ) { $show = $files[ 'thumb' ]; $prefix = 'thumb_'; } else { if ( isset( $files[ 'original' ] ) ) { $show = $files[ 'original' ]; $prefix = ''; } else { if ( isset( $files[ 'ico' ] ) ) { $show = $files[ 'ico' ]; $prefix = 'ico_'; } } } } if ( isset( $show ) ) { /* get filename for alt and title tags */ $fname = array_key_exists( 'original', $files ) ? pathinfo( $files[ 'original' ], PATHINFO_FILENAME ) : str_replace( $prefix, "", pathinfo( $show, PATHINFO_FILENAME ) ); $fname = ( pathinfo( $fname, PATHINFO_FILENAME ) ); /* in case it was an webp image */ switch ( $img ) { case 'thumb': $float = $this->thumbFloat; break; case 'image': $float = $this->imageFloat; break; } if ( $this->currentView == 'category' ) { $float = $this->float; } $isWebp = 'false'; $ext = pathinfo( $show, PATHINFO_EXTENSION ); // if ( $ext == 'webp' && $this->convert ) { /* webp image */ if ( $ext == 'webp' ) { /* webp image */ $isWebp = 'true'; $showOri = preg_replace( '/.webp$/', '', $show ); $data[ '_source_0' ] = [ '_complex' => 1, '_data' => null, '_attributes' => [ 'srcset' => FileSystem::FixUrl( Sobi::Cfg( 'live_site' ) . $show ), 'type' => 'image/webp', ], ]; $data[ '_source_1' ] = [ '_complex' => 1, '_data' => null, '_attributes' => [ 'srcset' => FileSystem::FixUrl( Sobi::Cfg( 'live_site' ) . $showOri ), 'type' => image_type_to_mime_type( exif_imagetype( $showOri ) ), ], ]; $data[ 'img' ] = [ '_complex' => 1, '_data' => null, '_attributes' => [ 'class' => $this->cssClass, 'src' => FileSystem::FixUrl( Sobi::Cfg( 'live_site' ) . $showOri ), 'alt' => $fname, 'title' => $fname, ], ]; if ( $float ) { $data[ 'img' ][ '_attributes' ][ 'style' ] = "float:{$float};"; } $_data = [ 'picture' => $data ]; } else { /* no webp image */ $data = [ '_complex' => 1, '_data' => null, '_attributes' => [ 'class' => $this->cssClass, 'src' => FileSystem::FixUrl( Sobi::Cfg( 'live_site' ) . $show ), 'alt' => $fname, 'title' => $fname, ], ]; if ( $float ) { $data[ '_attributes' ][ 'style' ] = "float:{$float};"; } $_data = [ 'img' => $data ]; } return [ '_complex' => 1, '_data' => $_data, '_attributes' => [ 'icon' => isset( $files[ 'ico' ] ) ? FileSystem::FixPath( $files[ 'ico' ] ) : null, 'image' => isset( $files[ 'image' ] ) ? FileSystem::FixPath( $files[ 'image' ] ) : null, 'thumbnail' => isset( $files[ 'thumb' ] ) ? FileSystem::FixPath( $files[ 'thumb' ] ) : null, 'original' => isset( $files[ 'original' ] ) ? FileSystem::FixPath( $files[ 'original' ] ) : null, 'class' => $this->cssClass, 'webp' => $isWebp, ], '_options' => [ 'exif' => $exifToPass ], ]; } } } /** * @param SPEntry $entry * @param string $request * * @return bool|string * @throws SPException */ public function validate( $entry, $request ) { return $this->verify( $entry, $request ); } /** * @throws SPException * @throws \Sobi\Error\Exception */ public function ProxyUpload() { $ident = Input::Cmd( 'ident', 'post' ); $data = Input::File( $ident, 'tmp_name' ); $secret = md5( Sobi::Cfg( 'secret' ) ); if ( $data ) { /// $imageType = $this->convert ? 'WEBP' : null; $properties = Input::File( $ident ); $orgName = $properties[ 'name' ]; /// $extension = $this->convert ? 'webp' : FileSystem::GetExt( $orgName ); $extension = FileSystem::GetExt( $orgName ); $orgName = str_replace( '.' . $extension, '.' . strtolower( $extension ), $orgName ); $pathInfo = pathinfo( $orgName ); $orgName = StringUtils::Nid( $pathInfo[ 'filename' ], false, false, true ) . '.' . strtolower( $extension ); if ( $properties[ 'size' ] > $this->maxSize ) { $this->message( [ 'type' => 'error', 'text' => SPLang::e( 'FIELD_IMG_TOO_LARGE', $this->name, $properties[ 'size' ], $this->maxSize ), 'id' => '', ] ); } $dirNameHash = md5( $orgName . time() . $secret ); $dirName = SPLoader::dirPath( "tmp.files.{$secret}.{$dirNameHash}", 'front', false ); FileSystem::Mkdir( $dirName ); $path = $dirName . $orgName; $orgImage = new Image(); if ( !( $orgImage->upload( $data, $path ) ) ) { $this->message( [ 'type' => 'error', 'text' => SPLang::e( 'CANNOT_UPLOAD_FILE' ), 'id' => '' ] ); } $files[ 'data' ][ 'exif' ] = $orgImage->exif(); $this->cleanExif( $files[ 'data' ][ 'exif' ] ); if ( Sobi::Cfg( 'image_field.fix_rotation', true ) ) { if ( $orgImage->fixRotation() ) { $orgImage->save(); } } // generate a cropped file in case the user doesn't crop it manually if ( $this->crop ) { $croppedImage = clone $orgImage; [ $originalWidth, $originalHeight ] = getimagesize( $path ); $aspectRatio = $this->resizeWidth / $this->resizeHeight; $width = $aspectRatio * $originalHeight > $originalWidth ? $originalWidth : $aspectRatio * $originalHeight; $height = $originalWidth / $aspectRatio > $originalHeight ? $originalHeight : $originalWidth / $aspectRatio; try { $croppedImage->setTransparency( $this->detectTransparency ); $croppedImage->crop( $width, $height ); $croppedImage->saveAs( $dirName . 'cropped_' . $orgName ); $ico = clone $croppedImage; } catch ( Exception $x ) { $this->message( [ 'type' => 'error', 'text' => SPLang::e( 'FIELD_IMG_CANNOT_CROP', $x->getMessage() ), 'id' => '', ] ); } } else { $ico = clone $orgImage; } // create the large image $image = clone $orgImage; try { // if cropping, generate the image representing the cropped area $previewSize = explode( ':', Sobi::Cfg( 'image.preview_size', '500:500' ) ); $image->setTransparency( $this->detectTransparency ); $image->resample( $previewSize[ 0 ], $previewSize[ 1 ] ); $image->saveAs( $dirName . 'resized_' . $orgName ); } catch ( Exception $x ) { $image->delete(); $this->message( [ 'type' => 'error', 'text' => SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ), 'id' => '', ] ); } // create small icon image try { $icoSize = explode( ':', Sobi::Cfg( 'image.ico_size', '100:100' ) ); $ico->setTransparency( $this->detectTransparency ); $ico->resample( $icoSize[ 0 ], $icoSize[ 1 ] ); $ico->saveAs( $dirName . 'icon_' . $orgName ); } catch ( Exception $x ) { $ico->delete(); $this->message( [ 'type' => 'error', 'text' => SPLang::e( 'FIELD_IMG_CANNOT_RESAMPLE', $x->getMessage() ), 'id' => '', ] ); } $path = $orgImage->getPathname(); $type = $this->check( $path ); $properties[ 'tmp_name' ] = $path; $out = SPConfig::serialize( $properties ); FileSystem::Write( SPLoader::dirPath( "tmp.files.{$secret}", 'front', false ) . '/' . $orgName . '.var', $out ); $response = [ 'type' => 'success', 'text' => $this->crop ? Sobi::Txt( 'IMAGE_UPLOADED_CROP', $properties[ 'name' ], $type ) : Sobi::Txt( 'FILE_UPLOADED', $properties[ 'name' ] ), 'id' => 'directory://' . $dirNameHash, 'data' => [ 'name' => $properties[ 'name' ], 'type' => $properties[ 'type' ], 'size' => $properties[ 'size' ], 'original' => $dirNameHash . '/' . $properties[ 'name' ], 'icon' => $dirNameHash . '/' . 'icon_' . $orgName, 'crop' => $this->crop, 'height' => $this->resizeHeight, 'width' => $this->resizeWidth, ], ]; } else { $response = [ 'type' => 'error', 'text' => SPLang::e( 'CANNOT_UPLOAD_FILE_NO_DATA' ), 'id' => '', ]; } $this->message( $response ); } /** * @param $file * * @return mixed * @throws SPException * @throws \Sobi\Error\Exception */ protected function check( $file ) { $allowed = SPLoader::loadIniFile( 'etc.files' ); $mType = SPFactory::Instance( 'services.fileinfo', $file )->mimeType(); if ( strlen( $mType ) && !( in_array( $mType, $allowed ) ) ) { FileSystem::Delete( $file ); $this->message( [ 'type' => 'error', 'text' => SPLang::e( 'FILE_WRONG_TYPE', $mType ), 'id' => '' ] ); } return $mType; } /** * @param $response * * @throws SPException */ protected function message( $response ) { SPFactory::mainframe() ->cleanBuffer() ->customHeader(); echo json_encode( $response ); exit; } /** * */ public function ProxyIcon() { $secret = md5( Sobi::Cfg( 'secret' ) ); $file = Input::String( 'file' ); $file = explode( '/', $file ); $dirName = SPLoader::dirPath( "tmp.files.{$secret}.{$file[0]}", 'front', true ); $fileName = $dirName . $file[ 1 ]; if ( !( function_exists( 'exif_imagetype' ) ) ) { header( 'Content-Type:' . 'image/xyz' ); } else { header( 'Content-Type:' . image_type_to_mime_type( exif_imagetype( $fileName ) ) ); } header( 'Content-Length: ' . filesize( $fileName ) ); readfile( $fileName ); exit; } /** * @param $directory * * @return array * @throws SPException */ private function getAjaxFiles( $directory ) { $secret = md5( Sobi::Cfg( 'secret' ) ); $coordinates = null; $dirNameHash = str_replace( 'directory://', null, $directory ); if ( strstr( $dirNameHash, '::coordinates://' ) ) { $struct = explode( '::coordinates://', $dirNameHash ); $dirNameHash = $struct[ 0 ]; $coordinates = $struct[ 1 ]; } $data = $dirNameHash; $dirName = SPLoader::dirPath( "tmp.files.{$secret}.{$dirNameHash}", 'front', false ); $files = scandir( $dirName ); return [ $data, $dirName, $files, $coordinates ]; } /** * @return mixed|null */ protected function getExistingFiles() { $files = $this->getRaw(); if ( is_string( $files ) ) { try { $files = SPConfig::unserialize( $files ); return $files; } catch ( SPException $x ) { return null; } } return $files; } /** * @param $entry * @param $request * @param $files * * @return \Sobi\Database\MySQLi|SPdb * @throws SPException * @throws \Sobi\Error\Exception */ protected function storeData( &$entry, $request, $files ) { /* @var SPdb $db */ if ( is_array( $files ) && count( $files ) ) { $db =& SPFactory::db(); if ( get_class( $this ) == 'SPField_Image' ) { // if saved from frontend $this->verify( $entry, $request ); } $time = Input::Now(); $IP = Input::Ip4( 'REMOTE_ADDR', 'SERVER', 0 ); $uid = Sobi::My( 'id' ); /* if we are here, we can save these data */ /* collect the needed params */ $save = count( $files ) ? SPConfig::serialize( $files ) : null; $params = []; $params[ 'publishUp' ] = $entry->get( 'publishUp' ); $params[ 'publishDown' ] = $entry->get( 'publishDown' ); $params[ 'fid' ] = $this->fid; $params[ 'sid' ] = $entry->get( 'id' ); $params[ 'section' ] = Sobi::Reg( 'current_section' ); $params[ 'lang' ] = Sobi::DefLang(); $params[ 'enabled' ] = $entry->get( 'state' ); $params[ 'baseData' ] = $db->escape( $save ); $params[ 'approved' ] = $entry->get( 'approved' ); $params[ 'confirmed' ] = $entry->get( 'confirmed' ); /* if it is the first version, it is new entry */ if ( $entry->get( 'version' ) == 1 ) { $params[ 'createdTime' ] = $time; $params[ 'createdBy' ] = $uid; $params[ 'createdIP' ] = $IP; } $params[ 'updatedTime' ] = $time; $params[ 'updatedBy' ] = $uid; $params[ 'updatedIP' ] = $IP; $params[ 'copy' ] = (int) !( $entry->get( 'approved' ) ); if ( ( Sobi::My( 'id' ) == $entry->get( 'owner' ) ) && ( $this->editLimit > 0 ) ) { --$this->editLimit; // storeData is called only if the image has change in some way } $params[ 'editLimit' ] = $this->editLimit; /* save it */ try { $db->insertUpdate( 'spdb_field_data', $params ); return $db; } catch ( SPException $x ) { Sobi::Error( $this->name(), SPLang::e( 'CANNOT_SAVE_FIELDS_DATA_DB_ERR', $x->getMessage() ), SPC::WARNING, 0, __LINE__, __FILE__ ); return $db; } } } /** * @param $entry * @param $request * @param $files * @param $cloneFiles * * @return \Sobi\Database\MySQLi|SPdb * @throws SPException * @throws \Sobi\Error\Exception */ protected function cloneFiles( &$entry, $request, $files, $cloneFiles ) { $orgName = basename( isset( $files[ 'original' ] ) ? $files[ 'original' ] : $files[ 'image' ] ); // clean the filename $prefix = strstr( $orgName, '_', true ); // everything before the first underline is treated as prefix $prefix = $prefix ? $prefix . '_' : ''; // for PHP < 7 $orgName = str_replace( $prefix, '', $orgName ); // remove the prefix $orgName = preg_replace( '/\d{1,}clone\_/', '', $orgName, 1 ); // remove clone identifier $sPath = $this->parseName( $entry, $orgName, $this->savePath ); if ( isset( $files[ 'original' ] ) && FileSystem::Exists( SOBI_ROOT . '/' . $files[ 'original' ] ) ) { $cloneFiles[ 'original' ] = $sPath . $this->parseName( $entry, $orgName, '{id}clone_{orgname}', true ); FileSystem::Copy( SOBI_ROOT . '/' . $files[ 'original' ], SOBI_ROOT . '/' . $cloneFiles[ 'original' ] ); } if ( isset( $files[ 'image' ] ) && FileSystem::Exists( SOBI_ROOT . '/' . $files[ 'image' ] ) ) { $prefix = strstr( $this->imageName, '_', true ); // everything before the first underline is treated as prefix $prefix = $prefix ? $prefix : ''; // for PHP < 7 $pattern = $prefix . '_{id}clone_' . str_replace( $prefix . '_', '', $this->imageName ); $cloneFiles[ 'image' ] = $sPath . $this->parseName( $entry, $orgName, $pattern, true ); FileSystem::Copy( SOBI_ROOT . '/' . $files[ 'image' ], SOBI_ROOT . '/' . $cloneFiles[ 'image' ] ); } if ( isset( $files[ 'thumb' ] ) && FileSystem::Exists( SOBI_ROOT . '/' . $files[ 'thumb' ] ) ) { $prefix = strstr( $this->thumbName, '_', true ); // everything before the first underline is treated as prefix $prefix = $prefix ? $prefix : ''; // for PHP < 7 $pattern = $prefix . '_{id}clone_' . str_replace( $prefix . '_', '', $this->thumbName ); $cloneFiles[ 'thumb' ] = $sPath . $this->parseName( $entry, $orgName, $pattern, true ); FileSystem::Copy( SOBI_ROOT . '/' . $files[ 'thumb' ], SOBI_ROOT . '/' . $cloneFiles[ 'thumb' ] ); } if ( isset( $files[ 'ico' ] ) && FileSystem::Exists( SOBI_ROOT . '/' . $files[ 'ico' ] ) ) { $cloneFiles[ 'ico' ] = $sPath . $this->parseName( $entry, strtolower( $orgName ), 'ico_{id}clone_{orgname}', true ); FileSystem::Copy( SOBI_ROOT . '/' . $files[ 'ico' ], SOBI_ROOT . '/' . $cloneFiles[ 'ico' ] ); } return $this->storeData( $entry, $request, $cloneFiles ); } /** * @param string $data * @param int $section * @param bool $startWith * @param bool $ids * * @return array */ public function searchSuggest( $data, $section, $startWith = true, $ids = false ) { return []; } }