VvebOIDC/system/extensions/extensions.php

270 lines
7.1 KiB
PHP

<?php
/**
* Vvveb
*
* Copyright (C) 2022 Ziadin Givan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Vvveb\System\Extensions;
use function Vvveb\__;
use function Vvveb\download;
use function Vvveb\getUrl;
use Vvveb\System\Event;
use Vvveb\System\Functions\Str;
use Vvveb\System\Import\Rss;
use function Vvveb\unzip;
abstract class Extensions {
static protected $extensions = [];
static protected $categories = [];
static protected $extension = 'extension';
static protected $baseDir = 'extension';
static protected $url;
static protected $feedUrl;
const KEY_VALUE_REGEX = '/^([\w ]+):\s+(.+)$/m';
static function getParams($comments) {
$results = [];
if (preg_match_all(static :: KEY_VALUE_REGEX, $comments, $matches)) {
$matches[1] = array_map(function ($key) {
return str_replace(' ','-',strtolower(trim($key)));
}, $matches[1]);
$results = array_combine($matches[1], $matches[2]);
}
return $results;
}
static function getComments($content) {
//$content = file_get_contents($file);
if (function_exists('token_get_all')) {
$docComments = [];
foreach (token_get_all($content) as $entry) {
if ($entry[0] == T_DOC_COMMENT || $entry[0] == T_COMMENT) {
$docComments[] = $entry[1];
}
}
return implode("\n", $docComments);
} else {
if (preg_match_all('@(?s)/\*.*?\*/@', $content, $matches, PREG_PATTERN_ORDER)) {
return implode("\n", $matches[0] ?? []);
}
}
return '';
}
static function getInfo($content, $name = false) {
$comments = static :: getComments($content);
$params = static :: getParams($comments);
if (isset($params['status'])) {
unset($params['status']);
}
if (isset($params['category']) && $name) {
static :: $categories[$params['category']][] = $name;
}
return $params;
}
static function getListInfo($path) {
if (isset(static :: $extensions[static :: $extension])) {
return static :: $extensions[static :: $extension];
} else {
static :: $extensions[static :: $extension] = [];
}
$adminPath = \Vvveb\adminPath();
$list = glob($path);
foreach ($list as $file) {
$content = file_get_contents($file);
$dir = Str::match('@(.+)/[a-z]+.\w+$@', $file);
$folder = Str::match('@/([^/]+)/[a-z]+.\w+$@', $file);
$info = static::getInfo($content, $folder);
$info['file'] = $file;
$info['folder'] = $folder;
$info['import'] = file_exists($dir . DS . 'import');
if (isset($info['settings'])) {
$info['settings']= str_replace('/admin/', $adminPath, $info['settings']);
}
// plugin folder does not match slug
if (! isset($info['slug']) || ($info['slug'] != $info['folder'])) {
$info['status'] = 'slug_folder_mismatch';
$info['slug'] = $info['folder'];
}
static :: $extensions[static :: $extension][$folder] = $info;
}
return static :: $extensions[static :: $extension];
}
static function getCategories() {
return static :: $categories;
}
static function download($url) {
//$temp = tmpfile();
$f = false;
$temp = tempnam(sys_get_temp_dir(), 'vvveb_plugin');
if ($content = download($url)) {
$f = file_put_contents($temp, $content, LOCK_EX);
return $temp;
}
return $f;
}
static function install($extensionZipFile, $slug = false, $validate = true) {
$extension = static :: $extension;
$success = true;
$extractTo = static :: $baseDir;
$fileCheck = "$extension.php";
$zip = new \ZipArchive();
if ($zip->open($extensionZipFile/*, \ZipArchive::OVERWRITE*/) === true) {
$info = false;
$folderName = $zip->getNameIndex(0);
//check if first entry is a directory
if (substr($folderName, -1, 1) != '/') {
if ($validate) {
throw new \Exception(sprintf('%s zip must have only %s folder!', $extensionZipFile, ucfirst($extension)));
}
} else {
$slug = trim($folderName, '/');
}
for ($i = $zip->numFiles - 1; ($i > 0 && $success == true); $i--) {
$file = $zip->getNameIndex($i);
if ($validate) {
//check if all files inside the extension folder
if (strpos($file, $folderName) === false) {
$extractTo .= DS . $slug;
if ($validate) {
throw new \Exception(sprintf(__('%s zip should not have other files than %s folder!'), $extension, $extension));
}
}
}
// plugin.php must be in the top most folder
if (strpos($file, $fileCheck) !== false && substr_count($file, '/') < 2) {
$content = $zip->getFromName($file);
$info = static::getInfo($content);
if (isset($info['slug']) && ($folderName == ($info['slug'] . '/'))) {
// Unzip Path
if ($zip->extractTo($extractTo)) {
$success = $info['slug'];
} else {
$success = false;
}
} else {
if ($validate) {
throw new \Exception(sprintf(__('%s slug `%s` does not match folder %s!'), $extension, $info['slug'] ?? '', $folderName));
} else {
if ($zip->extractTo($extractTo . DS . $slug)) {
$info = ['slug' => $slug];
$success = $info['slug'];
} else {
$success = false;
}
}
}
break;
}
}
// no extension.php found then treat as generic extension/theme
if ($success) {
$extractTo .= DS . $slug;
if ($zip->extractTo($extractTo)) {
$success = $slug;
$info = ['slug' => $slug];
} else {
$success = false;
}
}
$zip->close();
if (! $info) {
throw new \Exception(sprintf(__('No `%s.php` info found inside zip!', $extension)));
}
} else {
throw new \Exception(__('File is not a valid zip archive!'));
}
Event :: trigger(__CLASS__, __FUNCTION__, $extensionZipFile, $success);
return $success;
}
static function marketUrl() {
return static :: $url;
}
static function getMarketUrl($url, $params = [], $key = null) {
$query = http_build_query($params);
$content = getUrl($url . '?' . $query);
if ($content) {
$rss = new Rss($content);
$key = $key ?? static :: $extension . 's';
$result[$key] = $rss->get($params['start'] ?? 1, $params['limit'] ?? 10);
$result['count'] = $rss->value('count');
return $result;
}
return [];
}
static function getMarketCategories($params = [], $key = null) {
return static :: getMarketUrl(static :: $categoriesFeedUrl, $params, $key);
}
static function getMarketList($params = [], $key = null) {
return static :: getMarketUrl(static :: $feedUrl, $params, $key);
}
}