[^\/]+)\/?(?[^\/]+)?\/?$/'); define('SUPPLIER_REGEX', '/^[A-Za-z]\w{0,31}$/'); define('EVENT_REGEX', '/^\d{4}\-[01]\d\-[0123]\d$/'); define('ACTION_REGEX', '/^[a-z]{1,16}$/i'); $requestUrl = trim(str_replace($_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']), '?'); if (preg_match(REQUEST_REGEX, $requestUrl, $match)) { $requestSupplier = array_key_exists('supplier', $match) ? $match['supplier'] : null; $requestEvent = array_key_exists('event', $match) ? $match['event'] : null; if (!is_null($requestEvent)) $requestUrl = rtrim(str_replace($requestEvent, '', $requestUrl), '/'); if (!is_null($requestSupplier)) $requestUrl = rtrim(str_replace($requestSupplier, '', $requestUrl), '/'); } function generateUrl($supplier = null, $event = null) { global $requestUrl; if (is_null($supplier)) return $requestUrl; if (is_null($event)) return sprintf('%s/%s', $requestUrl, $supplier); return sprintf('%s/%s/%s', $requestUrl, $supplier, $event); } function findNext($start, $frequency, $excludes = [], $vsNow = true, $maxIterations = 1000, $direction = +1) { $now = new \DateTime('now'); $current = clone $start; $frequency = \DateInterval::createFromDateString($frequency); do { if ($direction === abs($direction)) { if (!$vsNow and ($maxIterations-- > 0)) { $current->add($frequency); } else { while ( ($current->getTimestamp() < $now->getTimestamp()) and ($maxIterations-- > 0) ) $current->add($frequency); } } else { if (!$vsNow and ($maxIterations-- > 0)) { $current->sub($frequency); } else { while ( ($current->getTimestamp() > $now->getTimestamp()) and ($maxIterations-- > 0) ) $current->sub($frequency); } } $nextEvent = $current->format('Y-m-d'); } while ( in_array($nextEvent, $excludes) and ($maxIterations > 0) ); return $current; } function findPrevious($start, $frequency, $excludes = [], $vsNow = true, $maxIterations = 1000) { return findNext($start, $frequency, $excludes, $vsNow, $maxIterations, -1); } define('CONFIG_FILE', __DIR__ . DIRECTORY_SEPARATOR . 'config.php'); define('DATA_FILE', __DIR__ . DIRECTORY_SEPARATOR . 'data.php'); if (file_exists(CONFIG_FILE)) require_once CONFIG_FILE; if (!isset($config)) $config = []; $action = (isset($_REQUEST['action']) and preg_match(ACTION_REGEX, $_REQUEST['action'])) ? $_REQUEST['action'] : null; $supplier = array_key_exists('supplier', $_REQUEST) ? $_REQUEST['supplier'] : $requestSupplier; $hasSupplier = is_string($supplier) and preg_match(SUPPLIER_REGEX, $supplier); $excludesFormatter = new \IntlDateFormatter('fr_FR.UTF8', \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, 'Europe/Paris'); if ($hasSupplier) { if (!isset($config[$supplier])) $config[$supplier] = []; $config[$supplier] = array_merge( [ 'title' => '', 'subtitle' => '%date%', 'description' => '', 'choices' => [], 'start' => 'now 00:00:00', 'frequency' => '1 day', 'password' => '', 'excludes' => [], ], $config[$supplier] ); $hasPassword = !empty($config[$supplier]['password']); if ($action === 'config') { if ($hasPassword) { if (!isset($_SERVER['PHP_AUTH_USER'])) { header(sprintf('WWW-Authenticate: Basic realm="Configuration de mon panier bio pour %s"', $supplier)); header('HTTP/1.0 401 Unauthorized'); printf('Cette configuration est protégée par mot de passe !'); exit; } elseif ( ($_SERVER['PHP_AUTH_USER'] !== $supplier) or ($_SERVER['PHP_AUTH_PW'] !== $config[$supplier]['password']) ) { header('HTTP/1.0 403 Forbidden'); printf('Cette configuration est protégée par mot de passe !'); exit; } } foreach (array_keys($config[$supplier]) as $key) if (isset($_REQUEST[$key])) $config[$supplier][$key] = (!in_array($key, ['title', 'subtitle', 'description']) ? filter_var($_REQUEST[$key], FILTER_SANITIZE_STRING) : $_REQUEST[$key]); } if (empty($config[$supplier]['start'])) $config[$supplier]['start'] = 'now 00:00:00'; foreach (['choices', 'excludes'] as $key) { if (is_string($config[$supplier][$key])) $config[$supplier][$key] = explode(PHP_EOL, $config[$supplier][$key]); if (!is_array($config[$supplier][$key])) $config[$supplier][$key] = []; $config[$supplier][$key] = array_filter( $config[$supplier][$key], function ($choice) { return is_string($choice) and !empty(trim($choice)); } ); $config[$supplier][$key] = array_map('trim', $config[$supplier][$key]); } $config[$supplier]['excludes'] = array_filter( array_map( function ($value) use ($excludesFormatter) { if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) return $value; $timestamp = $excludesFormatter->parse($value, $offset); if ($timestamp !== false) return (new \DateTimeImmutable('@' . $timestamp, new \DateTimeZone('Europe/Paris')))->format('Y-m-d'); try { return (new \DateTimeImmutable($value, new \DateTimeZone('Europe/Paris')))->format('Y-m-d'); } catch (\Exception $exception) { return null; } }, $config[$supplier]['excludes'] ), function ($value) { return !is_null($value); } ); } $isConfig = false; if ($action === 'config') { $output = fopen(CONFIG_FILE, 'w+'); if ($output) { if (flock($output, LOCK_EX)) { fwrite($output, 'format('Y-m-d'); header('Location: ' . generateUrl($supplier, $nextEvent)); die(); } else { $current = new \DateTime($event); $previous = findPrevious($current, $config[$supplier]['frequency'], $config[$supplier]['excludes'], false); $previousEvent = $previous->format('Y-m-d'); if (false and !array_key_exists($previousEvent, $data[$supplier])) unset($previousEvent); $next = findNext($current, $config[$supplier]['frequency'], $config[$supplier]['excludes'], false); $nextEvent = $next->format('Y-m-d'); if (false and !array_key_exists($nextEvent, $data[$supplier])) unset($nextEvent); } switch ($action) { case 'insert' : case 'delete' : $isBeginning = (!file_exists(DATA_FILE) or in_array(filesize(DATA_FILE), [ false, 0 ])); $output = fopen(DATA_FILE, 'a+'); if (!$output) break; if (!flock($output, LOCK_EX)) break; if ($isBeginning) fwrite($output, ' $prevItem) if ($prevItem['hash'] === $item['hash']) unset($items[$index]); } } $date = (new \IntlDateFormatter('fr_FR.UTF8', \IntlDateFormatter::FULL, \IntlDateFormatter::NONE, 'Europe/Paris'))->format(new \DateTime($event)); foreach (['title', 'subtitle', 'description'] as $key) { while (preg_match('/%([^%]+)%/i', $config[$supplier][$key], $match)) $config[$supplier][$key] = str_replace( $match[0], ${$match[1]}, $config[$supplier][$key] ); } if (empty($config[$supplier]['title'])) $config[$supplier]['title'] = $supplier; $stats = []; foreach ($items as $item) if (!empty($item['choice'])) $stats[$item['choice']] += 1; } ?> <?php if ($hasSupplier) : ?><?php echo strip_tags($config[$supplier]['title']); ?><?php if (!$isConfig) : ?> — <?php echo strip_tags($config[$supplier]['subtitle']); ?><?php endif; ?><?php else : ?><?php echo DEFAULT_TITLE; ?><?php endif; ?>

Configuration

Le titre de la page. Par défaut ce sera le nom du fournisseur
La description affichée sous le titre.
Les différents choix possibles. Un par ligne. Ou pas.
La date du premier événement, si nécessaire de le préciser.
La fréquence des événements dans le format décrit sur cette page.
Les dates à exclure. Une par ligne. Ou pas. En tous cas le format c'est celui de l'ICU : getPattern(); ?>
Ce mot de passe sera demandé pour accéder à la configuration la prochaine fois. Le nom d'utilisateur est le fournisseur courant (en l'occurrence ).
$choice) : ?>
Nom Choix  
  • Commandes
  • $count) : ?>