diff options
| author | George Halkiadakis <gchalkiadakis@sklavenitis.co.gr> | 2023-04-25 16:45:03 +0300 |
|---|---|---|
| committer | George Halkiadakis <gchalkiadakis@sklavenitis.co.gr> | 2023-04-25 16:45:03 +0300 |
| commit | b594effb847b86fc6b1dae49d7948c46bba35e58 (patch) | |
| tree | 8c5a090cf8746870521324797762bb0ff14c8ae5 | |
| parent | 65d07ba64d45c77d2db16272710ec5702b34d641 (diff) | |
| download | classroom-b594effb847b86fc6b1dae49d7948c46bba35e58.tar.gz classroom-b594effb847b86fc6b1dae49d7948c46bba35e58.tar.bz2 classroom-b594effb847b86fc6b1dae49d7948c46bba35e58.zip | |
alfa version; uploaded into test server (coder)
33 files changed, 745 insertions, 908 deletions
diff --git a/composer.json b/composer.json index dd8061e..22b1d93 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "require": { - "phpmailer/phpmailer": "^6.8" + "phpmailer/phpmailer": "^6.8", + "erusev/parsedown": "^1.7" }, "autoload": { "classmap": [ diff --git a/composer.lock b/composer.lock index b7c2f98..a48cbdf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,368 +4,33 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b3dacac13073b78ee8cc0715dbe88e27", + "content-hash": "da78e21983526d7429f27126efb61469", "packages": [ { - "name": "guzzlehttp/guzzle", - "version": "7.5.0", + "name": "erusev/parsedown", + "version": "1.7.4", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + "url": "https://github.com/erusev/parsedown.git", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3", + "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3", "shasum": "" }, "require": { - "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", - "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "provide": { - "psr/http-client-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", - "phpunit/phpunit": "^8.5.29 || ^9.5.23", - "psr/log": "^1.1 || ^2.0 || ^3.0" - }, - "suggest": { - "ext-curl": "Required for CURL handler support", - "ext-intl": "Required for Internationalized Domain Name (IDN) support", - "psr/log": "Required for using the Log middleware" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Jeremy Lindblom", - "email": "jeremeamia@gmail.com", - "homepage": "https://github.com/jeremeamia" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle is a PHP HTTP client library", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "psr-18", - "psr-7", - "rest", - "web service" - ], - "support": { - "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", - "type": "tidelift" - } - ], - "time": "2022-08-28T15:39:27+00:00" - }, - { - "name": "guzzlehttp/promises", - "version": "1.5.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "GuzzleHttp\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - } - ], - "description": "Guzzle promises library", - "keywords": [ - "promise" - ], - "support": { - "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", - "type": "tidelift" - } - ], - "time": "2022-08-28T14:55:35+00:00" - }, - { - "name": "guzzlehttp/psr7", - "version": "2.4.4", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.1", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.29 || ^9.5.23" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "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", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.4" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-03-09T13:19:02+00:00" - }, - { - "name": "mailjet/mailjet-apiv3-php", - "version": "v1.5.8", - "source": { - "type": "git", - "url": "https://github.com/mailjet/mailjet-apiv3-php.git", - "reference": "747518ce0eebf64d27e9903441a255c85472a139" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mailjet/mailjet-apiv3-php/zipball/747518ce0eebf64d27e9903441a255c85472a139", - "reference": "747518ce0eebf64d27e9903441a255c85472a139", - "shasum": "" - }, - "require": { - "ext-json": "*", - "guzzlehttp/guzzle": "^7.4.4", - "php": "^7.2|^8.0", - "psr/http-client": "^1.0" + "ext-mbstring": "*", + "php": ">=5.3.0" }, "require-dev": { - "mockery/mockery": "^1.4", - "php-coveralls/php-coveralls": "^2.0", - "phpunit/phpunit": "^8|^9" + "phpunit/phpunit": "^4.8.35" }, "type": "library", "autoload": { "psr-0": { - "Mailjet": "src/" + "Parsedown": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -374,25 +39,22 @@ ], "authors": [ { - "name": "Mailjet", - "email": "dev@mailjet.com", - "homepage": "https://dev.mailjet.com" + "name": "Emanuil Rusev", + "email": "hello@erusev.com", + "homepage": "http://erusev.com" } ], - "description": "PHP wrapper for the Mailjet API", - "homepage": "https://github.com/mailjet/mailjet-apiv3-php/", + "description": "Parser for Markdown.", + "homepage": "http://parsedown.org", "keywords": [ - "Mailjet", - "api", - "email", - "php", - "v3" + "markdown", + "parser" ], "support": { - "issues": "https://github.com/mailjet/mailjet-apiv3-php/issues", - "source": "https://github.com/mailjet/mailjet-apiv3-php/tree/v1.5.8" + "issues": "https://github.com/erusev/parsedown/issues", + "source": "https://github.com/erusev/parsedown/tree/1.7.x" }, - "time": "2022-10-15T16:24:08+00:00" + "time": "2019-12-30T22:54:17+00:00" }, { "name": "phpmailer/phpmailer", @@ -473,277 +135,6 @@ } ], "time": "2023-03-06T14:43:22+00:00" - }, - { - "name": "psr/http-client", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "shasum": "" - }, - "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Client\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", - "keywords": [ - "http", - "http-client", - "psr", - "psr-18" - ], - "support": { - "source": "https://github.com/php-fig/http-client/tree/master" - }, - "time": "2020-06-29T06:28:15+00:00" - }, - { - "name": "psr/http-factory", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "shasum": "" - }, - "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" - }, - "time": "2019-04-30T12:38:16+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/master" - }, - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.2.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-03-01T10:25:55+00:00" } ], "packages-dev": [], diff --git a/core/cli/cli-cache.php b/core/cli/cli-cache.php new file mode 100644 index 0000000..57d8090 --- /dev/null +++ b/core/cli/cli-cache.php @@ -0,0 +1,65 @@ +#!/usr/local/bin/php +<?php + +/** cli-cache + * regenerates/refreshes caches. + * ------------------------------------------------------ + * + * The sctipt implements a micro-anom application + * that queries directry the project's models. + * + * Advantages vs curl-cache: + * --- + * + Runs out of the web/apache interface; + * + Has less requirements, uses less total resources; + * + Avoids the typical block/waiting/receiving delays; + * + (theoretically) it is a faster implementation + * + * ------------------------------------------------------ + */ +$t0 = microtime(true); // time benchmark + +require_once 'micro/term_utilities.php'; // terminal colors + +echo textColor("\nInterface: ", NORMAL) . textColor(php_sapi_name(), GREEN). "\n"; + + +/** anom-Cli app initialization + * ------------------------------------------------------ + */ +define('MINI_APP_BASE', __DIR__.'/'); +echo "Base directory is ". textColor(MINI_APP_BASE, GREEN) ."\n"; + +require_once MINI_APP_BASE.'/micro/setup.php'; +echo "System is ready;\n"; + + + +try { + # your code goes here ------------------------------------------------------ + + + // Re-create category_products tree Cache + app\controllers\cli\CliContoller::cacheCategoriesTree(); + + $categories = app\controllers\cli\CliContoller::getCategories(); + + + foreach($categories as $key => $category) { + + echo textColor( textWidth($category['Title'], 72), NORMAL); + app\controllers\cli\CliContoller::cacheCategoryByUrl($category['FullFriendlyUrl']); + } + + # end of script ------------------------------------------------------------ + +} catch(Exception $e) { // Basic error handling + echo 'Caught exception: ', textColor($e->getMessage(), ERROR), "\n"; + die(); +} + + +$dt = number_format( microtime(true) - $t0 , 0) ."sec"; + +// echo ending... +echo textColor("\nOperation Completed {$dt}\n\n", NORMAL); diff --git a/core/cli/curl-cache.php b/core/cli/curl-cache.php new file mode 100755 index 0000000..6ab2e93 --- /dev/null +++ b/core/cli/curl-cache.php @@ -0,0 +1,155 @@ +#!/usr/local/bin/php +<?php + +/** curl-cache + * regenerates/refreshes caches + * ------------------------------------------------------ + * + * This scipt makes requests to the project's web API, + * These API calls SHOULD be impemented to cache some + * resource-expensive queries, so in result the script + * shall manage to create cache. + * + * NOTE: + * The script does not implement the anom framerork + * and it is provided for documentation puproses. + * + * ------------------------------------------------------ + */ + +// some defines and an into message +// ----------------------------------------------------------------------------- + +require_once 'micro/term_utilities.php'; // terminal utilites + +define('API_ROOT','http://127.0.0.1:8080/api/'); + +define('BATCH_LENGTH', 3); // see notes later on the script + + + +echo "\033[32mInterface: ". php_sapi_name() ."\033[39m\n\n"; + +$t0 = microtime(true); + +/** supplumentary functions (curl) + * ----------------------------------------------------------------------------- + */ + +/** single Curl get call + * + * @param $call (string): url that needs to be called + * + */ +function singleCurl($call) { + $cSRC = curl_init(); // create curl resource for SouRCe end-point + curl_setopt($cSRC, CURLOPT_URL, API_ROOT.$call); // set url + curl_setopt($cSRC, CURLOPT_RETURNTRANSFER, 1); //return the transfer as a string + $output = curl_exec($cSRC); // $output contains the output string in json format + curl_close($cSRC); // close curl resource to free up system resources + + $json = json_decode( $output ); // decode json to php array + + if (!$json->success) { + echo "\033[91mError reading " . $call ."\n\033[39m"; + return false; + } + + return $json; +} + +/** multiple curl get calls + * + * @param $array: an array of urls to be called + * + */ +function multiCurl($array) { + + $multiCurl = array(); // array of curl handlers + $result = array(); // data to be returned + $mh = curl_multi_init(); // multi handler + $module = array(); // array of module names / 1:1 to $result array + + foreach ($array as $i => $category) { + $module[$i] = $category->ID; // guid (module name) + $fetchURL = API_ROOT .'url/'. $category->FullFriendlyUrl; + $multiCurl[$i] = curl_init(); + curl_setopt($multiCurl[$i], CURLOPT_URL,$fetchURL); + curl_setopt($multiCurl[$i], CURLOPT_HEADER,0); + curl_setopt($multiCurl[$i], CURLOPT_RETURNTRANSFER,1); + curl_multi_add_handle($mh, $multiCurl[$i]); + } + + $index=null; + + do { + curl_multi_exec($mh,$index); + } while($index > 0); + + foreach($multiCurl as $k => $ch) { + $result[$k] = curl_multi_getcontent($ch); + curl_multi_remove_handle($mh, $ch); + } + curl_multi_close($mh); // close multi-curl +} + +// force printing string with specified length +function forceLen($str, $len = 72) { + $space = " "; + for($i = 0 ; $i<$len ; $i++) $space .= "."; + return mb_substr($str.$space, 0, $len-1) ." "; +} + + +/** main procedure + * ----------------------------------------------------------------------------- + */ + +echo "\033[39mReading Tree of product categories...\n"; +$tree = singleCurl('tree'); // ask for categories tree + +if ($tree !==false) echo "\033[32mCategories tree is cached\033[39m\n\n"; + +$categories = singleCurl('category'); + +// request every category to create cache if not exist +// send requests in small batches to avoid enormous server-stress +echo "\033[39mRequesting re-Cache for every category\033[39m\n"; +echo "\033[33mPlease Wait...\n\n"; + +$buffer = []; // buffer array +$counter = 0; // counter to track items in buffer +foreach($categories->result as $category) { + + echo "\033[39m". forceLen($category->Title) ."\033[33m"."Sent.\n\033[39m"; + + $counter++; + + // try several batch lengths to establish the ideal one that produces + // results in a smooth quite fase rthm, without stressing the server; + // so that visitors can navigate the production site white the script + // is running; + // for my testing this number is 3-5. I set the cou 3 items give fast results without stressing the server + // so "if ($counter > 2) { ... " is the smoothest option + if ($counter > 2) { + multiCurl($buffer); + $counter = 0; + $buffer = []; + } + + $buffer[] = $category; + +} + +multiCurl($buffer); // run once more for the last non-filled buffer + +$dt = number_format( microtime(true) - $t0 , 1) ."sec"; + +// echo ending... +echo "\n\033[32mOperation Completed \033[39m({$dt})\n\n"; +echo "\033[39m"; // back to default color; + + +// check +// http://www.idein.it/joomla/14-docker-php-apache-with-crontab +?>
\ No newline at end of file diff --git a/core/cli/info.md b/core/cli/info.md new file mode 100644 index 0000000..39f8c11 --- /dev/null +++ b/core/cli/info.md @@ -0,0 +1,23 @@ +# cli + +This section is used to setup an anom micro-framework for the cli-interface. +The cli interface is commonly used to support and optimize the whole app. + +A cli script may run manualy, for example using... + + php cli-cache.php + +although... + +it is recommended to run periodically using the cron service: + + 15 * * * * php path/to/cli-cache.php + +or ... + +when a docker instanse of the project fires up; for example including +the following line of code inside the docker/bin/docker-entrypoint.sh +(runs the script after 5 minutes delay to make sure that the instance +is up and running) + + at -f /var/www/core/cli/refresh-cache.php -t now +5 minutes diff --git a/docker/config/composer-naked.json b/docker/config/composer-naked.json index 7e0bf82..171c35e 100644 --- a/docker/config/composer-naked.json +++ b/docker/config/composer-naked.json @@ -1,6 +1,7 @@ { "require": { - "phpmailer/phpmailer": "^6.8" + "phpmailer/phpmailer": "^6.8", + "erusev/parsedown": "^1.7" }, "autoload": { "classmap": [ diff --git a/docker/config/composer-redis.json b/docker/config/composer-redis.json index 606c658..facb782 100644 --- a/docker/config/composer-redis.json +++ b/docker/config/composer-redis.json @@ -1,6 +1,8 @@ { "require": { - "predis/predis": "^2.0" + "predis/predis": "^2.0", + "phpmailer/phpmailer": "^6.8", + "erusev/parsedown": "^1.7" }, "autoload": { "classmap": [ diff --git a/public/app/config/.gitkeep b/public/app/config/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/app/config/.gitkeep diff --git a/public/app/controllers/.gitkeep b/public/app/controllers/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/app/controllers/.gitkeep diff --git a/public/app/controllers/Auth.php b/public/app/controllers/Auth.php index 0570a5e..8af7513 100644 --- a/public/app/controllers/Auth.php +++ b/public/app/controllers/Auth.php @@ -180,7 +180,11 @@ class Auth { } - + /** is_connected + * checks if the user is connected + * + * @return true|false + */ public static function is_connected() { $manager = new Classroom_manager(); @@ -193,13 +197,17 @@ class Auth { return $user; } else { - echo 'user is not connected'; + // user is not connected; return false; } } - + /** logout + * + * performs a secure logout; + * regenerates session; deletes cookies; + */ public static function logout() { $userManager = new Classroom_manager(); @@ -232,7 +240,7 @@ class Auth { return true; // permision 0 is always granted } - if ($user = $this->is_connected() === flase) { // if not connected + if ($user = self::is_connected() === false) { // if not connected return false; // then no other permition is granted } @@ -340,6 +348,18 @@ class Auth { } + /** hasValidRole + * like allowRoles() but dowes not stop execution + * + * @return true|false + */ + public static function hasValidRole($allowed) + { + $manager = new Classroom_manager(); + return ($manager->isGranted($allowed)); + } + + public static function in_admin_group() { $manager = new Classroom_manager(); diff --git a/public/app/controllers/admin/Course_admin.php b/public/app/controllers/admin/Course_admin.php index c2e06f2..72ddc79 100644 --- a/public/app/controllers/admin/Course_admin.php +++ b/public/app/controllers/admin/Course_admin.php @@ -213,8 +213,10 @@ class Course_admin { ] ); - // update media's privileges - self::update_medias_privileges($post['media'], $post['privilege_id']); + if (isset($post['media'])) { + // update media's privileges + self::update_medias_privileges($post['media'], $post['privilege_id']); + } // remove all old lesson's links to media files Registry::use('database')->runQuery( @@ -222,8 +224,10 @@ class Course_admin { [ ':lesson' => $post['id'] ] ); - // update lesson's media files - self::create_medias_for_lesson($post['media'], $post['id'], $post['privilege_id']); + if (isset($post['media'])) { + // update lesson's media files + self::create_medias_for_lesson($post['media'], $post['id'], $post['privilege_id']); + } return true; } diff --git a/public/app/controllers/cli/CliContoller.php b/public/app/controllers/cli/CliContoller.php index 310e2ae..4549b50 100644 --- a/public/app/controllers/cli/CliContoller.php +++ b/public/app/controllers/cli/CliContoller.php @@ -4,9 +4,11 @@ namespace app\controllers\cli; /** CliContoller * - * Re-generate cashes of commonly used entities - * like: product categories and trendy products - * (proxy calls make use of PROXY_IGNORE_CACHE) + * A typical cli constoller; this one is used to + * re-generate cashes of commonly used entities + * with expensive queries like (ex:) categories; + * + * Proxy calls make use of PROXY_IGNORE_CACHE * * CliController calls Model methods who create * cashed data with the exact same arguments as @@ -14,8 +16,8 @@ namespace app\controllers\cli; * the Model::method(arguments) triplete creates * the same keys as via the web interface. * - * NOTE: Only Cli interface is allowed - * [if (php_sapi_name() != 'cli') return false;] + * NOTE: Only Cli interface is allowed¨ + * `if (php_sapi_name() != 'cli') return false;` * * The Cli-interfaced script can run manualy or * (most usual scenario) scheduled via a cron job @@ -37,12 +39,12 @@ class CliContoller if (php_sapi_name() != 'cli') return false; # PROXY call: - # MARKET \ ProductCategories_model::tree(): + # CMS \ Course_model::courses_struct(): # + Refresh Cache # -------------------------------------- echo textColor("Creating category_products tree Cache ... ", \NORMAL); $reply = proxy( - [\app\models\market\ProductCategories_model::class, 'tree'], + [\app\models\cms\Course_model::class, 'courses_struct'], [], \CACHE_ROOT_TTL, \PROXY_IGNORE_CACHE ); @@ -50,36 +52,4 @@ class CliContoller } - - public static function getProductCategories() - { - return \Registry::use('database')->runQuery( - "SELECT * FROM product_categories", - [] - ); - } - - - - /** (re-) cacheCategoryByUrl( url ) - * regenerate cache of a category_product - * by category's friendly url - * @param $url (string): the category's friendly url - */ - public static function cacheCategoryByUrl(string $url) - { - // Only Cli interface is allowed - if (php_sapi_name() != 'cli') return false; - - # PROXY call: - # MARKET \ ProductCategories_model::tree(): - # + Refresh Cache - # -------------------------------------- - $reply = proxy( - [\app\models\market\ProductCategories_model::class, 'categoryFromUrl'], - [ $url ], \CACHE_CATEGORY_TTL, - \PROXY_IGNORE_CACHE - ); - echo ($reply == false) ? textColor("Failed\n", \FAIL) : textColor("Done!\n", \SUCCESS); - } }
\ No newline at end of file diff --git a/public/app/controllers/cms/Course.php b/public/app/controllers/cms/Course.php index 7e53d3a..0edc832 100644 --- a/public/app/controllers/cms/Course.php +++ b/public/app/controllers/cms/Course.php @@ -21,7 +21,7 @@ class Course { * shortcut method for checking access authorization * * returns true if user belogns to the admin group - * or has the specified permition/privilege + * OR has the specified permition/privilege * * @param $privilege_id (int) * @@ -35,7 +35,7 @@ class Course { { return ( Auth::in_admin_group() - || Auth::has_Permition([ $privilege_id ]) + || Auth::hasPermition([ $privilege_id ]) ); } @@ -99,24 +99,32 @@ class Course { * ------------------------------------------------------------------------- */ + /** course + * prepare and render the course view + * + * @param $id : course id + */ + public static function course($id) + { + $id = intval($id); // course id - public static function course($id) - { + // collect all data needed for course view + // --- -- -- - - - $cache = Cache_service::courses_struct(); $tree = $cache['tree']; $breadcrumbs = $cache['breadcrumbs']; $lessons = Course_model::lessons_of_course(intval($id)); - $id = intval($id); - - // print_r($breadcrumbs); die(); // find parent_ids of current category (to open the menu tree) + // --- $course_path = []; foreach($breadcrumbs[$id]['parents'] as $key => $parent) { $course_path[] = intval($parent['id']); } - $course_path[] = intval($id); + $course_path[] = $id; + // then Render + // --- -- -- - - - Render::view('templates/course', [ 'id' => $id, 'title' => $breadcrumbs[ $id ]['rec']['label'], @@ -125,19 +133,73 @@ class Course { 'lessons' => $lessons, 'course_path' => $course_path ]); - } + } + /** LESSONS + * ------------------------------------------------------------------------ + */ + /** lesson + * + * check if user is authorized to view the content; + * if so, prepare and render the lesson view + * + * @param $id : lesson id + */ + public static function lesson($id) + { + $id = intval($id); // lesson id - /** LESSONS - * ------------------------------------------------------------------------ - */ + $lesson = Course_model::lesson($id); // get lesson + $course_id = $lesson['course_id']; // get course id + $pri_id = $lesson['privilege_id']; // privilege needed + if (self::is_admin_or_permited($pri_id)) { + // collect all data needed for course view + // --- -- -- - - - + $cache = Cache_service::courses_struct(); + $tree = $cache['tree']; + $breadcrumbs = $cache['breadcrumbs']; + + // find parent_ids of current category (to open the menu tree) + // --- + $course_path = []; + foreach($breadcrumbs[$course_id]['parents'] as $key => $parent) { + $course_path[] = intval($parent['id']); + } + $course_path[] = intval($course_id); + + // then Render + // --- -- -- - - - + Render::view('templates/lesson', [ + // main content + 'id' => $id, + 'course_id' => $course_id, + 'title' => $lesson['title'], + 'lesson' => $lesson, + // needed for side panel and breadcrunbs + 'categories' => $tree, + 'breadcrumbs' => $breadcrumbs, + 'course_path' => $course_path + ]); + + } else { // user is not authorized + Render::view('error/general', [ + 'title' => 'Δεν έχετε πρόσβαση', + 'message' => 'Θα πρέπει να αγοράσετε το πακέτο πρόσβασης ' + . $pri_id + .' για να δείτε το περιεχόμενο! + <br><br> + Αγόρασε τώρα το <button class="btn">πακέτο πρόσβασης '. $pri_id .'</button>' + ]); + } + + } diff --git a/public/app/extends/.gitkeep b/public/app/extends/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/app/extends/.gitkeep diff --git a/public/app/extends/Cache_service.php b/public/app/extends/Cache_service.php index e44cc8e..9aac1d0 100644 --- a/public/app/extends/Cache_service.php +++ b/public/app/extends/Cache_service.php @@ -10,15 +10,19 @@ use app\models\admin\Privilege_model; * class that gathers common source-expensive queries * and helps for performance optimization * + * when forcing cache creation the following option is used: + * $options = PROXY_IGNORE_CACHE + * */ class Cache_service { public static function courses_struct( $options = 0 ) { - return proxy( // cache-get categories + return proxy( // cache-get courses [\app\models\cms\Course_model::class, 'courses_struct'], - [], CACHE_ROOT_TTL, $options + [], CACHE_ROOT_TTL, + $options ); } @@ -26,7 +30,8 @@ class Cache_service { return proxy( [\app\models\cms\Course_model::class, 'files'], - [], CACHE_ROOT_TTL, $options + [], CACHE_ROOT_TTL, + $options ); } @@ -35,11 +40,10 @@ class Cache_service { return proxy( [\app\models\admin\Privilege_model::class, 'privileges'], - [], CACHE_ROOT_TTL, $options + [], CACHE_ROOT_TTL, + $options ); } - - }
\ No newline at end of file diff --git a/public/app/models/.gitkeep b/public/app/models/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/app/models/.gitkeep diff --git a/public/app/models/cms/Course_model.php b/public/app/models/cms/Course_model.php index d8a8a0e..6378226 100644 --- a/public/app/models/cms/Course_model.php +++ b/public/app/models/cms/Course_model.php @@ -244,9 +244,8 @@ class Course_model { /** lessons of course * - * @param $course_id + * @param $id (int) : course_id */ - public static function lessons_of_course($id) { // TODO: order results in some way @@ -254,11 +253,28 @@ class Course_model { return Registry::use('database')->runQuery( "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id - WHERE lesson.status = 1 AND lesson.course_id = :id", + WHERE lesson.status = 1 AND lesson.course_id = :id + ORDER BY lesson_privilege.privilege_id ASC", [':id' => $id] ); } + /** lesson + * + * @param $id (int) : lesson id + */ + public static function lesson($id) + { + // TODO: order results in some way + + return Registry::use('database')->query( + "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson + LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id + WHERE lesson.status = 1 AND lesson.id = :id", + [':id' => $id] + )->getFirst(); + } + /** files diff --git a/public/app/routes/.gitkeep b/public/app/routes/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/app/routes/.gitkeep diff --git a/public/app/routes/backend.php b/public/app/routes/backend.php index 475f52b..ed02295 100644 --- a/public/app/routes/backend.php +++ b/public/app/routes/backend.php @@ -5,17 +5,25 @@ use app\controllers\admin\Course_admin; use app\models\admin\Privilege_model; use app\extends\Cache_service; + +/** admin pages + * ----------------------------------------------------------------------------- + * NOTE: + * All adminitration routes MUST INCLUDE an Auth::allowRoles() check + */ + // admin panel -// --- -Route::add('/admin', function () { +//////////////////////////////////////////////////////////////////////////////// + +Route::add('/admin', function () { // admin panel Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Render::view('admin/skeleton', [ 'title' => 'Διαχείριση Classroom', 'action' => 'panel', ]); }); -Route::add('/admin/panel', function () { - Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles +Route::add('/admin/panel', function () { // admin panel #2 + Auth::allowRoles([1, 2, 3]); Render::view('admin/skeleton', [ 'title' => 'Διαχείριση Classroom', 'action' => 'panel', @@ -24,8 +32,9 @@ Route::add('/admin/panel', function () { // admin lessons -// --- -Route::add('/admin/lessons', function () { +//////////////////////////////////////////////////////////////////////////////// + +Route::add('/admin/lessons', function () { // request admin-lessons page Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Render::view('admin/skeleton', [ 'title' => 'Διαχείριση Μαθημάτων', @@ -34,12 +43,11 @@ Route::add('/admin/lessons', function () { }); -// admin categories (=courses) ///////////////////////////////////////////////// -// ----------------------------------------------------------------------------- +// admin categories (=courses) +//////////////////////////////////////////////////////////////////////////////// -// manage categories page -// --- -Route::add('/admin/categories', function () { + +Route::add('/admin/categories', function () { // manage categories page Auth::allowRoles([1, 2, 3]); Render::view('admin/skeleton', [ 'title' => 'Διαχείριση Κεφαλαίων', @@ -48,37 +56,29 @@ Route::add('/admin/categories', function () { }); -// ajax: get all categories (courses; with breadcrumbs) -// --- -Route::add('/admin/api/categories', function () { + +Route::add('/admin/api/categories', function () { // ajax: get all courses; with breadcrumbs Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Course_admin::breadcrumbs(); }); - -// ajax: add new category (course) -// --- -Route::add('/admin/api/categories/add', function () { - Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles +Route::add('/admin/api/categories/add', function () { // ajax: add new category (course) + Auth::allowRoles([1, 2, 3]); Course_admin::add_course(); }, 'post'); -// ajax: edit category (course) -// --- -Route::add('/admin/api/categories/update', function () { - Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles - Course_admin::update_course(); - }, - 'post' -); +Route::add('/admin/api/categories/update', function () { // ajax: edit category (course) + Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles + Course_admin::update_course(); +}, +'post'); -// admin privileges //////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- +// admin privileges +//////////////////////////////////////////////////////////////////////////////// -// manage privileges page -// --- -Route::add('/admin/privileges', function () { + +Route::add('/admin/privileges', function () { // manage privileges page Auth::allowRoles([1, 2, 3]); Render::view('admin/skeleton', [ 'title' => 'Διαχείριση Δικαιωμάτων Πρόσβασης', @@ -88,7 +88,6 @@ Route::add('/admin/privileges', function () { // ajax: get all privileges (courses; with breadcrumbs) // use proxy (cache results); send as json -// --- Route::add('/admin/api/privileges', function () { Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Render::json( Cache_service::privileges_hierarchy() ); @@ -100,13 +99,12 @@ Route::add('/admin/api/privileges', function () { -// admin lesson //////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- +// admin lesson +//////////////////////////////////////////////////////////////////////////////// -// new lesson // --- -Route::add('/admin/edit_lesson', function () { +Route::add('/admin/edit_lesson', function () { // new lesson form Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Render::view('admin/skeleton', [ 'title' => 'Νέο Μάθημα', @@ -114,10 +112,8 @@ Route::add('/admin/edit_lesson', function () { ]); }); -// edit lesson (give lesson-id) -// --- -Route::add('/admin/edit_lesson/([0-9]*)', function ($id) { - Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles +Route::add('/admin/edit_lesson/([0-9]*)', function ($id) { // edit lesson form + Auth::allowRoles([1, 2, 3]); Render::view('admin/skeleton', [ 'title' => 'Επεξεργασία Μαθήματος', 'action' => 'edit_lesson', @@ -125,39 +121,35 @@ Route::add('/admin/edit_lesson/([0-9]*)', function ($id) { ]); }); -// ajax: get lesson -// --- -Route::add('/admin/api/lesson/([0-9]*)', function ($id) { + +Route::add('/admin/api/lesson/([0-9]*)', function ($id) { // ajax: get lesson Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Course_admin::get_lesson($id); }); -// ajax: add lesson -// --- -Route::add('/admin/api/lesson/add', function () { + +Route::add('/admin/api/lesson/add', function () { // ajax: add lesson Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Course_admin::add_lesson(); }, 'post'); -// ajax: update lesson -// --- -Route::add('/admin/api/lesson/update', function () { + +Route::add('/admin/api/lesson/update', function () { // ajax: update lesson Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Course_admin::update_lesson(); }, 'post'); -// ajax: get all lessons -// --- -Route::add('/admin/api/lessons', function () { + +Route::add('/admin/api/lessons', function () { // ajax: get all lessons Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles Course_admin::all_lessons(); }); -// admin files ///////////////////////////////////////////////////////////////// +// admin files //////////////////////////////////////////////////////////////////////////////// -Route::add('/admin/files', function () { +Route::add('/admin/files', function () { // admin-files panel Auth::allowRoles([1, 2, 3]); Render::view('admin/skeleton', [ 'title' => 'Διαχείριση Μέσων (media)', @@ -165,10 +157,9 @@ Route::add('/admin/files', function () { ]); }); -// ajax: get lesson -// --- -Route::add('/admin/api/files', function () { - Auth::allowRoles([1, 2, 3]); // allow few admin-panel roles + +Route::add('/admin/api/files', function () { // ajax: get lesson + Auth::allowRoles([1, 2, 3]); Render::json(Course_admin::files()); }); @@ -179,7 +170,3 @@ Route::add('/admin/api/file_upload', function () { Render::json(Course_admin::upload_file()); }, 'post'); - - - - diff --git a/public/app/routes/frontend.php b/public/app/routes/frontend.php index f9e74db..33fe401 100644 --- a/public/app/routes/frontend.php +++ b/public/app/routes/frontend.php @@ -6,22 +6,45 @@ use app\controllers\cms\Course; use app\models\cms\Course_model; use app\extends\Cache_service; + // Home/Welcome -// --- -Route::add('/', function() { // echo 'Welcome!'; }); - Render::view('welcome', [ // cache categories (while retrieving) +//////////////////////////////////////////////////////////////////////////////// + +Route::add('/', function() { + Render::view('welcome', [ 'categories' => Cache_service::courses_struct()['tree'] ]); - // header('Location: /some/default/url', true, 302); + // need redirect?... header('Location: /some/default/url', true, 302); }); -// Error Pages -// --- -Route::notFound( function() { +// Error pages +//////////////////////////////////////////////////////////////////////////////// + +Route::notFound( function() { // 404 error page header("HTTP/1.0 404 Not Found"); Render::view('error/404', ['message' => 'nobody knows it (but you got a secret smile;)']); -}); // 404 +}); + + + +// Common content +//////////////////////////////////////////////////////////////////////////////// + + +Route::add('/course/([0-9]*)', // request course by course-id + function($id) { Course::course($id); } +); + +Route::add('/lesson/([0-9]*)', // request lesson by lesson-id + function($id) { Course::lesson($id); } +); + +Route::add('/serve/file/([0-9a-zA-Z-_\.\/]*)', // serve file by path + function ($path) { Course::serve_file($path); } // (also ?type= is sent) +); + + @@ -42,36 +65,8 @@ Route::notFound( function() { -// Resolve urls -// --- -- -- - - - -Route::add('/course/([0-9]*)', - function($id) { Course::course($id); } -); - -Route::add('/lesson/([0-9a-f]*)/([0-9a-zA-Z-_]*)', - function($ticket, $lesson) { Lesson::show([$lesson, $ticket]); } -); - -Route::add('/media/([0-9a-f]*)/([0-9a-zA-Z-_]*)/([0-9]*)/', - function($ticket, $type, $id) { Media::serve([$type, $id, $ticket]); } -); - - -// 4-th level paths belong to products -// --- -Route::add('/about/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', - function($a, $b, $c) { Page::show([$a, $b, $c]); } -); - - -// serve file -// --- -Route::add('/serve/file/([0-9a-zA-Z-_\.\/]*)', function ($path) { - Course::serve_file($path); -}); - -/** URL-format Proposal: +/** URL-format Proposals and common Route regex solutions * ----------------------------------------------------------------------------- * * Page (almost-static content): @@ -88,80 +83,82 @@ Route::add('/serve/file/([0-9a-zA-Z-_\.\/]*)', function ($path) { * * Category * /{category}/[ {subcategory}[ /{group}]] -Route::add('/([0-9a-zA-Z-_]*)', function($a) { Category::url([$a]); }; -Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b) { Category::url([$a, $b]); }); -Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b, $c) { Category::url([$a, $b, $c]); }); - * ----------------------------------------------------------------------------- - */ - +# Route::add('/([0-9a-zA-Z-_]*)', function($a) { Category::url([$a]); }; +# Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b) { Category::url([$a, $b]); }); +# Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b, $c) { Category::url([$a, $b, $c]); }); + * + * othet routes used in some user-cases -/* EXAMPLES (use it for brainsrtorming) +# Route::add('/lesson/([0-9a-f]*)/([0-9a-zA-Z-_]*)', +# function($ticket, $lesson) { Lesson::show([$lesson, $ticket]); } +# ); +# +# Route::add('/media/([0-9a-f]*)/([0-9a-zA-Z-_]*)/([0-9]*)/', +# function($ticket, $type, $id) { Media::serve([$type, $id, $ticket]); } +# ); + * + * 4-th level paths belong to products +# Route::add('/about/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', +# function($a, $b, $c) { Page::show([$a, $b, $c]); } +# ); + * + * + * OTHER EXAMPLES (use it for brainsrtorming) * ----------------------------------------------------------------------------- * - // Typical Use - // --- - Route::add('/art', function() { Art::blah(); }); - Route::add('/art/db', function() { Art::fromDatabase(); }); - Route::add('/art/([0-9]*)', function($id) { Art::getInfo($id); }); - - - // Get-Post route example - // --- - Route::add('/contact-form', function() { - echo '<form method="post"><input type="text" name="test" /><input type="submit" value="send" /></form>'; - }, 'get'); + * +# // Typical Use +# // --- +# Route::add('/art', function() { Art::blah(); }); +# Route::add('/art/db', function() { Art::fromDatabase(); }); +# Route::add('/art/([0-9]*)', function($id) { Art::getInfo($id); }); + * + * Get-Post route example +# Route::add('/contact-form', function() { +# echo '<form method="post"><input type="text" name="test" /><input type="submit" value="send" /></form>'; +# }, 'get'); Route::add('/contact-form', function() { echo 'Hey! The form has been sent:<br/>'; print_r($_POST); }, 'post'); - - - // Accept number as parameter - // --- - Route::add('/foo/([0-9]*)/bar', function($var1) { - // echo $var1.' is a number!'; + * + * Accept number as parameter +# Route::add('/foo/([0-9]*)/bar', function($var1) { +# // echo $var1.' is a number!'; echo "{$var1} is a number!"; - }); - - - // About pages - // --- - Route::add('/about/([0-9a-zA-Z-_]*)', function($page) { - InfoPage::render('about/'.page); - }); - - - // handle a request like: /foo/123/bar/3254-lefki-zaxari-marata-1kg - // where first-numeric-part of last-uri-section (3254) is the product number - // --- - Route::add('/foo/([0-9]*)/bar/([0-9]*)-([0-9a-zA-Z-_]*)', function($num, $id, $name) { - echo $num .' is some nomber, id = '. $id .' and label = '. $name; - }); - - - // handle a request like: /zaxares-glykantika/lefki-zaxari-year-2022-45678 - // where last-numeric-part of last-uri-section (45678) is the product number - // --- - Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)-([0-9]*)', - function($category, $subcategory, $group, $label, $id) { - Shop::product([$$category, $subcategory, $group, $label], $id); - }); - - - // last chance to resolve (some friendly url) - // --- - Route::add('/([0-9a-zA-Z-_]*)', function($a) { Resolve::uri([$a]); }); - Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b) { Resolve::uri([$a, $b]); }); - Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b, $c) { Resolve::uri([$a, $b, $c]); }); - - - // Product page - // handle a request like: /pantopoleio/zaxares/lefki-zaxari/lefki-zaxari-year-2022-45678 - // where last-numeric-part of last-uri-section (45678) is the product number - // --- - Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)-([0-9]*)', - function($category, $subcategory, $group, $label, $id) { - Shop::product([$$category, $subcategory, $group, $label], $id); - }); -* ------------------------------------------------------------------------------ -*/ +# }); + * + * About pages +# Route::add('/about/([0-9a-zA-Z-_]*)', function($page) { +# InfoPage::render('about/'.page); +# }); + * + * handle a request like: /foo/123/bar/3254-lefki-zaxari-marata-1kg + * where first-numeric-part of last-uri-section (3254) is the product number +# Route::add('/foo/([0-9]*)/bar/([0-9]*)-([0-9a-zA-Z-_]*)', function($num, $id, $name) { +# echo $num .' is some nomber, id = '. $id .' and label = '. $name; +# }); + * + * handle a request like: /zaxares-glykantika/lefki-zaxari-year-2022-45678 + * where last-numeric-part of last-uri-section (45678) is the product number +# Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)-([0-9]*)', +# function($category, $subcategory, $group, $label, $id) { +# Shop::product([$$category, $subcategory, $group, $label], $id); +# }); + * + * last chance to resolve (some friendly url) +# Route::add('/([0-9a-zA-Z-_]*)', function($a) { Resolve::uri([$a]); }); +# Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b) { Resolve::uri([$a, $b]); }); +# Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)', function($a, $b, $c) { Resolve::uri([$a, $b, $c]); }); + * + * + * Product page + * handle a request like: /pantopoleio/zaxares/lefki-zaxari/lefki-zaxari-year-2022-45678 + * where last-numeric-part of last-uri-section (45678) is the product number +# Route::add('/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)/([0-9a-zA-Z-_]*)-([0-9]*)', +# function($category, $subcategory, $group, $label, $id) { +# Shop::product([$$category, $subcategory, $group, $label], $id); +# }); + * + * ------------------------------------------------------------------------------ + */ diff --git a/public/app/views/.gitkeep b/public/app/views/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/public/app/views/.gitkeep diff --git a/public/app/views/components/breadcrumbs.php b/public/app/views/components/breadcrumbs.php index 90b4af9..59c0fca 100644 --- a/public/app/views/components/breadcrumbs.php +++ b/public/app/views/components/breadcrumbs.php @@ -9,8 +9,8 @@ * @param $parents : array of parent nodes * ----------------------------------------------------------------------------- */ -?> +?> <div class="breadcrumb"> <!-- home --> <a href="/">Classroom</a> diff --git a/public/app/views/components/lessons_list.php b/public/app/views/components/lessons_list.php index 1c59164..46c1f7e 100644 --- a/public/app/views/components/lessons_list.php +++ b/public/app/views/components/lessons_list.php @@ -33,6 +33,10 @@ <a href="/lesson/<?=$post['id']?>"><?=$post['title']?></a> </h3> + <?php if (in_array('level', $fieldset)) : ?> + <p class="getegory">(επίπεδο πρόσβασης <?=$post['privilege_id']?>)</p> + <?php endif; ?> + <?php if (in_array('category', $fieldset)) : ?> <p class="category"><?=$post['course_id']?></p> <?php endif; ?> diff --git a/public/app/views/components/subcategories.php b/public/app/views/components/subcategories.php index 58f53fa..5c05a94 100644 --- a/public/app/views/components/subcategories.php +++ b/public/app/views/components/subcategories.php @@ -2,7 +2,8 @@ <?php if ($breadcrumbs[ $id ]['childs'] != []) : ?> - <h5>Υποκατηγορίες</h5> + <h5>Υποκεφάλαια</h5> + <div class="category-childs"> <?php foreach($breadcrumbs[ $id ]['childs'] as $key => $child) : ?> diff --git a/public/app/views/components/top_bar.php b/public/app/views/components/top_bar.php index 0e6e437..eaffe76 100644 --- a/public/app/views/components/top_bar.php +++ b/public/app/views/components/top_bar.php @@ -1,8 +1,31 @@ +<?php +/** top bar + * ----------------------------------------------------------------------------- + * + * rendrers breadcrumbs and user menu + * + * imported variables + * --- + * @param $id : category_id of last level + * @param $label : category title of last level + * @param $parents : array of parent nodes + * ----------------------------------------------------------------------------- + */ + +// initialize variables needed if not passed +// (needed while requiring breadcrumbs) +if (!isset($id)) { + $id = 0; + $label = ""; + $parents = []; +} + +?> <div class="top-bar"> <?php // breadcrumbs //////////////////////////////////////////////////////////////////////// - Render::template("components/breadcrumbs", [ + Render::view("components/breadcrumbs", [ 'id' => $id, 'label' => $label, 'parents' => $parents diff --git a/public/app/views/error/404.php b/public/app/views/error/404.php index 89c35cc..4bbf083 100644 --- a/public/app/views/error/404.php +++ b/public/app/views/error/404.php @@ -26,6 +26,7 @@ text-align: center; justify-center: center; opacity: .33; } + .btn { font-size: 18px; padding: 4px 12px; } </style> </head> <body> diff --git a/public/app/views/error/general.php b/public/app/views/error/general.php index 74566a4..0a27e56 100644 --- a/public/app/views/error/general.php +++ b/public/app/views/error/general.php @@ -1,10 +1,19 @@ <?php /** general message template + * ----------------------------------------------------------------------------- + * renders an error with custom title and message + * + * passed values: * - * passed: - * @var $title (string): page title - * @var $message (string): message + * optional @var $title (string): title + * optional @var $message (string): message + * + * NOTE: + * if title and/or message are not passed, then defaults are used + * + * ----------------------------------------------------------------------------- */ + ?><!DOCTYPE html> <html lang="en"> <head> @@ -39,6 +48,7 @@ opacity: .33; font-size: .87em } + .btn { font-size: 18px; padding: 4px 12px; } </style> </head> <body> diff --git a/public/app/views/templates/course.php b/public/app/views/templates/course.php index 9d42297..39e177f 100644 --- a/public/app/views/templates/course.php +++ b/public/app/views/templates/course.php @@ -63,7 +63,7 @@ //////////////////////////////////////////////////// Render::view("components/lessons_list", [ 'lessons' => $lessons, - 'fieldset' => ['intro'] // ['intro', 'date'] + 'fieldset' => ['intro', 'level'] // ['intro', 'date'] ]); ?> <?php endif; ?> diff --git a/public/app/views/templates/lesson.php b/public/app/views/templates/lesson.php new file mode 100644 index 0000000..3baafd0 --- /dev/null +++ b/public/app/views/templates/lesson.php @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title><?=SITE_TITLE?> - <?=$title?></title> + + <?php // header includes + //////////////////////////////////////////////////////////////////////// + Render::view('components/header_includes'); + ?> + +</head> +<body class="classroom"> + + <?php // top-bar + //////////////////////////////////////////////////////////////////////// + Render::view('components/top_bar', [ + 'id' => $id, + 'label' => $breadcrumbs[ $course_id ]['rec']['label'], + 'parents' => $breadcrumbs[ $course_id ]['parents'] + ]); + ?> + + +<div class="main-content container"> + <div class="row"> + + <div class="col col-md-5 col-lg-4 d-sm-none d-md-block side-bar"> + + <?php // tree of courses + //////////////////////////////////////////////////////////////////////// + Render::view('components/courses_menu', [ + 'categories' => $categories, + 'open_path' => $course_path + ]); + ?> + + </div> + + <div class="col col-sm-12 col-md-7 col-lg-8 lesson-view"> + + <!-- Lesson Title --> + <h3> + <?=$title?> + </h3> + + <!-- intro --> + <?php if ($lesson['intro'] != "") : ?> + <p class="intro"><?=$lesson['intro']?></p> + <?php endif; ?> + + <!-- body --> + <div class="article-body"> + <?php + + $parsedown = new Parsedown(); + echo $parsedown->text($lesson['body']); + + ?> + </div> + + + </div> + + </div> + </div> + + + <script src="/assets/js/classroom.js"></script> + <script src="/assets/js/lesson-features.js"></script> +</body> +</html> diff --git a/public/assets/css/class.css b/public/assets/css/class.css index a65bfa2..f132241 100644 --- a/public/assets/css/class.css +++ b/public/assets/css/class.css @@ -156,7 +156,7 @@ -.classroom .post img { max-width: 100%; } +.classroom .lesson-view img { max-width: 100%; } @@ -199,7 +199,7 @@ } /* --- post: medias -- */ - +/* DEPRICATED: not needed .classroom .content--wrapper .medias { padding: 2em 0 1em; border-top: 1px solid var(--class-grey-background); @@ -212,6 +212,7 @@ font-size: 12px; color: #999; } +*/ /* --- post: tags --- */ @@ -308,18 +309,20 @@ -.classroom .post h2 { padding-bottom: 0; } +.classroom .lesson-view h2 { padding-bottom: 0; font-size: 24px; } + +.classroom .lesson-view h2 span { float:right; margin-right: -3em;} -.classroom .post h2 span { float:right; margin-right: -3em;} +.classroom .lesson-view pre { padding: 1em; background: #f5f5f6;} -.classroom .post .intro { +.classroom .lesson-view .intro { font-style: italic; font-size: 18px; color: var(--class-blue-dark); padding-bottom: 1em; } -.classroom .post .post--date { +.classroom .lesson-view .post--date { padding: 0; color: var(--class-blue); } diff --git a/public/assets/css/overides.css b/public/assets/css/overides.css index e3d5e1b..c0f6bd9 100644 --- a/public/assets/css/overides.css +++ b/public/assets/css/overides.css @@ -128,4 +128,10 @@ ul.select2-results__options li { font-size: 14px; padding: 4px 8px; } .admin-container .edit-post .post-media ul li:last-child { border-bottom: none ;} .admin-container .edit-post .post-media ul li:hover { background: #eee; } -.edit-post .post-media li .btn-xs { font-size: 14px; width: 24px; height: 24px; padding:0; }
\ No newline at end of file +.edit-post .post-media li .btn-xs { font-size: 14px; width: 24px; height: 24px; padding:0; } + +.classroom .edit-post textarea[name=body] { + min-height:320px; + font-family: Consolas, 'Ubuntu Mono','Courier New', Courier, monospace; + color: #333; + }
\ No newline at end of file diff --git a/public/assets/js/admin/edit_lesson.js b/public/assets/js/admin/edit_lesson.js index 89e464f..fbcfabc 100644 --- a/public/assets/js/admin/edit_lesson.js +++ b/public/assets/js/admin/edit_lesson.js @@ -201,11 +201,14 @@ $(document).ready(function() { $('input[name=id]').val(lesson.id); // id $('input[name=title]').val(lesson.title); // title $('textarea[name=intro]').val(lesson.intro); // intro - $('textarea[name=body]').val(lesson.body); // body - $('input[name=status]').prop( // status - 'checked', - ((lesson.status==1)? true : false)) - .trigger('change'); + $('textarea[name=body]').val(lesson.body); // body + $('select[name=status]').val( + (lesson.status == 0) ? '0' : "one" + ).trigger('change'); + // $('input[name=status]').prop( // status + // 'checked', + // ((lesson.status==1)? true : false)) + // .trigger('change'); // $('#post-info').html('Δημιουργία: '+post.creation_date+'<br>Τελευταία Ενημέρωση: '+ post.update_date); @@ -408,8 +411,8 @@ $(document).ready(function() { // send POST (ajax) request $.post(request, data) .done(function( data ) { - console.log(data); - //window.location.href = '/admin/lessons'; // seems ok; bach to articles + // console.log(data); + window.location.href = '/admin/lessons'; // seems ok; bach to articles }); }); diff --git a/public/assets/js/post-features.js b/public/assets/js/lesson-features.js index ca2928c..369c8f7 100644 --- a/public/assets/js/post-features.js +++ b/public/assets/js/lesson-features.js @@ -1,6 +1,5 @@ -const valid_modals = [ // supported sort-code keys - 'product', - 'store' +const valid_codeblocks = [ // supported sort-code keys + 'youtube' ]; @@ -16,6 +15,20 @@ const symbol_escapes = [ ]; +var youtube_template = ( id => { + return '<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/'+ id +'?rel=0&controls=0" title="YouTube video player" frameborder="0" allow="clipboard-write; encrypted-media; picture-in-picture; web-share" allowfullscreen></iframe>' +}); + +/* + + +<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/6EA-MIYY1bg?controls=0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> + + + +*/ + + /** inject_codeblocks * --- -- -- - - - * @@ -43,17 +56,20 @@ function inject_codeblocks(code) { // parse (every) match // NOTE: // match array has 3 items; example: - // match[0] : `{{store=247; Pasalimani Market}}` - // match[1] : `store=247; Pasalimani Market` + // match[0] : `{{youtube=6EA-MIYY1bg}}` + // match[1] : `youtube=6EA-MIYY1bg` // match[3] : the (source) code string - let blockparts = unescaped_str( match[1] ).split(';'); - let main_unit = blockparts[0].replaceAll(' ','').split('='); - let target = main_unit[0].toLowerCase(); - let id = main_unit[1]; - let label = escaped_str(blockparts[1]); - if (valid_modals.includes(target)) { - let codeblock = `<a href="#" data-toggle="modal" data-target="#view-${target}" data-id="${id}" class="modal-toggler js-get_${target}">${label}</a>`; + let blockparts = unescaped_str( match[1] ).trim().split('='); + let target = blockparts[0].toLowerCase(); + let id = blockparts[1]; + // let label = escaped_str(blockparts[1]); + if (valid_codeblocks.includes(target)) { + + if (target == 'youtube') { + var codeblock = youtube_template(id); + } output = output.replaceAll(match[0], codeblock); + } }); @@ -80,15 +96,15 @@ $(document).ready(function() { /** translate short-codes to code-blocks * ------------------------------------------------------------------------- */ - var content = $('.content .article-body').html(); // get code with shorts - $('.content .article-body').html( inject_codeblocks(content) ); // put code with blocks + var content = $('.main-content .article-body').html(); // get code with shorts + $('.main-content .article-body').html( inject_codeblocks(content) ); // put code with blocks /** modal listeners * ------------------------------------------------------------------------- */ - +/* DEPRICATED: not needed // open store modal listener // --- -- -- - - - $(document).on('click', '.js-get_store', function (e) { @@ -144,5 +160,5 @@ $(document).ready(function() { }); }); - -});
\ No newline at end of file +*/ +}); |
