예제 #1
0
 /**
  * Finally delete content item
  */
 public function make()
 {
     // remove gallery files if exists
     foreach ($this->_records->get() as $record) {
         $galleryPath = '/upload/gallery/' . (int) $record->id;
         if (Directory::exist($galleryPath)) {
             Directory::remove($galleryPath);
         }
     }
     // finally remove from db
     $this->_records->forceDelete();
 }
예제 #2
0
 /**
  * Get available themes for environment
  * @param $env_name
  * @return array
  */
 public function getAvailableThemes($env_name)
 {
     $path = root . '/Apps/View/' . $env_name . '/';
     if (!Directory::exist($path)) {
         return [];
     }
     $scan = Directory::scan($path);
     $response = [];
     foreach ($scan as $object) {
         $response[] = substr(strrchr($object, '/'), 1);
     }
     return $response;
 }
예제 #3
0
 /**
  * Try to find sitemap indexes in storage directory
  * @throws SyntaxException
  */
 public function before()
 {
     if (!Directory::exist(static::INDEX_PATH)) {
         throw new SyntaxException(__('Directory %dir% for sitemaps is not exists', ['dir' => static::INDEX_PATH]));
     }
     $scan = File::listFiles(static::INDEX_PATH, ['.xml'], true);
     if (Obj::isArray($scan)) {
         foreach ($scan as $file) {
             if ($this->_lang !== null && !Str::contains('.' . $this->_lang, $file)) {
                 continue;
             }
             $this->files[] = static::INDEX_PATH . '/' . $file;
         }
     }
 }
예제 #4
0
 /**
  * Get default server information and prepare chmod info
  */
 public function before()
 {
     $this->phpVersion = phpversion();
     $this->pdo = extension_loaded('pdo');
     $this->gd = extension_loaded('gd') && function_exists('gd_info');
     // autoload is disabled, lets get chmod file & dirs from console app data
     File::inc('/Apps/Controller/Console/Main.php');
     $this->_chmodDirs = Main::$installDirs;
     // for each file or directory in list - check permissions
     foreach ($this->_chmodDirs as $object) {
         if (Str::endsWith('.php', $object)) {
             // sounds like a file
             $this->chmodCheck[$object] = File::exist($object) && File::writable($object);
         } else {
             $this->chmodCheck[$object] = Directory::exist($object) && Directory::writable($object);
         }
     }
 }
예제 #5
0
 /**
  * Lets construct viewer
  * @throws NativeException
  */
 public function __construct()
 {
     $this->lang = App::$Request->getLanguage();
     // get theme config and build full path
     $themeConfig = App::$Properties->get('theme');
     $this->themePath = root . DIRECTORY_SEPARATOR . 'Apps' . DIRECTORY_SEPARATOR . 'View' . DIRECTORY_SEPARATOR . env_name;
     if (isset($themeConfig[env_name]) && Str::length($themeConfig[env_name]) > 0) {
         $this->themePath .= DIRECTORY_SEPARATOR . $themeConfig[env_name];
     } else {
         $this->themePath .= DIRECTORY_SEPARATOR . 'default';
     }
     // check if theme is available
     if (!Directory::exist($this->themePath)) {
         throw new NativeException('Apps theme is not founded: ' . Str::replace(root, null, $this->themePath));
     }
     // get input args and build class properties
     $args = func_get_args();
     $this->path = array_shift($args);
     $this->params = array_shift($args);
     $this->sourcePath = array_shift($args);
 }
예제 #6
0
파일: Main.php 프로젝트: phpffcms/ffcms
 /**
  * Index page of admin dashboard
  * @return string
  * @throws \Ffcms\Core\Exception\SyntaxException
  * @throws \Ffcms\Core\Exception\NativeException
  */
 public function actionIndex()
 {
     // cache some data
     $rootSize = App::$Cache->get('root.size');
     if ($rootSize === null) {
         $rootSize = round(Directory::getSize('/') / (1024 * 1000), 2) . ' mb';
         App::$Cache->set('root.size', $rootSize, 86400);
         // 24 hours caching = 60 * 60 * 24
     }
     $loadAvg = App::$Cache->get('load.average');
     if ($loadAvg === null) {
         $loadAvg = Environment::loadAverage();
         App::$Cache->set('load.average', $loadAvg, 60 * 5);
         // 5 min cache
     }
     // prepare system statistic
     $stats = ['ff_version' => Version::VERSION . ' (' . Version::DATE . ')', 'php_version' => Environment::phpVersion() . ' (' . Environment::phpSAPI() . ')', 'os_name' => Environment::osName(), 'database_name' => App::$Database->connection()->getDatabaseName() . ' (' . App::$Database->connection()->getDriverName() . ')', 'file_size' => $rootSize, 'load_avg' => $loadAvg];
     // check directory chmods and other environment features
     $model = new EntityCheck();
     // render view output
     return $this->view->render('index', ['stats' => $stats, 'check' => $model]);
 }
예제 #7
0
 /**
  * Save changes in database
  */
 public function save()
 {
     $this->_content->title = $this->title;
     $this->_content->text = $this->text;
     $this->_content->path = $this->path;
     $this->_content->category_id = $this->categoryId;
     $this->_content->author_id = $this->authorId;
     $this->_content->display = $this->display;
     $this->_content->meta_title = $this->metaTitle;
     $this->_content->meta_keywords = $this->metaKeywords;
     $this->_content->meta_description = $this->metaDescription;
     $this->_content->source = $this->source;
     // check if rating is changed
     if ((int) $this->addRating !== 0) {
         $this->_content->rating += (int) $this->addRating;
     }
     // check if special comment hash is exist
     if ($this->_new || Str::length($this->_content->comment_hash) < 32) {
         $this->_content->comment_hash = $this->generateCommentHash();
     }
     // check if date is updated
     if (!Str::likeEmpty($this->createdAt) && !Str::startsWith('0000', Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP))) {
         $this->_content->created_at = Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP);
     }
     // save poster data
     $posterPath = '/upload/gallery/' . $this->galleryFreeId . '/orig/' . $this->poster;
     if (File::exist($posterPath)) {
         $this->_content->poster = $this->poster;
     }
     // get temporary gallery id
     $tmpGalleryId = $this->galleryFreeId;
     // save row
     $this->_content->save();
     // update tags data in special table (relation: content->content_tags = oneToMany)
     ContentTag::where('content_id', '=', $this->_content->id)->delete();
     $insertData = [];
     foreach ($this->metaKeywords as $lang => $keys) {
         // split keywords to tag array
         $tags = explode(',', $keys);
         foreach ($tags as $tag) {
             // cleanup tag from white spaces
             $tag = trim($tag);
             // prepare data to insert
             if (Str::length($tag) > 0) {
                 $insertData[] = ['content_id' => $this->_content->id, 'lang' => $lang, 'tag' => $tag];
             }
         }
     }
     // insert tags
     ContentTag::insert($insertData);
     // move files
     if ($tmpGalleryId !== $this->_content->id) {
         Directory::rename('/upload/gallery/' . $tmpGalleryId, $this->_content->id);
     }
 }
예제 #8
0
 /**
  * Find all bootatble instances and set it to object map
  */
 public function compileBootableClasses()
 {
     // list app root's
     foreach ($this->appRoots as $app) {
         $app .= '/Apps/Controller/' . env_name;
         $files = File::listFiles($app, ['.php'], true);
         foreach ($files as $file) {
             // define full class name with namespace
             $class = 'Apps\\Controller\\' . env_name . '\\' . Str::cleanExtension($file);
             // check if class exists (must be loaded over autoloader), boot method exist and this is controller instanceof
             if (class_exists($class) && method_exists($class, 'boot') && is_a($class, 'Ffcms\\Core\\Arch\\Controller', true)) {
                 $this->objects[] = $class;
             }
         }
     }
     // list widget root's
     foreach ($this->widgetRoots as $widget) {
         $widget .= '/Widgets/' . env_name;
         // widgets are packed in directory, classname should be the same with root directory name
         $dirs = Directory::scan($widget, GLOB_ONLYDIR, true);
         if (!Obj::isArray($dirs)) {
             continue;
         }
         foreach ($dirs as $instance) {
             $class = 'Widgets\\' . env_name . '\\' . $instance . '\\' . $instance;
             if (class_exists($class) && method_exists($class, 'boot') && is_a($class, 'Ffcms\\Core\\Arch\\Widget', true)) {
                 $this->objects[] = $class;
             }
         }
     }
 }
예제 #9
0
 public function createObject($name, $type)
 {
     $singleName = false;
     if (!Str::contains('/', $name)) {
         if ($type === 'ActiveRecord') {
             $singleName = true;
         } else {
             $this->message = 'Command dosn\'t contains valid name. Example: Front/SomeForm, Admin/SomePkg/SomeInput';
             return false;
         }
     }
     $objectDirPath = null;
     $objectNamespace = null;
     $objectName = null;
     $objectTemplate = null;
     if ($singleName) {
         $objectDirPath = root . '/Apps/' . $type . '/';
         $objectNamespace = 'Apps\\' . $type;
         $objectName = ucfirst($name);
     } else {
         $split = explode('/', $name);
         $workground = ucfirst(strtolower(array_shift($split)));
         $objectName = ucfirst(array_pop($split));
         $subName = false;
         if (count($split) > 0) {
             // some sub-namespace / folder path
             foreach ($split as $part) {
                 if (Str::length($part) > 0) {
                     $subName[] = ucfirst(strtolower($part));
                 }
             }
         }
         if ($type === 'Widget') {
             $objectDirPath = root . '/Widgets/' . $workground;
             $objectNamespace = 'Widgets\\' . $workground;
         } else {
             $objectDirPath = root . '/Apps/' . $type . '/' . $workground;
             $objectNamespace = 'Apps\\' . $type . '\\' . $workground;
         }
         if (false !== $subName) {
             $objectDirPath .= '/' . implode('/', $subName);
             $objectNamespace .= '\\' . implode('\\', $subName);
         }
         // try to find workground-based controller
         if (File::exist('/Private/Carcase/' . $workground . '/' . $type . '.tphp')) {
             $objectTemplate = File::read('/Private/Carcase/' . $workground . '/' . $type . '.tphp');
         }
     }
     if (!Directory::exist($objectDirPath) && !Directory::create($objectDirPath)) {
         $this->message = 'Directory could not be created: ' . $objectDirPath;
         return false;
     }
     if ($objectTemplate === null) {
         $objectTemplate = File::read('/Private/Carcase/' . $type . '.tphp');
         if (false === $objectTemplate) {
             $this->message = 'Php template file is not founded: /Private/Carcase/' . $type . '.tphp';
             return false;
         }
     }
     $objectContent = Str::replace(['%namespace%', '%name%'], [$objectNamespace, $objectName], $objectTemplate);
     $objectFullPath = $objectDirPath . '/' . $objectName . '.php';
     if (File::exist($objectFullPath)) {
         $this->message = $type . ' is always exist: ' . $objectFullPath;
         return false;
     }
     File::write($objectFullPath, $objectContent);
     $this->message = $type . ' template was created: [' . $objectName . '] in path: ' . Str::replace(root, '', $objectDirPath);
     return true;
 }
예제 #10
0
 /**
  * Prepare model attributes from passed objects
  * @throws ForbiddenException
  */
 public function before()
 {
     $this->id = $this->_content->id;
     $this->title = $this->_content->getLocaled('title');
     $this->text = $this->_content->getLocaled('text');
     // check if title and text are exists
     if (Str::length($this->title) < 1 || Str::length($this->text) < 1) {
         throw new ForbiddenException();
     }
     // get meta data
     $this->metaTitle = $this->_content->getLocaled('meta_title');
     if (Str::likeEmpty($this->metaTitle)) {
         $this->metaTitle = $this->title;
     }
     $this->metaDescription = $this->_content->getLocaled('meta_description');
     $tmpKeywords = $this->_content->getLocaled('meta_keywords');
     $this->metaKeywords = explode(',', $tmpKeywords);
     // set content date, category data
     $this->createDate = Date::humanize($this->_content->created_at);
     $this->catName = $this->_category->getLocaled('title');
     $this->catPath = $this->_category->path;
     // set user data
     if (App::$User->isExist($this->_content->author_id)) {
         $this->authorId = $this->_content->author_id;
         $profile = App::$User->identity($this->authorId)->getProfile();
         $this->authorName = $profile->getNickname();
     }
     $this->source = $this->_content->source;
     $this->views = $this->_content->views + 1;
     // check for dependence, add '' for general cat, ex: general/depend1/depend2/.../depend-n
     $catNestingArray = Arr::merge([0 => ''], explode('/', $this->catPath));
     if ($catNestingArray > 1) {
         // latest element its a current nesting level, lets cleanup it
         array_pop($catNestingArray);
         $catNestingPath = null;
         foreach ($catNestingArray as $cPath) {
             $catNestingPath .= $cPath;
             // try to find category by path in db
             $record = ContentCategory::getByPath($catNestingPath);
             if ($record !== null && $record->count() > 0) {
                 // if founded - add to nesting data
                 $this->catNesting[] = ['name' => $record->getLocaled('title'), 'path' => $record->path];
             }
             if (!Str::likeEmpty($catNestingPath)) {
                 $catNestingPath .= '/';
             }
         }
     }
     // build array of category nesting level
     $this->catNesting[] = ['name' => $this->catName, 'path' => $this->catPath];
     // get gallery images and poster data
     $galleryPath = '/upload/gallery/' . $this->_content->id;
     // check if gallery folder is exist
     if (Directory::exist($galleryPath)) {
         $originImages = File::listFiles($galleryPath . '/orig/', ['.jpg', '.png', '.gif', '.jpeg', '.bmp', '.webp'], true);
         // generate poster data
         if (Arr::in($this->_content->poster, $originImages)) {
             // original poster
             $posterName = $this->_content->poster;
             $this->posterFull = $galleryPath . '/orig/' . $posterName;
             if (!File::exist($this->posterFull)) {
                 $this->posterFull = null;
             }
             // thumb poster
             $posterSplit = explode('.', $posterName);
             array_pop($posterSplit);
             $posterCleanName = implode('.', $posterSplit);
             $this->posterThumb = $galleryPath . '/thumb/' . $posterCleanName . '.jpg';
             if (!File::exist($this->posterThumb)) {
                 $this->posterThumb = null;
             }
         }
         // generate full gallery
         foreach ($originImages as $image) {
             $imageSplit = explode('.', $image);
             array_pop($imageSplit);
             $imageClearName = implode('.', $imageSplit);
             // skip image used in poster
             if (Str::startsWith($imageClearName, $this->_content->poster)) {
                 continue;
             }
             $thumbPath = $galleryPath . '/thumb/' . $imageClearName . '.jpg';
             if (File::exist($thumbPath)) {
                 $this->galleryItems[$thumbPath] = $galleryPath . '/orig/' . $image;
             }
         }
     }
     // set rating data
     $this->rating = $this->_content->rating;
     $ignoredRate = App::$Session->get('content.rate.ignore');
     $this->canRate = true;
     if (Obj::isArray($ignoredRate) && Arr::in((string) $this->id, $ignoredRate)) {
         $this->canRate = false;
     }
     if (!App::$User->isAuth()) {
         $this->canRate = false;
     } elseif ($this->authorId === App::$User->identity()->getId()) {
         $this->canRate = false;
     }
     // update views count
     $this->_content->views += 1;
     $this->_content->save();
 }
예제 #11
0
 /**
  * Get available languages in the filesystem
  * @return array
  */
 public function getAvailableLangs()
 {
     $langs = ['en'];
     $scan = Directory::scan(root . '/I18n/' . env_name . '/', GLOB_ONLYDIR, true);
     foreach ($scan as $row) {
         $langs[] = trim($row, '/');
     }
     return $langs;
 }
예제 #12
0
 /**
  * Save input data to database
  */
 public function make()
 {
     // save data to db
     $this->_record->title = $this->title;
     $this->_record->text = $this->text;
     $this->_record->path = $this->path;
     $this->_record->category_id = (int) $this->categoryId;
     $this->_record->display = 0;
     // set to premoderation
     $this->_record->author_id = (int) $this->authorId;
     if ($this->_new === true) {
         $this->_record->comment_hash = $this->generateCommentHash();
     }
     $this->_record->save();
     // work with poster data
     if ($this->poster !== null) {
         // lets move poster from tmp to gallery
         $originDir = '/upload/gallery/' . $this->_record->id . '/orig/';
         $thumbDir = '/upload/gallery/' . $this->_record->id . '/thumb/';
         if (!Directory::exist($originDir)) {
             Directory::create($originDir);
         }
         if (!Directory::exist($thumbDir)) {
             Directory::create($thumbDir);
         }
         $fileName = App::$Security->simpleHash($this->poster->getClientOriginalName() . $this->poster->getSize());
         $newFullName = $fileName . '.' . $this->poster->guessExtension();
         // move poster to upload gallery directory
         $this->poster->move(Normalize::diskFullPath($originDir), $newFullName);
         // initialize image resizer
         $thumb = new Image();
         $thumb->setCacheDir(root . '/Private/Cache/images');
         // open original file, resize it and save
         $thumbSaveName = Normalize::diskFullPath($thumbDir) . '/' . $fileName . '.jpg';
         $thumb->open(Normalize::diskFullPath($originDir) . DIRECTORY_SEPARATOR . $newFullName)->cropResize($this->_configs['galleryResize'])->save($thumbSaveName, 'jpg', 90);
         $thumb = null;
         // update poster in database
         $this->_record->poster = $newFullName;
         $this->_record->save();
     }
 }
예제 #13
0
파일: Main.php 프로젝트: phpffcms/ffcms
 /**
  * Console installation
  * @return string
  * @throws NativeException
  */
 public function actionInstall()
 {
     if (File::exist('/Private/Install/install.lock')) {
         throw new NativeException('Installation is locked! Please delete /Private/Install/install.lock');
     }
     echo Console::$Output->writeHeader('License start');
     echo File::read('/LICENSE') . PHP_EOL;
     echo Console::$Output->writeHeader('License end');
     $config = Console::$Properties->get('database');
     $newConfig = [];
     // creating default directory's
     foreach (self::$installDirs as $obj) {
         // looks like a directory
         if (!Str::contains('.', $obj)) {
             Directory::create($obj, 0777);
         }
     }
     echo Console::$Output->write('Upload and private directories are successful created!');
     // set chmods
     echo $this->actionChmod();
     // database config from input
     echo Console::$Output->writeHeader('Database connection configuration');
     echo 'Driver(default:' . $config['driver'] . '):';
     $dbDriver = Console::$Input->read();
     if (Arr::in($dbDriver, ['mysql', 'pgsql', 'sqlite'])) {
         $newConfig['driver'] = $dbDriver;
     }
     // for sqlite its would be a path
     echo 'Host(default:' . $config['host'] . '):';
     $dbHost = Console::$Input->read();
     if (!Str::likeEmpty($dbHost)) {
         $newConfig['host'] = $dbHost;
     }
     echo 'Database name(default:' . $config['database'] . '):';
     $dbName = Console::$Input->read();
     if (!Str::likeEmpty($dbName)) {
         $newConfig['database'] = $dbName;
     }
     echo 'User(default:' . $config['username'] . '):';
     $dbUser = Console::$Input->read();
     if (!Str::likeEmpty($dbUser)) {
         $newConfig['username'] = $dbUser;
     }
     echo 'Password(default:' . $config['password'] . '):';
     $dbPwd = Console::$Input->read();
     if (!Str::likeEmpty($dbPwd)) {
         $newConfig['password'] = $dbPwd;
     }
     echo 'Table prefix(default:' . $config['prefix'] . '):';
     $dbPrefix = Console::$Input->read();
     if (!Str::likeEmpty($dbPrefix)) {
         $newConfig['prefix'] = $dbPrefix;
     }
     // merge configs and add new connection to db pull
     $dbConfigs = Arr::merge($config, $newConfig);
     Console::$Database->addConnection($dbConfigs, 'install');
     try {
         Console::$Database->connection('install')->getDatabaseName();
     } catch (\Exception $e) {
         return 'Testing database connection is failed! Run installer again and pass tested connection data! Log: ' . $e->getMessage();
     }
     // autoload isn't work here
     include root . '/Apps/Controller/Console/Db.php';
     // import db data
     $dbController = new DbController();
     echo $dbController->actionImportAll('install');
     // add system info about current install version
     $system = new System();
     $system->setConnection('install');
     $system->var = 'version';
     $system->data = Version::VERSION;
     $system->save();
     // set website send from email from input
     $emailConfig = Console::$Properties->get('adminEmail');
     echo 'Website sendFrom email(default: ' . $emailConfig . '):';
     $email = Console::$Input->read();
     if (!Str::isEmail($email)) {
         $email = $emailConfig;
     }
     // set base domain
     echo 'Website base domain name(ex. ffcms.org):';
     $baseDomain = Console::$Input->read();
     if (Str::likeEmpty($baseDomain)) {
         $baseDomain = Console::$Properties->get('baseDomain');
     }
     // generate other configuration data and security salt, key's and other
     echo Console::$Output->writeHeader('Writing configurations');
     /** @var array $allCfg */
     $allCfg = Console::$Properties->getAll('default');
     $allCfg['database'] = $dbConfigs;
     $allCfg['adminEmail'] = $email;
     $allCfg['baseDomain'] = $baseDomain;
     echo Console::$Output->write('Generate password salt for BLOWFISH crypt');
     $allCfg['passwordSalt'] = '$2a$07$' . Str::randomLatinNumeric(mt_rand(21, 30)) . '$';
     echo Console::$Output->write('Generate security cookies for debug panel');
     $allCfg['debug']['cookie']['key'] = 'fdebug_' . Str::randomLatinNumeric(mt_rand(8, 32));
     $allCfg['debug']['cookie']['value'] = Str::randomLatinNumeric(mt_rand(32, 128));
     // write config data
     $writeCfg = Console::$Properties->writeConfig('default', $allCfg);
     if ($writeCfg !== true) {
         return 'File /Private/Config/Default.php is unavailable to write data!';
     }
     File::write('/Private/Install/install.lock', 'Install is locked');
     return 'Configuration done! FFCMS 3 is successful installed! Visit your website. You can add administrator using command php console.php db/adduser';
 }
예제 #14
0
 /**
  * Download file from $url and save it into $path
  * @param string $url
  * @param string $path
  * @return bool
  */
 public static function saveFromUrl($url, $path)
 {
     if (!filter_var($url, FILTER_VALIDATE_URL) || !function_exists('curl_init')) {
         return false;
     }
     $path = Normalize::diskFullPath($path);
     // check if upload directory is exists
     $dir = dirname($path);
     if (!Directory::exist($dir)) {
         Directory::create($dir);
     }
     // initialize stream resource
     $stream = @fopen($path, 'w');
     // initialize curl & set required options, target url, destination save stream
     $curl = \curl_init();
     \curl_setopt($curl, CURLOPT_URL, $url);
     if (Str::startsWith('https', $url)) {
         \curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
         \curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
     }
     \curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
     \curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5);
     \curl_setopt($curl, CURLOPT_HEADER, 0);
     \curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322');
     \curl_setopt($curl, CURLOPT_FAILONERROR, true);
     \curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
     \curl_setopt($curl, CURLOPT_AUTOREFERER, true);
     \curl_setopt($curl, CURLOPT_TIMEOUT, 10);
     // set destination file path
     \curl_setopt($curl, CURLOPT_FILE, $stream);
     \curl_exec($curl);
     \curl_close($curl);
     fclose($stream);
     return true;
 }
예제 #15
0
파일: Content.php 프로젝트: phpffcms/ffcms
 /**
  * Show gallery images from upload directory
  * @param int $id
  * @return string
  * @throws NotFoundException
  * @throws NativeException
  */
 public function actionGallerylist($id)
 {
     // check if id is passed
     if (Str::likeEmpty($id)) {
         throw new NativeException('Wrong input data');
     }
     // check if user have permission to access there
     if (!App::$User->isAuth() || !App::$User->identity()->getRole()->can('global/file')) {
         throw new NativeException('Permission denied');
     }
     $thumbDir = Normalize::diskFullPath('/upload/gallery/' . $id . '/orig/');
     if (!Directory::exist($thumbDir)) {
         throw new NotFoundException('Nothing found');
     }
     $files = Directory::scan($thumbDir, null, true);
     if ($files === false || !Obj::isArray($files) || count($files) < 1) {
         throw new NotFoundException('Nothing found');
     }
     $output = [];
     foreach ($files as $file) {
         $fileExt = Str::lastIn($file, '.');
         $fileName = Str::sub($file, 0, -Str::length($fileExt));
         $output[] = ['thumbnailUrl' => '/upload/gallery/' . $id . '/thumb/' . $fileName . '.jpg', 'url' => '/upload/gallery/' . $id . '/orig/' . $file, 'name' => $file, 'size' => File::size('/upload/gallery/' . $id . '/orig/' . $file)];
     }
     $this->setJsonHeader();
     return json_encode(['status' => 1, 'files' => $output]);
 }