- Refactored fallback submission logic in `class-event-handler.php` to remove `wp_die`/`exit` calls and use redirects for error handling, enabling proper unit testing. - Implemented meta-data saving (dates, venue, organizer) in the fallback logic using `update_post_meta`. - Updated unit tests (`test-event-management.php`) to remove `markTestIncomplete` calls related to handler errors and uncommented meta assertions. Unit tests for fallback logic now pass. - Added Instructions section and Return to Dashboard button to the event form shortcode (`display_event_form_shortcode`). - Applied basic theme styling classes (`ast-container`, `notice`, `ast-button`) to the event form. - Updated `docs/implementation_plan.md` to reflect completion of tasks 4.1-4.5 and set focus to Task 5. Refs: Task 4.1, 4.2, 4.3, 4.4, 4.5
187 lines
4.9 KiB
PHP
187 lines
4.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @link http://patchwork2.org/
|
|
* @author Ignas Rudaitis <ignas.rudaitis@gmail.com>
|
|
* @copyright 2010-2023 Ignas Rudaitis
|
|
* @license http://www.opensource.org/licenses/mit-license.html
|
|
*/
|
|
namespace Patchwork\CodeManipulation;
|
|
|
|
require __DIR__ . '/CodeManipulation/Source.php';
|
|
require __DIR__ . '/CodeManipulation/Stream.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/Generic.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/CallRerouting.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/CodeManipulation.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/Namespaces.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/RedefinitionOfInternals.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/RedefinitionOfLanguageConstructs.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/ConflictPrevention.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/RedefinitionOfNew.php';
|
|
require __DIR__ . '/CodeManipulation/Actions/Arguments.php';
|
|
|
|
use Patchwork\Exceptions;
|
|
use Patchwork\Config;
|
|
|
|
const OUTPUT_DESTINATION = 'php://memory';
|
|
const OUTPUT_ACCESS_MODE = 'rb+';
|
|
|
|
function transform(Source $s)
|
|
{
|
|
foreach (State::$actions as $action) {
|
|
$action($s);
|
|
}
|
|
}
|
|
|
|
function transformString($code)
|
|
{
|
|
$source = new Source($code);
|
|
transform($source);
|
|
return (string) $source;
|
|
}
|
|
|
|
function transformForEval($code)
|
|
{
|
|
$prefix = "<?php ";
|
|
return substr(transformString($prefix . $code), strlen($prefix));
|
|
}
|
|
|
|
function cacheEnabled()
|
|
{
|
|
$location = Config\getCachePath();
|
|
if ($location === null) {
|
|
return false;
|
|
}
|
|
if (!is_dir($location) || !is_writable($location)) {
|
|
throw new Exceptions\CachePathUnavailable($location);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function getCachedPath($file)
|
|
{
|
|
if (State::$cacheIndexFile === null) {
|
|
$indexPath = Config\getCachePath() . '/index.csv';
|
|
if (file_exists($indexPath)) {
|
|
$table = array_map(
|
|
static function($line) {
|
|
return str_getcsv($line, ',', '"', '\\');
|
|
},
|
|
file($indexPath)
|
|
);
|
|
foreach ($table as $row) {
|
|
list($key, $value) = $row;
|
|
State::$cacheIndex[$key] = $value;
|
|
}
|
|
}
|
|
State::$cacheIndexFile = Stream::fopen($indexPath, 'a', false);
|
|
}
|
|
$hash = md5($file);
|
|
$key = $hash;
|
|
$suffix = 0;
|
|
while (isset(State::$cacheIndex[$key]) && State::$cacheIndex[$key] !== $file) {
|
|
$key = $hash . '_' . $suffix++;
|
|
}
|
|
if (!isset(State::$cacheIndex[$key])) {
|
|
Stream::fwrite(State::$cacheIndexFile, sprintf("%s,\"%s\"\n", $key, $file));
|
|
State::$cacheIndex[$key] = $file;
|
|
}
|
|
return Config\getCachePath() . '/' . $key . '.php';
|
|
}
|
|
|
|
function storeInCache(Source $source)
|
|
{
|
|
$handle = Stream::fopen(getCachedPath($source->file), 'w', false);
|
|
Stream::fwrite($handle, $source);
|
|
Stream::fclose($handle);
|
|
}
|
|
|
|
function availableCached($file)
|
|
{
|
|
if (!cacheEnabled()) {
|
|
return false;
|
|
}
|
|
$cached = getCachedPath($file);
|
|
return file_exists($cached) &&
|
|
filemtime($file) <= filemtime($cached) &&
|
|
Config\getTimestamp() <= filemtime($cached);
|
|
}
|
|
|
|
function internalToCache($file)
|
|
{
|
|
if (!cacheEnabled()) {
|
|
return false;
|
|
}
|
|
return strpos($file, Config\getCachePath() . '/') === 0
|
|
|| strpos($file, Config\getCachePath() . DIRECTORY_SEPARATOR) === 0;
|
|
}
|
|
|
|
|
|
function getContents($file)
|
|
{
|
|
$handle = Stream::fopen($file, 'r', true);
|
|
if ($handle === false) {
|
|
return false;
|
|
}
|
|
$contents = '';
|
|
while (!Stream::feof($handle)) {
|
|
$contents .= Stream::fread($handle, 8192);
|
|
}
|
|
Stream::fclose($handle);
|
|
return $contents;
|
|
}
|
|
|
|
function transformAndOpen($file)
|
|
{
|
|
foreach (State::$importListeners as $listener) {
|
|
$listener($file);
|
|
}
|
|
if (!internalToCache($file) && availableCached($file)) {
|
|
return Stream::fopen(getCachedPath($file), 'r', false);
|
|
}
|
|
$code = getContents($file);
|
|
if ($code === false) {
|
|
return false;
|
|
}
|
|
$source = new Source($code);
|
|
$source->file = $file;
|
|
transform($source);
|
|
if (!internalToCache($file) && cacheEnabled()) {
|
|
storeInCache($source);
|
|
return transformAndOpen($file);
|
|
}
|
|
$resource = fopen(OUTPUT_DESTINATION, OUTPUT_ACCESS_MODE);
|
|
if ($resource) {
|
|
fwrite($resource, $source);
|
|
rewind($resource);
|
|
}
|
|
return $resource;
|
|
}
|
|
|
|
function prime($file)
|
|
{
|
|
Stream::fclose(transformAndOpen($file));
|
|
}
|
|
|
|
function shouldTransform($file)
|
|
{
|
|
return !Config\isBlacklisted($file) || Config\isWhitelisted($file);
|
|
}
|
|
|
|
function register($actions)
|
|
{
|
|
State::$actions = array_merge(State::$actions, (array) $actions);
|
|
}
|
|
|
|
function onImport($listeners)
|
|
{
|
|
State::$importListeners = array_merge(State::$importListeners, (array) $listeners);
|
|
}
|
|
|
|
class State
|
|
{
|
|
static $actions = [];
|
|
static $importListeners = [];
|
|
static $cacheIndex = [];
|
|
static $cacheIndexFile;
|
|
}
|