Compare commits

...

5 commits
4.2.3 ... 4.2

Author SHA1 Message Date
chevereto
ed56a10e50 Automatic push 4.2.4 2024-12-05 13:33:13 +00:00
Rodolfo Berrios
ecf4742588
update screen 2024-11-27 18:24:25 -03:00
Rodolfo Berrios
0426ff90c1
Update README.md
edition compare
2024-11-27 18:17:38 -03:00
Rodolfo Berrios
62bee30a42
Add requirements
close #94
2024-11-27 18:13:17 -03:00
Rodolfo Berrios
a6d8888af6
Update README.md 2024-11-24 08:32:03 -03:00
37 changed files with 302 additions and 529 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

2
.gitignore vendored
View file

@ -2,7 +2,7 @@
/.env
/.idea
/app/.upgrading
/app/CHEVERETO_LICENSE_KEY
/app/CHEVERETO_LICENSE_KEY**
/app/vendor
/app/build
/app/.phpunit.cache

View file

@ -1,19 +0,0 @@
Chevereto 4.2.3 (2024-11-18)
- Added Bulk importer as a core feature
- Added translation placeholder for "hosted at" titles
- Added Akismet spam protection on tags
- Improved install screen
- Changed GMT references to UTC
- Updated Polish, Portuguese, Portuguese (Brazil) and Vietnamese translations
- Fixed bug with duplicated tags display
- Fixed bug with missing SQL update affecting Bulk importer
- Fixed bug with fail-safe ENV using array values
- Fixed bug with wrong column type for image_original_exifdata
- Fixed bug with Bulk importer not skipping missing directories
- Fixed bug with Open Graph double encoding
- Fixed bug with email subject extra encoding
- Fixed bug with Disqus dark theme detection/refresh
- Fixed bug with not working cookie law acceptance
- Fixed bug with tag filtering using /?match=all
- Fixed bug with PUP.js auto-close behavior

8
.package/4.2.4.txt Normal file
View file

@ -0,0 +1,8 @@
Chevereto 4.2.4 (2024-12-05)
- Added Chevereto Lite edition
- Added placeholders for comments code
- Improved comment code page id
- Improved ENV handling
- Fixed bug affecting CLI runtime
- Fixed bug affecting MariaDB 10.4 compatibility

View file

@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use function Chevereto\Legacy\getCheveretoEnv;
use function Chevereto\Legacy\loaderHandler;
class CustomTinkerwellDriver extends TinkerwellDriver
@ -25,7 +26,7 @@ class CustomTinkerwellDriver extends TinkerwellDriver
require $projectPath . '/app/legacy/load/loader.php';
include loaderHandler(
_cookie: [],
_env: $_ENV,
_env: getCheveretoEnv(),
_files: [],
_get: [],
_post: [],

289
README.md
View file

@ -8,21 +8,24 @@
[![Chevereto Docs](https://img.shields.io/badge/chevereto-docs-50C878?style=flat-square)](https://v4-docs.chevereto.com/)
[![Chevereto Community](https://img.shields.io/badge/chevereto-community-blue?style=flat-square)](https://chevereto.com/community)
[![Chevereto Discord](https://img.shields.io/badge/chevereto-discord-5865F2?style=flat-square)](https://chevereto.com/go/discord)
[![Chevereto Demo](https://img.shields.io/badge/chevereto-demo-d4af37?style=flat-square)](https://demo.chevereto.com)
[![Chevereto Demo](https://img.shields.io/badge/chevereto-demo-d4af37?style=flat-square&color=red)](https://demo.chevereto.com)
[![AGPL-3.0-only](https://img.shields.io/github/license/chevereto/chevereto?style=flat-square)](LICENSE)
[![Legacy stars](https://img.shields.io/github/stars/rodber/chevereto-free?style=flat-square&logo=github&label=Legacy%20stars&color=red)](https://github.com/rodber/chevereto-free)
[![Legacy stars](https://img.shields.io/github/stars/rodber/chevereto-free?style=flat-square&logo=github&label=Legacy%20stars&color=gold)](https://github.com/rodber/chevereto-free)
[![Awesome F/OSS](https://img.shields.io/badge/Awesome_F%2FOSS-Certified-black?colorA=&colorB=874efe&style=flat-square)](https://awsmfoss.com/chevereto/)
> 🔔 [Subscribe](https://chevereto.com/go/newsletter) to don't miss any update regarding Chevereto.
Chevereto is a robust, self-hosted media-sharing platform that prioritizes flexibility and control. It enables you to build and manage a media-sharing website on your own server, granting you complete autonomy over your hosting environment and policies. With Chevereto, you eliminate the risk of platform restrictions and shutdowns, ensuring your site operates entirely on your terms.
Our [commercial edition](https://chevereto.com/pricing) is designed for running very large media-sharing services, offering scalability and tools to manage multiple users, high traffic, and extensive media collections.
This is the repository for **Chevereto Free** edition. You can [compare editions](https://v4-docs.chevereto.com/introduction/editions/compare.html) to find the Chevereto edition that best suits your needs.
🕹️ [Live demo](https://demo.chevereto.com)
![screen](.github/screen/user-listing-selected.webp)
![screen](.github/screen/user-profile.jpeg)
## Requirements
* A [webserver](https://v4-docs.chevereto.com/application/stack/web-server.html) (Apache recommended)
* [PHP](https://v4-docs.chevereto.com/application/stack/php.html) 8.1+ with [extensions](https://v4-docs.chevereto.com/application/stack/php.html#extensions)
* [MySQL Server](https://v4-docs.chevereto.com/application/stack/mysql-server.html) 8.0.1+ or MariaDB Server 10.2.2+
## Install
@ -60,280 +63,6 @@ For Chevereto V2 users:
Chevereto [Documentation](https://v4-docs.chevereto.com) covers the system requirements, installation, configuration, and usage of the software. It also includes a [User manual](https://v4-user.chevereto.com/) and an [Admin manual](https://v4-admin.chevereto.com/).
## Features
This is a short, not exhaustive, list of features available on Chevereto editions. Feel free to request a demo of the pro edition at [chevereto.com](https://chevereto.com) (free of any charge, no payment required) to see all the features in action.
## Files supported
Upload and share the following media types from device file browser, drag and drop, on-the-fly device camera, clipboard, URL, ShareX and via API.
* image/avif
* image/jpeg
* image/gif
* image/png
* image/webp
* image/bmp
* video/quicktime
* video/mp4
* video/webm
### Uploading features
* Image & Video uploads
* AVIF JPEG PNG BMP GIF WEBP MOV MP4 WEBM
* API uploading (ShareX, etc)
* Equirectangular 360° images
* EXIF data (read, strip)
* Clipboard upload
* Drag-and-drop upload (drop zone)
* File delete link
* Time-based expirable uploads
* Generate thumbs & medium sized images
* Generate video frame image
* Duplicate media detection
* Auto file-naming options
* Storage modes (date, direct)
* Upload user interface (container, page)
* Upload plugin (PUP.js, postMessage)
* Bulk importer
| Uploading features | Free | Lite | Pro |
| ---------------------------- | :---: | :---: | :------------------: |
| Upload moderation | | ✔ | ✔ |
| Watermark image uploads | | | ✔ |
| Asset storage API | Local | Local | Any |
| External storage servers API | Local | Local | All (S3, SFTP, etc.) |
### External storage APIs
| Storage API | Free | Lite | Pro |
| --------------------- | :---: | :---: | :---: |
| Amazon S3 | | | ✔ |
| S3 compatible | | | ✔ |
| Google Cloud Storage | | | ✔ |
| Microsoft Azure | | | ✔ |
| Alibaba Cloud OSS | | | ✔ |
| SFTP | | | ✔ |
| FTP | | | ✔ |
| OpenStack Swift | | | ✔ |
| Backblaze B2 (legacy) | | | ✔ |
### Content features
* Listing viewer
* Dedicated Media, Album, Tags & Users listings
* Configure items per page
* Listing type (paginated/endless)
* Image listing size (fixed, fluid)
* Configure album listing requirement
* Configure listing columns per device (mobile, tablet, etc.)
### Organization features
* User defined Tags
* Albums & Sub-albums (nested)
* Categories
* Search
* Explore & Discovery
### Tags features
* On-the-fly tag creation
* Tag description
* Tag listings
* Tag filtering (users, albums)
* Tag autocomplete
* Top tags
* Exif camera model auto-tagging
### Album features
* Nested albums (breadcrumbs)
* Album cover image
* Album privacy
* Album password
* Album description
### Sharing features
* Direct link sharing
* Sharing button
* Media oEmbed
* HTML, Markdown & BBCodes
* Embed codes on upload complete
* Embed codes on selected media
* Embed codes media page
### User features
* User profiles
* Private user profiles
* User-based API
| User features | Free | Lite | Pro |
| --------------- | :---: | :-------------------: | :-------------------: |
| Roles available | admin | admin, manager & user | admin, manager & user |
| Multiple users | | ✔ | ✔ |
| User management | | ✔ | ✔ |
| Guest API | | ✔ | ✔ |
### Social features
* Call-to-action album buttons
* Random button
* Notifications
* List users
| Social features | Free | Lite | Pro |
| --------------- | :---: | :---: | :---: |
| Followers | | | ✔ |
| Likes | | | ✔ |
### Security features
* Two-Factor Authentication (2FA)
* Encrypt secrets
* Crypt-salted IDs
| Feature | Free | Lite | Pro |
| ---------- | :---: | :---: | :---: |
| IP banning | | | ✔ |
| Stop words | | | ✔ |
### Admin features
* Album creation on behalf of users
* Dashboard (admin UI)
* System stats & usage
* Website name
* Website doctitle
* Website description
* Website privacy mode (public, private)
* Default timezone
* Uploadable file extensions
* Guest uploads auto delete
* Upload threads
* Upload maximum image size
* Upload Exif removal
* Upload max file size (users and guest)
* Upload path
* Upload file naming
* Upload thumb size
* Upload medium size and dimension
* Semantics
* Default palette
* Default font
* Image load max file size
* Image first tab
* Embed codes (content)
* Custom JS & CSS
* Universal CDN support
* [Default language](https://v4-admin.chevereto.com/settings/languages.html#default-language)
* Logo & branding
* Logo type (vector, image, text)
* Logo height
* Logo favicon image
| Admin features | Free | Lite | Pro |
| --------------------------------------------------------------------------------------------- | :---: | :---: | :---: |
| Homepage style | | ✔ | ✔ |
| Homepage cover images | | ✔ | ✔ |
| Homepage title & paragraph | | ✔ | ✔ |
| Homepage call to action | | ✔ | ✔ |
| Pages | | ✔ | ✔ |
| Lock NSFW editing | | ✔ | ✔ |
| User min age required | | ✔ | ✔ |
| User avatar max file size | | ✔ | ✔ |
| User background max file size | | ✔ | ✔ |
| Guest API key | | ✔ | ✔ |
| Hide "Powered by Chevereto" footer | | | ✔ |
| [Enabled languages](https://v4-admin.chevereto.com/settings/languages.html#enabled-languages) | | | ✔ |
| Routing (user, image, album) | | | ✔ |
| Routing root | | | ✔ |
| External services | | | ✔ |
| Comments API (Disqus, JS) | | | ✔ |
| Analytics code | | | ✔ |
| Akismet spam protection | | | ✔ |
| StopForumSpam spam protection | | | ✔ |
| CAPTCHA (reCAPTCHA, hCaptcha) | | | ✔ |
| Configurable CAPTCHA threshold | | | ✔ |
| Shield by Project Arachnid | | | ✔ |
| ModerateContent (auto approve, block, flag) | | | ✔ |
| OAuth2 login providers (Amazon, Google, Discord, etc) | | | ✔ |
| Banners | | | ✔ |
| Watermark uploads (guest, user, admin) | | | ✔ |
| Watermark file toggles | | | ✔ |
| Watermark size requirement | | | ✔ |
| Watermark custom image | | | ✔ |
| Watermark position | | | ✔ |
| Watermark percentage | | | ✔ |
| Watermark margin | | | ✔ |
| Watermark opacity | | | ✔ |
### Admin toggles
* Search (users and guest)
* Explore (users and guest)
* Random (users and guest)
* NSFW listings
* Blur NSFW content
* NSFW on random mode
* Banners on NSFW
* Uploads (users and guest)
* Uploads (URL)
* Upload moderation
* Upload embed codes
* Upload redirection
* Upload duplication
* Upload expiration
* Upload NSFW checkbox
* Download button
* Right click
* Show Exif data
* Social share buttons
* Automatic updates check
* Dump update query
* Debug errors
| Admin toggles | Free | Lite | Pro |
| ------------------------------------------------------------------------------------------- | :---: | :---: | :---: |
| Consent screen (age gate) | | ✔ | ✔ |
| User sign up | | ✔ | ✔ |
| User content delete | | ✔ | ✔ |
| User notify sign up | | ✔ | ✔ |
| User email confirmation | | ✔ | ✔ |
| User email for social login | | ✔ | ✔ |
| [Auto language](https://v4-admin.chevereto.com/settings/languages.html#auto-language) | | | ✔ |
| [Language chooser](https://v4-admin.chevereto.com/settings/languages.html#language-chooser) | | | ✔ |
| SEO URLs (media and album) | | | ✔ |
| Cookie law compliance | | | ✔ |
| Flood protection | | | ✔ |
| Flood protection notify | | | ✔ |
| Watermarks | | | ✔ |
### System features
* Image handling GD & ImageMagick
* Theme palettes (10)
* One-click upgrade (web & CLI)
* Maintenance mode
* Email SMTP + phpmail()
* Cipher ID
* Test-email
* Export user
* Regenerate external storage stats
* Migrate external storage records
* Docker support
* CLI console
* Built-in debugger ([xrDebug](https://xrdebug.com))
* Built-in REPL (PsySH)
* Supports Tinkerwel REPL
* Queue handling
* Configurable cache TTL
* Hreflang
* Session storage (files, redis)
## Contributing
Chevereto is an open-source project, and while contributions are welcomed, they are entirely voluntary. We appreciate any assistance aimed at enhancing the software and making it better for the community. Please note that any contributions to this repository will fall under the AGPLv3 license, ensuring that your work remains open-source and accessible to all.

View file

@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use function Chevereto\Legacy\getCheveretoEnv;
use function Chevereto\Legacy\loaderHandler;
define('ACCESS', 'web');
@ -16,7 +17,7 @@ define('REPL', true);
require __DIR__ . '/legacy/load/loader.php';
include loaderHandler(
_cookie: [],
_env: $_ENV,
_env: getCheveretoEnv(),
_files: [],
_get: [],
_post: [],
@ -28,8 +29,7 @@ include loaderHandler(
);
return [
'startupMessage' =>
<<<EOM
'startupMessage' => <<<EOM
__ __
____/ / ___ _ _____ _______ / /____
/ __/ _ \/ -_) |/ / -_) __/ -_) __/ _ \\

View file

@ -25,7 +25,7 @@
"jeroendesloovere/xmp-metadata-extractor": "^2.0",
"league/flysystem": "^2.0",
"jenssegers/imagehash": "^0.5.0",
"guzzlehttp/psr7": "^1.7",
"guzzlehttp/psr7": "^1.7||^2",
"phpmailer/phpmailer": "^6.5",
"psr/cache": "^1",
"psr/log": "^1",

199
app/composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "44fd5c1cbd50c51668b3e303c353daea",
"content-hash": "7f3e8782b7fdee03d36b5cdabf54d349",
"packages": [
{
"name": "amphp/amp",
@ -1243,16 +1243,16 @@
},
{
"name": "chevere/trace",
"version": "2.0.0",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/chevere/trace.git",
"reference": "94a01bbd851dece35f1749ee120b6e04699e76ba"
"reference": "56e8ec7bfbb242ca5f986f5c72bffc14c7ec20ec"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chevere/trace/zipball/94a01bbd851dece35f1749ee120b6e04699e76ba",
"reference": "94a01bbd851dece35f1749ee120b6e04699e76ba",
"url": "https://api.github.com/repos/chevere/trace/zipball/56e8ec7bfbb242ca5f986f5c72bffc14c7ec20ec",
"reference": "56e8ec7bfbb242ca5f986f5c72bffc14c7ec20ec",
"shasum": ""
},
"require": {
@ -1285,9 +1285,9 @@
"homepage": "https://chevere.org",
"support": {
"issues": "https://github.com/chevere/trace/issues",
"source": "https://github.com/chevere/trace/tree/2.0.0"
"source": "https://github.com/chevere/trace/tree/2.0.1"
},
"time": "2024-06-03T16:15:25+00:00"
"time": "2024-11-21T13:31:14+00:00"
},
{
"name": "chevere/var-dump",
@ -1295,12 +1295,12 @@
"source": {
"type": "git",
"url": "https://github.com/chevere/var-dump.git",
"reference": "cf6cbd93056ef9eab1d61ae965405d0bb6bdf7a0"
"reference": "ea3145a28d3c69ea6e2d18ba8b4f06d47b876def"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chevere/var-dump/zipball/cf6cbd93056ef9eab1d61ae965405d0bb6bdf7a0",
"reference": "cf6cbd93056ef9eab1d61ae965405d0bb6bdf7a0",
"url": "https://api.github.com/repos/chevere/var-dump/zipball/ea3145a28d3c69ea6e2d18ba8b4f06d47b876def",
"reference": "ea3145a28d3c69ea6e2d18ba8b4f06d47b876def",
"shasum": ""
},
"require": {
@ -1311,7 +1311,7 @@
"php": "^8.1"
},
"require-dev": {
"phpstan/phpstan": "^1.9",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^9.5",
"symplify/easy-coding-standard": "^11.1"
},
@ -1347,7 +1347,7 @@
"issues": "https://github.com/chevere/var-dump/issues",
"source": "https://github.com/chevere/var-dump/tree/2.0"
},
"time": "2024-10-14T14:38:37+00:00"
"time": "2024-11-26T16:20:02+00:00"
},
{
"name": "chevere/var-support",
@ -1655,16 +1655,16 @@
},
{
"name": "composer/ca-bundle",
"version": "1.5.3",
"version": "1.5.4",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "3b1fc3f0be055baa7c6258b1467849c3e8204eb2"
"reference": "bc0593537a463e55cadf45fd938d23b75095b7e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/3b1fc3f0be055baa7c6258b1467849c3e8204eb2",
"reference": "3b1fc3f0be055baa7c6258b1467849c3e8204eb2",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/bc0593537a463e55cadf45fd938d23b75095b7e1",
"reference": "bc0593537a463e55cadf45fd938d23b75095b7e1",
"shasum": ""
},
"require": {
@ -1711,7 +1711,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
"source": "https://github.com/composer/ca-bundle/tree/1.5.3"
"source": "https://github.com/composer/ca-bundle/tree/1.5.4"
},
"funding": [
{
@ -1727,7 +1727,7 @@
"type": "tidelift"
}
],
"time": "2024-11-04T10:15:26+00:00"
"time": "2024-11-27T15:35:25+00:00"
},
{
"name": "evenement/evenement",
@ -1834,16 +1834,16 @@
},
{
"name": "firebase/php-jwt",
"version": "v6.10.1",
"version": "v6.10.2",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
"reference": "500501c2ce893c824c801da135d02661199f60c5"
"reference": "30c19ed0f3264cb660ea496895cfb6ef7ee3653b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5",
"reference": "500501c2ce893c824c801da135d02661199f60c5",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/30c19ed0f3264cb660ea496895cfb6ef7ee3653b",
"reference": "30c19ed0f3264cb660ea496895cfb6ef7ee3653b",
"shasum": ""
},
"require": {
@ -1891,44 +1891,50 @@
],
"support": {
"issues": "https://github.com/firebase/php-jwt/issues",
"source": "https://github.com/firebase/php-jwt/tree/v6.10.1"
"source": "https://github.com/firebase/php-jwt/tree/v6.10.2"
},
"time": "2024-05-18T18:05:11+00:00"
"time": "2024-11-24T11:22:49+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "1.9.1",
"version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b"
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b",
"reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "1.0",
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
"phpunit/phpunit": "^8.5.39 || ^9.6.20"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
@ -1967,6 +1973,11 @@
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
@ -1982,7 +1993,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/1.9.1"
"source": "https://github.com/guzzle/psr7/tree/2.7.0"
},
"funding": [
{
@ -1998,7 +2009,7 @@
"type": "tidelift"
}
],
"time": "2023-04-17T16:00:37+00:00"
"time": "2024-07-18T11:15:46+00:00"
},
{
"name": "intervention/image",
@ -2030,16 +2041,16 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
},
"laravel": {
"providers": [
"Intervention\\Image\\ImageServiceProvider"
],
"aliases": {
"Image": "Intervention\\Image\\Facades\\Image"
}
},
"providers": [
"Intervention\\Image\\ImageServiceProvider"
]
},
"branch-alias": {
"dev-master": "2.4-dev"
}
},
"autoload": {
@ -3202,16 +3213,16 @@
},
{
"name": "phpmailer/phpmailer",
"version": "v6.9.2",
"version": "v6.9.3",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "a7b17b42fa4887c92146243f3d2f4ccb962af17c"
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a7b17b42fa4887c92146243f3d2f4ccb962af17c",
"reference": "a7b17b42fa4887c92146243f3d2f4ccb962af17c",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e",
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e",
"shasum": ""
},
"require": {
@ -3271,7 +3282,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.2"
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3"
},
"funding": [
{
@ -3279,7 +3290,7 @@
"type": "github"
}
],
"time": "2024-10-09T10:07:50+00:00"
"time": "2024-11-24T18:04:13+00:00"
},
{
"name": "phpseclib/bcmath_compat",
@ -3984,12 +3995,12 @@
],
"type": "library",
"extra": {
"branch-alias": {
"dev-0.11": "0.11.x-dev"
},
"bamarni-bin": {
"bin-links": false,
"forward-command": false
},
"branch-alias": {
"dev-0.11": "0.11.x-dev"
}
},
"autoload": {
@ -4472,16 +4483,16 @@
},
{
"name": "react/http",
"version": "v1.10.0",
"version": "v1.11.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/http.git",
"reference": "8111281ee57f22b7194f5dba225e609ba7ce4d20"
"reference": "8db02de41dcca82037367f67a2d4be365b1c4db9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/http/zipball/8111281ee57f22b7194f5dba225e609ba7ce4d20",
"reference": "8111281ee57f22b7194f5dba225e609ba7ce4d20",
"url": "https://api.github.com/repos/reactphp/http/zipball/8db02de41dcca82037367f67a2d4be365b1c4db9",
"reference": "8db02de41dcca82037367f67a2d4be365b1c4db9",
"shasum": ""
},
"require": {
@ -4490,18 +4501,18 @@
"php": ">=5.3.0",
"psr/http-message": "^1.0",
"react/event-loop": "^1.2",
"react/promise": "^3 || ^2.3 || ^1.2.1",
"react/socket": "^1.12",
"react/stream": "^1.2"
"react/promise": "^3.2 || ^2.3 || ^1.2.1",
"react/socket": "^1.16",
"react/stream": "^1.4"
},
"require-dev": {
"clue/http-proxy-react": "^1.8",
"clue/reactphp-ssh-proxy": "^1.4",
"clue/socks-react": "^1.4",
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/async": "^4 || ^3 || ^2",
"react/async": "^4.2 || ^3 || ^2",
"react/promise-stream": "^1.4",
"react/promise-timer": "^1.9"
"react/promise-timer": "^1.11"
},
"type": "library",
"autoload": {
@ -4551,7 +4562,7 @@
],
"support": {
"issues": "https://github.com/reactphp/http/issues",
"source": "https://github.com/reactphp/http/tree/v1.10.0"
"source": "https://github.com/reactphp/http/tree/v1.11.0"
},
"funding": [
{
@ -4559,7 +4570,7 @@
"type": "open_collective"
}
],
"time": "2024-03-27T17:20:46+00:00"
"time": "2024-11-20T15:24:08+00:00"
},
{
"name": "react/promise",
@ -5124,16 +5135,16 @@
},
{
"name": "symfony/cache-contracts",
"version": "v2.5.3",
"version": "v2.5.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
"reference": "fee6db04d913094e2fb55ff8e7db5685a8134463"
"reference": "517c3a3619dadfa6952c4651767fcadffb4df65e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/fee6db04d913094e2fb55ff8e7db5685a8134463",
"reference": "fee6db04d913094e2fb55ff8e7db5685a8134463",
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/517c3a3619dadfa6952c4651767fcadffb4df65e",
"reference": "517c3a3619dadfa6952c4651767fcadffb4df65e",
"shasum": ""
},
"require": {
@ -5183,7 +5194,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/cache-contracts/tree/v2.5.3"
"source": "https://github.com/symfony/cache-contracts/tree/v2.5.4"
},
"funding": [
{
@ -5199,7 +5210,7 @@
"type": "tidelift"
}
],
"time": "2024-01-23T13:51:25+00:00"
"time": "2024-09-25T14:11:13+00:00"
},
{
"name": "symfony/console",
@ -5297,16 +5308,16 @@
},
{
"name": "symfony/deprecation-contracts",
"version": "v3.5.0",
"version": "v3.5.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
"shasum": ""
},
"require": {
@ -5344,7 +5355,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
},
"funding": [
{
@ -5360,7 +5371,7 @@
"type": "tidelift"
}
],
"time": "2024-04-18T09:32:20+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -5899,16 +5910,16 @@
},
{
"name": "symfony/service-contracts",
"version": "v3.5.0",
"version": "v3.5.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f"
"reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
"reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
"shasum": ""
},
"require": {
@ -5962,7 +5973,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/service-contracts/tree/v3.5.0"
"source": "https://github.com/symfony/service-contracts/tree/v3.5.1"
},
"funding": [
{
@ -5978,7 +5989,7 @@
"type": "tidelift"
}
],
"time": "2024-04-18T09:32:20+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/string",
@ -6826,16 +6837,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.12.11",
"version": "1.12.12",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733"
"reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d1fc20a962a91be578bcfe7cf939e6e1a2ff733",
"reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0",
"reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0",
"shasum": ""
},
"require": {
@ -6880,7 +6891,7 @@
"type": "github"
}
],
"time": "2024-11-17T14:08:01+00:00"
"time": "2024-11-28T22:13:23+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -8269,16 +8280,16 @@
},
{
"name": "symplify/easy-coding-standard",
"version": "12.3.6",
"version": "12.4.0",
"source": {
"type": "git",
"url": "https://github.com/easy-coding-standard/easy-coding-standard.git",
"reference": "c0f378782d06dfd21c66c3024e9d28f4e737645e"
"reference": "54d26710fc05c733cbd788a23a709e7815ab0a67"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/c0f378782d06dfd21c66c3024e9d28f4e737645e",
"reference": "c0f378782d06dfd21c66c3024e9d28f4e737645e",
"url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/54d26710fc05c733cbd788a23a709e7815ab0a67",
"reference": "54d26710fc05c733cbd788a23a709e7815ab0a67",
"shasum": ""
},
"require": {
@ -8314,7 +8325,7 @@
],
"support": {
"issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues",
"source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.3.6"
"source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.4.0"
},
"funding": [
{
@ -8326,7 +8337,7 @@
"type": "github"
}
],
"time": "2024-10-06T08:27:28+00:00"
"time": "2024-12-03T12:53:56+00:00"
},
{
"name": "theseer/tokenizer",

View file

@ -9,6 +9,7 @@
* file that was distributed with this source code.
*/
use function Chevereto\Legacy\getCheveretoEnv;
use function Chevereto\Legacy\loaderHandler;
if (PHP_SAPI !== 'cli') {
@ -46,7 +47,7 @@ define('ACCESS', $access);
require_once __DIR__ . '/../load/php-boot.php';
require_once loaderHandler(
$_COOKIE,
$_ENV,
getCheveretoEnv(),
$_FILES,
$_GET,
$_POST,

View file

@ -10,6 +10,7 @@
*/
use function Chevereto\Legacy\G\sanitize_path_slashes;
use function Chevereto\Legacy\getCheveretoEnv;
use function Chevereto\Legacy\loaderHandler;
define('ACCESS', 'web');
@ -35,7 +36,7 @@ if (in_array($urlPath, ['/upgrading', '/upgrading/'], true)
}
require_once loaderHandler(
$_COOKIE,
$_ENV,
getCheveretoEnv(),
$_FILES,
$_GET,
$_POST,

View file

@ -635,6 +635,7 @@ $settings_updates = [
'4.2.1' => null,
'4.2.2' => null,
'4.2.3' => null,
'4.2.4' => null,
];
/**

View file

@ -9,5 +9,5 @@
* file that was distributed with this source code.
*/
const APP_VERSION = '4.2.3';
const APP_VERSION = '4.2.4';
const APP_VERSION_AKA = 'regio';

View file

@ -85,7 +85,10 @@ set_exception_handler(function (Throwable $throwable) {
try {
$debugLevel = Config::system()->debugLevel();
} catch (Throwable) {
$debugLevel = (int) ($_ENV['CHEVERETO_DEBUG_LEVEL'] ?? 1);
$envDebugLevel = getenv('CHEVERETO_DEBUG_LEVEL');
$debugLevel = $envDebugLevel === false
? 1
: (int) $envDebugLevel;
}
$doDebug = in_array($debugLevel, [2, 3], true) || isDebug();
$publicHandler = $publicHandler->withIsDebug($doDebug);

View file

@ -198,9 +198,9 @@ return function (Handler $handler) {
'consent-screen' => _s('Consent screen'),
'users' => _n('User', 'Users', 20),
'guest-api' => _s('Guests %s', 'API'),
'login-providers' => _s('Login providers'),
'routing' => _s('Routing'),
'external-services' => _s('External services'),
'login-providers' => _s('Login providers'),
'cookie-compliance' => _s('Cookie compliance'),
'flood-protection' => _s('Flood protection'),
'banners' => _s('Banners'),
@ -249,7 +249,7 @@ return function (Handler $handler) {
'guest-api' => ['lite', 'CHEVERETO_ENABLE_API_GUEST'],
'homepage' => ['lite', 'CHEVERETO_ENABLE_USERS'],
'ip-bans' => ['pro', 'CHEVERETO_ENABLE_IP_BANS'],
'login-providers' => ['pro', 'CHEVERETO_ENABLE_LOGIN_PROVIDERS'],
'login-providers' => ['lite', 'CHEVERETO_ENABLE_LOGIN_PROVIDERS'],
'pages' => ['lite', 'CHEVERETO_ENABLE_PAGES'],
'routing' => ['pro', 'CHEVERETO_ENABLE_ROUTING'],
'users' => ['lite', 'CHEVERETO_ENABLE_USERS'],

View file

@ -19,6 +19,8 @@ use function Chevereto\Legacy\encodeID;
use function Chevereto\Legacy\flatten_array;
use function Chevereto\Legacy\G\get_current_url;
use function Chevereto\Legacy\G\get_global;
use function Chevereto\Legacy\G\get_route_name;
use function Chevereto\Legacy\G\get_route_path;
use function Chevereto\Legacy\G\redirect;
use function Chevereto\Legacy\G\require_theme_file;
use function Chevereto\Legacy\G\safe_html;
@ -175,7 +177,13 @@ return function (Handler $handler) {
'current' => true,
'url' => '#about',
];
$comments = getComments();
$commentsArgs = [
'url' => $image['url_short'],
'id' => $image['type'] . ':' . $image['id_encoded'],
'title' => $image['title_truncated_html'],
];
// $commentsArgs['id'] = str_replace_first(get_route_path(), get_route_name(), get_route_path(true)); // Legacy fix
$comments = getComments(...$commentsArgs);
if ($comments !== '') {
$tabs[] = [
'icon' => 'fas fa-comments',

View file

@ -1799,7 +1799,7 @@ return function (Handler $handler) {
if (! Login::isAdmin()) {
throw new Exception(_s('Request denied'), 403);
}
$licenseKey = $POST['key'] ?? '';
$licenseKey = trim($POST['key'] ?? '');
if ($licenseKey !== '') {
$check = fetch_url(
url: 'https://chevereto.com/api/license/check',
@ -1822,7 +1822,7 @@ return function (Handler $handler) {
if (version_compare($checkVersion, '4', '>=') === false) {
throw new Exception(
_s(
'Chevereto V%s license key used, required V%r or greater license key',
'Chevereto V%s license key detected. A Chevereto V%r license key is required.',
[
'%s' => $checkVersion,
'%r' => '4',
@ -1832,9 +1832,9 @@ return function (Handler $handler) {
);
}
}
$licenseFile = PATH_APP . 'CHEVERETO_LICENSE_KEY';
touch($licenseFile);
if (file_put_contents($licenseFile, $licenseKey) !== false) {
touch(PATH_APP_LICENSE_KEY);
$licenseContents = "<?php return '{$licenseKey}';";
if (file_put_contents(PATH_APP_LICENSE_KEY, $licenseContents) !== false) {
$json_array['status_code'] = 200;
$licenseAction = $licenseKey === ''
? _s('License key removed')

View file

@ -8,7 +8,7 @@ CREATE TABLE `%table_prefix%tags_albums` (
FOREIGN KEY (tag_album_tag_id) REFERENCES `%table_prefix%tags` (tag_id) ON DELETE CASCADE,
FOREIGN KEY (tag_album_album_id) REFERENCES `%table_prefix%albums` (album_id) ON DELETE CASCADE,
FOREIGN KEY (tag_album_user_id) REFERENCES `%table_prefix%users` (user_id) ON DELETE CASCADE,
UNIQUE INDEX `tag_album_UNIQUE` (`tag_album_tag_id` ASC, `tag_album_album_id` ASC, `tag_album_user_id` ASC) VISIBLE,
UNIQUE INDEX `tag_album_UNIQUE` (`tag_album_tag_id` ASC, `tag_album_album_id` ASC, `tag_album_user_id` ASC),
KEY `tag_album_count` (`tag_album_count`),
KEY `tag_album_last_used_datetime` (`tag_album_last_used_datetime`)
) ENGINE=%table_engine% DEFAULT CHARSET=utf8mb4;

View file

@ -4,5 +4,5 @@ CREATE TABLE `%table_prefix%tags_files` (
`tag_file_file_id` bigint(32) NOT NULL,
FOREIGN KEY (tag_file_tag_id) REFERENCES `%table_prefix%tags` (tag_id) ON DELETE CASCADE,
FOREIGN KEY (tag_file_file_id) REFERENCES `%table_prefix%images` (image_id) ON DELETE CASCADE,
UNIQUE INDEX `tag_file_UNIQUE` (`tag_file_tag_id` ASC, `tag_file_file_id` ASC) VISIBLE
UNIQUE INDEX `tag_file_UNIQUE` (`tag_file_tag_id` ASC, `tag_file_file_id` ASC)
) ENGINE=%table_engine% DEFAULT CHARSET=utf8mb4;

View file

@ -6,7 +6,7 @@ CREATE TABLE `%table_prefix%tags_users` (
`tag_user_last_used_datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (tag_user_tag_id) REFERENCES `%table_prefix%tags` (tag_id) ON DELETE CASCADE,
FOREIGN KEY (tag_user_user_id) REFERENCES `%table_prefix%users` (user_id) ON DELETE CASCADE,
UNIQUE INDEX `tag_user_UNIQUE` (`tag_user_tag_id` ASC, `tag_user_user_id` ASC) VISIBLE,
UNIQUE INDEX `tag_user_UNIQUE` (`tag_user_tag_id` ASC, `tag_user_user_id` ASC),
KEY `tag_user_count` (`tag_user_count`),
KEY `tag_user_last_used_datetime` (`tag_user_last_used_datetime`)
) ENGINE=%table_engine% DEFAULT CHARSET=utf8mb4;

View file

@ -20,12 +20,13 @@ function storeDownloadedUrl(string $url, string $filepath)
{
$clientArgs = [
'base_uri' => $url,
'timeout' => $_ENV['CHEVERETO_HTTP_TIMEOUT'] ?? 30,
// 'timeout' => $_ENV['CHEVERETO_HTTP_TIMEOUT'] ?? 30,
];
// @codeCoverageIgnoreStart
if (isset($_ENV['CHEVERETO_HTTP_PROXY'])) {
$clientArgs['proxy'] = $_ENV['CHEVERETO_HTTP_PROXY'];
}
// if (isset($_ENV['CHEVERETO_HTTP_PROXY'])) {
// $clientArgs['proxy'] = $_ENV['CHEVERETO_HTTP_PROXY'];
// }
// @codeCoverageIgnoreEnd
try {
$httpClient = new Client($clientArgs);

View file

@ -156,7 +156,7 @@ class Album
sessionVar()->put('album_view_stock', $id);
}
public static function getUrl(string $id_encoded, string $title = null): string
public static function getUrl(string $id_encoded, ?string $title = null): string
{
$seo = seoUrlfy($title ?? '');
$url = $seo === ''

View file

@ -92,7 +92,7 @@ class DB extends GDB
array|string $where,
string $clause = 'AND',
array $sort = [],
int $limit = null,
?int $limit = null,
int $fetch_style = PDO::FETCH_ASSOC,
array $valuesOperators = []
): mixed {

View file

@ -291,7 +291,7 @@ class Image
public static function getAlbumSlice(
int $image_id,
int $album_id = null,
?int $album_id = null,
int $padding = 2
): array {
$tables = DB::getTables();

View file

@ -50,7 +50,7 @@ class Page
return [];
}
public static function get(array $values, array $sort = [], int $limit = null): array
public static function get(array $values, array $sort = [], ?int $limit = null): array
{
return [];
}

View file

@ -59,7 +59,7 @@ class Storage
);
}
public static function get(array $values = [], array $sort = [], int $limit = null): array
public static function get(array $values = [], array $sort = [], ?int $limit = null): array
{
$valueOperators = [
'type_chain' => '&',

View file

@ -738,7 +738,7 @@ class User
return preg_replace('#<|>#', '', $name);
}
public static function cleanUnconfirmed(int $limit = null): void
public static function cleanUnconfirmed(?int $limit = null): void
{
$db = DB::getInstance();
$query = 'SELECT * FROM ' . DB::getTable('users') . ' WHERE user_status IN ("awaiting-confirmation", "awaiting-email") AND user_date_gmt <= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 2 DAY) ORDER BY user_id DESC';

View file

@ -84,10 +84,7 @@ class DB
{
if (! isset(self::$instance)) {
throw new LogicException(
message(
'No `%type%` initialized',
s: static::class
)
message('No `%type%` initialized', type: static::class)
);
}
@ -119,7 +116,7 @@ class DB
return self::$dbh->errorInfo();
}
public function bind(mixed $param, mixed $value, int $type = null): void
public function bind(mixed $param, mixed $value, ?int $type = null): void
{
if ($type === null) {
switch (true) {
@ -262,7 +259,7 @@ class DB
array|string $where,
string $clause = 'AND',
array $sort = [],
int $limit = null,
?int $limit = null,
int $fetch_style = PDO::FETCH_ASSOC,
array $valuesOperators = []
): mixed {

View file

@ -292,7 +292,7 @@ class Handler
);
}
public function mapRoute(string $route_name, array $args = null): callable
public function mapRoute(string $route_name, ?array $args = null): callable
{
$this->template = $route_name;
self::$base_request = $route_name;
@ -477,7 +477,7 @@ class Handler
return (bool) preg_match('{index\.php$}', ltrim($this->script_name, '/'));
}
private function loadTemplate(string $template = null): void
private function loadTemplate(?string $template = null): void
{
if ($template !== null) {
$this->template = $template;

View file

@ -22,6 +22,7 @@ use FFMpeg\FFProbe;
use GdImage;
use LogicException;
use Throwable;
use function Chevereto\Legacy\getCheveretoEnv;
use function Chevereto\Vars\env;
use function Chevereto\Vars\server;
use function Safe\curl_exec;
@ -1963,7 +1964,7 @@ function get_app_setting(string $key): mixed
];
$settingEnv = $settingsToEnv[$key] ?? null;
$env = null;
if (isset($settingEnv) && array_key_exists($settingEnv, $_ENV)) {
if (isset($settingEnv) && array_key_exists($settingEnv, getCheveretoEnv())) {
$env = getenv($settingEnv);
if ($env === false) {
$env = null;

View file

@ -26,10 +26,8 @@ use function Chevereto\Legacy\G\array_filter_array;
use function Chevereto\Legacy\G\check_value;
use function Chevereto\Legacy\G\dsq_hmacsha1;
use function Chevereto\Legacy\G\get_base_url;
use function Chevereto\Legacy\G\get_current_url;
use function Chevereto\Legacy\G\get_public_url;
use function Chevereto\Legacy\G\get_route_name;
use function Chevereto\Legacy\G\get_route_path;
use function Chevereto\Legacy\G\get_set_status_header_desc;
use function Chevereto\Legacy\G\json_document_output;
use function Chevereto\Legacy\G\json_prepare;
@ -37,7 +35,6 @@ use function Chevereto\Legacy\G\require_theme_file;
use function Chevereto\Legacy\G\safe_html;
use function Chevereto\Legacy\G\sanitize_path_slashes;
use function Chevereto\Legacy\G\set_status_header;
use function Chevereto\Legacy\G\str_replace_first;
use function Chevereto\Legacy\G\url_to_relative;
use function Chevereto\Vars\cookie;
use function Chevereto\Vars\env;
@ -1069,8 +1066,11 @@ function show_banner($banner, $sfw = true)
echo '<div id="' . $banner . '" class="ad-banner">' . $banner_code . '</div>';
}
}
function getComments(): string
{
function getComments(
string $url,
string $id,
?string $title = null
): string {
$html = '';
switch (getSetting('comments_api')) {
case 'js':
@ -1097,18 +1097,18 @@ function getComments(): string
$hmac = dsq_hmacsha1($message . ' ' . $timestamp, $disqus_secret);
$auth = $message . ' ' . $hmac . ' ' . $timestamp;
}
$html = strtr('<div id="disqus_thread"></div>
$template = '<div id="disqus_thread"></div>
<script>
var disqus_config = function() {
this.page.url = "%page_url";
this.page.identifier = "%page_id";
this.language = "%language_code";
this.page.remote_auth_s3 = "%auth";
this.page.api_key = "%api_key";
this.page.url = "{{ PAGE_URL }}";
this.page.identifier = "{{ PAGE_ID }}";
this.language = "{{ LANGUAGE_CODE }}";
this.page.remote_auth_s3 = "%auth%";
this.page.api_key = "%api_key%";
};
(function() {
var d = document, s = d.createElement("script");
s.src = "//%shortname.disqus.com/embed.js";
s.src = "//%shortname%.disqus.com/embed.js";
s.setAttribute("data-timestamp", +new Date());
(d.head || d.body).appendChild(s);
})();
@ -1118,19 +1118,22 @@ document.addEventListener("paletteChanged", function (e) {
}
});
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>', [
'%page_url' => Handler::var('canonical') ?? get_current_url(removeQs: ['lang'], public: true),
'%page_id' => str_replace_first(get_route_path(), get_route_name(), get_route_path(true)), // image.ID
'%shortname' => getSetting('disqus_shortname'),
'%language_code' => get_language_used()['base'],
'%auth' => $auth ?? null,
'%api_key' => $disqus_public,
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>';
$html = strtr($template, [
'%shortname%' => getSetting('disqus_shortname'),
'%auth%' => $auth ?? null,
'%api_key%' => $disqus_public,
]);
break;
}
return $html;
return strtr($html, [
'{{ PAGE_URL }}' => $url,
'{{ PAGE_ID }}' => $id,
'{{ PAGE_TITLE }}' => $title ?? '',
'{{ LANGUAGE_CODE }}' => get_language_used()['base'],
]);
}
function getThemeLogo(): string
@ -1149,9 +1152,6 @@ function getThemeLogo(): string
function badgePaid(string $edition): string
{
if ($edition === 'lite') {
$edition = 'pro';
}
if (! (bool) env()['CHEVERETO_ENABLE_EXPOSE_PAID_FEATURES']) {
return '';
}
@ -1164,9 +1164,6 @@ function badgePaid(string $edition): string
function linkPaid(string $edition): ?string
{
if ($edition === 'lite') {
$edition = 'pro';
}
if (! (bool) env()['CHEVERETO_ENABLE_EXPOSE_PAID_FEATURES']) {
return null;
}
@ -1179,9 +1176,6 @@ function linkPaid(string $edition): ?string
function inputDisabledPaid(string $edition): string
{
if ($edition === 'lite') {
$edition = 'pro';
}
if (in_array($edition, editionCombo()[env()['CHEVERETO_EDITION']], true)) {
return '';
}

View file

@ -939,7 +939,7 @@ function upload_to_content_images(array $source, string $what): void
}
}
function isSafeToExecute(int $max_execution_time = null, array $options = []): bool
function isSafeToExecute(?int $max_execution_time = null, array $options = []): bool
{
if ($max_execution_time === null) {
$max_execution_time = (int) ini_get('max_execution_time');
@ -1017,6 +1017,15 @@ function isShowEmbedContent(): bool
};
}
function getCheveretoEnv(): array
{
$env = getenv();
return array_filter($env, function ($key) {
return strpos($key, 'CHEVERETO_') === 0;
}, ARRAY_FILTER_USE_KEY);
}
/**
* Process the server context and returns the handler file to load.
*/
@ -1050,6 +1059,7 @@ function loaderHandler(
define('PATH_APP_LEGACY_ROUTES_OVERRIDES', PATH_APP_LEGACY_ROUTES . 'overrides/');
define('PATH_APP_COMPONENTS_LEGACY', PATH_PUBLIC . 'app/src/Components/Legacy/');
define('PATH_APP', PATH_PUBLIC . 'app/');
define('PATH_APP_LICENSE_KEY', PATH_APP . 'CHEVERETO_LICENSE_KEY.php');
define('PATH_APP_LEGACY_INSTALL', PATH_APP_LEGACY . 'install/');
define('PATH_APP_CONTENT', PATH_APP . 'content/');
define('PATH_APP_LANGUAGES', PATH_APP . 'languages/');
@ -1127,7 +1137,13 @@ function loaderHandler(
'session.save_path' => 'CHEVERETO_SESSION_SAVE_PATH',
// 'upload_max_filesize' => 'CHEVERETO_MAX_UPLOAD_SIZE', // INI_PERDIR
];
$iniToSkip = [
'max_execution_time' => PHP_SAPI === 'cli',
];
foreach ($iniToChevereto as $iniOption => $envName) {
if (($iniToSkip[$iniOption] ?? false) === true) {
continue;
}
if (! function_exists('ini_get')
|| ! function_exists('ini_set')
) {
@ -1169,19 +1185,17 @@ function loaderHandler(
$envVar['CHEVERETO_XRDEBUG_HOST'] = 'host.docker.internal';
}
}
foreach ($envVar as &$envValue) {
if (is_string($envValue)) {
continue;
}
try {
$envValue = (string) $envValue;
} catch (ErrorException) {
$type = getType($envValue);
$envValue = match ($type) {
'array' => '[]',
default => '',
};
foreach ($envVar as $envName => &$envValue) {
if (! is_string($envValue)) {
try {
$envValue = (string) $envValue;
} catch (ErrorException) {
$type = getType($envValue);
$envValue = match ($type) {
'array' => '[]',
default => '',
};
}
}
}
new EnvVar($envVar);
@ -1425,8 +1439,13 @@ function feedbackStep(string $doing, string $target)
function isDebug(): bool
{
$environment = getenv('CHEVERETO_ENVIRONMENT');
$debugLevel = $environment === false
? ''
: $environment;
try {
return ($_ENV['CHEVERETO_ENVIRONMENT'] ?? '') === 'dev'
return $debugLevel === 'dev'
|| (getSetting('debug_errors') && Login::isAdmin());
} catch (Throwable) {
return false;
@ -1507,9 +1526,9 @@ function adjustBrightness(string $hexCode, float $adjustPercent)
function getLicenseKey(): string
{
$licenseKey = env()['CHEVERETO_LICENSE_KEY'] ?? '';
$licenseFile = PATH_APP . 'CHEVERETO_LICENSE_KEY';
if ($licenseKey === '' && file_exists($licenseFile)) {
$licenseKey = file_get_contents($licenseFile);
if ($licenseKey === '' && file_exists(PATH_APP_LICENSE_KEY)) {
/** @var string $licenseKey */
$licenseKey = require PATH_APP_LICENSE_KEY;
}
return $licenseKey;

View file

@ -24,6 +24,11 @@ final class EnvVar
public const REGEX_KEY = '/^' . self::PREFIX . '[A-Z0-9_]+$/';
public const PUTENV = [
'CHEVERETO_ENVIRONMENT',
'CHEVERETO_DEBUG_LEVEL',
];
/**
* @param array<string, string> $array
*/
@ -40,6 +45,11 @@ final class EnvVar
K: string(self::REGEX_KEY)
)($array);
$this->assertNoInstance();
foreach (self::PUTENV as $putenv) {
if (array_key_exists($putenv, $array)) {
putenv($putenv . '=' . $array[$putenv]);
}
}
static::$array = $array;
static::$map = new Map($array);
}

View file

@ -10,25 +10,22 @@
*/
/*
Download (auto license):
php app/upgrading.php
-
Download (with license):
CHEVERETO_LICENSE_KEY=your_license_key php app/upgrading.php
* .upgrading/upgrading.lock
This setting affects non CLI (HTTP calls only).
It exists when the upgrade has been authorized at dashboard.
-
.upgrading/upgrading.lock
It contains the token for upgrade process, must be checked against request.
* .upgrading/downloading.lock
-
.upgrading/downloading.lock
It exists when the upgrade is downloading the new version.
* .upgrading/extracting.lock
-
.upgrading/extracting.lock
It exists when the upgrade is extracting the new version.
*/
*/
namespace Chevereto;
use Exception;
@ -36,14 +33,14 @@ use RuntimeException;
use stdClass;
use Throwable;
use ZipArchive;
use function Chevere\Filesystem\directoryForPath;
use function Chevereto\Legacy\getCheveretoEnv;
require_once __DIR__ . '/legacy/load/php-boot.php';
const ZIP_BALL = 'https://chevereto.com/api/download/%tag%';
const LOGGER = __DIR__ . '/.upgrading/process.log';
if (!file_exists(LOGGER)) {
if (! file_exists(LOGGER)) {
$loggerDir = dirname(LOGGER);
directoryForPath($loggerDir)->createIfNotExists();
touch(LOGGER);
@ -68,7 +65,14 @@ $logProcess = $workingDir . '/process.log';
$lockUpgrading = $workingDir . '/upgrading.lock';
$lockDownloading = $workingDir . '/downloading.lock';
$lockExtracting = $workingDir . '/extracting.lock';
$upgradingKey = $rootDir . '/app/CHEVERETO_LICENSE_KEY';
$FileKeyLegacy = $rootDir . '/app/CHEVERETO_LICENSE_KEY';
$fileKey = $rootDir . '/app/CHEVERETO_LICENSE_KEY.php';
if (file_exists($FileKeyLegacy)) {
$licenseKeyLegacy = file_get_contents($FileKeyLegacy);
$licenseKeyLegacy = trim($licenseKeyLegacy);
file_put_contents($fileKey, "<?php return '{$licenseKeyLegacy}';");
unlink($FileKeyLegacy);
}
if (PHP_SAPI !== 'cli') {
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
echo <<<HTML
@ -79,10 +83,10 @@ if (PHP_SAPI !== 'cli') {
</script></head><body><pre>
HTML;
}
if (!is_dir($workingDir)) {
if (! is_dir($workingDir)) {
mkdir($workingDir, 0755, true);
}
if (!is_writable($workingDir)) {
if (! is_writable($workingDir)) {
abort('[!] Working dir is not writable', 500);
}
$envFile = __DIR__ . '/env.php';
@ -90,16 +94,16 @@ $env = [];
if (file_exists($envFile)) {
$env = require $envFile;
}
$env = array_merge($_ENV, $_SERVER, $env);
$env = array_merge(getCheveretoEnv(), $_SERVER, $env);
if (($env['CHEVERETO_SERVICING'] ?? null) === 'docker') {
abort('[!] This feature is not available when using Docker', 403);
}
if (!class_exists('ZipArchive')) {
if (! class_exists('ZipArchive')) {
abort('[!] ZipArchive is not available');
}
$licenseKey = $env['CHEVERETO_LICENSE_KEY'] ?? '';
if ($licenseKey === '' && file_exists($upgradingKey)) {
$licenseKey = file_get_contents($upgradingKey);
if ($licenseKey === '' && file_exists($fileKey)) {
$licenseKey = require $fileKey;
}
$return = $_GET['return'] ?? '';
$parseUri = parse_url($_SERVER['REQUEST_URI'] ?? '');
@ -124,26 +128,26 @@ if (PHP_SAPI === 'cli') {
unlinkIfExists($lockDownloading);
unlinkIfExists($lockExtracting);
logger('Locks cleared');
die(0);
exit(0);
}
} else {
$singleStep = false;
$action = (string) ($_GET['action'] ?? '');
$token = (string) ($_GET['token'] ?? '');
if (!file_exists($lockUpgrading)) {
if (! file_exists($lockUpgrading)) {
abort('[!] Upgrade is not expected', 403);
}
$upgradeToken = file_get_contents($lockUpgrading);
if ($upgradeToken === false) {
abort('[!] Invalid token file', 403);
}
if (!hash_equals($upgradeToken, $token)) {
if (! hash_equals($upgradeToken, $token)) {
abort('[!] Invalid token', 403);
}
if (($env['CHEVERETO_CONTEXT'] ?? null) === 'saas') {
abort('[!] Upgrade is not needed on SaaS context', 403);
}
if (!in_array($action, $actions, true)) {
if (! in_array($action, $actions, true)) {
abort('[!] Provide action=download or action=extract', 400);
}
}
@ -187,7 +191,7 @@ if ($singleStep || $action === 'extract') {
if (file_exists($lockExtracting)) {
abort('[!] Extracting is already in progress', 400);
}
if (!file_exists($filePath)) {
if (! file_exists($filePath)) {
abort('[!] Package not downloaded', 400);
}
logger('Lock extracting process');
@ -258,7 +262,7 @@ function curl(string $url, array $curlOpts = []): object
curl_setopt($ch, CURLOPT_USERAGENT, 'Chevereto Upgrade');
$fp = false;
foreach ($curlOpts as $k => $v) {
if (CURLOPT_FILE == $k) {
if ($k == CURLOPT_FILE) {
$fp = $v;
}
curl_setopt($ch, $k, $v);
@ -279,7 +283,7 @@ function curl(string $url, array $curlOpts = []): object
} else {
$return->raw = $file_get_contents;
}
if (false !== strpos($transfer['content_type'], 'application/json')) {
if (strpos($transfer['content_type'], 'application/json') !== false) {
$return->json = json_decode($return->raw);
if (is_resource($fp)) {
$meta_data = stream_get_meta_data($fp);
@ -287,7 +291,7 @@ function curl(string $url, array $curlOpts = []): object
}
}
$code = $transfer['http_code'];
if (200 != $code && !isset($return->json)) {
if ($code != 200 && ! isset($return->json)) {
$return->json = new stdClass();
$return->json->error = new stdClass();
$return->json->error->message = 'Error performing HTTP request';
@ -300,11 +304,11 @@ function curl(string $url, array $curlOpts = []): object
function getFormatBytes($bytes, int $round = 1): string
{
if (!is_numeric($bytes)) {
if (! is_numeric($bytes)) {
return (string) $bytes;
}
if ($bytes < 1000) {
return "$bytes B";
return "{$bytes} B";
}
$units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
foreach ($units as $k => $v) {
@ -313,7 +317,7 @@ function getFormatBytes($bytes, int $round = 1): string
if ($bytes < $threshold) {
$size = round($bytes / $multiplier, $round);
return "$size $v";
return "{$size} {$v}";
}
}
}
@ -331,7 +335,7 @@ function getBytesToMb($bytes, int $round = 2): float
function downloadFile(string $url, array $params, string $filePath, bool $post = true): object
{
$fp = fopen($filePath, 'wb+');
if (!$fp) {
if (! $fp) {
throw new Exception("Can't open temp file " . $filePath . ' (wb+)');
}
$ops = [
@ -389,13 +393,13 @@ function downloadAction(string $workingDir, array $params): Response
function extractAction(string $pathTo, string $filePath): Response
{
if (!file_exists($pathTo) && !mkdir($pathTo)) {
if (! file_exists($pathTo) && ! mkdir($pathTo)) {
throw new Exception(sprintf("Working path %s doesn't exists and can't be created", $pathTo), 500);
}
if (!is_readable($pathTo)) {
if (! is_readable($pathTo)) {
throw new Exception(sprintf('Working path %s is not readable', $pathTo), 500);
}
if (!is_readable($filePath)) {
if (! is_readable($filePath)) {
throw new Exception(sprintf("Can't read %s", basename($filePath)), 500);
}
$zip = new ZipArchive();
@ -410,15 +414,18 @@ function extractAction(string $pathTo, string $filePath): Response
}
$numFiles = $zip->numFiles - 1;
$extraction = $zip->extractTo($pathTo);
if (!$extraction) {
throw new Exception("Unable to extract to");
if (! $extraction) {
throw new Exception('Unable to extract to');
}
$zip->close();
$timeTaken = round(microtime(true) - $timeStart, 2); //
clearstatcache(true, $pathTo);
return new Response(
strtr('Extraction completed for %n files in %ss', ['%n' => $numFiles, '%s' => $timeTaken]),
strtr('Extraction completed for %n files in %ss', [
'%n' => $numFiles,
'%s' => $timeTaken,
]),
[
'numFiles' => $numFiles,
'timeTaken' => $timeTaken,
@ -429,22 +436,22 @@ function extractAction(string $pathTo, string $filePath): Response
function abort(string $message)
{
logger('[ERROR] ' . $message);
die(255);
exit(255);
}
function passthruEnabled(): bool
{
if (!function_exists('passthru')) {
if (! function_exists('passthru')) {
return false;
}
$disabled = explode(',', ini_get('disable_functions'));
return !in_array('passthru', $disabled);
return ! in_array('passthru', $disabled);
}
function unlinkIfExists(string $file): void
{
if (!file_exists($file)) {
if (! file_exists($file)) {
return;
}
unlink($file);

View file

@ -31,7 +31,7 @@ foreach (StorageApis::getEnabled() as $k => $v) {
</select>
</div>
<?php if(!(bool) env()['CHEVERETO_ENABLE_EXTERNAL_STORAGE_PROVIDERS']) { ?>
<div class="input-below">(*) <?php _se('Not available in %s.', 'Chevereto ' . (string) env()['CHEVERETO_EDITION']); ?></div>
<div class="input-below">(*) <?php _se('Not available in %s edition.', '<b>Chevereto ' . ucfirst((string) env()['CHEVERETO_EDITION']) . '</b>'); ?></div>
<?php } ?>
<div class="input-below input-warning red-warning"><?php echo Handler::var('input_errors')['form-storage-api_id'] ?? ''; ?></div>
</div>
@ -192,7 +192,7 @@ foreach (StorageApis::getEnabled() as $k => $v) {
</div>
<div class="input-label">
<label for="form-storage-url">URL</label>
<input type="text" id="form-storage-url" name="form-storage-url" class="text-input" placeholder="<?php _se('Storage URL'); ?>" value="<?php echo Storage::getAPIRegions('s3')['us-east-1']['url']; ?>" required>
<input type="text" id="form-storage-url" name="form-storage-url" class="text-input" placeholder="<?php _se('Storage URL'); ?>" required>
<div class="input-below"><?php _se('Map files in this storage under this URL.'); ?></div>
</div>
<div class="input-label">

View file

@ -154,8 +154,8 @@ foreach ($regions ?? [] as $key => $region) {
<?php } ?>
<hr class="line-separator">
<div class="input-label">
<?php echo badgePaid('pro'); ?><label for="enable_likes"><?php _se('Likes'); ?></label>
<div class="c5 phablet-c1"><select <?php echo inputDisabledPaid('pro'); ?> type="text" name="enable_likes" id="enable_likes" class="text-input" <?php if (getSetting('website_mode') == 'personal') {
<?php echo badgePaid('lite'); ?><label for="enable_likes"><?php _se('Likes'); ?></label>
<div class="c5 phablet-c1"><select <?php echo inputDisabledPaid('lite'); ?> type="text" name="enable_likes" id="enable_likes" class="text-input" <?php if (getSetting('website_mode') == 'personal') {
echo ' disabled';
} ?>>
<?php
@ -165,8 +165,8 @@ foreach ($regions ?? [] as $key => $region) {
<?php personal_mode_warning(); ?>
</div>
<div class="input-label">
<?php echo badgePaid('pro'); ?><label for="enable_followers"><?php _se('Followers'); ?></label>
<div class="c5 phablet-c1"><select <?php echo inputDisabledPaid('pro'); ?> type="text" name="enable_followers" id="enable_followers" class="text-input" <?php if (getSetting('website_mode') == 'personal') {
<?php echo badgePaid('lite'); ?><label for="enable_followers"><?php _se('Followers'); ?></label>
<div class="c5 phablet-c1"><select <?php echo inputDisabledPaid('lite'); ?> type="text" name="enable_followers" id="enable_followers" class="text-input" <?php if (getSetting('website_mode') == 'personal') {
echo ' disabled';
} ?>>
<?php

View file

@ -29,7 +29,7 @@ if (!defined('ACCESS') || !ACCESS) {
"Provide Chevereto license key by assigning the environment variable %e or by creating the %f file containing the license key.",
[
'%e' => '<code class="code font-weight-bold word-break-break-all">CHEVERETO_LICENSE_KEY</code>',
'%f' => '<code class="code font-weight-bold word-break-break-all">' . PATH_APP . 'CHEVERETO_LICENSE_KEY</code>',
'%f' => '<code class="code font-weight-bold word-break-break-all">' . PATH_APP . 'CHEVERETO_LICENSE_KEY.php</code>',
]
); ?></p>
<p><?php _se('You can also set the license key in the textarea below.'); ?></p>