From 56bcc143142e2654ed46462c3251f586cc857571 Mon Sep 17 00:00:00 2001 From: Maurits van der Schee Date: Thu, 2 Jul 2020 14:47:53 +0200 Subject: [PATCH 01/17] bugfix --- .../PhpCrudUi/Controller/MultiResponder.php | 5 + .../PhpCrudUi/Controller/RecordController.php | 13 + .../PhpCrudUi/Document/RedirectDocument.php | 25 ++ src/Tqdev/PhpCrudUi/Record/RecordService.php | 10 +- templates/record/list.html | 2 +- ui.php | 260 ++++++++++++------ 6 files changed, 228 insertions(+), 87 deletions(-) create mode 100644 src/Tqdev/PhpCrudUi/Document/RedirectDocument.php diff --git a/src/Tqdev/PhpCrudUi/Controller/MultiResponder.php b/src/Tqdev/PhpCrudUi/Controller/MultiResponder.php index 274353f..71ea810 100644 --- a/src/Tqdev/PhpCrudUi/Controller/MultiResponder.php +++ b/src/Tqdev/PhpCrudUi/Controller/MultiResponder.php @@ -8,6 +8,7 @@ use Tqdev\PhpCrudApi\Record\ErrorCode; use Tqdev\PhpCrudApi\ResponseFactory; use Tqdev\PhpCrudUi\Document\CsvDocument; +use Tqdev\PhpCrudUi\Document\RedirectDocument; use Tqdev\PhpCrudUi\Document\TemplateDocument; class MultiResponder implements Responder @@ -45,6 +46,10 @@ public function success($result): ResponseInterface $result->addVariables($this->variables); $result->setTemplatePath($this->templatePath); return ResponseFactory::fromHtml(ResponseFactory::OK, (string) $result); + } elseif ($result instanceof RedirectDocument) { + $result->addVariables($this->variables); + $response = ResponseFactory::fromStatus(ResponseFactory::FOUND); + return $response->withHeader('Location', (string) $result); } else { throw new \Exception('Document type not supported: ' . get_class($result)); } diff --git a/src/Tqdev/PhpCrudUi/Controller/RecordController.php b/src/Tqdev/PhpCrudUi/Controller/RecordController.php index fc8c770..111133a 100644 --- a/src/Tqdev/PhpCrudUi/Controller/RecordController.php +++ b/src/Tqdev/PhpCrudUi/Controller/RecordController.php @@ -26,6 +26,7 @@ public function __construct(Router $router, Responder $responder, RecordService $router->register('GET', '/*/delete/*', array($this, 'deleteForm')); $router->register('POST', '/*/delete/*', array($this, 'delete')); $router->register('GET', '/*/list', array($this, '_list')); + $router->register('POST', '/*/list', array($this, 'search')); $router->register('GET', '/*/export', array($this, 'export')); $this->service = $service; $this->responder = $responder; @@ -140,6 +141,18 @@ public function _list(ServerRequestInterface $request): ResponseInterface return $this->responder->success($result); } + public function search(ServerRequestInterface $request): ResponseInterface + { + $table = RequestUtils::getPathSegment($request, 1); + $action = RequestUtils::getPathSegment($request, 2); + $params = RequestUtils::getParams($request); + if (!$this->service->hasTable($table, $action)) { + return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table); + } + $result = $this->service->search($table, $action, $params); + return $this->responder->success($result); + } + public function export(ServerRequestInterface $request): ResponseInterface { $table = RequestUtils::getPathSegment($request, 1); diff --git a/src/Tqdev/PhpCrudUi/Document/RedirectDocument.php b/src/Tqdev/PhpCrudUi/Document/RedirectDocument.php new file mode 100644 index 0000000..6c0b990 --- /dev/null +++ b/src/Tqdev/PhpCrudUi/Document/RedirectDocument.php @@ -0,0 +1,25 @@ +path = $path; + $this->variables = $variables; + } + + public function addVariables(array $variables) /*: void*/ + { + $this->variables = array_merge($variables, $this->variables); + } + + public function __toString(): string + { + return $this->variables['base'] . $this->path; + } +} diff --git a/src/Tqdev/PhpCrudUi/Record/RecordService.php b/src/Tqdev/PhpCrudUi/Record/RecordService.php index d6f3ac3..8f22c12 100644 --- a/src/Tqdev/PhpCrudUi/Record/RecordService.php +++ b/src/Tqdev/PhpCrudUi/Record/RecordService.php @@ -5,6 +5,7 @@ use Tqdev\PhpCrudUi\Client\CrudApi; use Tqdev\PhpCrudUi\Column\SpecificationService; use Tqdev\PhpCrudUi\Document\CsvDocument; +use Tqdev\PhpCrudUi\Document\RedirectDocument; use Tqdev\PhpCrudUi\Document\TemplateDocument; class RecordService @@ -216,7 +217,6 @@ public function _list(string $table, string $action, array $params): TemplateDoc { $types = $this->definition->getTypes($table, $action); $references = $this->definition->getReferences($table, $action); - $referenced = $this->definition->getReferenced($table, $action); $primaryKey = $this->definition->getPrimaryKey($table, $action); $columns = $this->definition->getColumns($table, $action); @@ -281,6 +281,14 @@ public function _list(string $table, string $action, array $params): TemplateDoc return new TemplateDocument('layouts/default', 'record/list', $variables); } + public function search(string $table, string $action, array $params) + { + //die(var_dump($params)); + $params = ['search' => 'asdas']; + $query = http_build_query($params); + return new RedirectDocument('/' . $table . '/list?' . $query, []); + } + public function export(string $table, string $action): CsvDocument { $references = $this->definition->getReferences($table, $action); diff --git a/templates/record/list.html b/templates/record/list.html index b624902..a0264ce 100644 --- a/templates/record/list.html +++ b/templates/record/list.html @@ -45,7 +45,7 @@

{{table|humanize}}

-
+  
diff --git a/ui.php b/ui.php index 5eebd49..7ae513a 100644 --- a/ui.php +++ b/ui.php @@ -109,30 +109,49 @@ $_HTML['record/create'] = <<<'END_OF_HTML' -

create {{table}}

+

New item

- {{for:column:columns}} -
- - {{if:column.values}} - - {{else}} - - {{endif}} -
+ {{for:value:key:record}} + {{if:key|neq(primaryKey)}} +
+ + {{if:value.values}} + + {{else}} + {{if:value.type.format|eq("int32")}} + + {{elseif:value.type.format|eq("int64")}} + + {{elseif:value.type.format|eq("decimal")}} + + {{elseif:value.type.format|eq("date-time")}} + + {{elseif:value.type.format|eq("date")}} + + {{elseif:value.type.format|eq("time")}} + + {{elseif:value.type.format|eq("large-string")}} + + {{elseif:value.type.format|eq("boolean")}} + + {{else}} + + {{endif}} + {{endif}} +
+ {{endif}} {{endfor}}
- END_OF_HTML; } @@ -157,17 +176,22 @@ $_HTML['record/delete'] = <<<'END_OF_HTML' -

delete {{table}}

+

{{name|or("Delete item")}}

+{{if:name}} +

Are you sure you want to delete '{{name}}'?

+{{else}} +

Are you sure you want to delete item #{{id}}?

+{{endif}}

The action cannot be undone.

- + - Cancel + Cancel
END_OF_HTML; } @@ -216,7 +240,6 @@
  • {{table|humanize}}
  • -{{if:primaryKey}}

    {{table|humanize}}

    @@ -224,10 +247,11 @@ filter Search + {{if:primaryKey}} New item + {{endif}}
    -{{endif}} @@ -258,7 +282,7 @@ class="icon search">Search
    -
    +  
    @@ -271,8 +295,8 @@ class="icon search">Search Action {{endif}} {{for:column:columns}} - {{if:column|neq(primaryKey)}} - {{column|humanize}} + {{if:column.text|neq(primaryKey)}} + {{column.text|humanize}} {{endif}} {{endfor}} @@ -281,13 +305,9 @@ class="icon search">Search {{for:record:records}} {{for:field:name:record}} - {{if:primaryKey}} {{if:name|eq(primaryKey)}} view {{endif}} - {{endif}} - {{endfor}} - {{for:field:name:record}} {{if:name|neq(primaryKey)}} {{if:field.table}} {{field.text}} @@ -332,32 +352,34 @@ class="icon search">Search
  • {{table|humanize}}
  • -

    view {{table|humanize}}

    +

    {{name|or("View item")}}

    - - + + {{for:field:name:record}} + {{if:name|neq(primaryKey)}} + {{endif}} {{endfor}}
    keyvalueKeyValue
    {{name|humanize}} {{if:field.table}} - {{field.text}} + {{field.text}} {{else}} - {{if:field.type.format|eq("large-string")}} -
    {{field.text}}
    - {{else}} - {{if:field.text}}{{field.text}}{{else}}-{{endif}} - {{endif}} + {{if:field.type.format|eq("large-string")}} +
    {{field.text}}
    + {{else}} + {{if:field.text}}{{field.text}}{{else}}-{{endif}} + {{endif}} {{endif}}
    @@ -375,7 +397,8 @@ class="icon search">Search
  • {{relation.0|humanize}}
  • {{endfor}} -{{endif}} + {{endif}} + END_OF_HTML; } @@ -387,45 +410,46 @@ class="icon search">Search
  • {{table|humanize}}
  • -

    update {{table}}

    +

    {{name|or("Edit item")}}

    {{for:value:key:record}} -
    - - {{if:value.values}} - - {{else}} - {{if:value.type.format|eq("int32")}} - - {{elseif:value.type.format|eq("int64")}} - - {{elseif:value.type.format|eq("decimal")}} - - {{elseif:value.type.format|eq("date-time")}} - - {{elseif:value.type.format|eq("date")}} - - {{elseif:value.type.format|eq("time")}} - - {{elseif:value.type.format|eq("large-string")}} - - {{elseif:value.type.format|eq("boolean")}} - - {{else}} - - {{endif}} - {{endif}} -
    + {{if:key|neq(primaryKey)}} +
    + + {{if:value.values}} + + {{else}} + {{if:value.type.format|eq("int32")}} + + {{elseif:value.type.format|eq("int64")}} + + {{elseif:value.type.format|eq("decimal")}} + + {{elseif:value.type.format|eq("date-time")}} + + {{elseif:value.type.format|eq("date")}} + + {{elseif:value.type.format|eq("time")}} + + {{elseif:value.type.format|eq("large-string")}} + + {{elseif:value.type.format|eq("boolean")}} + + {{else}} + + {{endif}} + {{endif}} +
    + {{endif}} {{endfor}}
    - END_OF_HTML; } @@ -11688,6 +11712,8 @@ public static function getTableNames(ServerRequestInterface $request, Reflection class ResponseFactory { const OK = 200; + const MOVED_PERMANENTLY = 301; + const FOUND = 302; const UNAUTHORIZED = 401; const FORBIDDEN = 403; const NOT_FOUND = 404; @@ -12146,6 +12172,7 @@ public function referenceId(string $table, /* object */ $record) use Tqdev\PhpCrudApi\Record\ErrorCode; use Tqdev\PhpCrudApi\ResponseFactory; use Tqdev\PhpCrudUi\Document\CsvDocument; + use Tqdev\PhpCrudUi\Document\RedirectDocument; use Tqdev\PhpCrudUi\Document\TemplateDocument; class MultiResponder implements Responder @@ -12183,6 +12210,10 @@ public function success($result): ResponseInterface $result->addVariables($this->variables); $result->setTemplatePath($this->templatePath); return ResponseFactory::fromHtml(ResponseFactory::OK, (string) $result); + } elseif ($result instanceof RedirectDocument) { + $result->addVariables($this->variables); + $response = ResponseFactory::fromStatus(ResponseFactory::FOUND); + return $response->withHeader('Location', (string) $result); } else { throw new \Exception('Document type not supported: ' . get_class($result)); } @@ -12217,6 +12248,7 @@ public function __construct(Router $router, Responder $responder, RecordService $router->register('GET', '/*/delete/*', array($this, 'deleteForm')); $router->register('POST', '/*/delete/*', array($this, 'delete')); $router->register('GET', '/*/list', array($this, '_list')); + $router->register('POST', '/*/list', array($this, 'search')); $router->register('GET', '/*/export', array($this, 'export')); $this->service = $service; $this->responder = $responder; @@ -12331,6 +12363,18 @@ public function _list(ServerRequestInterface $request): ResponseInterface return $this->responder->success($result); } + public function search(ServerRequestInterface $request): ResponseInterface + { + $table = RequestUtils::getPathSegment($request, 1); + $action = RequestUtils::getPathSegment($request, 2); + $params = RequestUtils::getParams($request); + if (!$this->service->hasTable($table, $action)) { + return $this->responder->error(ErrorCode::TABLE_NOT_FOUND, $table); + } + $result = $this->service->search($table, $action, $params); + return $this->responder->success($result); + } + public function export(ServerRequestInterface $request): ResponseInterface { $table = RequestUtils::getPathSegment($request, 1); @@ -12370,6 +12414,32 @@ public function __toString(): string } } +// file: src/Tqdev/PhpCrudUi/Document/RedirectDocument.php +namespace Tqdev\PhpCrudUi\Document { + + class RedirectDocument + { + private $path; + private $variables; + + public function __construct(string $path, array $variables) + { + $this->path = $path; + $this->variables = $variables; + } + + public function addVariables(array $variables) /*: void*/ + { + $this->variables = array_merge($variables, $this->variables); + } + + public function __toString(): string + { + return $this->variables['base'] . $this->path; + } + } +} + // file: src/Tqdev/PhpCrudUi/Document/TemplateDocument.php namespace Tqdev\PhpCrudUi\Document { @@ -12541,6 +12611,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface use Tqdev\PhpCrudUi\Client\CrudApi; use Tqdev\PhpCrudUi\Column\SpecificationService; use Tqdev\PhpCrudUi\Document\CsvDocument; + use Tqdev\PhpCrudUi\Document\RedirectDocument; use Tqdev\PhpCrudUi\Document\TemplateDocument; class RecordService @@ -12584,20 +12655,24 @@ public function home(): TemplateDocument public function createForm(string $table, string $action): TemplateDocument { + $types = $this->definition->getTypes($table, $action); $references = $this->definition->getReferences($table, $action); $primaryKey = $this->definition->getPrimaryKey($table, $action); $columns = $this->definition->getColumns($table, $action); - - foreach ($columns as $i => $column) { + $record = array(); + foreach ($columns as $column) { $values = $this->getDropDownValues($references[$column]); - $columns[$i] = array('name' => $column, 'values' => $values); + $type = $types[$column]; + //TODO: sensible default + $default = ''; + $record[$column] = array('value' => $default, 'values' => $values, 'type' => $type); } $variables = array( 'table' => $table, 'action' => $action, - 'columns' => $columns, + 'record' => $record, 'primaryKey' => $primaryKey, ); @@ -12625,6 +12700,7 @@ public function read(string $table, string $action, string $id, array $params): $types = $this->definition->getTypes($table, $action); $references = $this->definition->getReferences($table, $action); $referenced = $this->definition->getReferenced($table, $action); + $primaryKey = $this->definition->getPrimaryKey($table, $action); $args = array(); $args['join'] = array_values(array_filter($references)); @@ -12640,7 +12716,7 @@ public function read(string $table, string $action, string $id, array $params): $relatedTable = false; $relatedValue = false; $text = $value; - $type = isset($types[$key]) ? $types[$key] : null; + $type = $types[$key]; if (isset($references[$key]) && $references[$key]) { $relatedTable = $references[$key]; $relatedValue = $this->definition->referenceId($relatedTable, $value); @@ -12654,7 +12730,7 @@ public function read(string $table, string $action, string $id, array $params): 'action' => $action, 'id' => $id, 'name' => $name, - 'references' => $references, + 'primaryKey' => $primaryKey, 'referenced' => $referenced, 'record' => $record, ); @@ -12669,16 +12745,19 @@ public function updateForm(string $table, string $action, string $id): TemplateD $primaryKey = $this->definition->getPrimaryKey($table, $action); $record = $this->api->readRecord($table, $id, []); + $name = $this->definition->referenceText($table, $record); foreach ($record as $key => $value) { $values = $this->getDropDownValues($references[$key]); - $record[$key] = array('type' => $types[$key], 'value' => $value, 'values' => $values); + $type = $types[$key]; + $record[$key] = array('value' => $value, 'values' => $values, 'type' => $type); } $variables = array( 'table' => $table, 'action' => $action, 'id' => $id, + 'name' => $name, 'primaryKey' => $primaryKey, 'record' => $record, ); @@ -12715,6 +12794,7 @@ public function deleteForm(string $table, string $action, string $id): TemplateD 'table' => $table, 'action' => $action, 'id' => $id, + 'name' => $name, 'primaryKey' => $primaryKey, 'name' => $name, ); @@ -12741,11 +12821,14 @@ public function delete(string $table, string $action, string $id): TemplateDocum public function _list(string $table, string $action, array $params): TemplateDocument { + $types = $this->definition->getTypes($table, $action); $references = $this->definition->getReferences($table, $action); - $referenced = $this->definition->getReferenced($table, $action); $primaryKey = $this->definition->getPrimaryKey($table, $action); $columns = $this->definition->getColumns($table, $action); + foreach ($columns as $i => $key) { + $columns[$i] = array('text' => $key, 'type' => $types[$key]); + } $pageParams = isset($params['page']) ? $params['page'][0] : '1,50'; list($pageNumber, $pageSize) = explode(',', $pageParams, 2); @@ -12773,12 +12856,13 @@ public function _list(string $table, string $action, array $params): TemplateDoc $relatedTable = false; $relatedValue = $value; $text = $value; + $type = $types[$key]; if ($references[$key]) { $relatedTable = $references[$key]; $relatedValue = $this->definition->referenceId($relatedTable, $value); $text = $this->definition->referenceText($relatedTable, $value); } - $data['records'][$i][$key] = array('text' => $text, 'table' => $relatedTable, 'value' => $relatedValue); + $data['records'][$i][$key] = array('text' => $text, 'table' => $relatedTable, 'value' => $relatedValue, 'type' => $type); } } @@ -12792,8 +12876,6 @@ public function _list(string $table, string $action, array $params): TemplateDoc 'table' => $table, 'action' => $action, 'filters' => $filters, - 'references' => $references, - 'referenced' => $referenced, 'primaryKey' => $primaryKey, 'columns' => $columns, 'records' => $data['records'], @@ -12805,6 +12887,14 @@ public function _list(string $table, string $action, array $params): TemplateDoc return new TemplateDocument('layouts/default', 'record/list', $variables); } + public function search(string $table, string $action, array $params) + { + die(var_dump($params)); + $params = []; + $query = http_build_query($params); + return new RedirectDocument('/' . $table . '/list?' . $query, []); + } + public function export(string $table, string $action): CsvDocument { $references = $this->definition->getReferences($table, $action); @@ -13467,7 +13557,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface // file: webroot/css/style.css namespace { $_STATIC['/css/style.css'] = <<<'END_OF_STATIC_FILE' -Cioge21hcmdpbjogMDsgcGFkZGluZzogMDsgYm94LXNpemluZzogYm9yZGVyLWJveDt9Cmh0bWwge2ZvbnQtc2l6ZTogMTZweDt9CmJvZHkge2ZvbnQtc2l6ZTogMXJlbTsgbGluZS1oZWlnaHQ6IDEuNDt9CgouY29udGVudCB7bWluLWhlaWdodDogMTAwdmg7fQoubmF2aWdhdGlvbiB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiB3aGl0ZTsgCiAgICBkaXNwbGF5OiBmbGV4OyAKICAgIHBvc2l0aW9uOiBmaXhlZDsgCiAgICB3aWR0aDogMTAwJTsgCiAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICBib3gtc2hhZG93OiAwcHggMHB4IDEwcHggcmdiYSgwLDAsMCwwLjI1KTsKICAgIHotaW5kZXg6IDk7Cn0KLnRpdGxlIHtwYWRkaW5nOiAwLjVyZW0gMnJlbTsgY29sb3I6IGJsYWNrOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7fQoudGl0bGUgc3BhbiB7Zm9udC13ZWlnaHQ6IGJvbGQ7IGRpc3BsYXk6IGJsb2NrO30KLmJvZHkge3BhZGRpbmc6IDZyZW0gMnJlbTsgZmxleC1ncm93OiAxOyBwYWRkaW5nLWJvdHRvbTogMTByZW07fQouaGFtYnVyZ2VyIHtwYWRkaW5nOiAwIDFyZW0gMC41cmVtOyBtYXJnaW46IDAuM3JlbSAxcmVtOyBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgY29sb3I6IGJsYWNrOyBmb250LXNpemU6IDIuMjVyZW07IGxpbmUtaGVpZ2h0OiAxLjI7IHRyYW5zZm9ybTogc2NhbGVYKDEuNSk7fQoKdGguc2VsZWN0ZWQ6OmFmdGVyIHtjb250ZW50OiAiIOKWviI7fQoKdGQsIHRoLCBkbCA+ICoge3BhZGRpbmc6IDAuMnJlbSAxLjVyZW0gMC4ycmVtIDAuNXJlbTsgdGV4dC1hbGlnbjogbGVmdDt9CnRkLCB0aCB7bWF4LXdpZHRoOiA0MHJlbTt9CnRhYmxlLmxpc3QgdGQsIHRhYmxlLmxpc3QgdGgge3RleHQtb3ZlcmZsb3c6IGVsbGlwc2lzOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBvdmVyZmxvdzogaGlkZGVuOyBtYXgtd2lkdGg6IDI1cmVtO30KdGFibGUucmVhZCB0aGVhZCB7ZGlzcGxheTogbm9uZTt9CmxhYmVsIHtwYWRkaW5nOiBjYWxjKDAuMnJlbSArIDFweCkgMS41cmVtIGNhbGMoMC4ycmVtICsgMXB4KSAwOyB0ZXh0LW92ZXJmbG93OiBlbGxpcHNpczsgd2hpdGUtc3BhY2U6IG5vd3JhcDsgb3ZlcmZsb3c6IGhpZGRlbjt9CnRkOmxhc3QtY2hpbGQgYSB7ZGlzcGxheTogaW5saW5lLWJsb2NrOyBtYXJnaW4tcmlnaHQ6IDAuNXJlbTt9Ci5sb2dvIGltZyB7aGVpZ2h0OiAycmVtOyB2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO30KYSB7Y29sb3I6IGJsYWNrO30KbGFiZWwge3dpZHRoOiAxNXJlbTsgbWluLXdpZHRoOiAxNXJlbTsgcGFkZGluZy1yaWdodDogMnJlbTt9CmgxIHtmb250LXNpemU6IDIuMnJlbTsgbWFyZ2luLWJvdHRvbTogMC41cmVtOyBtYXJnaW4tcmlnaHQ6IDFyZW07IGxpbmUtaGVpZ2h0OiAxO30KaDIge2ZvbnQtc2l6ZTogMS41cmVtOyBtYXJnaW4tYm90dG9tOiAwLjVyZW07fQp0YWJsZSB7Ym9yZGVyLWNvbGxhcHNlOiBjb2xsYXBzZTsgbWFyZ2luLXRvcDogMS41cmVtO30KdGFibGUgdHI6bnRoLWNoaWxkKGV2ZW4pIHtiYWNrZ3JvdW5kOiByZ2JhKDAsMCwwLDAuMDUpO30KdGFibGUucmVhZCB0cjpudGgtY2hpbGQoZXZlbikge2JhY2tncm91bmQ6IG5vbmU7fQp0YWJsZS5yZWFkIHRyOm50aC1jaGlsZChvZGQpIHtiYWNrZ3JvdW5kOiByZ2JhKDAsMCwwLDAuMDUpO30KdGFibGUgdHIgdGgge3BhZGRpbmctdG9wOiAwLjNyZW07IHBhZGRpbmctYm90dG9tOiAwLjNyZW07IGZvbnQtd2VpZ2h0OiBib2xkOyBib3JkZXItYm90dG9tOiAxcHggc29saWQgYmxhY2s7fQpwLCB0YWJsZSB7bWFyZ2luLWJvdHRvbTogMS40cmVtO30KCi50aXRsZWJhciB7ZGlzcGxheTogZmxleDsgYWxpZ24taXRlbXM6IGNlbnRlcjsgbWFyZ2luLWJvdHRvbTogMC41cmVtOyBmbGV4LXdyYXA6IHdyYXA7fQoudGl0bGViYXIgPiBkaXYge21hcmdpbi1ib3R0b206IDAuNXJlbTt9CgouY29scyB7Y29sdW1uLXdpZHRoOiA3MDBweDsgbWFyZ2luLXRvcDogLTJlbTt9Ci5jb2wgewogICAgLXdlYmtpdC1jb2x1bW4tYnJlYWstaW5zaWRlOiBhdm9pZDsgLyogQ2hyb21lLCBTYWZhcmkgKi8KICAgIHBhZ2UtYnJlYWstaW5zaWRlOiBhdm9pZDsgICAgICAgICAgIC8qIFRoZW9yZXRpY2FsbHkgRkYgMjArICovCiAgICBicmVhay1pbnNpZGU6IGF2b2lkLWNvbHVtbjsgICAgICAgICAvKiBJRSAxMSAqLwogICAgZGlzcGxheTp0YWJsZTsgICAgICAgICAgICAgICAgICAgICAgLyogQWN0dWFsbHkgRkYgMjArICovCn0KCmlucHV0LCB0ZXh0YXJlYSwgc2VsZWN0ewogICAgaGVpZ2h0OiBjYWxjKDJyZW0gKyAycHgpOyAKICAgIGxpbmUtaGVpZ2h0OiAxLjRyZW07IAogICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOyAKICAgIGJvcmRlcjogMXB4IHNvbGlkIGJsYWNrOyAKICAgIHBhZGRpbmc6IDAgMC41cmVtOyAKICAgIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50OwogICAgY29sb3I6IGJsYWNrOwogICAgZm9udC1mYW1pbHk6IGluaGVyaXQ7CiAgICBmb250LXNpemU6IGluaGVyaXQ7CiAgICBmbGV4LWdyb3c6IDE7CiAgICBiYWNrZ3JvdW5kOiB3aGl0ZTsKfQpidXR0b24sIGlucHV0W3R5cGU9InN1Ym1pdCJdLCAuYnRuIHsKICAgIGhlaWdodDogY2FsYygycmVtICsgMnB4KTsgCiAgICBsaW5lLWhlaWdodDogMnJlbTsgCiAgICBib3JkZXI6IDFweCBzb2xpZCB0cmFuc3BhcmVudDsgCiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7IAogICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOyAKICAgIHBhZGRpbmc6IDAgMXJlbTsgCiAgICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDsKICAgIGNvbG9yOiBibGFjazsKICAgIGZvbnQtZmFtaWx5OiBpbmhlcml0OwogICAgZm9udC1zaXplOiBpbmhlcml0OwogICAgYmFja2dyb3VuZC1jb2xvcjogcmdiKDIyMiwgMjIyLCAyMjIpOwogICAgY3Vyc29yOiBwb2ludGVyOwogICAgYm9yZGVyLXJhZGl1czogMC4xNXJlbTsKfQpidXR0b246Zm9jdXMsIGlucHV0W3R5cGU9InN1Ym1pdCJdOmZvY3VzLCAuYnRuOmZvY3VzLCAuaWNvbjpmb2N1cyB7Ym9yZGVyLWNvbG9yOiBibGFjazt9CnNlbGVjdDo6LW1zLWV4cGFuZCB7ZGlzcGxheTogbm9uZTt9CnNlbGVjdCB7CiAgICAtd2Via2l0LWFwcGVhcmFuY2U6IG5vbmU7CiAgICAtbW96LWFwcGVhcmFuY2U6IG5vbmU7CiAgICBhcHBlYXJhbmNlOiBub25lOwogICAgYmFja2dyb3VuZDogd2hpdGUgdXJsKCcuLi9pbWcvZG93bi5zdmcnKSBjYWxjKDEwMCUgLSAwLjZyZW0pIDUwJSBuby1yZXBlYXQ7CiAgICBiYWNrZ3JvdW5kLXNpemU6IGF1dG8gMC41cmVtOwogICAgcGFkZGluZy1yaWdodDogMS43NXJlbTsKICAgIG1heC13aWR0aDogY2FsYygxMDAlIC0gMTVyZW0pOwp9CmlucHV0W3R5cGU9InJhZGlvIl0sIGlucHV0W3R5cGU9ImNoZWNrYm94Il0sIGlucHV0W3R5cGU9ImZpbGUiXSB7Ym9yZGVyOiAwOyBoZWlnaHQ6IGF1dG87IGxpbmUtaGVpZ2h0OiBhdXRvOyBtYXJnaW4tcmlnaHQ6IDAuNHJlbTsgdmVydGljYWwtYWxpZ246IG1pZGRsZTsgcG9zaXRpb246IHJlbGF0aXZlOyBib3R0b206IDAuMXJlbTt9CmlucHV0W3R5cGU9ImZpbGUiXSB7cGFkZGluZzogMDsgbWFyZ2luOiAwOyBib3R0b206IDA7fQppbnB1dFt0eXBlPSdudW1iZXInXSB7LW1vei1hcHBlYXJhbmNlOiB0ZXh0ZmllbGQ7fQppbnB1dDo6LXdlYmtpdC1vdXRlci1zcGluLWJ1dHRvbiwgaW5wdXQ6Oi13ZWJraXQtaW5uZXItc3Bpbi1idXR0b24gey13ZWJraXQtYXBwZWFyYW5jZTogbm9uZTt9CmlucHV0W3R5cGU9Im51bWJlciJdIHstbW96LWFwcGVhcmFuY2U6IHRleHRmaWVsZDt9CmlucHV0Ojotd2Via2l0LW91dGVyLXNwaW4tYnV0dG9uLCAKaW5wdXQ6Oi13ZWJraXQtaW5uZXItc3Bpbi1idXR0b24gey13ZWJraXQtYXBwZWFyYW5jZTogbm9uZTt9CnRleHRhcmVhIHttaW4taGVpZ2h0OiAxMHJlbTsgcGFkZGluZzogMC4zNXJlbSAwLjVyZW07IH0KCi5pY29uIHsKICAgIGRpc3BsYXk6IGlubGluZS1ibG9jazsgCiAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7IAogICAgd2lkdGg6IDA7IAogICAgaGVpZ2h0OiAwOyAKICAgIG92ZXJmbG93OiBoaWRkZW47IAogICAgcGFkZGluZy10b3A6IDJyZW07IAogICAgcGFkZGluZy1sZWZ0OiAycmVtOyAKICAgIHBvc2l0aW9uOiByZWxhdGl2ZTsKICAgIGJhY2tncm91bmQtY29sb3I6IHJnYigyMjIsIDIyMiwgMjIyKTsKICAgIGN1cnNvcjogcG9pbnRlcjsKICAgIGJvcmRlci1yYWRpdXM6IDAuMTVyZW07CiAgICBib3JkZXI6IDFweCBzb2xpZCB0cmFuc3BhcmVudDsKICAgIHZlcnRpY2FsLWFsaWduOiBib3R0b207Cn0KLmljb246OmJlZm9yZSB7Y29udGVudDogJyc7IHBvc2l0aW9uOiBhYnNvbHV0ZTsgd2lkdGg6IDJyZW07IGhlaWdodDogMnJlbTsgbGVmdDogMDsgdG9wOiAwO30KLmljb24uZmlsdGVyOjpiZWZvcmUgewogICAgYmFja2dyb3VuZDogdXJsKCcuLi9pbWcvZmlsdGVyLnN2ZycpIGNlbnRlciBjZW50ZXIgLyBhdXRvIDQ1JSBuby1yZXBlYXQ7fQouaWNvbi5zZWFyY2g6OmJlZm9yZSB7CiAgICBiYWNrZ3JvdW5kOiB1cmwoJy4uL2ltZy9zZWFyY2guc3ZnJykgY2VudGVyIGNlbnRlciAvIGF1dG8gNTclIG5vLXJlcGVhdDt9Ci5pY29uLnByZXY6OmJlZm9yZSB7CiAgICBiYWNrZ3JvdW5kOiB1cmwoJy4uL2ltZy9wcmV2LnN2ZycpIGNlbnRlciA0OCUgLyBhdXRvIDQwJSBuby1yZXBlYXQ7fQouaWNvbi5uZXh0OjpiZWZvcmUgewogICAgYmFja2dyb3VuZDogdXJsKCcuLi9pbWcvbmV4dC5zdmcnKSBjZW50ZXIgNDglIC8gYXV0byA0MCUgbm8tcmVwZWF0O30KZm9ybSB7bWFyZ2luLXRvcDogMS41cmVtO30KZm9ybSwgZGwge3dpZHRoOiAxMDAlOyBtYXgtd2lkdGg6IDQwcmVtO30KZm9ybSA+IGRpdiB7ZGlzcGxheTogZmxleDsgbWFyZ2luLWJvdHRvbTogMC4yNXJlbTt9CmZvcm0gPiBkaXYgPiAqOm50aC1jaGlsZCgyKSB7ZmxleC1ncm93OiAxO30KZm9ybSA+IGRpdiA+ICo6bnRoLWNoaWxkKDMpIHttYXJnaW4tbGVmdDogMC4yNXJlbTt9CmZvcm0gPiBidXR0b24ge21hcmdpbi10b3A6IDEuNXJlbTt9CgpkbCB7ZGlzcGxheTogZmxleDsgbWFyZ2luLWJvdHRvbTogMS40cmVtO30KZHQge3dpZHRoOiAxMXJlbTsgbWluLXdpZHRoOiAxMXJlbTsgcGFkZGluZy1yaWdodDogMnJlbTt9CmRkIHtmbGV4LWdyb3c6IDE7fQpkbCArIGRsIHttYXJnaW4tdG9wOiAtMS40cmVtO30KZGwgKyBkbCBkdCB7Ym9yZGVyLXRvcDogMXB4IHNvbGlkIHRyYW5zcGFyZW50O30KZGwgKyBkbCBkZCB7Ym9yZGVyLXRvcDogMXB4IHNvbGlkIHNpbHZlcjt9Cgp1bC5icmVhZGNydW1iIHttYXJnaW4tYm90dG9tOiAwLjNyZW07IGZvbnQtc2l6ZTogMC45cmVtOyBvcGFjaXR5OiAwLjQ1O30KdWwuYnJlYWRjcnVtYiBsaSB7ZGlzcGxheTogaW5saW5lOyBsaXN0LXN0eWxlOiBub25lO30KdWwuYnJlYWRjcnVtYiBsaSArIGxpOjpiZWZvcmUge2NvbnRlbnQ6ICIvICI7fQp1bC5icmVhZGNydW1iIGxpIGEge3RleHQtZGVjb3JhdGlvbjogbm9uZTt9Cgp1bC5yZWxhdGVkIGxpLCB1bC5ob21lIGxpIHtsaXN0LXN0eWxlOiBub25lO30KCgouZmlsdGVyYmFyIHtwYWRkaW5nOiAwLjVyZW0gMC43cmVtOyBiYWNrZ3JvdW5kOiByZ2JhKDAsMCwwLDAuMDUpOyBkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47IGFsaWduLWl0ZW1zOiBjZW50ZXI7IG1hcmdpbi1ib3R0b206IDAuNXJlbTt9CgouY2xvc2U6OmJlZm9yZXtjb250ZW50OiAiKyI7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgdHJhbnNmb3JtOiByb3RhdGUoNDVkZWcpOyBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7fQoKLmFkZEZpbHRlciB7bWFyZ2luLWJvdHRvbTogMC41cmVtOyBkaXNwbGF5OiBub25lO30KLmFkZEZpbHRlci52aXNpYmxlIHtkaXNwbGF5OiBibG9jazt9Ci5hZGRTZWFyY2gge21hcmdpbi1ib3R0b206IDAuNXJlbTsgZGlzcGxheTogbm9uZTt9Ci5hZGRTZWFyY2gudmlzaWJsZSB7ZGlzcGxheTogYmxvY2s7fQouZm9vdGVyYWN0aW9ucyB7bWFyZ2luLXRvcDogMS41cmVtO30KCi5wYWdpbmF0aW9uIHtkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBmb250LXNpemU6IDAuOXJlbTt9Ci5wYWdpbmF0aW9uIC5pY29uIHttYXJnaW4tcmlnaHQ6IDAuMjVyZW07fQouZGlzYWJsZWQge29wYWNpdHk6IDAuNDsgY3Vyc29yOiBkZWZhdWx0O30KLnBhZ2luYXRpb24gLmljb246bGFzdC1jaGlsZCB7bWFyZ2luLXJpZ2h0OiAwOyBtYXJnaW4tbGVmdDogMC4yNXJlbTt9Ci5oaWRkZW4ge2Rpc3BsYXk6IG5vbmU7fQoKQG1lZGlhIG9ubHkgc2NyZWVuIGFuZCAobWluLXdpZHRoOiAxNTAwcHgpIHsKICAgIC5jb250ZW50IHtkaXNwbGF5OiBibG9jazt9CiAgICAudGl0bGUge3Bvc2l0aW9uOiBhYnNvbHV0ZTsgYm90dG9tOiAxLjVyZW07fQogICAgLm5hdmlnYXRpb24ge2Rpc3BsYXk6IGJsb2NrOyB3aWR0aDogMTVyZW07IGhlaWdodDogMTAwdmg7fQogICAgLmJvZHkge3BhZGRpbmctdG9wOiAzcmVtOyBtYXJnaW4tbGVmdDogMTVyZW07IHBhZGRpbmctbGVmdDogNXJlbTt9CiAgICAuaGFtYnVyZ2VyIHtmb250LXNpemU6IDIuNXJlbTsgbGluZS1oZWlnaHQ6IDEuMjsgbWFyZ2luLWxlZnQ6IDEuM3JlbTsgbWFyZ2luLXRvcDogMXJlbTt9Cn0KCi5tb2JpbGUtb25seSB7ZGlzcGxheTogbm9uZTt9CkBtZWRpYSBvbmx5IHNjcmVlbiBhbmQgKG1heC13aWR0aDogNjAwcHgpIHsKCiAgICB1bC5yZWxhdGVkIGxpIGEsIHVsLmhvbWUgbGkgYSAge2Rpc3BsYXk6IGlubGluZS1ibG9jazsgcGFkZGluZzogMC4ycmVtIDA7fQoKICAgIHRhYmxlLnJlYWQgdHIge2Rpc3BsYXk6IGJsb2NrOyBwYWRkaW5nOiAwLjdyZW0gMC41cmVtO30KICAgIHRhYmxlLnJlYWQgdHIgdGQge2Rpc3BsYXk6IGJsb2NrOyBwYWRkaW5nOiAwO30KICAgIHRhYmxlLnJlYWQgdGQ6bnRoLWNoaWxkKG9kZCkge2ZvbnQtd2VpZ2h0OiBib2xkO30KICAgIAogICAgLm1vYmlsZS1vbmx5IHtkaXNwbGF5OiBpbml0aWFsO30KICAgIAogICAgZm9ybSA+IGRpdiB7ZmxleC1kaXJlY3Rpb246IGNvbHVtbjt9CiAgICBzZWxlY3Qge21heC13aWR0aDogMTAwJTt9Cgp9Cg== +Cioge21hcmdpbjogMDsgcGFkZGluZzogMDsgYm94LXNpemluZzogYm9yZGVyLWJveDt9Cmh0bWwge2ZvbnQtc2l6ZTogMTZweDt9CmJvZHkge2ZvbnQtc2l6ZTogMXJlbTsgbGluZS1oZWlnaHQ6IDEuNDt9CgouY29udGVudCB7bWluLWhlaWdodDogMTAwdmg7fQoubmF2aWdhdGlvbiB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiB3aGl0ZTsgCiAgICBkaXNwbGF5OiBmbGV4OyAKICAgIHBvc2l0aW9uOiBmaXhlZDsgCiAgICB3aWR0aDogMTAwJTsgCiAgICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47CiAgICBib3gtc2hhZG93OiAwcHggMHB4IDEwcHggcmdiYSgwLDAsMCwwLjI1KTsKICAgIHotaW5kZXg6IDk7Cn0KLnRpdGxlIHtwYWRkaW5nOiAwLjVyZW0gMnJlbTsgY29sb3I6IGJsYWNrOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7fQoudGl0bGUgc3BhbiB7Zm9udC13ZWlnaHQ6IGJvbGQ7IGRpc3BsYXk6IGJsb2NrO30KLmJvZHkge3BhZGRpbmc6IDZyZW0gMnJlbTsgZmxleC1ncm93OiAxOyBwYWRkaW5nLWJvdHRvbTogMTByZW07fQouaGFtYnVyZ2VyIHtwYWRkaW5nOiAwIDFyZW0gMC41cmVtOyBtYXJnaW46IDAuM3JlbSAxcmVtOyBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgY29sb3I6IGJsYWNrOyBmb250LXNpemU6IDIuMjVyZW07IGxpbmUtaGVpZ2h0OiAxLjI7IHRyYW5zZm9ybTogc2NhbGVYKDEuNSk7fQoKdGguc2VsZWN0ZWQ6OmFmdGVyIHtjb250ZW50OiAiIOKWviI7fQoKdGQsIHRoLCBkbCA+ICoge3BhZGRpbmc6IDAuMnJlbSAxLjVyZW0gMC4ycmVtIDAuNXJlbTsgdGV4dC1hbGlnbjogbGVmdDt9CnRkLCB0aCB7bWF4LXdpZHRoOiA0MHJlbTt9CnRhYmxlLmxpc3QgdGQsIHRhYmxlLmxpc3QgdGgge3RleHQtb3ZlcmZsb3c6IGVsbGlwc2lzOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBvdmVyZmxvdzogaGlkZGVuOyBtYXgtd2lkdGg6IDI1cmVtO30KbGFiZWwge3BhZGRpbmc6IGNhbGMoMC4ycmVtICsgMXB4KSAxLjVyZW0gY2FsYygwLjJyZW0gKyAxcHgpIDA7IHRleHQtb3ZlcmZsb3c6IGVsbGlwc2lzOyB3aGl0ZS1zcGFjZTogbm93cmFwOyBvdmVyZmxvdzogaGlkZGVuO30KdGQ6bGFzdC1jaGlsZCBhIHtkaXNwbGF5OiBpbmxpbmUtYmxvY2s7IG1hcmdpbi1yaWdodDogMC41cmVtO30KLmxvZ28gaW1nIHtoZWlnaHQ6IDJyZW07IHZlcnRpY2FsLWFsaWduOiBtaWRkbGU7fQphIHtjb2xvcjogYmxhY2s7fQpsYWJlbCB7d2lkdGg6IDE1cmVtOyBtaW4td2lkdGg6IDE1cmVtOyBwYWRkaW5nLXJpZ2h0OiAycmVtO30KaDEge2ZvbnQtc2l6ZTogMi4ycmVtOyBtYXJnaW4tYm90dG9tOiAwLjVyZW07IG1hcmdpbi1yaWdodDogMXJlbTsgbGluZS1oZWlnaHQ6IDE7fQpoMiB7Zm9udC1zaXplOiAxLjVyZW07IG1hcmdpbi1ib3R0b206IDAuNXJlbTt9CnRhYmxlIHtib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOyBtYXJnaW4tdG9wOiAxLjVyZW07fQp0YWJsZSB0cjpudGgtY2hpbGQoZXZlbikge2JhY2tncm91bmQ6IHJnYmEoMCwwLDAsMC4wNSk7fQp0YWJsZSB0ciB0aCB7cGFkZGluZy10b3A6IDAuM3JlbTsgcGFkZGluZy1ib3R0b206IDAuM3JlbTsgZm9udC13ZWlnaHQ6IGJvbGQ7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCBibGFjazt9CnAsIHRhYmxlIHttYXJnaW4tYm90dG9tOiAxLjRyZW07fQoKLnRpdGxlYmFyIHtkaXNwbGF5OiBmbGV4OyBhbGlnbi1pdGVtczogY2VudGVyOyBtYXJnaW4tYm90dG9tOiAwLjVyZW07IGZsZXgtd3JhcDogd3JhcDt9Ci50aXRsZWJhciA+IGRpdiB7bWFyZ2luLWJvdHRvbTogMC41cmVtO30KCi5jb2xzIHtjb2x1bW4td2lkdGg6IDcwMHB4OyBtYXJnaW4tdG9wOiAtMmVtO30KLmNvbCB7CiAgICAtd2Via2l0LWNvbHVtbi1icmVhay1pbnNpZGU6IGF2b2lkOyAvKiBDaHJvbWUsIFNhZmFyaSAqLwogICAgcGFnZS1icmVhay1pbnNpZGU6IGF2b2lkOyAgICAgICAgICAgLyogVGhlb3JldGljYWxseSBGRiAyMCsgKi8KICAgIGJyZWFrLWluc2lkZTogYXZvaWQtY29sdW1uOyAgICAgICAgIC8qIElFIDExICovCiAgICBkaXNwbGF5OnRhYmxlOyAgICAgICAgICAgICAgICAgICAgICAvKiBBY3R1YWxseSBGRiAyMCsgKi8KfQoKaW5wdXQsIHRleHRhcmVhLCBzZWxlY3R7CiAgICBoZWlnaHQ6IGNhbGMoMnJlbSArIDJweCk7IAogICAgbGluZS1oZWlnaHQ6IDEuNHJlbTsgCiAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7IAogICAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7IAogICAgcGFkZGluZzogMCAwLjVyZW07IAogICAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7CiAgICBjb2xvcjogYmxhY2s7CiAgICBmb250LWZhbWlseTogaW5oZXJpdDsKICAgIGZvbnQtc2l6ZTogaW5oZXJpdDsKICAgIGZsZXgtZ3JvdzogMTsKICAgIGJhY2tncm91bmQ6IHdoaXRlOwp9CmJ1dHRvbiwgaW5wdXRbdHlwZT0ic3VibWl0Il0sIC5idG4gewogICAgaGVpZ2h0OiBjYWxjKDJyZW0gKyAycHgpOyAKICAgIGxpbmUtaGVpZ2h0OiAycmVtOyAKICAgIGJvcmRlcjogMXB4IHNvbGlkIHRyYW5zcGFyZW50OyAKICAgIGRpc3BsYXk6IGlubGluZS1ibG9jazsgCiAgICB0ZXh0LWRlY29yYXRpb246IG5vbmU7IAogICAgcGFkZGluZzogMCAxcmVtOyAKICAgIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50OwogICAgY29sb3I6IGJsYWNrOwogICAgZm9udC1mYW1pbHk6IGluaGVyaXQ7CiAgICBmb250LXNpemU6IGluaGVyaXQ7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2IoMjIyLCAyMjIsIDIyMik7CiAgICBjdXJzb3I6IHBvaW50ZXI7CiAgICBib3JkZXItcmFkaXVzOiAwLjE1cmVtOwp9CmJ1dHRvbjpmb2N1cywgaW5wdXRbdHlwZT0ic3VibWl0Il06Zm9jdXMsIC5idG46Zm9jdXMsIC5pY29uOmZvY3VzIHtib3JkZXItY29sb3I6IGJsYWNrO30Kc2VsZWN0OjotbXMtZXhwYW5kIHtkaXNwbGF5OiBub25lO30Kc2VsZWN0IHsKICAgIC13ZWJraXQtYXBwZWFyYW5jZTogbm9uZTsKICAgIC1tb3otYXBwZWFyYW5jZTogbm9uZTsKICAgIGFwcGVhcmFuY2U6IG5vbmU7CiAgICBiYWNrZ3JvdW5kOiB3aGl0ZSB1cmwoJy4uL2ltZy9kb3duLnN2ZycpIGNhbGMoMTAwJSAtIDAuNnJlbSkgNTAlIG5vLXJlcGVhdDsKICAgIGJhY2tncm91bmQtc2l6ZTogYXV0byAwLjVyZW07CiAgICBwYWRkaW5nLXJpZ2h0OiAxLjc1cmVtOwogICAgbWF4LXdpZHRoOiBjYWxjKDEwMCUgLSAxNXJlbSk7Cn0KaW5wdXRbdHlwZT0icmFkaW8iXSwgaW5wdXRbdHlwZT0iY2hlY2tib3giXSwgaW5wdXRbdHlwZT0iZmlsZSJdIHtib3JkZXI6IDA7IGhlaWdodDogYXV0bzsgbGluZS1oZWlnaHQ6IGF1dG87IG1hcmdpbi1yaWdodDogMC40cmVtOyB2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlOyBwb3NpdGlvbjogcmVsYXRpdmU7IGJvdHRvbTogMC4xcmVtO30KaW5wdXRbdHlwZT0iZmlsZSJdIHtwYWRkaW5nOiAwOyBtYXJnaW46IDA7IGJvdHRvbTogMDt9CmlucHV0W3R5cGU9J251bWJlciddIHstbW96LWFwcGVhcmFuY2U6IHRleHRmaWVsZDt9CmlucHV0Ojotd2Via2l0LW91dGVyLXNwaW4tYnV0dG9uLCBpbnB1dDo6LXdlYmtpdC1pbm5lci1zcGluLWJ1dHRvbiB7LXdlYmtpdC1hcHBlYXJhbmNlOiBub25lO30KaW5wdXRbdHlwZT0ibnVtYmVyIl0gey1tb3otYXBwZWFyYW5jZTogdGV4dGZpZWxkO30KaW5wdXQ6Oi13ZWJraXQtb3V0ZXItc3Bpbi1idXR0b24sIAppbnB1dDo6LXdlYmtpdC1pbm5lci1zcGluLWJ1dHRvbiB7LXdlYmtpdC1hcHBlYXJhbmNlOiBub25lO30KdGV4dGFyZWEge21pbi1oZWlnaHQ6IDEwcmVtOyBwYWRkaW5nOiAwLjM1cmVtIDAuNXJlbTsgfQoKLmljb24gewogICAgZGlzcGxheTogaW5saW5lLWJsb2NrOyAKICAgIHRleHQtZGVjb3JhdGlvbjogbm9uZTsgCiAgICB3aWR0aDogMDsgCiAgICBoZWlnaHQ6IDA7IAogICAgb3ZlcmZsb3c6IGhpZGRlbjsgCiAgICBwYWRkaW5nLXRvcDogMnJlbTsgCiAgICBwYWRkaW5nLWxlZnQ6IDJyZW07IAogICAgcG9zaXRpb246IHJlbGF0aXZlOwogICAgYmFja2dyb3VuZC1jb2xvcjogcmdiKDIyMiwgMjIyLCAyMjIpOwogICAgY3Vyc29yOiBwb2ludGVyOwogICAgYm9yZGVyLXJhZGl1czogMC4xNXJlbTsKICAgIGJvcmRlcjogMXB4IHNvbGlkIHRyYW5zcGFyZW50OwogICAgdmVydGljYWwtYWxpZ246IGJvdHRvbTsKfQouaWNvbjo6YmVmb3JlIHtjb250ZW50OiAnJzsgcG9zaXRpb246IGFic29sdXRlOyB3aWR0aDogMnJlbTsgaGVpZ2h0OiAycmVtOyBsZWZ0OiAwOyB0b3A6IDA7fQouaWNvbi5maWx0ZXI6OmJlZm9yZSB7CiAgICBiYWNrZ3JvdW5kOiB1cmwoJy4uL2ltZy9maWx0ZXIuc3ZnJykgY2VudGVyIGNlbnRlciAvIGF1dG8gNDUlIG5vLXJlcGVhdDt9Ci5pY29uLnNlYXJjaDo6YmVmb3JlIHsKICAgIGJhY2tncm91bmQ6IHVybCgnLi4vaW1nL3NlYXJjaC5zdmcnKSBjZW50ZXIgY2VudGVyIC8gYXV0byA1NyUgbm8tcmVwZWF0O30KLmljb24ucHJldjo6YmVmb3JlIHsKICAgIGJhY2tncm91bmQ6IHVybCgnLi4vaW1nL3ByZXYuc3ZnJykgY2VudGVyIDQ4JSAvIGF1dG8gNDAlIG5vLXJlcGVhdDt9Ci5pY29uLm5leHQ6OmJlZm9yZSB7CiAgICBiYWNrZ3JvdW5kOiB1cmwoJy4uL2ltZy9uZXh0LnN2ZycpIGNlbnRlciA0OCUgLyBhdXRvIDQwJSBuby1yZXBlYXQ7fQpmb3JtIHttYXJnaW4tdG9wOiAxLjVyZW07fQpmb3JtLCBkbCB7d2lkdGg6IDEwMCU7IG1heC13aWR0aDogNDByZW07fQpmb3JtID4gZGl2IHtkaXNwbGF5OiBmbGV4OyBtYXJnaW4tYm90dG9tOiAwLjI1cmVtO30KZm9ybSA+IGRpdiA+ICo6bnRoLWNoaWxkKDIpIHtmbGV4LWdyb3c6IDE7fQpmb3JtID4gZGl2ID4gKjpudGgtY2hpbGQoMykge21hcmdpbi1sZWZ0OiAwLjI1cmVtO30KZm9ybSA+IGJ1dHRvbiB7bWFyZ2luLXRvcDogMS41cmVtO30KCmRsIHtkaXNwbGF5OiBmbGV4OyBtYXJnaW4tYm90dG9tOiAxLjRyZW07fQpkdCB7d2lkdGg6IDExcmVtOyBtaW4td2lkdGg6IDExcmVtOyBwYWRkaW5nLXJpZ2h0OiAycmVtO30KZGQge2ZsZXgtZ3JvdzogMTt9CmRsICsgZGwge21hcmdpbi10b3A6IC0xLjRyZW07fQpkbCArIGRsIGR0IHtib3JkZXItdG9wOiAxcHggc29saWQgdHJhbnNwYXJlbnQ7fQpkbCArIGRsIGRkIHtib3JkZXItdG9wOiAxcHggc29saWQgc2lsdmVyO30KCnVsLmJyZWFkY3J1bWIge21hcmdpbi1ib3R0b206IDAuM3JlbTsgZm9udC1zaXplOiAwLjlyZW07IG9wYWNpdHk6IDAuNDU7fQp1bC5icmVhZGNydW1iIGxpIHtkaXNwbGF5OiBpbmxpbmU7IGxpc3Qtc3R5bGU6IG5vbmU7fQp1bC5icmVhZGNydW1iIGxpICsgbGk6OmJlZm9yZSB7Y29udGVudDogIi8gIjt9CnVsLmJyZWFkY3J1bWIgbGkgYSB7dGV4dC1kZWNvcmF0aW9uOiBub25lO30KCnVsLnJlbGF0ZWQgbGksIHVsLmhvbWUgbGkge2xpc3Qtc3R5bGU6IG5vbmU7fQoKCi5maWx0ZXJiYXIge3BhZGRpbmc6IDAuNXJlbSAwLjdyZW07IGJhY2tncm91bmQ6IHJnYmEoMCwwLDAsMC4wNSk7IGRpc3BsYXk6IGZsZXg7IGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2VlbjsgYWxpZ24taXRlbXM6IGNlbnRlcjsgbWFyZ2luLWJvdHRvbTogMC41cmVtO30KCi5jbG9zZTo6YmVmb3Jle2NvbnRlbnQ6ICIrIjsgdGV4dC1kZWNvcmF0aW9uOiBub25lOyB0cmFuc2Zvcm06IHJvdGF0ZSg0NWRlZyk7IGRpc3BsYXk6IGlubGluZS1ibG9jazt9CgouYWRkRmlsdGVyIHttYXJnaW4tYm90dG9tOiAwLjVyZW07IGRpc3BsYXk6IG5vbmU7fQouYWRkRmlsdGVyLnZpc2libGUge2Rpc3BsYXk6IGJsb2NrO30KLmFkZFNlYXJjaCB7bWFyZ2luLWJvdHRvbTogMC41cmVtOyBkaXNwbGF5OiBub25lO30KLmFkZFNlYXJjaC52aXNpYmxlIHtkaXNwbGF5OiBibG9jazt9Ci5mb290ZXJhY3Rpb25zIHttYXJnaW4tdG9wOiAxLjVyZW07fQoKLnBhZ2luYXRpb24ge2Rpc3BsYXk6IGZsZXg7IGFsaWduLWl0ZW1zOiBjZW50ZXI7IGZvbnQtc2l6ZTogMC45cmVtO30KLnBhZ2luYXRpb24gLmljb24ge21hcmdpbi1yaWdodDogMC4yNXJlbTt9Ci5kaXNhYmxlZCB7b3BhY2l0eTogMC40OyBjdXJzb3I6IGRlZmF1bHQ7fQoucGFnaW5hdGlvbiAuaWNvbjpsYXN0LWNoaWxkIHttYXJnaW4tcmlnaHQ6IDA7IG1hcmdpbi1sZWZ0OiAwLjI1cmVtO30KLmhpZGRlbiB7ZGlzcGxheTogbm9uZTt9CgpAbWVkaWEgb25seSBzY3JlZW4gYW5kIChtaW4td2lkdGg6IDE1MDBweCkgewogICAgLmNvbnRlbnQge2Rpc3BsYXk6IGJsb2NrO30KICAgIC50aXRsZSB7cG9zaXRpb246IGFic29sdXRlOyBib3R0b206IDEuNXJlbTt9CiAgICAubmF2aWdhdGlvbiB7ZGlzcGxheTogYmxvY2s7IHdpZHRoOiAxNXJlbTsgaGVpZ2h0OiAxMDB2aDt9CiAgICAuYm9keSB7cGFkZGluZy10b3A6IDNyZW07IG1hcmdpbi1sZWZ0OiAxNXJlbTsgcGFkZGluZy1sZWZ0OiA1cmVtO30KICAgIC5oYW1idXJnZXIge2ZvbnQtc2l6ZTogMi41cmVtOyBsaW5lLWhlaWdodDogMS4yOyBtYXJnaW4tbGVmdDogMS4zcmVtOyBtYXJnaW4tdG9wOiAxcmVtO30KfQoKLm1vYmlsZS1vbmx5IHtkaXNwbGF5OiBub25lO30KQG1lZGlhIG9ubHkgc2NyZWVuIGFuZCAobWF4LXdpZHRoOiA2MDBweCkgewoKICAgIHVsLnJlbGF0ZWQgbGkgYSwgdWwuaG9tZSBsaSBhICB7ZGlzcGxheTogaW5saW5lLWJsb2NrOyBwYWRkaW5nOiAwLjJyZW0gMDt9CgogICAgdGFibGUucmVhZCB0aGVhZCB7ZGlzcGxheTogbm9uZTt9CiAgICB0YWJsZS5yZWFkIHRyIHtkaXNwbGF5OiBibG9jazsgcGFkZGluZzogMC43cmVtIDAuNXJlbTt9CiAgICB0YWJsZS5yZWFkIHRyIHRkIHtkaXNwbGF5OiBibG9jazsgcGFkZGluZzogMDt9CiAgICB0YWJsZS5yZWFkIHRkOm50aC1jaGlsZChvZGQpIHtmb250LXdlaWdodDogYm9sZDt9CiAgICAKICAgIC5tb2JpbGUtb25seSB7ZGlzcGxheTogaW5pdGlhbDt9CiAgICAKICAgIGZvcm0gPiBkaXYge2ZsZXgtZGlyZWN0aW9uOiBjb2x1bW47fQogICAgc2VsZWN0IHttYXgtd2lkdGg6IDEwMCU7fQoKfQo= END_OF_STATIC_FILE; } From a67d8f2caec521c48d9f35dba63b8933b072231f Mon Sep 17 00:00:00 2001 From: Maurits van der Schee Date: Thu, 2 Jul 2020 14:51:32 +0200 Subject: [PATCH 02/17] bugfix --- templates/record/list.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/record/list.html b/templates/record/list.html index a0264ce..1adb7f4 100644 --- a/templates/record/list.html +++ b/templates/record/list.html @@ -31,7 +31,7 @@

    {{table|humanize}}

      {{for:column:columns}} - + {{endfor}}   -    
    @@ -58,8 +58,8 @@

    {{table|humanize}}

    Action {{endif}} {{for:column:columns}} - {{if:column.text|neq(primaryKey)}} - {{column.text|humanize}} + {{if:column|neq(primaryKey)}} + {{column|humanize}} {{endif}} {{endfor}} diff --git a/templates/record/read.html b/templates/record/read.html index 432379f..d3341c1 100644 --- a/templates/record/read.html +++ b/templates/record/read.html @@ -19,13 +19,8 @@

    {{name|or("View item")}}

    {{name|humanize}} -<<<<<<< HEAD {{if:field.table|and(field.text)}} - {{field.text}} -======= - {{if:field.table}} {{field.text}} ->>>>>>> e05e30d176d5c9d9c8ad0e5a3129907a68235423 {{else}} {{if:field.type.format|eq("large-string")}}
    {{field.text}}
    @@ -50,7 +45,10 @@

    {{name|or("View item")}}

    Related

    -

    Projectnaam

    +

    {{info.title}}


    - END_OF_HTML; } @@ -2205,7 +2204,7 @@ public function withBody(StreamInterface $body): self return $new; } - private function setHeaders(array $headers): void + private function setHeaders(array $headers) /*:void*/ { foreach ($headers as $header => $value) { if (\is_int($header)) { @@ -2410,7 +2409,7 @@ public function withUri(UriInterface $uri, $preserveHost = false): self return $new; } - private function updateHostFromUri(): void + private function updateHostFromUri() /*:void*/ { if ('' === $host = $this->uri->getHost()) { return; @@ -2448,7 +2447,7 @@ final class Response implements ResponseInterface use MessageTrait; /** @var array Map of standard HTTP status code/reason phrases */ - private const PHRASES = [ + /*private*/ const PHRASES = [ 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect', @@ -2714,7 +2713,7 @@ final class Stream implements StreamInterface private $size; /** @var array Hash of readable and writable stream types */ - private const READ_WRITE_HASH = [ + /*private*/ const READ_WRITE_HASH = [ 'read' => [ 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, @@ -2804,7 +2803,7 @@ public function __toString() } } - public function close(): void + public function close() /*:void*/ { if (isset($this->stream)) { if (\is_resource($this->stream)) { @@ -2828,7 +2827,7 @@ public function detach() return $result; } - public function getSize(): ?int + public function getSize() /*:?int*/ { if (null !== $this->size) { return $this->size; @@ -2872,7 +2871,7 @@ public function isSeekable(): bool return $this->seekable; } - public function seek($offset, $whence = \SEEK_SET): void + public function seek($offset, $whence = \SEEK_SET) /*:void*/ { if (!$this->seekable) { throw new \RuntimeException('Stream is not seekable'); @@ -2883,7 +2882,7 @@ public function seek($offset, $whence = \SEEK_SET): void } } - public function rewind(): void + public function rewind() /*:void*/ { $this->seek(0); } @@ -2966,7 +2965,7 @@ public function getMetadata($key = null) final class UploadedFile implements UploadedFileInterface { /** @var array */ - private const ERRORS = [ + /*private*/ const ERRORS = [ \UPLOAD_ERR_OK => 1, \UPLOAD_ERR_INI_SIZE => 1, \UPLOAD_ERR_FORM_SIZE => 1, @@ -3045,7 +3044,7 @@ public function __construct($streamOrFile, $size, $errorStatus, $clientFilename /** * @throws \RuntimeException if is moved or not ok */ - private function validateActive(): void + private function validateActive() /*:void*/ { if (\UPLOAD_ERR_OK !== $this->error) { throw new \RuntimeException('Cannot retrieve stream due to upload error'); @@ -3069,7 +3068,7 @@ public function getStream(): StreamInterface return Stream::create($resource); } - public function moveTo($targetPath): void + public function moveTo($targetPath) /*:void*/ { $this->validateActive(); @@ -3111,12 +3110,12 @@ public function getError(): int return $this->error; } - public function getClientFilename(): ?string + public function getClientFilename() /*:?string*/ { return $this->clientFilename; } - public function getClientMediaType(): ?string + public function getClientMediaType() /*:?string*/ { return $this->clientMediaType; } @@ -3141,11 +3140,11 @@ final class Uri implements UriInterface { use LowercaseTrait; - private const SCHEMES = ['http' => 80, 'https' => 443]; + /*private*/ const SCHEMES = ['http' => 80, 'https' => 443]; - private const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~'; + /*private*/ const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~'; - private const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;='; + /*private*/ const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;='; /** @var string Uri scheme. */ private $scheme = ''; @@ -3227,7 +3226,7 @@ public function getHost(): string return $this->host; } - public function getPort(): ?int + public function getPort() /*:?int*/ { return $this->port; } @@ -3395,7 +3394,7 @@ private static function isNonStandardPort(string $scheme, int $port): bool return !isset(self::SCHEMES[$scheme]) || $port !== self::SCHEMES[$scheme]; } - private function filterPort($port): ?int + private function filterPort($port) /*:?int*/ { if (null === $port) { return null; @@ -3507,7 +3506,7 @@ public function fromGlobals(): ServerRequestInterface /** * {@inheritdoc} */ - public function fromArrays(array $server, array $headers = [], array $cookie = [], array $get = [], ?array $post = null, array $files = [], $body = null): ServerRequestInterface + public function fromArrays(array $server, array $headers = [], array $cookie = [], array $get = [], /*?array*/ $post = null, array $files = [], $body = null): ServerRequestInterface { $method = $this->getMethodFromEnv($server); $uri = $this->getUriFromEnvWithHTTP($server); @@ -3772,8 +3771,7 @@ public function fromArrays( array $server, array $headers = [], array $cookie = [], - array $get = [], - ?array $post = null, + array $get = [], /*?array*/ $post = null, array $files = [], $body = null ): ServerRequestInterface; @@ -12174,6 +12172,21 @@ public function getColumns(string $table, string $action): array return array_keys($properties); } + public function getInfo() + { + $info = array(); + if (isset($this->definition['info'])) { + $info = $this->definition['info']; + if (!isset($info['title'])) { + $info['title'] = 'PHP-CRUD-UI'; + } + if (!isset($info['x-subtitle'])) { + $info['x-subtitle'] = 'by TQdev.com'; + } + } + return $info; + } + public function getMenu() { $items = array(); @@ -13669,13 +13682,13 @@ public function getPolygonColumnName(): string use Tqdev\PhpCrudApi\Record\ErrorCode; use Tqdev\PhpCrudApi\ResponseUtils; use Tqdev\PhpCrudUi\Client\CrudApi; + use Tqdev\PhpCrudUi\Client\CurlCaller; + use Tqdev\PhpCrudUi\Client\LocalCaller; use Tqdev\PhpCrudUi\Column\SpecificationService; - use Tqdev\PhpCrudUi\Controller\RecordController; use Tqdev\PhpCrudUi\Controller\MultiResponder; - use Tqdev\PhpCrudUi\Record\RecordService; - use Tqdev\PhpCrudUi\Client\LocalCaller; - use Tqdev\PhpCrudUi\Client\CurlCaller; + use Tqdev\PhpCrudUi\Controller\RecordController; use Tqdev\PhpCrudUi\Middleware\StaticFileMiddleware; + use Tqdev\PhpCrudUi\Record\RecordService; class Ui implements RequestHandlerInterface { @@ -13702,6 +13715,7 @@ public function __construct(Config $config) break; } } + $responder->setVariable('info', $definition->getInfo()); $responder->setVariable('base', $router->getBasePath()); $responder->setVariable('menu', $definition->getMenu()); $responder->setVariable('table', ''); @@ -13816,9 +13830,9 @@ public function handle(ServerRequestInterface $request): ResponseInterface $config = new Config([ 'api' => [ - 'username' => 'masterlist', - 'password' => 'masterlist', - 'database' => 'masterlist', + 'username' => 'php-crud-api', + 'password' => 'php-crud-api', + 'database' => 'php-crud-api', ], 'templatePath' => '../templates', ]); From 63d889bbdc8c05197bb23c0b006ba152eb6a62b9 Mon Sep 17 00:00:00 2001 From: Maurits van der Schee Date: Tue, 1 Sep 2020 11:40:04 +0200 Subject: [PATCH 17/17] hamburger --- .../PhpCrudUi/Controller/RecordController.php | 7 ++++++ src/Tqdev/PhpCrudUi/Record/RecordService.php | 5 ++++ templates/layouts/default.html | 4 ++-- templates/layouts/menu.html | 24 +++++++++++++++++++ templates/record/menu.html | 8 +++++++ webroot/css/style.css | 2 +- 6 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 templates/layouts/menu.html create mode 100644 templates/record/menu.html diff --git a/src/Tqdev/PhpCrudUi/Controller/RecordController.php b/src/Tqdev/PhpCrudUi/Controller/RecordController.php index 17c6f93..d69d0d4 100644 --- a/src/Tqdev/PhpCrudUi/Controller/RecordController.php +++ b/src/Tqdev/PhpCrudUi/Controller/RecordController.php @@ -18,6 +18,7 @@ class RecordController public function __construct(Router $router, Responder $responder, RecordService $service) { $router->register('GET', '/', array($this, 'home')); + $router->register('GET', '/menu', array($this, 'menu')); $router->register('GET', '/*/create', array($this, 'createForm')); $router->register('POST', '/*/create', array($this, 'create')); $router->register('GET', '/*/read/*', array($this, 'read')); @@ -39,6 +40,12 @@ public function home(ServerRequestInterface $request): ResponseInterface return $this->responder->success($result); } + public function menu(ServerRequestInterface $request): ResponseInterface + { + $result = $this->service->menu(); + return $this->responder->success($result); + } + public function createForm(ServerRequestInterface $request): ResponseInterface { $table = RequestUtils::getPathSegment($request, 1); diff --git a/src/Tqdev/PhpCrudUi/Record/RecordService.php b/src/Tqdev/PhpCrudUi/Record/RecordService.php index d81d8a8..d9142c9 100644 --- a/src/Tqdev/PhpCrudUi/Record/RecordService.php +++ b/src/Tqdev/PhpCrudUi/Record/RecordService.php @@ -48,6 +48,11 @@ public function home(): TemplateDocument return new TemplateDocument('layouts/default', 'record/home', array()); } + public function menu(): TemplateDocument + { + return new TemplateDocument('layouts/menu', 'record/menu', array()); + } + public function createForm(string $table, string $action): TemplateDocument { $types = $this->definition->getTypes($table, $action); diff --git a/templates/layouts/default.html b/templates/layouts/default.html index d22a50a..c9a7178 100644 --- a/templates/layouts/default.html +++ b/templates/layouts/default.html @@ -8,11 +8,11 @@ - +
    {{content}} diff --git a/templates/layouts/menu.html b/templates/layouts/menu.html new file mode 100644 index 0000000..4f21af0 --- /dev/null +++ b/templates/layouts/menu.html @@ -0,0 +1,24 @@ + + + + + {{info.title}} + + + + + + +
    + +
    + {{content}} +
    +
    + + + \ No newline at end of file diff --git a/templates/record/menu.html b/templates/record/menu.html new file mode 100644 index 0000000..a9eec79 --- /dev/null +++ b/templates/record/menu.html @@ -0,0 +1,8 @@ +
    + \ No newline at end of file diff --git a/webroot/css/style.css b/webroot/css/style.css index cff20f7..ee649f4 100644 --- a/webroot/css/style.css +++ b/webroot/css/style.css @@ -16,7 +16,7 @@ body {font-size: 1rem; line-height: 1.4;} .title {padding: 0.5rem 2rem; color: black; text-decoration: none;} .title span {font-weight: bold; display: block;} .body {padding: 6rem 2rem; flex-grow: 1; padding-bottom: 10rem;} -.hamburger {padding: 0 1rem 0.5rem; margin: 0.3rem 1rem; display: inline-block; text-decoration: none; color: black; font-size: 2.25rem; line-height: 1.2; transform: scaleX(1.5);} +.hamburger {padding: 1rem; margin: 0.3rem 1rem; display: inline-block; text-decoration: none; color: black; font-size: 2.25rem; line-height: 1.2; transform: scaleX(1.5);} th.selected::after {content: " ▾";}