Compare commits

16 Commits

57 changed files with 7616 additions and 70 deletions

23
CakePHP/.github/ISSUE_TEMPLATE.md vendored Normal file

@ -0,0 +1,23 @@
This is a (multiple allowed):
* [x] bug
* [ ] enhancement
* [ ] feature-discussion (RFC)
* CakePHP Application Skeleton Version: EXACT RELEASE VERSION OR COMMIT HASH, HERE.
* Platform and Target: YOUR WEB-SERVER, DATABASE AND OTHER RELEVANT INFO AND HOW THE REQUEST IS BEING MADE, HERE.
### What you did
EXPLAIN WHAT YOU DID, PREFERABLY WITH CODE EXAMPLES, HERE.
### What happened
EXPLAIN WHAT IS ACTUALLY HAPPENING, HERE.
### What you expected to happen
EXPLAIN WHAT IS TO BE EXPECTED, HERE.
P.S. Remember, an issue is not the place to ask questions. You can use [Stack Overflow](https://stackoverflow.com/questions/tagged/cakephp)
for that or join the #cakephp channel on irc.freenode.net, where we will be more
than happy to help answer your questions.
Before you open an issue, please check if a similar issue already exists or has been closed before.

@ -0,0 +1,14 @@
<!---
**PLEASE NOTE:**
This is only a issue tracker for issues related to the CakePHP Application Skeleton.
For CakePHP Framework issues please use this [issue tracker](https://github.com/cakephp/cakephp/issues).
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.
The best way to propose a feature is to open an issue first and discuss your ideas there before implementing them.
Always follow the [contribution guidelines](https://github.com/cakephp/cakephp/blob/master/.github/CONTRIBUTING.md) guidelines when submitting a pull request. In particular, make sure existing tests still pass, and add tests for all new behavior. When fixing a bug, you may want to add a test to verify the fix.
-->

12
CakePHP/.github/dependabot.yml vendored Normal file

@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: composer
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10

89
CakePHP/.github/workflows/ci.yml vendored Normal file

@ -0,0 +1,89 @@
name: CI
on:
push:
branches:
- '4.x'
- '4.next'
- '5.x'
pull_request:
branches:
- '*'
permissions:
contents: read
jobs:
testsuite:
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
php-version: ['7.4', '8.0', '8.1']
name: PHP ${{ matrix.php-version }}
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, intl, pdo_sqlite
coverage: none
- name: Composer install
run: |
if [[ ${{ matrix.php-version }} == '8.1' ]]; then
composer update --ignore-platform-reqs
else
composer update
fi
composer run-script post-install-cmd --no-interaction
- name: Run PHPUnit
run: |
cp config/app_local.example.php config/app_local.php
vendor/bin/phpunit
env:
DATABASE_TEST_URL: sqlite://./testdb.sqlite
coding-standard:
name: Coding Standard
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring, intl
coverage: none
- name: Composer install
run: composer install
- name: Run PHP CodeSniffer
run: composer cs-check
static-analysis:
name: Static Analysis
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
extensions: mbstring, intl
coverage: none
- name: Composer install
run: composer require --dev phpstan/phpstan:^1.0.0
- name: Run phpstan
run: vendor/bin/phpstan

29
CakePHP/.github/workflows/stale.yml vendored Normal file

@ -0,0 +1,29 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
stale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open for 120 days with no activity. Remove the `stale` label or comment or this will be closed in 15 days'
stale-pr-message: 'This pull request is stale because it has been open 30 days with no activity. Remove the `stale` label or comment on this issue, or it will be closed in 15 days'
stale-issue-label: 'stale'
stale-pr-label: 'stale'
days-before-stale: 120
days-before-close: 15
exempt-issue-labels: 'pinned'
exempt-pr-labels: 'pinned'

@ -0,0 +1,47 @@
#
# Bash completion file for CakePHP console.
# Copy this file to a file named `cake` under `/etc/bash_completion.d/`.
# For more info check https://book.cakephp.org/4/en/console-commands/completion.html#how-to-enable-bash-autocompletion-for-the-cakephp-console
#
_cake()
{
local cur prev opts cake
COMPREPLY=()
cake="${COMP_WORDS[0]}"
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
if [[ "$cur" == -* ]] ; then
if [[ ${COMP_CWORD} = 1 ]] ; then
opts=$(${cake} completion options)
elif [[ ${COMP_CWORD} = 2 ]] ; then
opts=$(${cake} completion options "${COMP_WORDS[1]}")
else
opts=$(${cake} completion options "${COMP_WORDS[1]}" "${COMP_WORDS[2]}")
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
if [[ ${COMP_CWORD} = 1 ]] ; then
opts=$(${cake} completion commands)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
if [[ ${COMP_CWORD} = 2 ]] ; then
opts=$(${cake} completion subcommands $prev)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
if [[ $COMPREPLY = "" ]] ; then
_filedir
return 0
fi
return 0
fi
return 0
}
complete -F _cake cake bin/cake

5790
CakePHP/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

6
CakePHP/phpcs.xml Normal file

@ -0,0 +1,6 @@
<?xml version="1.0"?>
<ruleset name="App">
<config name="installed_paths" value="../../cakephp/cakephp-codesniffer"/>
<rule ref="CakePHP"/>
</ruleset>

8
CakePHP/phpstan.neon Normal file

@ -0,0 +1,8 @@
parameters:
level: 8
checkMissingIterableValueType: false
treatPhpDocTypesAsCertain: false
paths:
- src/
excludePaths:
- src/Console/Installer.php

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Controller;
/**
* Addresses Controller
*
* @property \App\Model\Table\AddressesTable $Addresses
* @method \App\Model\Entity\Address[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class AddressesController extends AppController
{
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
$addresses = $this->paginate($this->Addresses);
$this->set(compact('addresses'));
}
/**
* View method
*
* @param string|null $id Address id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
$address = $this->Addresses->get($id, [
'contain' => [],
]);
$this->set(compact('address'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add()
{
$address = $this->Addresses->newEmptyEntity();
if ($this->request->is('post')) {
$address = $this->Addresses->patchEntity($address, $this->request->getData());
if ($this->Addresses->save($address)) {
$this->Flash->success(__('The address has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The address could not be saved. Please, try again.'));
}
$this->set(compact('address'));
}
/**
* Edit method
*
* @param string|null $id Address id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
$address = $this->Addresses->get($id, [
'contain' => [],
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$address = $this->Addresses->patchEntity($address, $this->request->getData());
if ($this->Addresses->save($address)) {
$this->Flash->success(__('The address has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The address could not be saved. Please, try again.'));
}
$this->set(compact('address'));
}
/**
* Delete method
*
* @param string|null $id Address id.
* @return \Cake\Http\Response|null|void Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$address = $this->Addresses->get($id);
if ($this->Addresses->delete($address)) {
$this->Flash->success(__('The address has been deleted.'));
} else {
$this->Flash->error(__('The address could not be deleted. Please, try again.'));
}
return $this->redirect(['action' => 'index']);
}
}

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Controller;
/**
* Users Controller
*
* @property \App\Model\Table\UsersTable $Users
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
*/
class UsersController extends AppController
{
/**
* Index method
*
* @return \Cake\Http\Response|null|void Renders view
*/
public function index()
{
$users = $this->paginate($this->Users);
$this->set(compact('users'));
}
/**
* View method
*
* @param string|null $id User id.
* @return \Cake\Http\Response|null|void Renders view
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function view($id = null)
{
$user = $this->Users->get($id, [
'contain' => [],
]);
$this->set(compact('user'));
}
/**
* Add method
*
* @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise.
*/
public function add()
{
$user = $this->Users->newEmptyEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
$this->set(compact('user'));
}
/**
* Edit method
*
* @param string|null $id User id.
* @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function edit($id = null)
{
$user = $this->Users->get($id, [
'contain' => [],
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$user = $this->Users->patchEntity($user, $this->request->getData());
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
}
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
$this->set(compact('user'));
}
/**
* Delete method
*
* @param string|null $id User id.
* @return \Cake\Http\Response|null|void Redirects to index.
* @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
*/
public function delete($id = null)
{
$this->request->allowMethod(['post', 'delete']);
$user = $this->Users->get($id);
if ($this->Users->delete($user)) {
$this->Flash->success(__('The user has been deleted.'));
} else {
$this->Flash->error(__('The user could not be deleted. Please, try again.'));
}
return $this->redirect(['action' => 'index']);
}
}

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
/**
* Address Entity
*
* @property int $id
* @property int $owner
* @property string $first
* @property string $last
* @property string $street
* @property string $zip
* @property string $city
* @property string $phone
*/
class Address extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* @var array<string, bool>
*/
protected $_accessible = [
'owner' => true,
'first' => true,
'last' => true,
'street' => true,
'zip' => true,
'city' => true,
'phone' => true,
];
}

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\Model\Entity;
use Cake\ORM\Entity;
/**
* User Entity
*
* @property int $id
* @property string $password
* @property string $nick
* @property string $first
* @property string $last
* @property bool $is_admin
*/
class User extends Entity
{
/**
* Fields that can be mass assigned using newEntity() or patchEntity().
*
* Note that when '*' is set to true, this allows all unspecified fields to
* be mass assigned. For security purposes, it is advised to set '*' to false
* (or remove it), and explicitly make individual fields accessible as needed.
*
* @var array<string, bool>
*/
protected $_accessible = [
'password' => true,
'nick' => true,
'first' => true,
'last' => true,
'is_admin' => true,
];
/**
* Fields that are excluded from JSON versions of the entity.
*
* @var array<string>
*/
protected $_hidden = [
'password',
];
}

@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
/**
* Addresses Model
*
* @method \App\Model\Entity\Address newEmptyEntity()
* @method \App\Model\Entity\Address newEntity(array $data, array $options = [])
* @method \App\Model\Entity\Address[] newEntities(array $data, array $options = [])
* @method \App\Model\Entity\Address get($primaryKey, $options = [])
* @method \App\Model\Entity\Address findOrCreate($search, ?callable $callback = null, $options = [])
* @method \App\Model\Entity\Address patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method \App\Model\Entity\Address[] patchEntities(iterable $entities, array $data, array $options = [])
* @method \App\Model\Entity\Address|false save(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \App\Model\Entity\Address saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \App\Model\Entity\Address[]|\Cake\Datasource\ResultSetInterface|false saveMany(iterable $entities, $options = [])
* @method \App\Model\Entity\Address[]|\Cake\Datasource\ResultSetInterface saveManyOrFail(iterable $entities, $options = [])
* @method \App\Model\Entity\Address[]|\Cake\Datasource\ResultSetInterface|false deleteMany(iterable $entities, $options = [])
* @method \App\Model\Entity\Address[]|\Cake\Datasource\ResultSetInterface deleteManyOrFail(iterable $entities, $options = [])
*/
class AddressesTable extends Table
{
/**
* Initialize method
*
* @param array $config The configuration for the Table.
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('addresses');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
}
/**
* Default validation rules.
*
* @param \Cake\Validation\Validator $validator Validator instance.
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
->integer('owner')
->requirePresence('owner', 'create')
->notEmptyString('owner');
$validator
->scalar('first')
->maxLength('first', 80)
->requirePresence('first', 'create')
->notEmptyString('first');
$validator
->scalar('last')
->maxLength('last', 80)
->requirePresence('last', 'create')
->notEmptyString('last');
$validator
->scalar('street')
->maxLength('street', 80)
->requirePresence('street', 'create')
->notEmptyString('street');
$validator
->scalar('zip')
->maxLength('zip', 10)
->requirePresence('zip', 'create')
->notEmptyString('zip');
$validator
->scalar('city')
->maxLength('city', 80)
->requirePresence('city', 'create')
->notEmptyString('city');
$validator
->scalar('phone')
->maxLength('phone', 30)
->requirePresence('phone', 'create')
->notEmptyString('phone');
return $validator;
}
}

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
/**
* Users Model
*
* @method \App\Model\Entity\User newEmptyEntity()
* @method \App\Model\Entity\User newEntity(array $data, array $options = [])
* @method \App\Model\Entity\User[] newEntities(array $data, array $options = [])
* @method \App\Model\Entity\User get($primaryKey, $options = [])
* @method \App\Model\Entity\User findOrCreate($search, ?callable $callback = null, $options = [])
* @method \App\Model\Entity\User patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method \App\Model\Entity\User[] patchEntities(iterable $entities, array $data, array $options = [])
* @method \App\Model\Entity\User|false save(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \App\Model\Entity\User saveOrFail(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface|false saveMany(iterable $entities, $options = [])
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface saveManyOrFail(iterable $entities, $options = [])
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface|false deleteMany(iterable $entities, $options = [])
* @method \App\Model\Entity\User[]|\Cake\Datasource\ResultSetInterface deleteManyOrFail(iterable $entities, $options = [])
*/
class UsersTable extends Table
{
/**
* Initialize method
*
* @param array $config The configuration for the Table.
* @return void
*/
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('users');
$this->setDisplayField('id');
$this->setPrimaryKey('id');
}
/**
* Default validation rules.
*
* @param \Cake\Validation\Validator $validator Validator instance.
* @return \Cake\Validation\Validator
*/
public function validationDefault(Validator $validator): Validator
{
$validator
->scalar('password')
->maxLength('password', 256)
->requirePresence('password', 'create')
->notEmptyString('password');
$validator
->scalar('nick')
->maxLength('nick', 20)
->requirePresence('nick', 'create')
->notEmptyString('nick')
->add('nick', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']);
$validator
->scalar('first')
->maxLength('first', 40)
->requirePresence('first', 'create')
->notEmptyString('first');
$validator
->scalar('last')
->maxLength('last', 40)
->requirePresence('last', 'create')
->notEmptyString('last');
$validator
->boolean('is_admin')
->requirePresence('is_admin', 'create')
->notEmptyString('is_admin');
return $validator;
}
/**
* Returns a rules checker object that will be used for validating
* application integrity.
*
* @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
* @return \Cake\ORM\RulesChecker
*/
public function buildRules(RulesChecker $rules): RulesChecker
{
$rules->add($rules->isUnique(['nick']), ['errorField' => 'nick']);
return $rules;
}
}

@ -0,0 +1,33 @@
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\Address $address
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('List Addresses'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="addresses form content">
<?= $this->Form->create($address) ?>
<fieldset>
<legend><?= __('Add Address') ?></legend>
<?php
echo $this->Form->control('owner');
echo $this->Form->control('first');
echo $this->Form->control('last');
echo $this->Form->control('street');
echo $this->Form->control('zip');
echo $this->Form->control('city');
echo $this->Form->control('phone');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>

@ -0,0 +1,38 @@
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\Address $address
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Form->postLink(
__('Delete'),
['action' => 'delete', $address->id],
['confirm' => __('Are you sure you want to delete # {0}?', $address->id), 'class' => 'side-nav-item']
) ?>
<?= $this->Html->link(__('List Addresses'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="addresses form content">
<?= $this->Form->create($address) ?>
<fieldset>
<legend><?= __('Edit Address') ?></legend>
<?php
echo $this->Form->control('owner');
echo $this->Form->control('first');
echo $this->Form->control('last');
echo $this->Form->control('street');
echo $this->Form->control('zip');
echo $this->Form->control('city');
echo $this->Form->control('phone');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>

@ -0,0 +1,56 @@
<?php
/**
* @var \App\View\AppView $this
* @var iterable<\App\Model\Entity\Address> $addresses
*/
?>
<div class="addresses index content">
<?= $this->Html->link(__('New Address'), ['action' => 'add'], ['class' => 'button float-right']) ?>
<h3><?= __('Addresses') ?></h3>
<div class="table-responsive">
<table>
<thead>
<tr>
<th><?= $this->Paginator->sort('id') ?></th>
<th><?= $this->Paginator->sort('owner') ?></th>
<th><?= $this->Paginator->sort('first') ?></th>
<th><?= $this->Paginator->sort('last') ?></th>
<th><?= $this->Paginator->sort('street') ?></th>
<th><?= $this->Paginator->sort('zip') ?></th>
<th><?= $this->Paginator->sort('city') ?></th>
<th><?= $this->Paginator->sort('phone') ?></th>
<th class="actions"><?= __('Actions') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($addresses as $address): ?>
<tr>
<td><?= $this->Number->format($address->id) ?></td>
<td><?= $this->Number->format($address->owner) ?></td>
<td><?= h($address->first) ?></td>
<td><?= h($address->last) ?></td>
<td><?= h($address->street) ?></td>
<td><?= h($address->zip) ?></td>
<td><?= h($address->city) ?></td>
<td><?= h($address->phone) ?></td>
<td class="actions">
<?= $this->Html->link(__('View'), ['action' => 'view', $address->id]) ?>
<?= $this->Html->link(__('Edit'), ['action' => 'edit', $address->id]) ?>
<?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $address->id], ['confirm' => __('Are you sure you want to delete # {0}?', $address->id)]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="paginator">
<ul class="pagination">
<?= $this->Paginator->first('<< ' . __('first')) ?>
<?= $this->Paginator->prev('< ' . __('previous')) ?>
<?= $this->Paginator->numbers() ?>
<?= $this->Paginator->next(__('next') . ' >') ?>
<?= $this->Paginator->last(__('last') . ' >>') ?>
</ul>
<p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?></p>
</div>
</div>

@ -0,0 +1,56 @@
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\Address $address
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('Edit Address'), ['action' => 'edit', $address->id], ['class' => 'side-nav-item']) ?>
<?= $this->Form->postLink(__('Delete Address'), ['action' => 'delete', $address->id], ['confirm' => __('Are you sure you want to delete # {0}?', $address->id), 'class' => 'side-nav-item']) ?>
<?= $this->Html->link(__('List Addresses'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
<?= $this->Html->link(__('New Address'), ['action' => 'add'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="addresses view content">
<h3><?= h($address->id) ?></h3>
<table>
<tr>
<th><?= __('First') ?></th>
<td><?= h($address->first) ?></td>
</tr>
<tr>
<th><?= __('Last') ?></th>
<td><?= h($address->last) ?></td>
</tr>
<tr>
<th><?= __('Street') ?></th>
<td><?= h($address->street) ?></td>
</tr>
<tr>
<th><?= __('Zip') ?></th>
<td><?= h($address->zip) ?></td>
</tr>
<tr>
<th><?= __('City') ?></th>
<td><?= h($address->city) ?></td>
</tr>
<tr>
<th><?= __('Phone') ?></th>
<td><?= h($address->phone) ?></td>
</tr>
<tr>
<th><?= __('Id') ?></th>
<td><?= $this->Number->format($address->id) ?></td>
</tr>
<tr>
<th><?= __('Owner') ?></th>
<td><?= $this->Number->format($address->owner) ?></td>
</tr>
</table>
</div>
</div>
</div>

@ -0,0 +1,31 @@
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\User $user
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('List Users'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="users form content">
<?= $this->Form->create($user) ?>
<fieldset>
<legend><?= __('Add User') ?></legend>
<?php
echo $this->Form->control('password');
echo $this->Form->control('nick');
echo $this->Form->control('first');
echo $this->Form->control('last');
echo $this->Form->control('is_admin');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>

@ -0,0 +1,36 @@
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\User $user
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Form->postLink(
__('Delete'),
['action' => 'delete', $user->id],
['confirm' => __('Are you sure you want to delete # {0}?', $user->id), 'class' => 'side-nav-item']
) ?>
<?= $this->Html->link(__('List Users'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="users form content">
<?= $this->Form->create($user) ?>
<fieldset>
<legend><?= __('Edit User') ?></legend>
<?php
echo $this->Form->control('password');
echo $this->Form->control('nick');
echo $this->Form->control('first');
echo $this->Form->control('last');
echo $this->Form->control('is_admin');
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
</div>
</div>

@ -0,0 +1,50 @@
<?php
/**
* @var \App\View\AppView $this
* @var iterable<\App\Model\Entity\User> $users
*/
?>
<div class="users index content">
<?= $this->Html->link(__('New User'), ['action' => 'add'], ['class' => 'button float-right']) ?>
<h3><?= __('Users') ?></h3>
<div class="table-responsive">
<table>
<thead>
<tr>
<th><?= $this->Paginator->sort('id') ?></th>
<th><?= $this->Paginator->sort('nick') ?></th>
<th><?= $this->Paginator->sort('first') ?></th>
<th><?= $this->Paginator->sort('last') ?></th>
<th><?= $this->Paginator->sort('is_admin') ?></th>
<th class="actions"><?= __('Actions') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?= $this->Number->format($user->id) ?></td>
<td><?= h($user->nick) ?></td>
<td><?= h($user->first) ?></td>
<td><?= h($user->last) ?></td>
<td><?= h($user->is_admin) ?></td>
<td class="actions">
<?= $this->Html->link(__('View'), ['action' => 'view', $user->id]) ?>
<?= $this->Html->link(__('Edit'), ['action' => 'edit', $user->id]) ?>
<?= $this->Form->postLink(__('Delete'), ['action' => 'delete', $user->id], ['confirm' => __('Are you sure you want to delete # {0}?', $user->id)]) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="paginator">
<ul class="pagination">
<?= $this->Paginator->first('<< ' . __('first')) ?>
<?= $this->Paginator->prev('< ' . __('previous')) ?>
<?= $this->Paginator->numbers() ?>
<?= $this->Paginator->next(__('next') . ' >') ?>
<?= $this->Paginator->last(__('last') . ' >>') ?>
</ul>
<p><?= $this->Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?></p>
</div>
</div>

@ -0,0 +1 @@
<?php

@ -0,0 +1,44 @@
<?php
/**
* @var \App\View\AppView $this
* @var \App\Model\Entity\User $user
*/
?>
<div class="row">
<aside class="column">
<div class="side-nav">
<h4 class="heading"><?= __('Actions') ?></h4>
<?= $this->Html->link(__('Edit User'), ['action' => 'edit', $user->id], ['class' => 'side-nav-item']) ?>
<?= $this->Form->postLink(__('Delete User'), ['action' => 'delete', $user->id], ['confirm' => __('Are you sure you want to delete # {0}?', $user->id), 'class' => 'side-nav-item']) ?>
<?= $this->Html->link(__('List Users'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
<?= $this->Html->link(__('New User'), ['action' => 'add'], ['class' => 'side-nav-item']) ?>
</div>
</aside>
<div class="column-responsive column-80">
<div class="users view content">
<h3><?= h($user->id) ?></h3>
<table>
<tr>
<th><?= __('Nick') ?></th>
<td><?= h($user->nick) ?></td>
</tr>
<tr>
<th><?= __('First') ?></th>
<td><?= h($user->first) ?></td>
</tr>
<tr>
<th><?= __('Last') ?></th>
<td><?= h($user->last) ?></td>
</tr>
<tr>
<th><?= __('Id') ?></th>
<td><?= $this->Number->format($user->id) ?></td>
</tr>
<tr>
<th><?= __('Is Admin') ?></th>
<td><?= $user->is_admin ? __('Yes') : __('No'); ?></td>
</tr>
</table>
</div>
</div>
</div>

@ -0,0 +1,11 @@
<?php
/**
* @var \App\View\AppView $this
* @var array $params
* @var string $message
*/
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="message" onclick="this.classList.add('hidden');"><?= $message ?></div>

@ -0,0 +1,11 @@
<?php
/**
* @var \App\View\AppView $this
* @var array $params
* @var string $message
*/
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="message warning" onclick="this.classList.add('hidden');"><?= $message ?></div>

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
/**
* AddressesFixture
*/
class AddressesFixture extends TestFixture
{
/**
* Init method
*
* @return void
*/
public function init(): void
{
$this->records = [
[
'id' => 1,
'owner' => 1,
'first' => 'Lorem ipsum dolor sit amet',
'last' => 'Lorem ipsum dolor sit amet',
'street' => 'Lorem ipsum dolor sit amet',
'zip' => 'Lorem ip',
'city' => 'Lorem ipsum dolor sit amet',
'phone' => 'Lorem ipsum dolor sit amet',
],
];
parent::init();
}
}

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
/**
* UsersFixture
*/
class UsersFixture extends TestFixture
{
/**
* Init method
*
* @return void
*/
public function init(): void
{
$this->records = [
[
'id' => 1,
'password' => 'Lorem ipsum dolor sit amet',
'nick' => 'Lorem ipsum dolor ',
'first' => 'Lorem ipsum dolor sit amet',
'last' => 'Lorem ipsum dolor sit amet',
'is_admin' => 1,
],
];
parent::init();
}
}

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Controller;
use App\Controller\AddressesController;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
/**
* App\Controller\AddressesController Test Case
*
* @uses \App\Controller\AddressesController
*/
class AddressesControllerTest extends TestCase
{
use IntegrationTestTrait;
/**
* Fixtures
*
* @var array<string>
*/
protected $fixtures = [
'app.Addresses',
];
/**
* Test index method
*
* @return void
* @uses \App\Controller\AddressesController::index()
*/
public function testIndex(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test view method
*
* @return void
* @uses \App\Controller\AddressesController::view()
*/
public function testView(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test add method
*
* @return void
* @uses \App\Controller\AddressesController::add()
*/
public function testAdd(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test edit method
*
* @return void
* @uses \App\Controller\AddressesController::edit()
*/
public function testEdit(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test delete method
*
* @return void
* @uses \App\Controller\AddressesController::delete()
*/
public function testDelete(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
}

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Controller;
use App\Controller\UsersController;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
/**
* App\Controller\UsersController Test Case
*
* @uses \App\Controller\UsersController
*/
class UsersControllerTest extends TestCase
{
use IntegrationTestTrait;
/**
* Fixtures
*
* @var array<string>
*/
protected $fixtures = [
'app.Users',
];
/**
* Test index method
*
* @return void
* @uses \App\Controller\UsersController::index()
*/
public function testIndex(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test view method
*
* @return void
* @uses \App\Controller\UsersController::view()
*/
public function testView(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test add method
*
* @return void
* @uses \App\Controller\UsersController::add()
*/
public function testAdd(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test edit method
*
* @return void
* @uses \App\Controller\UsersController::edit()
*/
public function testEdit(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test delete method
*
* @return void
* @uses \App\Controller\UsersController::delete()
*/
public function testDelete(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
}

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Model\Table;
use App\Model\Table\AddressesTable;
use Cake\TestSuite\TestCase;
/**
* App\Model\Table\AddressesTable Test Case
*/
class AddressesTableTest extends TestCase
{
/**
* Test subject
*
* @var \App\Model\Table\AddressesTable
*/
protected $Addresses;
/**
* Fixtures
*
* @var array<string>
*/
protected $fixtures = [
'app.Addresses',
];
/**
* setUp method
*
* @return void
*/
protected function setUp(): void
{
parent::setUp();
$config = $this->getTableLocator()->exists('Addresses') ? [] : ['className' => AddressesTable::class];
$this->Addresses = $this->getTableLocator()->get('Addresses', $config);
}
/**
* tearDown method
*
* @return void
*/
protected function tearDown(): void
{
unset($this->Addresses);
parent::tearDown();
}
/**
* Test validationDefault method
*
* @return void
* @uses \App\Model\Table\AddressesTable::validationDefault()
*/
public function testValidationDefault(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
}

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Model\Table;
use App\Model\Table\UsersTable;
use Cake\TestSuite\TestCase;
/**
* App\Model\Table\UsersTable Test Case
*/
class UsersTableTest extends TestCase
{
/**
* Test subject
*
* @var \App\Model\Table\UsersTable
*/
protected $Users;
/**
* Fixtures
*
* @var array<string>
*/
protected $fixtures = [
'app.Users',
];
/**
* setUp method
*
* @return void
*/
protected function setUp(): void
{
parent::setUp();
$config = $this->getTableLocator()->exists('Users') ? [] : ['className' => UsersTable::class];
$this->Users = $this->getTableLocator()->get('Users', $config);
}
/**
* tearDown method
*
* @return void
*/
protected function tearDown(): void
{
unset($this->Users);
parent::tearDown();
}
/**
* Test validationDefault method
*
* @return void
* @uses \App\Model\Table\UsersTable::validationDefault()
*/
public function testValidationDefault(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
/**
* Test buildRules method
*
* @return void
* @uses \App\Model\Table\UsersTable::buildRules()
*/
public function testBuildRules(): void
{
$this->markTestIncomplete('Not implemented yet.');
}
}

4
CakePHP/tests/schema.sql Normal file

@ -0,0 +1,4 @@
-- Test database schema.
--
-- If you are not using CakePHP migrations you can put
-- your application's schema in this file and use it in tests.

8
CakePHP/webroot/css/normalize.min.css vendored Normal file

@ -0,0 +1,8 @@
/**
* Minified by jsDelivr using clean-css v4.2.1.
* Original file: /npm/normalize.css@8.0.1/normalize.css
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}

88
addressbook.sql Normal file

@ -0,0 +1,88 @@
-- MariaDB dump 10.19 Distrib 10.5.15-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: tracer_addressbook
-- ------------------------------------------------------
-- Server version 10.5.15-MariaDB-0+deb11u1-log
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `addresses`
--
DROP TABLE IF EXISTS `addresses`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `addresses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`owner` int(11) NOT NULL,
`first` varchar(80) NOT NULL,
`last` varchar(80) NOT NULL,
`street` varchar(80) NOT NULL,
`zip` varchar(10) NOT NULL,
`city` varchar(80) NOT NULL,
`phone` varchar(30) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_user` (`owner`),
CONSTRAINT `fk_user` FOREIGN KEY (`owner`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `addresses`
--
LOCK TABLES `addresses` WRITE;
/*!40000 ALTER TABLE `addresses` DISABLE KEYS */;
INSERT INTO `addresses` VALUES (1,2,'a \"test\"','a','Webfoot Street','1313','Duckburg2','555-12345'),(4,1,'c','b','street4','zip4','city4','phone4'),(6,1,'Huey','Duck','Webfoot Street','1010','Duckburg','555.3456'),(7,1,'Dewey','Duck','Webfoot Street','2020','Duckburg','555-9876'),(8,1,'Louie','Duck','Webfoot Street','3030','Duckburg 3','555-8765'),(11,1,'b','Clapton','sdfg','12456^^^>','https://xd.adobe.com/','23343'),(14,1,'d','aa','','','<script>alert(\'test\')</script>','x'),(16,1,'Adam \"The Badass\"','Black','piouhpouhpouh','132213','piugpiugh','9760978');
/*!40000 ALTER TABLE `addresses` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`password` varchar(256) NOT NULL,
`nick` varchar(20) NOT NULL,
`first` varchar(40) NOT NULL,
`last` varchar(40) NOT NULL,
`is_admin` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `nick` (`nick`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `users`
--
LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES (1,'$argon2i$v=19$m=65536,t=4,p=1$OFV0Rnl6OXNWZXZJNTFjTw$fC9K1ykszZ/UaEm21XR9M3+XxnBc+dZ7PRIn5aaGw8I','donald','Donald','Duck',1),(2,'$argon2i$v=19$m=65536,t=4,p=1$NVRKNm0xUmplYkcwTFZXdw$GLp1jjLDBRjKSw6nH8SqqSls6fQPi4Hb7ot0k3naf5s','Daisy','Daisy','Duck',0);
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2022-11-01 18:37:04

@ -10,9 +10,9 @@
namespace App\Controller;
use App\Entity\User;
use App\Repository\UserRepository;
use App\Service\Router;
use App\Service\Template;
use App\Repository\UserRepository;
class AddressBookAdminController
{

@ -9,12 +9,13 @@
namespace App\Controller;
use App\Entity\User;
use App\AddressRepository;
use App\Entity\AddressBookEntry;
use App\Entity\User;
use App\Enums\StatusCode;
use App\Enums\UserAuth;
use App\Service\Router;
use App\Service\Template;
use App\Repository\AddressRepository;
class AddressBookController
{
@ -71,16 +72,61 @@ class AddressBookController
{
$_POST = json_decode(json: file_get_contents(filename: "php://input"), associative: true);
$address = new AddressBookEntry(owner: $_POST['owner'], first: $_POST['first'], last: $_POST['last'], street: $_POST['street'], zip: $_POST['zip'], city: $_POST['city'], phone: $_POST['phone'], id: $_POST['id']);
$this->addressRepository->update(address: $address);
if (empty($_POST)) {
$this->template->renderJson(results: [
'status' => 400,
'message' => 'BAD REQUEST'
]);
}
if ($address = new AddressBookEntry(owner: $_POST['owner'], first: $_POST['first'], last: $_POST['last'], street: $_POST['street'], zip: $_POST['zip'], city: $_POST['city'], phone: $_POST['phone'], id: $_POST['id'])) {
if ($this->addressRepository->update(address: $address)) {
$status = 200;
$message = 'OK';
} else {
$status = 400;
$message = 'BAD_REQUEST';
}
} else {
$status = 400;
$message = "BAD REQUEST";
}
$this->template->renderJson(results: [
'status' => $status,
'message' => $message
]);
}
public function deleteAddress(): void
{
echo "in del";
$_POST = json_decode(json: file_get_contents(filename: "php://input"), associative: true);
if (empty($_POST)) {
$this->template->renderJson(results: [
'status' => 400,
'message' => 'BAD REQUEST'
]);
}
if ($address = $this->addressRepository->findByID(id: $_POST['id'])) {
$this->addressRepository->delete(addressBookEntry: $address);
if ($this->addressRepository->delete(addressBookEntry: $address)) {
$this->template->renderJson(results: [
'status' => 200,
'message' => 'OK'
]);
} else {
$this->template->renderJson(results: [
'status' => 400,
'message' => 'BAD REQUEST'
]);
}
} else {
$this->template->renderJson(results: [
'status' => 400,
'message' => 'BAD REQUEST'
]);
}
}

@ -9,8 +9,8 @@
namespace App\Repository;
use Mikro24\Service\DatabaseConnection;
use App\Entity\AddressBookEntry;
use App\Service\DatabaseConnection;
use PDO;
use PDOException;
@ -147,9 +147,7 @@ class AddressRepository
$statement->bindParam(param: 'zip', var: $zip);
$statement->bindParam(param: 'city', var: $city);
$statement->bindParam(param: 'phone', var: $phone);
$statement->execute();
return $statement->rowCount();
return $statement->execute();
} catch (PDOException $e) {
echo $e->getMessage();
return false;
@ -167,9 +165,7 @@ class AddressRepository
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$id = $addressBookEntry->getId();
$statement->bindParam(param: 'id', var: $id);
$statement->execute();
return $statement->rowCount();
return $statement->execute();
} catch (PDOException $e) {
exit($e->getMessage());
}

@ -30,8 +30,15 @@ function editAddress(id) {
.then(
response => response.text()
).then(
html => console.log(html)
);
json => {
let jsonObject = JSON.parse(json)
if (jsonObject.status === 200) {
setInfo('Data successfully saved.')
} else {
setError(jsonObject.message);
}
}
);
document.getElementById('first_' + id).disabled = true
document.getElementById('last_' + id).disabled = true
@ -66,8 +73,15 @@ function deleteAddress(id) {
.then(
response => response.text()
).then(
html => console.log(html)
);
json => {
let jsonObject = JSON.parse(json)
if (jsonObject.status === 200) {
setInfo('Data successfully saved.')
} else {
setError(jsonObject.message);
}
}
);
let row = document.getElementById('row_' + id)
row.parentNode.removeChild(row)
}
@ -84,17 +98,29 @@ function sortBy(column) {
document.getElementById(title).innerHTML = upCase(title)
)
// switch direction on every call
let currentSortOrder = document.getElementById(column + '_sort')
console.log("col", column)
console.log("curcol", currentColumn)
if (currentColumn === column) {
console.log("in switch")
// switch direction on every call on same column
if (currentSortOrder === 'asc') {
currentSortOrder = 'desc'
} else {
currentSortOrder = 'asc'
}
console.log("col", column)
} else {
currentColumn = column
}
let currentTitleElement = document.getElementById(column)
let currentTitle = currentTitleElement.innerHTML
let newTitle
if (currentSortOrder.innerHTML === 'asc') {
currentSortOrder.innerHTML = 'desc'
if (currentSortOrder === 'asc') {
newTitle = currentTitle[0] + currentTitle.substring(1) + '&nbsp;&#11015;'
} else {
currentSortOrder.innerHTML = 'asc'
newTitle = currentTitle[0] + currentTitle.substring(1) + '&nbsp;&#11014;'
}
currentTitleElement.innerHTML = newTitle
@ -117,9 +143,8 @@ function sortBy(column) {
let rowYNumber = rowYId.match(/\d+/)
let valueY = document.getElementById(column + '_' + rowYNumber).value
let currentSortOrder = document.getElementById(column + '_sort')
let sortOrder
if (currentSortOrder.innerHTML === 'asc') {
if (currentSortOrder === 'asc') {
sortOrder = 1
} else {
sortOrder = -1
@ -132,6 +157,47 @@ function sortBy(column) {
}
}
function setInfo(info) {
const infoBox = document.getElementById('info_box')
infoBox.innerHTML = info
infoBox.style.display = 'block'
infoBox.classList.add('panel_float')
setTimeout(() => {
infoBox.style.display = 'none'
}, 2500)
}
function setError(error) {
const errorBox = document.getElementById('error_box')
const errorText = document.getElementById('error_text')
const infoButton = document.getElementById('info_button')
if (errorBox.style.display === 'block') {
errorBox.style.display = 'none'
return
}
if (infoButton != null) {
infoButton.disabled = true
}
errorText.innerHTML = error
errorBox.style.display = 'block'
errorBox.classList.add('panel_float')
}
function closeError() {
const errorBox = document.getElementById('error_box')
const infoButton = document.getElementById('info_button')
if (infoButton) {
infoButton.disabled = false
}
errorBox.style.display = 'none'
}
// global scope
let currentSortOrder = 'desc'
let currentColumn = 'last'
document.addEventListener('DOMContentLoaded', () => {
const table = document.getElementById('address_table') || false
if (table) {

@ -36,3 +36,92 @@ label {
display: block;
padding: 1ex;
}
.panel_float {
position: fixed;
overflow: hidden;
z-index: 2400;
opacity: 0.70;
margin: auto;
top: 110px !important;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-ms-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
}
#info_box {
background-color: #3fc52a;
border: solid #cdcdcd;
border-radius: 5px;
color: #1f1f1f;
display: none;
padding: 10px;
font-weight: bold;
position: absolute;
z-index: 10;
width: 50%;
margin-left: 200px;
margin-right: 200px;
}
.info_button {
border-radius: 5px;
border: solid #cdcdcd;
color: #1f1f1f;
padding: 8px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
background-color: #3fc52a;
transition-duration: 0.4s;
cursor: pointer;
}
#error_box {
background-color: #e06844;
border: solid #cdcdcd;
border-radius: 5px;
color: #1f1f1f;
display: none;
padding: 10px;
font-weight: bold;
position: absolute;
z-index: 10;
width: 50%;
margin-left: 200px;
margin-right: 200px;
}
.error_button {
border-radius: 5px;
border: solid #cdcdcd;
color: #1f1f1f;
padding: 8px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
background-color: #e06844;
transition-duration: 0.4s;
cursor: pointer;
}
.close_button {
margin-left: 15px;
color: white;
font-weight: bold;
float: right;
font-size: 22px;
line-height: 20px;
cursor: pointer;
transition: 0.3s;
}
.close_button:hover {
color: black;
}

@ -7,7 +7,7 @@
*
*/
namespace App\Controller;
namespace App\Controllers;
use App\Entity\User;
use App\Repository\UserRepository;

@ -7,10 +7,10 @@
*
*/
namespace App\Repository;
namespace Mikro24\Repositories;
use App\Service\DatabaseConnection;
use App\Entity\User;
use Mikro24\Service\DatabaseConnection;
use Mikro24\Models\User;
use PDO;
use PDOException;
@ -137,7 +137,7 @@ class UserRepository
}
public function update(User $user): bool|int
public function update(User $user): bool
{
$id = $user->getId();
$nick = $user->getNick();
@ -169,9 +169,7 @@ class UserRepository
$statement->bindParam(param: 'first', var: $first);
$statement->bindParam(param: 'last', var: $last);
$statement->bindParam(param: 'is_admin', var: $isAdmin);
$statement->execute();
return $statement->rowCount();
return $statement->execute();
} catch (PDOException $e) {
echo $e->getMessage();
return false;
@ -179,7 +177,7 @@ class UserRepository
}
public function delete(User $user): int
public function delete(User $user): bool
{
$sql = "
DELETE FROM " . DatabaseConnection::TABLE_USERS . "
@ -189,9 +187,7 @@ class UserRepository
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$id = $user->getId();
$statement->bindParam(param: 'id', var: $id);
$statement->execute();
return $statement->rowCount();
return $statement->execute();
} catch (PDOException $e) {
exit($e->getMessage());
}

@ -7,7 +7,7 @@
*
*/
namespace App\Service;
namespace Mikro24\Services;
/**
*
@ -19,7 +19,7 @@ class Config
public function __construct()
{
// Check for either config.json.local or config.json.
$configFile = dirname(path: __DIR__, levels: 2) . "/config.json.local";
$configFile = dirname(path: __DIR__, levels: 2) . "/config.local.json";
if (!file_exists(filename: $configFile)) {
$configFile = dirname(path: __DIR__, levels: 2) . "/config.json";
}

@ -7,14 +7,14 @@
*
*/
namespace App\Service;
namespace Mikro24\Services;
use App\Controller\AddressBookAdminController;
use App\Controller\AddressBookController;
use App\Controller\SecurityController;
use App\Entity\User;
use App\Repository\AddressRepository;
use App\Repository\UserRepository;
use Mikro24\Controllers\SecurityController;
use Mikro24\Models\User;
use Mikro24\Repositories\UserRepository;
use App\Controllers\AddressBookAdminController;
use App\Controllers\AddressBookController;
use App\Repositories\AddressRepository;
/*
* A quick and dirty class container for DI.
@ -57,10 +57,10 @@ class Container
public function get(string $className): object
{
return match ($className) {
'App\Controller\AddressBookController' => $this->addressBook,
'App\Controller\AddressBookAdminController' => $this->addressBookAdmin,
'App\Controller\SecurityController' => $this->securityController,
'App\Service\Router' => $this->router,
'App\Controllers\AddressBookController' => $this->addressBook,
'App\Controllers\AddressBookAdminController' => $this->addressBookAdmin,
'Mikro24\Controllers\SecurityController' => $this->securityController,
'Mikro24\Service\Router' => $this->router,
//default => throw new Exception(message: "Missing class definition: $class")
default => die("Missing class definition: $className")
};

@ -7,7 +7,7 @@
*
*/
namespace App\Service;
namespace Mikro24\Services;
use PDO;

@ -7,10 +7,10 @@
*
*/
namespace App\Service;
namespace Mikro24\Services;
use App\Entity\Route;
use Mikro24\Models\Route;
use Closure;
/*

@ -6,7 +6,7 @@
* file that was distributed with this source code.
*/
namespace App\Service;
namespace Mikro24\Services;
/*
* As I'm not allowed to use 3rd party code like Twig or Smarty I ended up
@ -41,4 +41,14 @@ class Template
}
exit(0);
}
/*
* For AJAX calls, return json
*/
public function renderJson(array $results): never
{
http_response_code(response_code: $results['status']);
echo json_encode(value: $results);
exit(0);
}
}

@ -1,3 +1,7 @@
<script src="/assets/js/functions.js"></script>
<?php if (!empty($message)): ?>
<script>setError('<?= $message ?>')</script>
<?php endif; ?>
</body>
</html>

@ -18,5 +18,10 @@
<?php else: ?>
<a href="<?= $router->path('app_logout'); ?>">&#9099;&nbsp;Logout</a>
<?php endif; ?>
<br>
<div id="info_box">Info</div>
<div id="error_box">
<span class="close_button" onclick="closeError()">&times;</span>
<div id="error_text"></div>
</div>
<br>

@ -1,6 +1,10 @@
<?php include dirname(path: __DIR__) . '/_header.html.php'; ?>
<br>
<h1>Address Book - Admin</h1>
<a href="<?= $router->path('app_admin_users'); ?>">&#128113;&nbsp;Users</a>
<?php include dirname(path: __DIR__) . '/_footer.html.php' ?>
<button type="button" class="info_button" id="info_button" onclick="setInfo('Test Info - auto hide')">Info</button>
<button type="button" class="error_button" onclick="setError('Test Error - must be closed manually')">Error</button>

@ -33,14 +33,6 @@
</td>
</tr>
<?php endforeach; ?>
<tr style="display:none;">
<td id="first_sort">desc</td>
<td id="last_sort">desc</td>
<td id="street_sort">desc</td>
<td id="zip_sort">desc</td>
<td id="city_sort">desc</td>
<td id="phone_sort">desc</td>
</tr>
</table>
</form>

@ -5,12 +5,6 @@
<?php else: ?>
<br>
<?php if ($message): ?>
<div class="info">
<?= $message ?>
</div>
<?php endif; ?>
<form method="POST">
<label for="nick">Username</label>
<input type="text" name="nick" id="nick">