From 010f5c15760dcd0e8e6f8bee8ed3976ae5365431 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 20 Mar 2022 16:06:32 +0100 Subject: [PATCH] ajoute les emplacements --- index.php | 576 ++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 312 insertions(+), 264 deletions(-) diff --git a/index.php b/index.php index bae70ba..ba5e3ee 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ define('DEFAULT_TITLE', 'Mon panier bio'); -define('SUPPLIER_REGEX', '[A-Za-z]\w{0,31}'); +define('SUPPLIER_REGEX', '[A-Za-z]\w{0,31}(-\w+)?'); define('EVENT_REGEX', '\d{4}\-[01]\d\-[0123]\d'); define('EVENT_FORMAT', 'Y-m-d'); define('REQUEST_REGEX', '/^https?:\/\/.+\/(?' . SUPPLIER_REGEX . ')\/?(?' . EVENT_REGEX . ')?\/?$/'); @@ -52,15 +52,16 @@ function generatePassword($length = 20) { range('0', '9'), [ '!', '?', '~', '@', '#', '$', '%', '*', ';', ':', '-', '+', '=', ',', '.', '_' ] ); + $value =''; while ($length-- > 0) $value .= $chars[mt_rand(0, count($chars) - 1)]; return $value; } -function generateUrl($supplier = null, $event = null) { +function generateUrl($supplier = null, $event = null, $iframe = null) { global $requestUrl, $inIframe; - $queryString = $inIframe ? '?iframe' : ''; + $queryString = ($inIframe or !is_null($iframe)) ? '?iframe' : ''; if (is_null($supplier)) return $requestUrl . $queryString; @@ -113,271 +114,299 @@ define('DATA_FILE', __DIR__ . DIRECTORY_SEPARATOR . 'data.php'); if (file_exists(CONFIG_FILE)) require_once CONFIG_FILE; if (!isset($config)) $config = []; +$availableLocations = []; +foreach (array_keys($config) as $supplier) { + $hasLocation = (($pos = strpos($supplier, '-')) !== false); + if (!$hasLocation) + continue; + $locationKey = substr($supplier, ($pos + 1)); + $shortSupplier = substr($supplier, 0, $pos); + $locationValue = empty($config[$supplier]['location']) ? $locationKey : $config[$supplier]['location']; + $availableLocations[$shortSupplier][$locationKey] = $locationValue; +} + $inIframe = isset($_REQUEST['iframe']); $action = (isset($_REQUEST['action']) and preg_match(ACTION_REGEX, $_REQUEST['action'])) ? $_REQUEST['action'] : null; $supplier = array_key_exists('supplier', $_REQUEST) ? $_REQUEST['supplier'] : $requestSupplier; +$hasLocation = (($pos = strpos($supplier, '-')) !== false); +if ($hasLocation) { + $location = substr($supplier, ($pos + 1)); +} $hasSupplier = is_string($supplier) and preg_match('/^' . SUPPLIER_REGEX . '$/', $supplier); $excludesFormatter = new \IntlDateFormatter('fr_FR.UTF8', \IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, 'Europe/Paris'); -$supplierIsNew = false; -if ($hasSupplier) { - - if (!isset($config[$supplier])) { - $config[$supplier] = []; - $supplierIsNew = true; - } - - $config[$supplier] = array_merge( - [ - 'title' => '', - 'subtitle' => '%date% (%ago%)', - 'description' => '', - 'choices' => [], - 'start' => 'now 00:00:00', - 'end' => '+1 year 23:59:59', - '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); - $first = new \DateTime($config[$supplier]['start']); - if (true and ($previous->getTimestamp() < $first->getTimestamp())) - 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); - $last = new \DateTime($config[$supplier]['end']); - if (true and ($next->getTimestamp() > $last->getTimestamp())) - unset($nextEvent); - } - - switch ($action) { - case 'insert' : - case 'delete' : - $item = []; - - foreach (['name', 'choice', 'action'] as $field) - $item[$field] = filter_var($_REQUEST[$field], FILTER_SANITIZE_STRING); - $item['timestamp'] = time(); - $hash = md5(implode([ trim($item['name']), $item['choice'], ])); - $item['hash'] = $hash; - - $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']) - $alreadyInserted = true; - if (!$alreadyInserted) - $items[] = $item; - } elseif ($item['action'] === 'delete') { - foreach ($items as $index => $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)); - $ago = ago(new \DateTimeImmutable($event)); - $color = isInPast($event) ? 'text-danger' : 'text-muted'; - - $currentEvent = findNext(new \DateTime($config[$supplier]['start']), $config[$supplier]['frequency'], $config[$supplier]['excludes'], true); - $currentDate = (new \IntlDateFormatter('fr_FR.UTF8', \IntlDateFormatter::FULL, \IntlDateFormatter::NONE, 'Europe/Paris'))->format($currentEvent); - $currentAgo = ago($currentEvent); - - 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; - -} - -if ($supplierIsNew and !empty($suppliers)) { - $closestSuppliers = array_filter( - array_map( - function ($other) use ($supplier) { - return [ - 'supplier' => $other, - 'score' => levenshtein($supplier, $other), - ]; - }, - $suppliers - ), - function ($item) { - return $item['score'] > 0; - } - ); - usort($closestSuppliers, function ($a, $b) { - if ($a['score'] == $b['score']) { - return 0; - } - return ($a['score'] < $b['score']) ? -1 : 1; - }); +if ( + $hasSupplier + and !isset($config[$supplier]) + and !$hasLocation + and is_array($availableLocations[$supplier]) + and !empty($availableLocations[$supplier]) +) { + $supplierNeedLocation = true; +} else { + $supplierNeedLocation = false; + + $supplierIsNew = false; + if ($hasSupplier) { + + if (!isset($config[$supplier])) { + $config[$supplier] = []; + $supplierIsNew = true; + } + + $config[$supplier] = array_merge( + [ + 'title' => '', + 'subtitle' => '%date% (%ago%)', + 'location' => '', + 'description' => '', + 'choices' => [], + 'start' => 'now 00:00:00', + 'end' => '+1 year 23:59:59', + '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); + $first = new \DateTime($config[$supplier]['start']); + if (true and ($previous->getTimestamp() < $first->getTimestamp())) + 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); + $last = new \DateTime($config[$supplier]['end']); + if (true and ($next->getTimestamp() > $last->getTimestamp())) + unset($nextEvent); + } + + switch ($action) { + case 'insert' : + case 'delete' : + $item = []; + + foreach (['name', 'choice', 'action'] as $field) + $item[$field] = filter_var($_REQUEST[$field], FILTER_SANITIZE_STRING); + $item['timestamp'] = time(); + $hash = md5(implode([ trim($item['name']), $item['choice'], ])); + $item['hash'] = $hash; + + $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']) + $alreadyInserted = true; + if (!$alreadyInserted) + $items[] = $item; + } elseif ($item['action'] === 'delete') { + foreach ($items as $index => $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)); + $ago = ago(new \DateTimeImmutable($event)); + $color = isInPast($event) ? 'text-danger' : 'text-muted'; + + $currentEvent = findNext(new \DateTime($config[$supplier]['start']), $config[$supplier]['frequency'], $config[$supplier]['excludes'], true); + $currentDate = (new \IntlDateFormatter('fr_FR.UTF8', \IntlDateFormatter::FULL, \IntlDateFormatter::NONE, 'Europe/Paris'))->format($currentEvent); + $currentAgo = ago($currentEvent); + + 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; + + } + + if ($supplierIsNew and !empty($suppliers)) { + $closestSuppliers = array_filter( + array_map( + function ($other) use ($supplier) { + return [ + 'supplier' => $other, + 'score' => levenshtein($supplier, $other), + ]; + }, + $suppliers + ), + function ($item) { + return $item['score'] > 0; + } + ); + usort($closestSuppliers, function ($a, $b) { + if ($a['score'] == $b['score']) { + return 0; + } + return ($a['score'] < $b['score']) ? -1 : 1; + }); + } } $linkUrl = !$hasSupplier ? generateUrl() : (!$hasEvent ? generateUrl($supplier) : generateUrl($supplier, $event)); @@ -429,7 +458,19 @@ $linkUrl = !$hasSupplier ? generateUrl() : (!$hasEvent ? generateUrl($supplier)
- + +
+ +
+ $locationValue) : ?> + + +
+
+
- +
@@ -486,6 +527,13 @@ $linkUrl = !$hasSupplier ? generateUrl() : (!$hasEvent ? generateUrl($supplier)
+ +
+ +
L'emplacement de la commande. N'est vraiement utile que s'il y a plusieurs emplacements différents. Par convention c'est le cas quand il y a un tiret (-) dans le nom du fournisseur.
+
+
+
@@ -746,7 +794,7 @@ $linkUrl = !$hasSupplier ? generateUrl() : (!$hasEvent ? generateUrl($supplier)
- +

-