new: [initial] import of the project files

- Cerebrate v0.1 initial WIP version

[Features]
- initial version of TrustDB
  - Organisation
  - User scope
  - Alignments between Users and Organisations
  - semi-implemented encryption key store

[Internals]
- MISP systems transposed to CakePHP4 / Bootstrap 4 / Cerebrate
  - UI factories
    - Index factories
    - Form factories
  - internal:
    - RestResponse library
    - ACL library in progress
  - new clean UI using BS4

- New systems:
  - UI factories
    - view factories

- Datamodel import/cleaning via SQL scripts
pull/14/head
iglocska 2020-05-29 13:41:58 +02:00
parent f14b332538
commit 56368d1de7
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
189 changed files with 50051 additions and 0 deletions

23
app/.editorconfig Normal file
View File

@ -0,0 +1,23 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.bat]
end_of_line = crlf
[*.yml]
indent_size = 2
[*.twig]
insert_final_newline = false
[Makefile]
indent_style = tab

54
app/.gitattributes vendored Normal file
View File

@ -0,0 +1,54 @@
# Define the line ending behavior of the different file extensions
# Set default behavior, in case users don't have core.autocrlf set.
* text=auto
* text eol=lf
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
*.php text
*.default text
*.ctp text
*.sql text
*.md text
*.po text
*.js text
*.css text
*.ini text
*.properties text
*.txt text
*.xml text
*.svg text
*.yml text
.htaccess text
# Declare files that will always have CRLF line endings on checkout.
*.bat eol=crlf
# Declare files that will always have LF line endings on checkout.
*.pem eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.gif binary
*.webp binary
*.ico binary
*.mo binary
*.pdf binary
*.phar binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.eot binary
*.gz binary
*.bz2 binary
*.7z binary
*.zip binary
*.webm binary
*.mp4 binary
*.ogv binary
# Remove files for archives generated using `git archive`
phpstan.neon export-ignore
.github export-ignore

43
app/.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
# CakePHP specific files #
##########################
/config/app_local.php
/config/.env
/logs/*
/tmp/*
/vendor/*
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
.directory
# Tool specific files #
#######################
# PHPUnit
.phpunit.result.cache
# vim
*~
*.swp
*.swo
# sublime text & textmate
*.sublime-*
*.stTheme.cache
*.tmlanguage.cache
*.tmPreferences.cache
# Eclipse
.settings/*
# JetBrains, aka PHPStorm, IntelliJ IDEA
.idea/*
# NetBeans
nbproject/*
# Visual Studio Code
.vscode
# Sass preprocessor
.sass-cache/

5
app/.htaccess Normal file
View File

@ -0,0 +1,5 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>

176
app/INSTALL/MYSQL.sql Normal file
View File

@ -0,0 +1,176 @@
CREATE TABLE IF NOT EXISTS alignment_tags (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
alignment_id int(10) UNSIGNED NOT NULL,
tag_id int(10) UNSIGNED NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS alignments (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
individual_id int(10) UNSIGNED NOT NULL,
organisation_id int(10) UNSIGNED NOT NULL,
type varchar(191) DEFAULT 'member',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS authkeys (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
authkey varchar(40) CHARSET ascii COLLATE ascii_general_ci,
created int(10) UNSIGNED NOT NULL,
valid_until int(10) UNSIGNED NOT NULL,
user_id int(10) UNSIGNED NOT NULL,
PRIMARY KEY (id),
INDEX (authkey),
INDEX (created),
INDEX (valid_until)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS broods (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid varchar(40) CHARSET ascii COLLATE ascii_general_ci DEFAULT NULL,
name varchar(191) NOT NULL,
url varchar(191) NOT NULL,
description text,
organisation_id int(10) UNSIGNED NOT NULL,
alignment_id int(10) UNSIGNED NOT NULL,
trusted tinyint(1),
pull tinyint(1),
authkey varchar(40) CHARSET ascii COLLATE ascii_general_ci,
PRIMARY KEY (id),
INDEX (uuid),
INDEX (name),
INDEX (url),
INDEX (authkey)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS individuals (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid varchar(40) CHARSET ascii COLLATE ascii_general_ci DEFAULT NULL,
email varchar(191) NOT NULL,
first_name varchar(191) NOT NULL,
last_name varchar(191) NOT NULL,
position text,
PRIMARY KEY (id),
INDEX (uuid),
INDEX (email),
INDEX (first_name),
INDEX (last_name)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS individual_encryption_keys (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
individual_id int(10) UNSIGNED NOT NULL,
encryption_key_id int(10) UNSIGNED NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS encryption_keys (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid varchar(40) CHARSET ascii COLLATE ascii_general_ci DEFAULT NULL,
type varchar(191) NOT NULL,
encryption_key text,
revoked tinyint(1),
expires int(10) UNSIGNED,
PRIMARY KEY (id),
INDEX (uuid),
INDEX (type),
INDEX (expires)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS organisation_encryption_keys (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
organisation_id int(10) UNSIGNED NOT NULL,
encryption_key_id int(10) UNSIGNED NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS organisations (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid varchar(40) CHARSET ascii COLLATE ascii_general_ci DEFAULT NULL,
name varchar(191) NOT NULL,
url varchar(191),
nationality varchar(191),
sector varchar(191),
type varchar(191),
contacts text,
PRIMARY KEY (id),
INDEX (uuid),
INDEX (name),
INDEX (url),
INDEX (nationality),
INDEX (sector),
INDEX (type)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS roles (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid varchar(40) CHARSET ascii COLLATE ascii_general_ci DEFAULT NULL,
name varchar(191) NOT NULL,
is_default tinyint(1),
perm_admin tinyint(1),
PRIMARY KEY (id),
INDEX (name),
INDEX (uuid)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS tags (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL,
description text,
colour varchar(6) CHARSET ascii COLLATE ascii_general_ci NOT NULL,
PRIMARY KEY (id),
INDEX (name)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS user_keys (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
user_id int(10) UNSIGNED NOT NULL,
authkey_id int(10) UNSIGNED NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS users (
id int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid varchar(40) CHARSET ascii COLLATE ascii_general_ci DEFAULT NULL,
email varchar(191) NOT NULL,
password varchar(191),
role_id int(11) UNSIGNED NOT NULL,
individual_id int(11) UNSIGNED NOT NULL,
PRIMARY KEY (id),
INDEX (uuid),
INDEX (email)
) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE alignment_tags
ADD FOREIGN KEY (alignment_id) REFERENCES alignments(id),
ADD FOREIGN KEY (tag_id) REFERENCES tags(id);
ALTER TABLE alignments
ADD FOREIGN KEY (individual_id) REFERENCES individuals(id),
ADD FOREIGN KEY (organisation_id) REFERENCES organisations(id);
ALTER TABLE authkeys
ADD FOREIGN KEY (user_id) REFERENCES users(id);
ALTER TABLE broods
ADD FOREIGN KEY (alignment_id) REFERENCES alignments(id);
ALTER TABLE individual_encryption_keys
ADD FOREIGN KEY (individual_id) REFERENCES individuals(id),
ADD FOREIGN KEY (encryption_key_id) REFERENCES encryption_keys(id);
ALTER TABLE organisation_encryption_keys
ADD FOREIGN KEY (organisation_id) REFERENCES organisations(id),
ADD FOREIGN KEY (encryption_key_id) REFERENCES encryption_keys(id);
ALTER TABLE user_keys
ADD FOREIGN KEY (user_id) REFERENCES users(id),
ADD FOREIGN KEY (authkey_id) REFERENCES authkeys(id);
ALTER TABLE users
ADD FOREIGN KEY (role_id) REFERENCES roles(id),
ADD FOREIGN KEY (individual_id) REFERENCES individuals(id);

14
app/INSTALL/clean.sql Normal file
View File

@ -0,0 +1,14 @@
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS alignment_keys;
DROP TABLE IF EXISTS alignments;
DROP TABLE IF EXISTS authkeys;
DROP TABLE IF EXISTS broods;
DROP TABLE IF EXISTS individuals;
DROP TABLE IF EXISTS encryption_keys;
DROP TABLE IF EXISTS organisation_encryption_keys;
DROP TABLE IF EXISTS organisations;
DROP TABLE IF EXISTS roles;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS user_keys;
DROP TABLE IF EXISTS users;
SET FOREIGN_KEY_CHECKS=1;

6
app/INSTALL/data.sql Normal file
View File

@ -0,0 +1,6 @@
INSERT INTO organisations (uuid, name, url, nationality, sector, type, contacts)
VALUES ('11111111-1111-1111-1111-111111111111', 'foo', 'http://google.com', 'NO', '', '', '');
INSERT INTO organisations (uuid, name, url, nationality, sector, type, contacts)
VALUES ('11111111-1111-1111-1111-111111111112', 'bar', 'http://bing.com', 'HU', '', '', '');

53
app/README.md Normal file
View File

@ -0,0 +1,53 @@
# CakePHP Application Skeleton
[![Build Status](https://img.shields.io/travis/cakephp/app/master.svg?style=flat-square)](https://travis-ci.org/cakephp/app)
[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/app.svg?style=flat-square)](https://packagist.org/packages/cakephp/app)
[![PHPStan](https://img.shields.io/badge/PHPStan-level%207-brightgreen.svg?style=flat-square)](https://github.com/phpstan/phpstan)
A skeleton for creating applications with [CakePHP](https://cakephp.org) 4.x.
The framework source code can be found here: [cakephp/cakephp](https://github.com/cakephp/cakephp).
## Installation
1. Download [Composer](https://getcomposer.org/doc/00-intro.md) or update `composer self-update`.
2. Run `php composer.phar create-project --prefer-dist cakephp/app [app_name]`.
If Composer is installed globally, run
```bash
composer create-project --prefer-dist cakephp/app
```
In case you want to use a custom app dir name (e.g. `/myapp/`):
```bash
composer create-project --prefer-dist cakephp/app myapp
```
You can now either use your machine's webserver to view the default home page, or start
up the built-in webserver with:
```bash
bin/cake server -p 8765
```
Then visit `http://localhost:8765` to see the welcome page.
## Update
Since this skeleton is a starting point for your application and various files
would have been modified as per your needs, there isn't a way to provide
automated upgrades, so you have to do any updates manually.
## Configuration
Read and edit the environment specific `config/app_local.php` and setup the
`'Datasources'` and any other configuration relevant for your application.
Other environment agnostic settings can be changed in `config/app.php`.
## Layout
The app skeleton uses [Milligram](https://milligram.io/) (v1.3) minimalist CSS
framework by default. You can, however, replace it with any other library or
custom styles.

75
app/bin/cake Executable file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env sh
################################################################################
#
# Cake is a shell script for invoking CakePHP shell commands
#
# CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
#
# @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
# @link https://cakephp.org CakePHP(tm) Project
# @since 1.2.0
# @license https://opensource.org/licenses/mit-license.php MIT License
#
################################################################################
# Canonicalize by following every symlink of the given name recursively
canonicalize() {
NAME="$1"
if [ -f "$NAME" ]
then
DIR=$(dirname -- "$NAME")
NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME")
fi
while [ -h "$NAME" ]; do
DIR=$(dirname -- "$NAME")
SYM=$(readlink "$NAME")
NAME=$(cd "$DIR" > /dev/null && cd "$(dirname -- "$SYM")" > /dev/null && pwd)/$(basename -- "$SYM")
done
echo "$NAME"
}
# Find a CLI version of PHP
findCliPhp() {
for TESTEXEC in php php-cli /usr/local/bin/php
do
SAPI=$(echo "<?= PHP_SAPI ?>" | $TESTEXEC 2>/dev/null)
if [ "$SAPI" = "cli" ]
then
echo $TESTEXEC
return
fi
done
echo "Failed to find a CLI version of PHP; falling back to system standard php executable" >&2
echo "php";
}
# If current path is a symlink, resolve to real path
realname="$0"
if [ -L "$realname" ]
then
realname=$(readlink -f "$0")
fi
CONSOLE=$(dirname -- "$(canonicalize "$realname")")
APP=$(dirname "$CONSOLE")
# If your CLI PHP is somewhere that this doesn't find, you can define a PHP environment
# variable with the correct path in it.
if [ -z "$PHP" ]
then
PHP=$(findCliPhp)
fi
if [ "$(basename "$realname")" != 'cake' ]
then
exec "$PHP" "$CONSOLE"/cake.php "$(basename "$realname")" "$@"
else
exec "$PHP" "$CONSOLE"/cake.php "$@"
fi
exit

27
app/bin/cake.bat Normal file
View File

@ -0,0 +1,27 @@
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: Cake is a Windows batch script for invoking CakePHP shell commands
::
:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
::
:: Licensed under The MIT License
:: Redistributions of files must retain the above copyright notice.
::
:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: @link https://cakephp.org CakePHP(tm) Project
:: @since 2.0.0
:: @license https://opensource.org/licenses/mit-license.php MIT License
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off
SET app=%0
SET lib=%~dp0
php "%lib%cake.php" %*
echo.
exit /B %ERRORLEVEL%

12
app/bin/cake.php Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/php -q
<?php
// Check platform requirements
require dirname(__DIR__) . '/config/requirements.php';
require dirname(__DIR__) . '/vendor/autoload.php';
use App\Application;
use Cake\Console\CommandRunner;
// Build the runner with an application and root executable name.
$runner = new CommandRunner(new Application(dirname(__DIR__) . '/config'), 'cake');
exit($runner->run($argv));

56
app/composer.json Normal file
View File

@ -0,0 +1,56 @@
{
"name": "cakephp/app",
"description": "CakePHP skeleton app",
"homepage": "https://cakephp.org",
"type": "project",
"license": "MIT",
"require": {
"php": ">=7.2",
"cakephp/cakephp": "^4.0",
"cakephp/migrations": "^3.0",
"cakephp/plugin-installer": "^1.2",
"mobiledetect/mobiledetectlib": "^2.8"
},
"require-dev": {
"cakephp/bake": "^2.0.3",
"cakephp/cakephp-codesniffer": "~4.0.0",
"cakephp/debug_kit": "^4.0",
"josegonzalez/dotenv": "^3.2",
"phpunit/phpunit": "^8.5",
"psy/psysh": "@stable"
},
"suggest": {
"markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.",
"dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan/Psalm compatibility.",
"phpstan/phpstan": "PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code."
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Test\\": "tests/",
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
}
},
"scripts": {
"post-install-cmd": "App\\Console\\Installer::postInstall",
"post-create-project-cmd": "App\\Console\\Installer::postInstall",
"post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump",
"check": [
"@test",
"@cs-check"
],
"cs-check": "phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/",
"cs-fix": "phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP src/ tests/",
"stan": "phpstan analyse src/",
"test": "phpunit --colors=always"
},
"prefer-stable": true,
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

4548
app/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

38
app/config/.env.example Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Used as a default to seed config/.env which
# enables you to use environment variables to configure
# the aspects of your application that vary by
# environment.
#
# Having this file in production is considered a **SECURITY RISK** and also decreases
# the boostrap performance of your application.
#
# To use this file, first copy it into `config/.env`. Also ensure the related
# code block for loading this file is uncommented in `config/boostrap.php`
#
# In development .env files are parsed by PHP
# and set into the environment. This provides a simpler
# development workflow over standard environment variables.
export APP_NAME="__APP_NAME__"
export DEBUG="true"
export APP_ENCODING="UTF-8"
export APP_DEFAULT_LOCALE="en_US"
export APP_DEFAULT_TIMEZONE="UTC"
export SECURITY_SALT="__SALT__"
# Uncomment these to define cache configuration via environment variables.
#export CACHE_DURATION="+2 minutes"
#export CACHE_DEFAULT_URL="file://tmp/cache/?prefix=${APP_NAME}_default&duration=${CACHE_DURATION}"
#export CACHE_CAKECORE_URL="file://tmp/cache/persistent?prefix=${APP_NAME}_cake_core&serialize=true&duration=${CACHE_DURATION}"
#export CACHE_CAKEMODEL_URL="file://tmp/cache/models?prefix=${APP_NAME}_cake_model&serialize=true&duration=${CACHE_DURATION}"
# Uncomment these to define email transport configuration via environment variables.
#export EMAIL_TRANSPORT_DEFAULT_URL=""
# Uncomment these to define database configuration via environment variables.
#export DATABASE_URL="mysql://my_app:secret@localhost/${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true&quoteIdentifiers=false&persistent=false"
#export DATABASE_TEST_URL="mysql://my_app:secret@localhost/test_${APP_NAME}?encoding=utf8&timezone=UTC&cacheMetadata=true&quoteIdentifiers=false&persistent=false"
# Uncomment these to define logging configuration via environment variables.
#export LOG_DEBUG_URL="file://logs/?levels[]=notice&levels[]=info&levels[]=debug&file=debug"
#export LOG_ERROR_URL="file://logs/?levels[]=warning&levels[]=error&levels[]=critical&levels[]=alert&levels[]=emergency&file=error"

401
app/config/app.php Normal file
View File

@ -0,0 +1,401 @@
<?php
use Cake\Cache\Engine\FileEngine;
use Cake\Database\Connection;
use Cake\Database\Driver\Mysql;
use Cake\Database\Driver\Sqlite;
use Cake\Error\ExceptionRenderer;
use Cake\Log\Engine\FileLog;
use Cake\Mailer\Transport\MailTransport;
return [
/*
* Debug Level:
*
* Production Mode:
* false: No error messages, errors, or warnings shown.
*
* Development Mode:
* true: Errors and warnings shown.
*/
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
/*
* Configure basic information about the application.
*
* - namespace - The namespace to find app classes under.
* - defaultLocale - The default locale for translation, formatting currencies and numbers, date and time.
* - encoding - The encoding used for HTML + database connections.
* - base - The base directory the app resides in. If false this
* will be auto detected.
* - dir - Name of app directory.
* - webroot - The webroot directory.
* - wwwRoot - The file path to webroot.
* - baseUrl - To configure CakePHP to *not* use mod_rewrite and to
* use CakePHP pretty URLs, remove these .htaccess
* files:
* /.htaccess
* /webroot/.htaccess
* And uncomment the baseUrl key below.
* - fullBaseUrl - A base URL to use for absolute links. When set to false (default)
* CakePHP generates required value based on `HTTP_HOST` environment variable.
* However, you can define it manually to optimize performance or if you
* are concerned about people manipulating the `Host` header.
* - imageBaseUrl - Web path to the public images directory under webroot.
* - cssBaseUrl - Web path to the public css directory under webroot.
* - jsBaseUrl - Web path to the public js directory under webroot.
* - paths - Configure paths for non class based resources. Supports the
* `plugins`, `templates`, `locales` subkeys, which allow the definition of
* paths for plugins, view templates and locale files respectively.
*/
'App' => [
'namespace' => 'App',
'encoding' => env('APP_ENCODING', 'UTF-8'),
'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'),
'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'UTC'),
'base' => false,
'dir' => 'src',
'webroot' => 'webroot',
'wwwRoot' => WWW_ROOT,
//'baseUrl' => env('SCRIPT_NAME'),
'fullBaseUrl' => false,
'imageBaseUrl' => 'img/',
'cssBaseUrl' => 'css/',
'jsBaseUrl' => 'js/',
'paths' => [
'plugins' => [ROOT . DS . 'plugins' . DS],
'templates' => [ROOT . DS . 'templates' . DS],
'locales' => [RESOURCES . 'locales' . DS],
],
],
/*
* Security and encryption configuration
*
* - salt - A random string used in security hashing methods.
* The salt value is also used as the encryption key.
* You should treat it as extremely sensitive data.
*/
'Security' => [
'salt' => env('SECURITY_SALT'),
],
/*
* Apply timestamps with the last modified time to static assets (js, css, images).
* Will append a querystring parameter containing the time the file was modified.
* This is useful for busting browser caches.
*
* Set to true to apply timestamps when debug is true. Set to 'force' to always
* enable timestamping regardless of debug value.
*/
'Asset' => [
//'timestamp' => true,
// 'cacheTime' => '+1 year'
],
/*
* Configure the cache adapters.
*/
'Cache' => [
'default' => [
'className' => FileEngine::class,
'path' => CACHE,
'url' => env('CACHE_DEFAULT_URL', null),
],
/*
* Configure the cache used for general framework caching.
* Translation cache files are stored with this configuration.
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
* If you set 'className' => 'Null' core cache will be disabled.
*/
'_cake_core_' => [
'className' => FileEngine::class,
'prefix' => 'myapp_cake_core_',
'path' => CACHE . 'persistent' . DS,
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKECORE_URL', null),
],
/*
* Configure the cache for model and datasource caches. This cache
* configuration is used to store schema descriptions, and table listings
* in connections.
* Duration will be set to '+2 minutes' in bootstrap.php when debug = true
*/
'_cake_model_' => [
'className' => FileEngine::class,
'prefix' => 'myapp_cake_model_',
'path' => CACHE . 'models' . DS,
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKEMODEL_URL', null),
],
/*
* Configure the cache for routes. The cached routes collection is built the
* first time the routes are processed through `config/routes.php`.
* Duration will be set to '+2 seconds' in bootstrap.php when debug = true
*/
'_cake_routes_' => [
'className' => FileEngine::class,
'prefix' => 'myapp_cake_routes_',
'path' => CACHE,
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKEROUTES_URL', null),
],
],
/*
* Configure the Error and Exception handlers used by your application.
*
* By default errors are displayed using Debugger, when debug is true and logged
* by Cake\Log\Log when debug is false.
*
* In CLI environments exceptions will be printed to stderr with a backtrace.
* In web environments an HTML page will be displayed for the exception.
* With debug true, framework errors like Missing Controller will be displayed.
* When debug is false, framework errors will be coerced into generic HTTP errors.
*
* Options:
*
* - `errorLevel` - int - The level of errors you are interested in capturing.
* - `trace` - boolean - Whether or not backtraces should be included in
* logged errors/exceptions.
* - `log` - boolean - Whether or not you want exceptions logged.
* - `exceptionRenderer` - string - The class responsible for rendering
* uncaught exceptions. If you choose a custom class you should place
* the file for that class in src/Error. This class needs to implement a
* render method.
* - `skipLog` - array - List of exceptions to skip for logging. Exceptions that
* extend one of the listed exceptions will also be skipped for logging.
* E.g.:
* `'skipLog' => ['Cake\Http\Exception\NotFoundException', 'Cake\Http\Exception\UnauthorizedException']`
* - `extraFatalErrorMemory` - int - The number of megabytes to increase
* the memory limit by when a fatal error is encountered. This allows
* breathing room to complete logging or error handling.
*/
'Error' => [
'errorLevel' => E_ALL,
'exceptionRenderer' => ExceptionRenderer::class,
'skipLog' => [],
'log' => true,
'trace' => true,
],
/*
* Email configuration.
*
* By defining transports separately from delivery profiles you can easily
* re-use transport configuration across multiple profiles.
*
* You can specify multiple configurations for production, development and
* testing.
*
* Each transport needs a `className`. Valid options are as follows:
*
* Mail - Send using PHP mail function
* Smtp - Send using SMTP
* Debug - Do not send the email, just return the result
*
* You can add custom transports (or override existing transports) by adding the
* appropriate file to src/Mailer/Transport. Transports should be named
* 'YourTransport.php', where 'Your' is the name of the transport.
*/
'EmailTransport' => [
'default' => [
'className' => MailTransport::class,
/*
* The keys host, port, timeout, username, password, client and tls
* are used in SMTP transports
*/
'host' => 'localhost',
'port' => 25,
'timeout' => 30,
/*
* It is recommended to set these options through your environment or app_local.php
*/
//'username' => null,
//'password' => null,
'client' => null,
'tls' => false,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
],
/*
* Email delivery profiles
*
* Delivery profiles allow you to predefine various properties about email
* messages from your application and give the settings a name. This saves
* duplication across your application and makes maintenance and development
* easier. Each profile accepts a number of keys. See `Cake\Mailer\Email`
* for more information.
*/
'Email' => [
'default' => [
'transport' => 'default',
'from' => 'you@localhost',
/*
* Will by default be set to config value of App.encoding, if that exists otherwise to UTF-8.
*/
//'charset' => 'utf-8',
//'headerCharset' => 'utf-8',
],
],
/*
* Connection information used by the ORM to connect
* to your application's datastores.
*
* ### Notes
* - Drivers include Mysql Postgres Sqlite Sqlserver
* See vendor\cakephp\cakephp\src\Database\Driver for complete list
* - Do not use periods in database name - it may lead to error.
* See https://github.com/cakephp/cakephp/issues/6471 for details.
* - 'encoding' is recommended to be set to full UTF-8 4-Byte support.
* E.g set it to 'utf8mb4' in MariaDB and MySQL and 'utf8' for any
* other RDBMS.
*/
'Datasources' => [
/**
* These configurations should contain permanent settings used
* by all environments.
*
* The values in app_local.php will override any values set here
* and should be used for local and per-environment configurations.
*
* Environment variable based configurations can be loaded here or
* in app_local.php depending on the applications needs.
*/
'default' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
/**
* For MariaDB/MySQL the internal default changed from utf8 to utf8mb4, aka full utf-8 support, in CakePHP 3.6
*/
//'encoding' => 'utf8mb4',
/**
* If your MySQL server is configured with `skip-character-set-client-handshake`
* then you MUST use the `flags` config to set your charset encoding.
* For e.g. `'flags' => [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4']`
*/
'flags' => [],
'cacheMetadata' => true,
'log' => false,
/*
* Set identifier quoting to true if you are using reserved words or
* special characters in your table or column names. Enabling this
* setting will result in queries built using the Query Builder having
* identifiers quoted when creating SQL. It should be noted that this
* decreases performance because each query needs to be traversed and
* manipulated before being executed.
*/
'quoteIdentifiers' => false,
/*
* During development, if using MySQL < 5.6, uncommenting the
* following line could boost the speed at which schema metadata is
* fetched from the database. It can also be set directly with the
* mysql configuration directive 'innodb_stats_on_metadata = 0'
* which is the recommended value in production environments
*/
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'className' => Connection::class,
'driver' => Mysql::class,
'persistent' => false,
'timezone' => 'UTC',
//'encoding' => 'utf8mb4',
'flags' => [],
'cacheMetadata' => true,
'quoteIdentifiers' => false,
'log' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
],
/*
* Configures logging options
*/
'Log' => [
'debug' => [
'className' => FileLog::class,
'path' => LOGS,
'file' => 'debug',
'url' => env('LOG_DEBUG_URL', null),
'scopes' => false,
'levels' => ['notice', 'info', 'debug'],
],
'error' => [
'className' => FileLog::class,
'path' => LOGS,
'file' => 'error',
'url' => env('LOG_ERROR_URL', null),
'scopes' => false,
'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
],
// To enable this dedicated query log, you need set your datasource's log flag to true
'queries' => [
'className' => FileLog::class,
'path' => LOGS,
'file' => 'queries',
'url' => env('LOG_QUERIES_URL', null),
'scopes' => ['queriesLog'],
],
],
/*
* Session configuration.
*
* Contains an array of settings to use for session configuration. The
* `defaults` key is used to define a default preset to use for sessions, any
* settings declared here will override the settings of the default config.
*
* ## Options
*
* - `cookie` - The name of the cookie to use. Defaults to value set for `session.name` php.ini config.
* Avoid using `.` in cookie names, as PHP will drop sessions from cookies with `.` in the name.
* - `cookiePath` - The url path for which session cookie is set. Maps to the
* `session.cookie_path` php.ini config. Defaults to base path of app.
* - `timeout` - The time in minutes the session should be valid for.
* Pass 0 to disable checking timeout.
* Please note that php.ini's session.gc_maxlifetime must be equal to or greater
* than the largest Session['timeout'] in all served websites for it to have the
* desired effect.
* - `defaults` - The default configuration set to use as a basis for your session.
* There are four built-in options: php, cake, cache, database.
* - `handler` - Can be used to enable a custom session handler. Expects an
* array with at least the `engine` key, being the name of the Session engine
* class to use for managing the session. CakePHP bundles the `CacheSession`
* and `DatabaseSession` engines.
* - `ini` - An associative array of additional ini values to set.
*
* The built-in `defaults` options are:
*
* - 'php' - Uses settings defined in your php.ini.
* - 'cake' - Saves session files in CakePHP's /tmp directory.
* - 'database' - Uses CakePHP's database sessions.
* - 'cache' - Use the Cache class to save sessions.
*
* To define a custom session handler, save it at src/Network/Session/<name>.php.
* Make sure the class implements PHP's `SessionHandlerInterface` and set
* Session.handler to <name>
*
* To use database sessions, load the SQL file located at config/schema/sessions.sql
*/
'Session' => [
'defaults' => 'php',
],
];

View File

@ -0,0 +1,92 @@
<?php
/*
* Local configuration file to provide any overrides to your app.php configuration.
* Copy and save this file as app_local.php and make changes as required.
* Note: It is not recommended to commit files with credentials such as app_local.php
* into source code version control.
*/
return [
/*
* Debug Level:
*
* Production Mode:
* false: No error messages, errors, or warnings shown.
*
* Development Mode:
* true: Errors and warnings shown.
*/
'debug' => filter_var(env('DEBUG', true), FILTER_VALIDATE_BOOLEAN),
/*
* Security and encryption configuration
*
* - salt - A random string used in security hashing methods.
* The salt value is also used as the encryption key.
* You should treat it as extremely sensitive data.
*/
'Security' => [
'salt' => env('SECURITY_SALT', '__SALT__'),
],
/*
* Connection information used by the ORM to connect
* to your application's datastores.
*
* See app.php for more configuration options.
*/
'Datasources' => [
'default' => [
'host' => 'localhost',
/*
* CakePHP will use the default DB port based on the driver selected
* MySQL on MAMP uses port 8889, MAMP users will want to uncomment
* the following line and set the port accordingly
*/
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'my_app',
/**
* If not using the default 'public' schema with the PostgreSQL driver
* set it here.
*/
//'schema' => 'myapp',
/**
* You can use a DSN string to set the entire configuration
*/
'url' => env('DATABASE_URL', null),
],
/*
* The test connection is used during the test suite.
*/
'test' => [
'host' => 'localhost',
//'port' => 'non_standard_port_number',
'username' => 'my_app',
'password' => 'secret',
'database' => 'test_myapp',
//'schema' => 'myapp',
],
],
/*
* Email configuration.
*
* Host and credential configuration in case you are using SmtpTransport
*
* See app.php for more configuration options.
*/
'EmailTransport' => [
'default' => [
'host' => 'localhost',
'port' => 25,
'username' => null,
'password' => null,
'client' => null,
'url' => env('EMAIL_TRANSPORT_DEFAULT_URL', null),
],
],
];

215
app/config/bootstrap.php Normal file
View File

@ -0,0 +1,215 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.8
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
/*
* Configure paths required to find CakePHP + general filepath constants
*/
require __DIR__ . '/paths.php';
/*
* Bootstrap CakePHP.
*
* Does the various bits of setup that CakePHP needs to do.
* This includes:
*
* - Registering the CakePHP autoloader.
* - Setting the default application paths.
*/
require CORE_PATH . 'config' . DS . 'bootstrap.php';
use Cake\Cache\Cache;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Datasource\ConnectionManager;
use Cake\Error\ConsoleErrorHandler;
use Cake\Error\ErrorHandler;
use Cake\Http\ServerRequest;
use Cake\Log\Log;
use Cake\Mailer\Mailer;
use Cake\Mailer\TransportFactory;
use Cake\Routing\Router;
use Cake\Utility\Security;
/*
* See https://github.com/josegonzalez/php-dotenv for API details.
*
* Uncomment block of code below if you want to use `.env` file during development.
* You should copy `config/.env.example` to `config/.env` and set/modify the
* variables as required.
*
* The purpose of the .env file is to emulate the presence of the environment
* variables like they would be present in production.
*
* If you use .env files, be careful to not commit them to source control to avoid
* security risks. See https://github.com/josegonzalez/php-dotenv#general-security-information
* for more information for recommended practices.
*/
// if (!env('APP_NAME') && file_exists(CONFIG . '.env')) {
// $dotenv = new \josegonzalez\Dotenv\Loader([CONFIG . '.env']);
// $dotenv->parse()
// ->putenv()
// ->toEnv()
// ->toServer();
// }
/*
* Read configuration file and inject configuration into various
* CakePHP classes.
*
* By default there is only one configuration file. It is often a good
* idea to create multiple configuration files, and separate the configuration
* that changes from configuration that does not. This makes deployment simpler.
*/
try {
Configure::config('default', new PhpConfig());
Configure::load('app', 'default', false);
} catch (\Exception $e) {
exit($e->getMessage() . "\n");
}
/*
* Load an environment local configuration file to provide overrides to your configuration.
* Notice: For security reasons app_local.php **should not** be included in your git repo.
*/
if (file_exists(CONFIG . 'app_local.php')) {
Configure::load('app_local', 'default');
}
/*
* When debug = true the metadata cache should only last
* for a short time.
*/
if (Configure::read('debug')) {
Configure::write('Cache._cake_model_.duration', '+2 minutes');
Configure::write('Cache._cake_core_.duration', '+2 minutes');
// disable router cache during development
Configure::write('Cache._cake_routes_.duration', '+2 seconds');
}
/*
* Set the default server timezone. Using UTC makes time calculations / conversions easier.
* Check http://php.net/manual/en/timezones.php for list of valid timezone strings.
*/
date_default_timezone_set(Configure::read('App.defaultTimezone'));
/*
* Configure the mbstring extension to use the correct encoding.
*/
mb_internal_encoding(Configure::read('App.encoding'));
/*
* Set the default locale. This controls how dates, number and currency is
* formatted and sets the default language to use for translations.
*/
ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
/*
* Register application error and exception handlers.
*/
$isCli = PHP_SAPI === 'cli';
if ($isCli) {
(new ConsoleErrorHandler(Configure::read('Error')))->register();
} else {
(new ErrorHandler(Configure::read('Error')))->register();
}
/*
* Include the CLI bootstrap overrides.
*/
if ($isCli) {
require __DIR__ . '/bootstrap_cli.php';
}
/*
* Set the full base URL.
* This URL is used as the base of all absolute links.
*/
$fullBaseUrl = Configure::read('App.fullBaseUrl');
if (!$fullBaseUrl) {
$s = null;
if (env('HTTPS')) {
$s = 's';
}
$httpHost = env('HTTP_HOST');
if (isset($httpHost)) {
$fullBaseUrl = 'http' . $s . '://' . $httpHost;
}
unset($httpHost, $s);
}
if ($fullBaseUrl) {
Router::fullBaseUrl($fullBaseUrl);
}
unset($fullBaseUrl);
Cache::setConfig(Configure::consume('Cache'));
ConnectionManager::setConfig(Configure::consume('Datasources'));
TransportFactory::setConfig(Configure::consume('EmailTransport'));
Mailer::setConfig(Configure::consume('Email'));
Log::setConfig(Configure::consume('Log'));
Security::setSalt(Configure::consume('Security.salt'));
/*
* Setup detectors for mobile and tablet.
*/
ServerRequest::addDetector('mobile', function ($request) {
$detector = new \Detection\MobileDetect();
return $detector->isMobile();
});
ServerRequest::addDetector('tablet', function ($request) {
$detector = new \Detection\MobileDetect();
return $detector->isTablet();
});
/*
* You can set whether the ORM uses immutable or mutable Time types.
* The default changed in 4.0 to immutable types. You can uncomment
* below to switch back to mutable types.
*
* You can enable default locale format parsing by adding calls
* to `useLocaleParser()`. This enables the automatic conversion of
* locale specific date formats. For details see
* @link https://book.cakephp.org/4/en/core-libraries/internationalization-and-localization.html#parsing-localized-datetime-data
*/
// TypeFactory::build('time')
// ->useMutable();
// TypeFactory::build('date')
// ->useMutable();
// TypeFactory::build('datetime')
// ->useMutable();
// TypeFactory::build('timestamp')
// ->useMutable();
// TypeFactory::build('datetimefractional')
// ->useMutable();
// TypeFactory::build('timestampfractional')
// ->useMutable();
// TypeFactory::build('datetimetimezone')
// ->useMutable();
// TypeFactory::build('timestamptimezone')
// ->useMutable();
/*
* Custom Inflector rules, can be set to correctly pluralize or singularize
* table, model, controller names or whatever other string is passed to the
* inflection functions.
*/
//Inflector::rules('plural', ['/^(inflect)or$/i' => '\1ables']);
//Inflector::rules('irregular', ['red' => 'redlings']);
//Inflector::rules('uninflected', ['dontinflectme']);
//Inflector::rules('transliteration', ['/å/' => 'aa']);

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
use Cake\Core\Configure;
/*
* Additional bootstrapping and configuration for CLI environments should
* be put here.
*/
// Set the fullBaseUrl to allow URLs to be generated in shell tasks.
// This is useful when sending email from shells.
//Configure::write('App.fullBaseUrl', php_uname('n'));
// Set logs to different files so they don't have permission conflicts.
Configure::write('Log.debug.file', 'cli-debug');
Configure::write('Log.error.file', 'cli-error');

View File

@ -0,0 +1,11 @@
<?php
return [
'prevDisabled' => '<li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">{{text}}</a></li>',
'prevActive' => '<li class="page-item"><a class="page-link" href="{{url}}">{{text}}</a></li>',
'nextDisabled' => '<li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">{{text}}</a></li>',
'nextActive' => '<li class="page-item"><a class="page-link" href="{{url}}">{{text}}</a></li>',
'number' => '<li class="page-item"><a class="page-link" href="{{url}}">{{text}}</a></li>',
'sortAsc' => '<a href="{{url}}">{{text}} <i class="fas fa-sort-up"></i></a>',
'sortDesc' => '<a href="{{url}}">{{text}} <i class="fas fa-sort-down"></i></a>'
];

94
app/config/paths.php Normal file
View File

@ -0,0 +1,94 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license MIT License (https://opensource.org/licenses/mit-license.php)
*/
/*
* Use the DS to separate the directories in other defines
*/
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
/*
* These defines should only be edited if you have cake installed in
* a directory layout other than the way it is distributed.
* When using custom settings be sure to use the DS and do not add a trailing DS.
*/
/*
* The full path to the directory which holds "src", WITHOUT a trailing DS.
*/
define('ROOT', dirname(__DIR__));
/*
* The actual directory name for the application directory. Normally
* named 'src'.
*/
define('APP_DIR', 'src');
/*
* Path to the application's directory.
*/
define('APP', ROOT . DS . APP_DIR . DS);
/*
* Path to the config directory.
*/
define('CONFIG', ROOT . DS . 'config' . DS);
/*
* File path to the webroot directory.
*
* To derive your webroot from your webserver change this to:
*
* `define('WWW_ROOT', rtrim($_SERVER['DOCUMENT_ROOT'], DS) . DS);`
*/
define('WWW_ROOT', ROOT . DS . 'webroot' . DS);
/*
* Path to the tests directory.
*/
define('TESTS', ROOT . DS . 'tests' . DS);
/*
* Path to the temporary files directory.
*/
define('TMP', ROOT . DS . 'tmp' . DS);
/*
* Path to the logs directory.
*/
define('LOGS', ROOT . DS . 'logs' . DS);
/*
* Path to the cache files directory. It can be shared between hosts in a multi-server setup.
*/
define('CACHE', TMP . 'cache' . DS);
/**
* Path to the resources directory.
*/
define('RESOURCES', ROOT . DS . 'resources' . DS);
/**
* The absolute path to the "cake" directory, WITHOUT a trailing DS.
*
* CakePHP should always be installed with composer, so look there.
*/
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp');
/*
* Path to the cake directory.
*/
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
define('CAKE', CORE_PATH . 'src' . DS);

View File

@ -0,0 +1,46 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.5.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
/*
* You can empty out this file, if you are certain that you match all requirements.
*/
/*
* You can remove this if you are confident that your PHP version is sufficient.
*/
if (version_compare(PHP_VERSION, '7.2.0') < 0) {
trigger_error('Your PHP version must be equal or higher than 7.2.0 to use CakePHP.', E_USER_ERROR);
}
/*
* You can remove this if you are confident you have intl installed.
*/
if (!extension_loaded('intl')) {
trigger_error('You must enable the intl extension to use CakePHP.', E_USER_ERROR);
}
/*
* You can remove this if you are confident you have proper version of intl.
*/
if (version_compare(INTL_ICU_VERSION, '50.1', '<')) {
trigger_error('ICU >= 50.1 is needed to use CakePHP. Please update the `libicu` package of your system.' . PHP_EOL, E_USER_ERROR);
}
/*
* You can remove this if you are confident you have mbstring installed.
*/
if (!extension_loaded('mbstring')) {
trigger_error('You must enable the mbstring extension to use CakePHP.', E_USER_ERROR);
}

99
app/config/routes.php Normal file
View File

@ -0,0 +1,99 @@
<?php
/**
* Routes configuration.
*
* In this file, you set up routes to your controllers and their actions.
* Routes are very important mechanism that allows you to freely connect
* different URLs to chosen controllers and their actions (functions).
*
* It's loaded within the context of `Application::routes()` method which
* receives a `RouteBuilder` instance `$routes` as method argument.
*
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;
/*
* The default class to use for all routes
*
* The following route classes are supplied with CakePHP and are appropriate
* to set as the default:
*
* - Route
* - InflectedRoute
* - DashedRoute
*
* If no call is made to `Router::defaultRouteClass()`, the class used is
* `Route` (`Cake\Routing\Route\Route`)
*
* Note that `Route` does not do any inflections on URLs which will result in
* inconsistently cased URLs when used with `:plugin`, `:controller` and
* `:action` markers.
*/
/** @var \Cake\Routing\RouteBuilder $routes */
$routes->setRouteClass(DashedRoute::class);
$routes->scope('/', function (RouteBuilder $builder) {
$builder->setExtensions(['json']);
// Register scoped middleware for in scopes.
$builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([
'httpOnly' => true,
]));
/*
* Apply a middleware to the current route scope.
* Requires middleware to be registered through `Application::routes()` with `registerMiddleware()`
*/
$builder->applyMiddleware('csrf');
/*
* Here, we are connecting '/' (base path) to a controller called 'Pages',
* its action called 'display', and we pass a param to select the view file
* to use (in this case, templates/Pages/home.php)...
*/
$builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
/*
* ...and connect the rest of 'Pages' controller's URLs.
*/
$builder->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
/*
* Connect catchall routes for all controllers.
*
* The `fallbacks` method is a shortcut for
*
* ```
* $builder->connect('/:controller', ['action' => 'index']);
* $builder->connect('/:controller/:action/*', []);
* ```
*
* You can remove these routes once you've connected the
* routes you want in your application.
*/
$builder->fallbacks();
});
/*
* If you need a different set of middleware or none at all,
* open new scope and define routes there.
*
* ```
* $routes->scope('/api', function (RouteBuilder $builder) {
* // No $builder->applyMiddleware() here.
* // Connect API actions here.
* });
* ```
*/

View File

@ -0,0 +1,18 @@
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE i18n (
id int NOT NULL auto_increment,
locale varchar(6) NOT NULL,
model varchar(255) NOT NULL,
foreign_key int(10) NOT NULL,
field varchar(255) NOT NULL,
content text,
PRIMARY KEY (id),
UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
INDEX I18N_FIELD(model, foreign_key, field)
);

View File

@ -0,0 +1,15 @@
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE `sessions` (
`id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
`modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
`data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
`expires` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

16
app/index.php Normal file
View File

@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';

40
app/phpunit.xml.dist Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="tests/bootstrap.php"
>
<php>
<ini name="memory_limit" value="-1"/>
<ini name="apc.enable_cli" value="1"/>
</php>
<!-- Add any additional test suites you want to run here -->
<testsuites>
<testsuite name="app">
<directory>tests/TestCase/</directory>
</testsuite>
<!-- Add plugin test suites here. -->
</testsuites>
<!-- Setup a listener for fixtures -->
<listeners>
<listener class="Cake\TestSuite\Fixture\FixtureInjector">
<arguments>
<object class="Cake\TestSuite\Fixture\FixtureManager"/>
</arguments>
</listener>
</listeners>
<!-- Ignore vendor tests in code coverage reports -->
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
<directory suffix=".php">plugins/*/src/</directory>
<exclude>
<file>src/Console/Installer.php</file>
</exclude>
</whitelist>
</filter>
</phpunit>

0
app/plugins/.gitkeep Normal file
View File

0
app/resources/.gitkeep Normal file
View File

108
app/src/Application.php Normal file
View File

@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App;
use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\MiddlewareQueue;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
/**
* Application setup class.
*
* This defines the bootstrapping logic and middleware layers you
* want to use in your application.
*/
class Application extends BaseApplication
{
/**
* Load all the application configuration and bootstrap logic.
*
* @return void
*/
public function bootstrap(): void
{
// Call parent to load bootstrap from files.
parent::bootstrap();
if (PHP_SAPI === 'cli') {
$this->bootstrapCli();
}
/*
* Only try to load DebugKit in development mode
* Debug Kit should not be installed on a production system
*/
if (Configure::read('debug')) {
$this->addPlugin('DebugKit');
}
// Load more plugins here
}
/**
* Setup the middleware queue your application will use.
*
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(new ErrorHandlerMiddleware(Configure::read('Error')))
// Handle plugin/theme assets like CakePHP normally does.
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime'),
]))
// Add routing middleware.
// If you have a large number of routes connected, turning on routes
// caching in production could improve performance. For that when
// creating the middleware instance specify the cache config name by
// using it's second constructor argument:
// `new RoutingMiddleware($this, '_cake_routes_')`
->add(new RoutingMiddleware($this));
return $middlewareQueue;
}
/**
* Bootrapping for CLI application.
*
* That is when running commands.
*
* @return void
*/
protected function bootstrapCli(): void
{
try {
$this->addPlugin('Bake');
} catch (MissingPluginException $e) {
// Do not halt if the plugin is missing
}
$this->addPlugin('Migrations');
// Load more plugins here
}
}

View File

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Command;
use Cake\Console\Arguments;
use Cake\Console\Command;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use Cake\Log\Log;
use Psy\Shell as PsyShell;
/**
* Simple console wrapper around Psy\Shell.
*/
class ConsoleCommand extends Command
{
/**
* Start the Command and interactive console.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return int|null|void The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io)
{
if (!class_exists('Psy\Shell')) {
$io->err('<error>Unable to load Psy\Shell.</error>');
$io->err('');
$io->err('Make sure you have installed psysh as a dependency,');
$io->err('and that Psy\Shell is registered in your autoloader.');
$io->err('');
$io->err('If you are using composer run');
$io->err('');
$io->err('<info>$ php composer.phar require --dev psy/psysh</info>');
$io->err('');
return static::CODE_ERROR;
}
$io->out("You can exit with <info>`CTRL-C`</info> or <info>`exit`</info>");
$io->out('');
Log::drop('debug');
Log::drop('error');
$io->setLoggers(false);
restore_error_handler();
restore_exception_handler();
$psy = new PsyShell();
$psy->run();
}
/**
* Display help for this console.
*
* @param \Cake\Console\ConsoleOptionParser $parser The parser to update
* @return \Cake\Console\ConsoleOptionParser
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser->setDescription(
'This shell provides a REPL that you can use to interact with ' .
'your application in a command line designed to run PHP code. ' .
'You can use it to run adhoc queries with your models, or ' .
'explore the features of CakePHP and your application.' .
"\n\n" .
'You will need to have psysh installed for this Shell to work.'
);
return $parser;
}
}

View File

@ -0,0 +1,246 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Console;
if (!defined('STDIN')) {
define('STDIN', fopen('php://stdin', 'r'));
}
use Cake\Utility\Security;
use Composer\Script\Event;
use Exception;
/**
* Provides installation hooks for when this application is installed through
* composer. Customize this class to suit your needs.
*/
class Installer
{
/**
* An array of directories to be made writable
*/
public const WRITABLE_DIRS = [
'logs',
'tmp',
'tmp/cache',
'tmp/cache/models',
'tmp/cache/persistent',
'tmp/cache/views',
'tmp/sessions',
'tmp/tests',
];
/**
* Does some routine installation tasks so people don't have to.
*
* @param \Composer\Script\Event $event The composer event object.
* @throws \Exception Exception raised by validator.
* @return void
*/
public static function postInstall(Event $event)
{
$io = $event->getIO();
$rootDir = dirname(dirname(__DIR__));
static::createAppLocalConfig($rootDir, $io);
static::createWritableDirectories($rootDir, $io);
static::setFolderPermissions($rootDir, $io);
static::setSecuritySalt($rootDir, $io);
$class = 'Cake\Codeception\Console\Installer';
if (class_exists($class)) {
$class::customizeCodeceptionBinary($event);
}
}
/**
* Create config/app_local.php file if it does not exist.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createAppLocalConfig($dir, $io)
{
$appLocalConfig = $dir . '/config/app_local.php';
$appLocalConfigTemplate = $dir . '/config/app_local.example.php';
if (!file_exists($appLocalConfig)) {
copy($appLocalConfigTemplate, $appLocalConfig);
$io->write('Created `config/app_local.php` file');
}
}
/**
* Create the `logs` and `tmp` directories.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createWritableDirectories($dir, $io)
{
foreach (static::WRITABLE_DIRS as $path) {
$path = $dir . '/' . $path;
if (!file_exists($path)) {
mkdir($path);
$io->write('Created `' . $path . '` directory');
}
}
}
/**
* Set globally writable permissions on the "tmp" and "logs" directory.
*
* This is not the most secure default, but it gets people up and running quickly.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setFolderPermissions($dir, $io)
{
// ask if the permissions should be changed
if ($io->isInteractive()) {
$validator = function ($arg) {
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
return $arg;
}
throw new Exception('This is not a valid answer. Please choose Y or n.');
};
$setFolderPermissions = $io->askAndValidate(
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
$validator,
10,
'Y'
);
if (in_array($setFolderPermissions, ['n', 'N'])) {
return;
}
}
// Change the permissions on a path and output the results.
$changePerms = function ($path) use ($io) {
$currentPerms = fileperms($path) & 0777;
$worldWritable = $currentPerms | 0007;
if ($worldWritable == $currentPerms) {
return;
}
$res = chmod($path, $worldWritable);
if ($res) {
$io->write('Permissions set on ' . $path);
} else {
$io->write('Failed to set permissions on ' . $path);
}
};
$walker = function ($dir) use (&$walker, $changePerms) {
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (!is_dir($path)) {
continue;
}
$changePerms($path);
$walker($path);
}
};
$walker($dir . '/tmp');
$changePerms($dir . '/tmp');
$changePerms($dir . '/logs');
}
/**
* Set the security.salt value in the application's config file.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setSecuritySalt($dir, $io)
{
$newKey = hash('sha256', Security::randomBytes(64));
static::setSecuritySaltInFile($dir, $io, $newKey, 'app_local.php');
}
/**
* Set the security.salt value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $newKey key to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setSecuritySaltInFile($dir, $io, $newKey, $file)
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
$content = str_replace('__SALT__', $newKey, $content, $count);
if ($count == 0) {
$io->write('No Security.salt placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated Security.salt value in config/' . $file);
return;
}
$io->write('Unable to update Security.salt value.');
}
/**
* Set the APP_NAME value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $appName app name to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setAppNameInFile($dir, $io, $appName, $file)
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
$content = str_replace('__APP_NAME__', $appName, $content, $count);
if ($count == 0) {
$io->write('No __APP_NAME__ placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated __APP_NAME__ value in config/' . $file);
return;
}
$io->write('Unable to update __APP_NAME__ value.');
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use \Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
use Cake\Error\Debugger;
class AlignmentsController extends AppController
{
public function index($organisation_id = null)
{
$query = $this->Alignments->find();
if (!empty($organisation_id)) {
$query->where(['organisation_id' => $organisation_id]);
}
if ($this->_isRest()) {
$alignments = $query->all();
return $this->RestResponse->viewData($alignments, 'json');
} else {
$this->loadComponent('Paginator');
$alignments = $this->Paginator->paginate($query);
$this->set('data', $alignments);
$this->set('metaGroup', 'ContactDB');
}
}
public function view($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid alignment.'));
}
$individual = $this->Alignments->get($id);
if ($this->_isRest()) {
return $this->RestResponse->viewData($individual, 'json');
} else {
}
$this->set('metaGroup', 'ContactDB');
$this->set('alignment', $individual);
}
public function delete($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid alignment.'));
}
$individual = $this->Alignments->get($id);
if ($this->request->is('post') || $this->request->is('delete')) {
if ($this->Alignments->delete($individual)) {
$message = __('Individual deleted.');
if ($this->_isRest()) {
$individual = $this->Alignments->get($id);
return $this->RestResponse->saveSuccessResponse('Alignments', 'delete', $id, 'json', $message);
} else {
$this->Flash->success($message);
return $this->redirect($this->referer());
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('scope', 'alignments');
$this->set('id', $individual['id']);
$this->set('alignment', $individual);
$this->viewBuilder()->setLayout('ajax');
$this->render('/genericTemplates/delete');
}
public function add($scope, $source_id)
{
if (empty($scope) || empty($source_id)) {
throw new NotAcceptableException(__('Invalid input. scope and source_id expected as URL parameters in the format /alignments/add/[scope]/[source_id].'));
}
$this->loadModel('Individuals');
$this->loadModel('Organisations');
$alignment = $this->Alignments->newEmptyEntity();
if ($this->request->is('post')) {
$this->Alignments->patchEntity($alignment, $this->request->getData());
if ($scope === 'individuals') {
$alignment['individual_id'] = $source_id;
} else {
$alignment['organisation_id'] = $source_id;
}
if ($this->Alignments->save($alignment)) {
$message = __('Alignment added.');
if ($this->_isRest()) {
$alignment = $this->Alignments->get($this->Alignments->id);
return $this->RestResponse->viewData($alignment, 'json');
} else {
$this->Flash->success($message);
$this->redirect($this->referer());
}
} else {
$message = __('Alignment could not be added.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Individuals', 'addAlignment', false, $message);
} else {
$this->Flash->error($message);
//$this->redirect($this->referer());
}
}
}
if ($scope === 'organisations') {
$individuals = $this->Individuals->find('list', ['valueField' => 'email']);
$this->set('individuals', $individuals);
$organisation = $this->Organisations->find()->where(['id' => $source_id])->first();
if (empty($organisation)) {
throw new NotFoundException(__('Invalid organisation'));
}
$this->set(compact('organisation'));
} else {
$organisations = $this->Organisations->find('list', ['valueField' => 'name']);
$this->set('organisations', $organisations);
$individual = $this->Individuals->find()->where(['id' => $source_id])->first();
if (empty($individual)) {
throw new NotFoundException(__('Invalid individual'));
}
$this->set(compact('individual'));
}
$this->set(compact('alignment'));
$this->set('scope', $scope);
$this->set('source_id', $source_id);
$this->set('metaGroup', 'ContactDB');
}
}

View File

@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
use Cake\Controller\Controller;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Event\EventInterface;
use Cake\Utility\Text;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
/**
* Application Controller
*
* Add your application-wide methods in the class below, your controllers
* will inherit them.
*
* @link https://book.cakephp.org/4/en/controllers.html#the-app-controller
*/
class AppController extends Controller
{
public $isRest = null;
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading components.
*
* e.g. `$this->loadComponent('FormProtection');`
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('RestResponse');
$this->loadComponent('ACL');
if (Configure::read('debug')) {
Configure::write('DebugKit.panels', ['DebugKit.Packages' => true]);
Configure::write('DebugKit.forceEnable', true);
}
/*
* Enable the following component for recommended CakePHP form protection settings.
* see https://book.cakephp.org/4/en/controllers/components/form-protection.html
*/
//$this->loadComponent('FormProtection');
}
public function beforeFilter(EventInterface $event)
{
$this->set('ajax', $this->request->is('ajax'));
}
protected function _isRest()
{
// This method is surprisingly slow and called many times for one request, so it make sense to cache the result.
if ($this->isRest !== null) {
return $this->isRest;
}
if ($this->request->is('json')) {
if (!empty($this->request->input()) && empty($this->request->input('json_decode'))) {
throw new MethodNotAllowedException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.');
}
$this->isRest = true;
return true;
} else {
$this->isRest = false;
return false;
}
}
protected function _isJson($data)
{
return (json_decode($data) != null) ? true : false;
}
public function generateUUID()
{
$uuid = Text::uuid();
return $this->RestResponse->viewData(['uuid' => $uuid], 'json');
}
}

View File

View File

@ -0,0 +1,875 @@
<?php
namespace App\Controller\Component;
use Cake\Controller\Component;
class ACLComponent extends Component
{
// syntax:
// $__aclList[$controller][$action] = $permission_rules
// $controller == '*' - any controller can have this action
// $action == array() - site admin only has access
// $action == '*' - any role has access
// $action == array('OR' => array()) - any role in the array has access
// $action == array('AND' => array()) - roles with all permissions in the array have access
// If we add any new functionality to MISP and we don't add it to this list, it will only be visible to site admins.
private $__aclList = array(
'*' => array(
'blackhole' => array(),
'checkAction' => array(),
'checkAuthUser' => array(),
'checkExternalAuthUser' => array(),
'cleanModelCaches' => array(),
'debugACL' => array(),
'generateCount' => array(),
'getActions' => array(),
'pruneDuplicateUUIDs' => array(),
'queryACL' => array(),
'removeDuplicateEvents' => array(),
'restSearch' => array('*'),
'updateDatabase' => array(),
'upgrade2324' => array(),
),
'attributes' => array(
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
'add_threatconnect' => array('perm_add'),
'addTag' => array('perm_tagger'),
'attributeReplace' => array('perm_add'),
'attributeStatistics' => array('*'),
'bro' => array('*'),
'checkAttachments' => array(),
'checkComposites' => array('perm_admin'),
'checkOrphanedAttributes' => array(),
'delete' => array('perm_add'),
'deleteSelected' => array('perm_add'),
'describeTypes' => array('*'),
'download' => array('*'),
'downloadAttachment' => array('*'),
'downloadSample' => array('*'),
'edit' => array('perm_add'),
'editField' => array('perm_add'),
'editSelected' => array('perm_add'),
'exportSearch' => array('*'),
'fetchEditForm' => array('perm_add'),
'fetchViewValue' => array('*'),
'generateCorrelation' => array(),
'hoverEnrichment' => array('perm_add'),
'index' => array('*'),
'pruneOrphanedAttributes' => array(),
'removeTag' => array('perm_tagger'),
'reportValidationIssuesAttributes' => array(),
'restore' => array('perm_add'),
'restSearch' => array('*'),
'returnAttributes' => array('*'),
'rpz' => array('*'),
'search' => array('*'),
'searchAlternate' => array('*'),
'toggleCorrelation' => array('perm_add'),
'text' => array('*'),
'toggleToIDS' => array('perm_add'),
'updateAttributeValues' => array('perm_add'),
'view' => array('*'),
'viewPicture' => array('*'),
),
'dashboards' => array(
'getForm' => array('*'),
'index' => array('*'),
'updateSettings' => array('*'),
'getEmptyWidget' => array('*'),
'renderWidget' => array('*'),
'listTemplates' => array('*'),
'saveTemplate' => array('*'),
'export' => array('*'),
'import' => array('*'),
'deleteTemplate' => array('*')
),
'decayingModel' => array(
"update" => array(),
"export" => array('*'),
"import" => array('*'),
"view" => array('*'),
"index" => array('*'),
"add" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"edit" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"delete" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"enable" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"disable" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"decayingTool" => array( 'OR' => array('perm_admin', 'perm_decaying')),
"getAllDecayingModels" => array('*'),
"decayingToolBasescore" => array('*'),
"decayingToolSimulation" => array('*'),
"decayingToolRestSearch" => array('*'),
"decayingToolComputeSimulation" => array('*')
),
'decayingModelMapping' => array(
"viewAssociatedTypes" => array('*'),
"linkAttributeTypeToModel" => array( 'OR' => array('perm_admin', 'perm_decaying'))
),
'communities' => array(
'index' => array(),
'requestAccess' => array(),
'view' => array()
),
'eventBlacklists' => array(
'add' => array(),
'delete' => array(),
'edit' => array(),
'index' => array(),
'massDelete' => array()
),
'eventDelegations' => array(
'acceptDelegation' => array('perm_add'),
'delegateEvent' => array('perm_delegate'),
'deleteDelegation' => array('perm_add'),
'index' => array('*'),
'view' => array('*'),
),
'events' => array(
'add' => array('perm_add'),
'addIOC' => array('perm_add'),
'addTag' => array('perm_tagger'),
'add_misp_export' => array('perm_modify'),
'alert' => array('perm_publish'),
'automation' => array('perm_auth'),
'checkLocks' => array('perm_add'),
'checkPublishedStatus' => array('*'),
'checkuuid' => array('perm_sync'),
'contact' => array('*'),
'csv' => array('*'),
'cullEmptyEvents' => array(),
'delegation_index' => array('*'),
'delete' => array('perm_add'),
'deleteNode' => array('*'),
'dot' => array(),
'downloadExport' => array('*'),
'downloadOpenIOCEvent' => array('*'),
'edit' => array('perm_add'),
'enrichEvent' => array('perm_add'),
'export' => array('*'),
'exportChoice' => array('*'),
'exportModule' => array('*'),
'filterEventIdsForPush' => array('perm_sync'),
'filterEventIndex' => array('*'),
'freeTextImport' => array('perm_add'),
'getEditStrategy' => array('perm_add'),
'getEventInfoById' => array('*'),
'getEventGraphReferences' => array('*'),
'getEventGraphTags' => array('*'),
'getEventGraphGeneric' => array('*'),
'getEventTimeline' => array('*'),
'genDistributionGraph' => array('*'),
'getDistributionGraph' => array('*'),
'getReferenceData' => array('*'),
'getReferences' => array('*'),
'getObjectTemplate' => array('*'),
'handleModuleResults' => array('*'),
'hids' => array('*'),
'index' => array('*'),
'importChoice' => array('*'),
'importModule' => array('*'),
'massDelete' => array('perm_site_admin'),
'merge' => array('perm_modify'),
'nids' => array('*'),
'proposalEventIndex' => array('*'),
'publish' => array('perm_publish'),
'publishSightings' => array('perm_sighting'),
'pushEventToZMQ' => array('perm_publish_zmq'),
'pushEventToKafka' => array('perm_publish_kafka'),
'pushProposals' => array('perm_sync'),
'queryEnrichment' => array('perm_add'),
'removePivot' => array('*'),
'removeTag' => array('perm_tagger'),
'reportValidationIssuesEvents' => array(),
'restSearch' => array('*'),
'saveFreeText' => array('perm_add'),
'stix' => array('*'),
'stix2' => array('*'),
'strposarray' => array(),
'toggleCorrelation' => array('perm_add'),
'unpublish' => array('perm_modify'),
'updateGraph' => array('*'),
'upload_analysis_file' => array('perm_add'),
'upload_sample' => array('AND' => array('perm_auth', 'perm_add')),
'upload_stix' => array('perm_add'),
'view' => array('*'),
'viewEventAttributes' => array('*'),
'viewEventGraph' => array('*'),
'viewGraph' => array('*'),
'viewGalaxyMatrix' => array('*'),
'xml' => array('*')
),
'favouriteTags' => array(
'toggle' => array('*'),
'getToggleField' => array('*')
),
'feeds' => array(
'add' => array(),
'cacheFeeds' => array(),
'compareFeeds' => array('*'),
'delete' => array(),
'disable' => array(),
'edit' => array(),
'enable' => array(),
'feedCoverage' => array('*'),
'fetchFromAllFeeds' => array(),
'fetchFromFeed' => array(),
'fetchSelectedFromFreetextIndex' => array(),
'getEvent' => array(),
'importFeeds' => array(),
'index' => array('*'),
'loadDefaultFeeds' => array('perm_site_admin'),
'previewEvent' => array('*'),
'previewIndex' => array('*'),
'searchCaches' => array('*'),
'toggleSelected' => array('perm_site_admin'),
'view' => array('*'),
),
'galaxies' => array(
'attachCluster' => array('perm_tagger'),
'attachMultipleClusters' => array('perm_tagger'),
'delete' => array(),
'index' => array('*'),
'selectGalaxy' => array('perm_tagger'),
'selectGalaxyNamespace' => array('perm_tagger'),
'selectCluster' => array('perm_tagger'),
'showGalaxies' => array('*'),
'update' => array(),
'view' => array('*'),
'viewGraph' => array('*')
),
'galaxyClusters' => array(
'attachToEvent' => array('perm_tagger'),
'delete' => array('perm_site_admin'),
'detach' => array('perm_tagger'),
'index' => array('*'),
'view' => array('*'),
'viewGalaxyMatrix' => array('*')
),
'galaxyElements' => array(
'index' => array('*')
),
'jobs' => array(
'cache' => array('*'),
'getError' => array(),
'getGenerateCorrelationProgress' => array('*'),
'getProgress' => array('*'),
'index' => array(),
'clearJobs' => array()
),
'logs' => array(
'admin_index' => array('perm_audit'),
'admin_search' => array('perm_audit'),
'event_index' => array('*'),
'maxDateActivity' => array('*'),
'returnDates' => array('*'),
'testForStolenAttributes' => array(),
'pruneUpdateLogs' => array()
),
'modules' => array(
'index' => array('perm_auth'),
'queryEnrichment' => array('perm_auth'),
),
'news' => array(
'add' => array(),
'edit' => array(),
'delete' => array(),
'index' => array('*'),
),
'noticelists' => array(
'delete' => array(),
'enableNoticelist' => array(),
'getToggleField' => array(),
'index' => array('*'),
'toggleEnable' => array(),
'update' => array(),
'view' => array('*')
),
'objects' => array(
'add' => array('perm_add'),
'addValueField' => array('perm_add'),
'delete' => array('perm_add'),
'edit' => array('perm_add'),
'get_row' => array('perm_add'),
'orphanedObjectDiagnostics' => array(),
'editField' => array('perm_add'),
'fetchEditForm' => array('perm_add'),
'fetchViewValue' => array('*'),
'quickAddAttributeForm' => array('perm_add'),
'quickFetchTemplateWithValidObjectAttributes' => array('perm_add'),
'restSearch' => array('*'),
'proposeObjectsFromAttributes' => array('*'),
'groupAttributesIntoObject' => array('perm_add'),
'revise_object' => array('perm_add'),
'view' => array('*'),
),
'objectReferences' => array(
'add' => array('perm_add'),
'delete' => array('perm_add'),
'view' => array('*'),
),
'objectTemplates' => array(
'activate' => array(),
'add' => array('perm_object_template'),
'edit' => array('perm_object_template'),
'delete' => array('perm_object_template'),
'getToggleField' => array(),
'objectChoice' => array('*'),
'objectMetaChoice' => array('perm_add'),
'view' => array('*'),
'viewElements' => array('*'),
'index' => array('*'),
'update' => array('perm_site_admin')
),
'objectTemplateElements' => array(
'viewElements' => array('*')
),
'orgBlacklists' => array(
'add' => array(),
'delete' => array(),
'edit' => array(),
'index' => array(),
),
'organisations' => array(
'admin_add' => array(),
'admin_delete' => array(),
'admin_edit' => array(),
'admin_generateuuid' => array(),
'admin_merge' => array(),
'fetchOrgsForSG' => array('*'),
'fetchSGOrgRow' => array('*'),
'getUUIDs' => array('perm_sync'),
'index' => array('*'),
'landingpage' => array('*'),
'view' => array('*'),
),
'pages' => array(
'display' => array('*'),
),
'posts' => array(
'add' => array('*'),
'delete' => array('*'),
'edit' => array('*'),
'pushMessageToZMQ' => array('perm_site_admin')
),
'regexp' => array(
'admin_add' => array('perm_regexp_access'),
'admin_clean' => array('perm_regexp_access'),
'admin_delete' => array('perm_regexp_access'),
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'cleanRegexModifiers' => array('perm_regexp_access'),
'index' => array('*'),
),
'restClientHistory' => array(
'delete' => array('*'),
'index' => array('*')
),
'roles' => array(
'admin_add' => array(),
'admin_delete' => array(),
'admin_edit' => array(),
'admin_index' => array('perm_admin'),
'admin_set_default' => array(),
'index' => array('*'),
'view' => array('*'),
),
'servers' => array(
'add' => array(),
'dbSchemaDiagnostic' => array(),
'cache' => array(),
'changePriority' => array(),
'checkout' => array(),
'clearWorkerQueue' => array(),
'createSync' => array('perm_sync'),
'delete' => array(),
'deleteFile' => array(),
'edit' => array(),
'fetchServersForSG' => array('*'),
'filterEventIndex' => array(),
'getApiInfo' => array('*'),
'getGit' => array(),
'getInstanceUUID' => array('perm_sync'),
'getPyMISPVersion' => array('*'),
'getRemoteUser' => array(),
'getSetting' => array(),
'getSubmodulesStatus' => array(),
'getSubmoduleQuickUpdateForm' => array(),
'getWorkers' => array(),
'getVersion' => array('*'),
'import' => array(),
'index' => array(),
'ondemandAction' => array(),
'postTest' => array('perm_sync'),
'previewEvent' => array(),
'previewIndex' => array(),
'pull' => array(),
'purgeSessions' => array(),
'push' => array(),
'releaseUpdateLock' => array(),
'resetRemoteAuthKey' => array(),
'rest' => array('perm_auth'),
'restartDeadWorkers' => array(),
'restartWorkers' => array(),
'serverSettings' => array(),
'serverSettingsEdit' => array(),
'serverSettingsReloadSetting' => array(),
'startWorker' => array(),
'startZeroMQServer' => array(),
'statusZeroMQServer' => array(),
'stopWorker' => array(),
'stopZeroMQServer' => array(),
'testConnection' => array(),
'update' => array(),
'updateJSON' => array(),
'updateProgress' => array(),
'updateSubmodule' => array(),
'uploadFile' => array(),
'viewDeprecatedFunctionUse' => array()
),
'shadowAttributes' => array(
'accept' => array('perm_add'),
'acceptSelected' => array('perm_add'),
'add' => array('perm_add'),
'add_attachment' => array('perm_add'),
'delete' => array('perm_add'),
'discard' => array('perm_add'),
'discardSelected' => array('perm_add'),
'download' => array('*'),
'edit' => array('perm_add'),
'editField' => array('perm_add'),
'fetchEditForm' => array('perm_add'),
'generateCorrelation' => array(),
'getProposalsByUuid' => array('perm_sync'),
'getProposalsByUuidList' => array('perm_sync'),
'index' => array('*'),
'view' => array('*'),
),
'sharingGroups' => array(
'add' => array('perm_sharing_group'),
'addServer' => array('perm_sharing_group'),
'addOrg' => array('perm_sharing_group'),
'delete' => array('perm_sharing_group'),
'edit' => array('perm_sharing_group'),
'index' => array('*'),
'removeServer' => array('perm_sharing_group'),
'removeOrg' => array('perm_sharing_group'),
'view' => array('*'),
),
'sightings' => array(
'add' => array('perm_sighting'),
'restSearch' => array('perm_sighting'),
'advanced' => array('perm_sighting'),
'delete' => array('perm_sighting'),
'index' => array('*'),
'listSightings' => array('*'),
'quickDelete' => array('perm_sighting'),
'viewSightings' => array('*'),
'bulkSaveSightings' => array('OR' => array('perm_sync', 'perm_sighting')),
'quickAdd' => array('perm_sighting')
),
'sightingdb' => array(
'add' => array(),
'edit' => array(),
'delete' => array(),
'index' => array(),
'requestStatus' => array(),
'search' => array()
),
'tagCollections' => array(
'add' => array('perm_tag_editor'),
'addTag' => array('perm_tag_editor'),
'delete' => array('perm_tag_editor'),
'edit' => array('perm_tag_editor'),
'getRow' => array('perm_tag_editor'),
'import' => array('perm_tag_editor'),
'index' => array('*'),
'removeTag' => array('perm_tag_editor'),
'view' => array('*')
),
'tags' => array(
'add' => array('perm_tag_editor'),
'attachTagToObject' => array('perm_tagger'),
'delete' => array(),
'edit' => array(),
'index' => array('*'),
'quickAdd' => array('perm_tag_editor'),
'removeTagFromObject' => array('perm_tagger'),
'search' => array('*'),
'selectTag' => array('perm_tagger'),
'selectTaxonomy' => array('perm_tagger'),
'showEventTag' => array('*'),
'showAttributeTag' => array('*'),
'showTagControllerTag' => array('*'),
'tagStatistics' => array('*'),
'view' => array('*'),
'viewGraph' => array('*'),
'viewTag' => array('*')
),
'tasks' => array(
'index' => array(),
'setTask' => array(),
),
'taxonomies' => array(
'addTag' => array(),
'delete' => array(),
'disable' => array(),
'disableTag' => array(),
'enable' => array(),
'index' => array('*'),
'taxonomyMassConfirmation' => array('perm_tagger'),
'taxonomyMassHide' => array('perm_tagger'),
'taxonomyMassUnhide' => array('perm_tagger'),
'toggleRequired' => array('perm_site_admin'),
'update' => array(),
'view' => array('*'),
'unhideTag' => array('perm_tagger'),
'hideTag' => array('perm_tagger'),
),
'templateElements' => array(
'add' => array('perm_template'),
'delete' => array('perm_template'),
'edit' => array('perm_template'),
'index' => array('*'),
'templateElementAddChoices' => array('perm_template'),
),
'templates' => array(
'add' => array('perm_template'),
'delete' => array('perm_template'),
'deleteTemporaryFile' => array('perm_add'),
'edit' => array('perm_template'),
'index' => array('*'),
'populateEventFromTemplate' => array('perm_add'),
'saveElementSorting' => array('perm_template'),
'submitEventPopulation' => array('perm_add'),
'templateChoices' => array('*'),
'uploadFile' => array('*'),
'view' => array('*'),
),
'threads' => array(
'index' => array('*'),
'view' => array('*'),
'viewEvent' => array('*'),
),
'users' => array(
'acceptRegistrations' => array('perm_site_admin'),
'admin_add' => array('perm_admin'),
'admin_delete' => array('perm_admin'),
'admin_edit' => array('perm_admin'),
'admin_email' => array('perm_admin'),
'admin_filterUserIndex' => array('perm_admin'),
'admin_index' => array('perm_admin'),
'admin_monitor' => array('perm_site_admin'),
'admin_quickEmail' => array('perm_admin'),
'admin_view' => array('perm_admin'),
'attributehistogram' => array('*'),
'change_pw' => array('*'),
'checkAndCorrectPgps' => array(),
'checkIfLoggedIn' => array('*'),
'dashboard' => array('*'),
'delete' => array('perm_admin'),
'discardRegistrations' => array('perm_site_admin'),
'downloadTerms' => array('*'),
'edit' => array('*'),
'email_otp' => array('*'),
'searchGpgKey' => array('*'),
'fetchGpgKey' => array('*'),
'histogram' => array('*'),
'initiatePasswordReset' => array('perm_admin'),
'login' => array('*'),
'logout' => array('*'),
'register' => array('*'),
'registrations' => array('perm_site_admin'),
'resetAllSyncAuthKeys' => array(),
'resetauthkey' => array('*'),
'request_API' => array('*'),
'routeafterlogin' => array('*'),
'statistics' => array('*'),
'tagStatisticsGraph' => array('*'),
'terms' => array('*'),
'updateLoginTime' => array('*'),
'verifyCertificate' => array(),
'verifyGPG' => array(),
'view' => array('*'),
),
'userSettings' => array(
'index' => array('*'),
'view' => array('*'),
'setSetting' => array('*'),
'getSetting' => array('*'),
'delete' => array('*'),
'setHomePage' => array('*')
),
'warninglists' => array(
'checkValue' => array('perm_auth'),
'delete' => array(),
'enableWarninglist' => array(),
'getToggleField' => array(),
'index' => array('*'),
'toggleEnable' => array(),
'update' => array(),
'view' => array('*')
),
'whitelists' => array(
'admin_add' => array('perm_regexp_access'),
'admin_delete' => array('perm_regexp_access'),
'admin_edit' => array('perm_regexp_access'),
'admin_index' => array('perm_regexp_access'),
'index' => array('*'),
),
'eventGraph' => array(
'view' => array('*'),
'add' => array('perm_add'),
'delete' => array('perm_modify'),
)
);
private function __checkLoggedActions($user, $controller, $action)
{
$loggedActions = array(
'servers' => array(
'index' => array(
'role' => array(
'NOT' => array(
'perm_site_admin'
)
),
'message' => __('This could be an indication of an attempted privilege escalation on older vulnerable versions of MISP (<2.4.115)')
)
)
);
foreach ($loggedActions as $k => $v) {
$loggedActions[$k] = array_change_key_case($v);
}
$message = '';
if (!empty($loggedActions[$controller])) {
if (!empty($loggedActions[$controller][$action])) {
$message = $loggedActions[$controller][$action]['message'];
$hit = false;
if (empty($loggedActions[$controller][$action]['role'])) {
$hit = true;
} else {
$role_req = $loggedActions[$controller][$action]['role'];
if (empty($role_req['OR']) && empty($role_req['AND']) && empty($role_req['NOT'])) {
$role_req = array('OR' => $role_req);
}
if (!empty($role_req['NOT'])) {
foreach ($role_req['NOT'] as $k => $v) {
if (!$user['Role'][$v]) {
$hit = true;
continue;
}
}
}
if (!$hit && !empty($role_req['AND'])) {
$subhit = true;
foreach ($role_req['AND'] as $k => $v) {
$subhit = $subhit && $user['Role'][$v];
}
if ($subhit) {
$hit = true;
}
}
if (!$hit && !empty($role_req['OR'])) {
foreach ($role_req['OR'] as $k => $v) {
if ($user['Role'][$v]) {
$hit = true;
continue;
}
}
}
if ($hit) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'User',
'model_id' => $user['id'],
'email' => $user['email'],
'action' => 'security',
'user_id' => $user['id'],
'title' => __('User triggered security alert by attempting to access /%s/%s. Reason why this endpoint is of interest: %s', $controller, $action, $message),
));
}
}
}
}
}
// The check works like this:
// If the user is a site admin, return true
// If the requested action has an OR-d list, iterate through the list. If any of the permissions are set for the user, return true
// If the requested action has an AND-ed list, iterate through the list. If any of the permissions for the user are not set, turn the check to false. Otherwise return true.
// If the requested action has a permission, check if the user's role has it flagged. If yes, return true
// If we fall through all of the checks, return an exception.
public function checkAccess($user, $controller, $action, $soft = false)
{
$controller = lcfirst(Inflector::camelize($controller));
$action = strtolower($action);
$aclList = $this->__aclList;
foreach ($aclList as $k => $v) {
$aclList[$k] = array_change_key_case($v);
}
$this->__checkLoggedActions($user, $controller, $action);
if ($user['Role']['perm_site_admin']) {
return true;
}
if (!isset($aclList[$controller])) {
return $this->__error(404, 'Invalid controller.', $soft);
}
if ($user['Role']['perm_site_admin']) {
return true;
}
if (isset($aclList[$controller][$action]) && !empty($aclList[$controller][$action])) {
if (in_array('*', $aclList[$controller][$action])) {
return true;
}
if (isset($aclList[$controller][$action]['OR'])) {
foreach ($aclList[$controller][$action]['OR'] as $permission) {
if ($user['Role'][$permission]) {
return true;
}
}
} elseif (isset($aclList[$controller][$action]['AND'])) {
$allConditionsMet = true;
foreach ($aclList[$controller][$action]['AND'] as $permission) {
if (!$user['Role'][$permission]) {
$allConditionsMet = false;
}
}
if ($allConditionsMet) {
return true;
}
} elseif ($user['Role'][$aclList[$controller][$action][0]]) {
return true;
}
}
return $this->__error(403, 'You do not have permission to use this functionality.', $soft);
}
private function __error($code, $message, $soft = false)
{
if ($soft) {
return $code;
}
switch ($code) {
case 404:
throw new NotFoundException($message);
break;
case 403:
throw new MethodNotAllowedException($message);
default:
throw new InternalErrorException('Unknown error: ' . $message);
}
}
private function __findAllFunctions()
{
$functionFinder = '/function[\s\n]+(\S+)[\s\n]*\(/';
$dir = new Folder(APP . 'Controller');
$files = $dir->find('.*\.php');
$results = array();
foreach ($files as $file) {
$controllerName = lcfirst(str_replace('Controller.php', "", $file));
if ($controllerName === 'app') {
$controllerName = '*';
}
$functionArray = array();
$fileContents = file_get_contents(APP . 'Controller' . DS . $file);
$fileContents = preg_replace('/\/\*[^\*]+?\*\//', '', $fileContents);
preg_match_all($functionFinder, $fileContents, $functionArray);
foreach ($functionArray[1] as $function) {
if (substr($function, 0, 1) !== '_' && $function !== 'beforeFilter' && $function !== 'afterFilter') {
$results[$controllerName][] = $function;
}
}
}
return $results;
}
public function printAllFunctionNames($content = false)
{
$results = $this->__findAllFunctions();
ksort($results);
return $results;
}
public function findMissingFunctionNames($content = false)
{
$results = $this->__findAllFunctions();
$missing = array();
foreach ($results as $controller => $functions) {
foreach ($functions as $function) {
if (!isset($this->__aclList[$controller])
|| !in_array($function, array_keys($this->__aclList[$controller]))) {
$missing[$controller][] = $function;
}
}
}
return $missing;
}
public function printRoleAccess($content = false)
{
$results = array();
$this->Role = ClassRegistry::init('Role');
$conditions = array();
if (is_numeric($content)) {
$conditions = array('Role.id' => $content);
}
$roles = $this->Role->find('all', array(
'recursive' => -1,
'conditions' => $conditions
));
if (empty($roles)) {
throw new NotFoundException('Role not found.');
}
foreach ($roles as $role) {
$urls = $this->__checkRoleAccess($role['Role']);
$results[$role['Role']['id']] = array('name' => $role['Role']['name'], 'urls' => $urls);
}
return $results;
}
private function __checkRoleAccess($role)
{
$result = array();
foreach ($this->__aclList as $controller => $actions) {
$controllerNames = Inflector::variable($controller) == Inflector::underscore($controller) ? array(Inflector::variable($controller)) : array(Inflector::variable($controller), Inflector::underscore($controller));
foreach ($controllerNames as $controllerName) {
foreach ($actions as $action => $permissions) {
if ($role['perm_site_admin']) {
$result[] = DS . $controllerName . DS . $action;
} elseif (in_array('*', $permissions)) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
} elseif (isset($permissions['OR'])) {
$access = false;
foreach ($permissions['OR'] as $permission) {
if ($role[$permission]) {
$access = true;
}
}
if ($access) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
}
} elseif (isset($permissions['AND'])) {
$access = true;
foreach ($permissions['AND'] as $permission) {
if ($role[$permission]) {
$access = false;
}
}
if ($access) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
}
} elseif (isset($permissions[0]) && $role[$permissions[0]]) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
}
}
}
}
return $result;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use \Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
use Cake\Http\Exception\NotAcceptableException;
use Cake\Error\Debugger;
class EncryptionKeysController extends AppController
{
public function index($owner_type = null, $owner_id = null)
{
$query = $this->EncryptionKeys->find();
if (!empty($owner_type)) {
$query->where(['owner_type' => $owner_type]);
}
if (!empty($owner_id)) {
$query->where(['owner_id' => $owner_id]);
}
if ($this->_isRest()) {
$alignments = $query->all();
return $this->RestResponse->viewData($alignments, 'json');
} else {
$this->loadComponent('Paginator');
$encrpyion_keys = $this->Paginator->paginate($query);
$this->set('data', $encrpyion_keys);
$this->set('metaGroup', 'ContactDB');
}
}
public function delete($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid encrpyion keys.'));
}
$individual = $this->Alignments->get($id);
if ($this->request->is('post') || $this->request->is('delete')) {
if ($this->Alignments->delete($individual)) {
$message = __('Individual deleted.');
if ($this->_isRest()) {
$individual = $this->Alignments->get($id);
return $this->RestResponse->saveSuccessResponse('Alignments', 'delete', $id, 'json', $message);
} else {
$this->Flash->success($message);
return $this->redirect($this->referer());
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('scope', 'alignments');
$this->set('id', $individual['id']);
$this->set('alignment', $individual);
$this->viewBuilder()->setLayout('ajax');
$this->render('/genericTemplates/delete');
}
public function add($owner_type = false, $owner_id = false)
{
if (empty($owner_type) && !empty($this->request->getData('owner_type'))) {
$owner_type = $this->request->getData('owner_type');
}
if (empty($owner_id) && !empty($this->request->getData('owner_id'))) {
$owner_id = $this->request->getData('owner_id');
}
if (empty($owner_type) || empty($owner_id)) {
throw new NotAcceptableException(__('Invalid input. owner_type and owner_id expected as parameters in the format /encryption_keys/add/[owner_type]/[owner_id] or passed as a JSON.'));
}
if ($owner_type === 'individual') {
$this->loadModel('Individuals');
$owner = $this->Individuals->find()->where(['id' => $owner_id])->first();
if (empty($owner)) {
throw new NotFoundException(__('Invalid owner individual.'));
}
} else {
$this->loadModel('Organisations');
$owner = $this->Organisations->find()->where(['id' => $owner_id])->first();
if (empty($owner)) {
throw new NotFoundException(__('Invalid owner individual.'));
}
}
$encryptionKey = $this->EncryptionKeys->newEmptyEntity();
if ($this->request->is('post')) {
$this->EncryptionKeys->patchEntity($encryptionKey, $this->request->getData());
$encrypionKey['owner_type'] = $owner_type;
if ($this->EncryptionKeys->save($encryptionKey)) {
$message = __('EncryptionKey added.');
if ($this->_isRest()) {
$encryptionKey = $this->EncryptionKeys->get($this->EncryptionKeys->id);
return $this->RestResponse->viewData($encryptionKey, 'json');
} else {
$this->Flash->success($message);
$this->redirect($this->referer());
}
} else {
$message = __('EncryptionKey could not be added.');
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('EncryptionKeys', 'add', false, $message);
} else {
$this->Flash->error($message);
$this->redirect($this->referer());
}
}
}
$this->set(compact('owner'));
$this->set(compact('encryptionKey'));
$this->set(compact('owner_id'));
$this->set(compact('owner_type'));
$this->set('metaGroup', 'ContactDB');
}
}

View File

@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.4
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
use Cake\Event\EventInterface;
/**
* Error Handling Controller
*
* Controller used by ExceptionRenderer to render error responses.
*/
class ErrorController extends AppController
{
/**
* Initialization hook method.
*
* @return void
*/
public function initialize(): void
{
$this->loadComponent('RequestHandler');
}
/**
* beforeFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeFilter(EventInterface $event)
{
}
/**
* beforeRender callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function beforeRender(EventInterface $event)
{
parent::beforeRender($event);
$this->viewBuilder()->setTemplatePath('Error');
}
/**
* afterFilter callback.
*
* @param \Cake\Event\EventInterface $event Event.
* @return \Cake\Http\Response|null|void
*/
public function afterFilter(EventInterface $event)
{
}
}

View File

@ -0,0 +1,155 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
class IndividualsController extends AppController
{
public function index()
{
$query = $this->Individuals->find();
$filterFields = ['uuid', 'email', 'first_name', 'last_name', 'position'];
if (!empty($this->request->getQuery('quickFilter'))) {
$quickFilter = $this->request->getQuery('quickFilter');
$conditions = [];
foreach ($filterFields as $field) {
$conditions[] = [$field . ' LIKE' => '%' . $quickFilter . '%'];
}
}
$quickFilter = $this->request->getQuery('quickFilter');
foreach ($filterFields as $filterField) {
$tempFilter = $this->request->getQuery($filterField);
if (!empty($tempFilter)) {
if (strpos($tempFilter, '%') !== false) {
$conditions[] = [$filterField . ' LIKE' => $tempFilter];
} else {
$conditions[] = [$filterField => $tempFilter];
}
}
}
$query->contain(['Alignments' => 'Organisations']);
if (!empty($this->request->getQuery('organisation_id'))) {
$query->matching('Alignments', function($q) {
return $q->where(['Alignments.organisation_id' => $this->request->getQuery('organisation_id')]);
});
}
if (!empty($conditions)) {
$query->where($conditions);
}
if ($this->_isRest()) {
$individuals = $query->all();
return $this->RestResponse->viewData($individuals, 'json');
} else {
$this->loadComponent('Paginator');
$individuals = $this->Paginator->paginate($query);
$this->set('data', $individuals);
$this->set('alignmentScope', 'individuals');
$this->set('metaGroup', 'ContactDB');
}
}
public function add()
{
$individual = $this->Individuals->newEmptyEntity();
if ($this->request->is('post')) {
$individual = $this->Individuals->patchEntity($individual, $this->request->getData());
if ($this->Individuals->save($individual)) {
$message = __('Individual added.');
if ($this->_isRest()) {
$individual = $this->Individuals->get($id);
return $this->RestResponse->viewData($individual, 'json');
} else {
$this->Flash->success($message);
$this->redirect(['action' => 'index']);
}
} else {
$message = __('Individual could not be added.');
if ($this->_isRest()) {
} else {
$this->Flash->error($message);
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('individual', $individual);
}
public function view($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid organisation.'));
}
$individual = $this->Individuals->get($id, [
'contain' => ['Alignments' => 'Organisations']
]);
if ($this->_isRest()) {
return $this->RestResponse->viewData($individual, 'json');
} else {
}
$this->set('metaGroup', 'ContactDB');
$this->set('individual', $individual);
}
public function edit($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid organisation.'));
}
$individual = $this->Individuals->get($id);
if ($this->request->is(['post', 'put'])) {
$this->Individuals->patchEntity($individual, $this->request->getData());
if ($this->Individuals->save($individual)) {
$message = __('Individual updated.');
if ($this->_isRest()) {
$individual = $this->Individuals->get($id);
return $this->RestResponse->viewData($individual, 'json');
} else {
$this->Flash->success($message);
return $this->redirect(['action' => 'index']);
}
} else {
if ($this->_isRest()) {
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('individual', $individual);
$this->render('add');
}
public function delete($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid organisation.'));
}
$individual = $this->Individuals->get($id);
if ($this->request->is('post') || $this->request->is('delete')) {
if ($this->Individuals->delete($individual)) {
$message = __('Individual deleted.');
if ($this->_isRest()) {
$individual = $this->Individuals->get($id);
return $this->RestResponse->saveSuccessResponse('Individuals', 'delete', $id, 'json', $message);
} else {
$this->Flash->success($message);
return $this->redirect($this->referer());
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('scope', 'individuals');
$this->set('id', $individual['id']);
$this->set('individual', $individual);
$this->viewBuilder()->setLayout('ajax');
$this->render('/genericTemplates/delete');
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use \Cake\Database\Expression\QueryExpression;
class OrganisationsController extends AppController
{
public function index()
{
$filterFields = ['name', 'uuid', 'nationality', 'sector', 'type', 'url'];
if (!empty($this->request->getQuery('quickFilter'))) {
$quickFilter = $this->request->getQuery('quickFilter');
$conditions = [];
foreach ($filterFields as $field) {
$conditions[] = [$field . ' LIKE' => '%' . $quickFilter . '%'];
}
}
$quickFilter = $this->request->getQuery('quickFilter');
foreach ($filterFields as $filterField) {
$tempFilter = $this->request->getQuery($filterField);
if (!empty($tempFilter)) {
if (strpos($tempFilter, '%') !== false) {
$conditions[] = [$filterField . ' LIKE' => $tempFilter];
} else {
$conditions[] = [$filterField => $tempFilter];
}
}
}
$query = $this->Organisations->find();
$query->contain(['Alignments' => 'Individuals']);
if (!empty($this->request->getQuery('individual_id'))) {
$query->matching('Alignments', function($q) {
return $q->where(['Alignments.individual_id' => $this->request->getQuery('individual_id')]);
});
}
if (!empty($conditions)) {
$query->where([
'OR' => $conditions
]);
}
if ($this->_isRest()) {
$organisations = $query->all();
return $this->RestResponse->viewData($organisations, 'json');
} else {
$this->loadComponent('Paginator');
$organisations = $this->Paginator->paginate($query);
$this->set('data', $organisations);
$this->set('metaGroup', 'ContactDB');
}
}
public function add()
{
$organisation = $this->Organisations->newEmptyEntity();
if ($this->request->is('post')) {
$organisation = $this->Organisations->patchEntity($organisation, $this->request->getData());
if ($this->Organisations->save($organisation)) {
$message = __('Organisation added.');
if ($this->_isRest()) {
$organisation = $this->Organisations->get($id);
return $this->RestResponse->viewData($organisation, 'json');
} else {
$this->Flash->success($message);
$this->redirect(['action' => 'index']);
}
} else {
$message = __('Organisation could not be added.');
if ($this->_isRest()) {
} else {
$this->Flash->error($message);
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('organisation', $organisation);
}
public function view($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid organisation.'));
}
$organisation = $this->Organisations->get($id, [
'contain' => ['Alignments' => 'Individuals']
]);
if ($this->_isRest()) {
return $this->RestResponse->viewData($organisation, 'json');
} else {
}
$this->set('metaGroup', 'ContactDB');
$this->set('organisation', $organisation);
}
public function edit($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid organisation.'));
}
$organisation = $this->Organisations->get($id);
if ($this->request->is(['post', 'put'])) {
$this->Organisations->patchEntity($organisation, $this->request->getData());
if ($this->Organisations->save($organisation)) {
$message = __('Organisation updated.');
if ($this->_isRest()) {
$organisation = $this->Organisations->get($id);
return $this->RestResponse->viewData($organisation, 'json');
} else {
$this->Flash->success($message);
return $this->redirect(['action' => 'index']);
}
} else {
if ($this->_isRest()) {
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('organisation', $organisation);
$this->render('add');
}
public function delete($id)
{
if (empty($id)) {
throw new NotFoundException(__('Invalid organisation.'));
}
$organisation = $this->Organisations->get($id);
if ($this->request->is('post') || $this->request->is('delete')) {
if ($this->Organisations->delete($organisation)) {
$message = __('Organisation deleted.');
if ($this->_isRest()) {
$organisation = $this->Organisations->get($id);
return $this->RestResponse->saveSuccessResponse('Organisations', 'delete', $id, 'json', $message);
} else {
$this->Flash->success($message);
return $this->redirect($this->referer());
}
}
}
$this->set('metaGroup', 'ContactDB');
$this->set('scope', 'organisations');
$this->set('id', $organisation['id']);
$this->set('organisation', $organisation);
$this->viewBuilder()->setLayout('ajax');
$this->render('/genericTemplates/delete');
}
}

View File

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.2.9
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Http\Exception\ForbiddenException;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Response;
use Cake\View\Exception\MissingTemplateException;
/**
* Static content controller
*
* This controller will render views from templates/Pages/
*
* @link https://book.cakephp.org/4/en/controllers/pages-controller.html
*/
class PagesController extends AppController
{
/**
* Displays a view
*
* @param array ...$path Path segments.
* @return \Cake\Http\Response|null
* @throws \Cake\Http\Exception\ForbiddenException When a directory traversal attempt.
* @throws \Cake\View\Exception\MissingTemplateException When the view file could not
* be found and in debug mode.
* @throws \Cake\Http\Exception\NotFoundException When the view file could not
* be found and not in debug mode.
* @throws \Cake\View\Exception\MissingTemplateException In debug mode.
*/
public function display(...$path): ?Response
{
if (!$path) {
return $this->redirect('/');
}
if (in_array('..', $path, true) || in_array('.', $path, true)) {
throw new ForbiddenException();
}
$page = $subpage = null;
if (!empty($path[0])) {
$page = $path[0];
}
if (!empty($path[1])) {
$subpage = $path[1];
}
$this->set(compact('page', 'subpage'));
try {
return $this->render(implode('/', $path));
} catch (MissingTemplateException $exception) {
if (Configure::read('debug')) {
throw $exception;
}
throw new NotFoundException();
}
return $this->render();
}
}

View File

View File

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class Alignment extends AppModel
{
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class AppModel extends Entity
{
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class EncryptionKey extends AppModel
{
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class Individual extends AppModel
{
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class Organisation extends AppModel
{
}

View File

View File

@ -0,0 +1,26 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class AlignmentsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->belongsTo('Individuals');
$this->belongsTo('Organisations');
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('individual_id')
->notEmptyString('organisation_id')
->requirePresence(['individual_id', 'organisation_id'], 'create');
return $validator;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class AppTable extends Table
{
public function initialize(array $config): void
{
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class EncryptionKeysTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->belongsTo(
'Individuals',
[
'foreignKey' => 'owner_id',
'conditions' => ['owner_type' => 'individual']
]
);
$this->belongsTo(
'Organisations',
[
'foreignKey' => 'owner_id',
'conditions' => ['owner_type' => 'organisation']
]
);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('type')
->notEmptyString('encryption_key')
->notEmptyString('uuid')
->notEmptyString('owner_id')
->notEmptyString('owner_type')
->requirePresence(['type', 'encryption_key', 'uuid', 'owner_id', 'owner_type'], 'create');
return $validator;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class IndividualsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->hasMany('Alignments');
$this->hasMany(
'EncryptionKeys',
[
'foreignKey' => 'owner_id',
'conditions' => ['owner_type' => 'individual']
]
);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('email')
->notEmptyString('uuid')
->requirePresence(['email', 'uuid'], 'create');
return $validator;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class OrganisationsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->hasMany('Alignments');
$this->hasMany(
'EncryptionKeys',
[
'foreignKey' => 'owner_id',
'conditions' => ['owner_type' => 'organisation']
]
);
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('name')
->notEmptyString('uuid')
->requirePresence(['name', 'uuid'], 'create');
return $validator;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Model\Validation;
use Cake\Validation\Validator;
class ContactValidator extends Validator
{
public function __construct()
{
parent::__construct();
}
}

46
app/src/View/AjaxView.php Normal file
View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.4
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\View;
/**
* A view class that is used for AJAX responses.
* Currently only switches the default layout and sets the response type -
* which just maps to text/html by default.
*/
class AjaxView extends AppView
{
/**
* The name of the layout file to render the view inside of. The name
* specified is the filename of the layout in /templates/Layout without
* the .php extension.
*
* @var string
*/
public $layout = 'ajax';
/**
* Initialization hook method.
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->response = $this->response->withType('ajax');
}
}

45
app/src/View/AppView.php Normal file
View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\View;
use Cake\View\View;
use Cake\Utility\Hash;
/**
* Application View
*
* Your application's default view class
*
* @link https://book.cakephp.org/4/en/views.html#the-app-view
*/
class AppView extends View
{
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading helpers.
*
* e.g. `$this->loadHelper('Html');`
*
* @return void
*/
public function initialize(): void
{
parent::initialize();
$this->loadHelper('Hash');
$this->loadHelper('Paginator', ['templates' => 'cerebrate-pagination-templates']);
}
}

View File

View File

View File

@ -0,0 +1,450 @@
<?php
namespace App\View\Helper;
use Cake\View\Helper;
use Cake\Utility\Hash;
/*
* Simple helper untangling the mess that the introduction of fas / fab namespaces for icons brought in FA5+
*/
class FontAwesomeHelper extends Helper {
public function getClass($icon) {
return $this->findNamespace($icon) . ' fa-' . $icon;
}
public function findNamespace($icon)
{
$fab_icons = array(
'500px',
'accessible-icon',
'accusoft',
'acquisitions-incorporated',
'adn',
'adobe',
'adversal',
'affiliatetheme',
'airbnb',
'algolia',
'alipay',
'amazon',
'amazon-pay',
'amilia',
'android',
'angellist',
'angrycreative',
'angular',
'app-store',
'app-store-ios',
'apper',
'apple',
'apple-pay',
'artstation',
'asymmetrik',
'atlassian',
'audible',
'autoprefixer',
'avianex',
'aviato',
'aws',
'bandcamp',
'battle-net',
'behance',
'behance-square',
'bimobject',
'bitbucket',
'bitcoin',
'bity',
'black-tie',
'blackberry',
'blogger',
'blogger-b',
'bluetooth',
'bluetooth-b',
'bootstrap',
'btc',
'buffer',
'buromobelexperte',
'canadian-maple-leaf',
'cc-amazon-pay',
'cc-amex',
'cc-apple-pay',
'cc-diners-club',
'cc-discover',
'cc-jcb',
'cc-mastercard',
'cc-paypal',
'cc-stripe',
'cc-visa',
'centercode',
'centos',
'chrome',
'chromecast',
'cloudscale',
'cloudsmith',
'cloudversify',
'codepen',
'codiepie',
'confluence',
'connectdevelop',
'contao',
'cpanel',
'creative-commons',
'creative-commons-by',
'creative-commons-nc',
'creative-commons-nc-eu',
'creative-commons-nc-jp',
'creative-commons-nd',
'creative-commons-pd',
'creative-commons-pd-alt',
'creative-commons-remix',
'creative-commons-sa',
'creative-commons-sampling',
'creative-commons-sampling-plus',
'creative-commons-share',
'creative-commons-zero',
'critical-role',
'css3',
'css3-alt',
'cuttlefish',
'd-and-d',
'd-and-d-beyond',
'dashcube',
'delicious',
'deploydog',
'deskpro',
'dev',
'deviantart',
'dhl',
'diaspora',
'digg',
'digital-ocean',
'discord',
'discourse',
'dochub',
'docker',
'draft2digital',
'dribbble',
'dribbble-square',
'dropbox',
'drupal',
'dyalog',
'earlybirds',
'ebay',
'edge',
'elementor',
'ello',
'ember',
'empire',
'envira',
'erlang',
'ethereum',
'etsy',
'evernote',
'expeditedssl',
'facebook',
'facebook-f',
'facebook-messenger',
'facebook-square',
'fantasy-flight-games',
'fedex',
'fedora',
'figma',
'firefox',
'first-order',
'first-order-alt',
'firstdraft',
'flickr',
'flipboard',
'fly',
'font-awesome',
'font-awesome-alt',
'font-awesome-flag',
'fonticons',
'fonticons-fi',
'fort-awesome',
'fort-awesome-alt',
'forumbee',
'foursquare',
'free-code-camp',
'freebsd',
'fulcrum',
'galactic-republic',
'galactic-senate',
'get-pocket',
'gg',
'gg-circle',
'git',
'git-square',
'github',
'github-alt',
'github-square',
'gitkraken',
'gitlab',
'gitter',
'glide',
'glide-g',
'gofore',
'goodreads',
'goodreads-g',
'google',
'google-drive',
'google-play',
'google-plus',
'google-plus-g',
'google-plus-square',
'google-wallet',
'gratipay',
'grav',
'gripfire',
'grunt',
'gulp',
'hacker-news',
'hacker-news-square',
'hackerrank',
'hips',
'hire-a-helper',
'hooli',
'hornbill',
'hotjar',
'houzz',
'html5',
'hubspot',
'imdb',
'instagram',
'intercom',
'internet-explorer',
'invision',
'ioxhost',
'itch-io',
'itunes',
'itunes-note',
'java',
'jedi-order',
'jenkins',
'jira',
'joget',
'joomla',
'js',
'js-square',
'jsfiddle',
'kaggle',
'keybase',
'keycdn',
'kickstarter',
'kickstarter-k',
'korvue',
'laravel',
'lastfm',
'lastfm-square',
'leanpub',
'less',
'line',
'linkedin',
'linkedin-in',
'linode',
'linux',
'lyft',
'magento',
'mailchimp',
'mandalorian',
'markdown',
'mastodon',
'maxcdn',
'medapps',
'medium',
'medium-m',
'medrt',
'meetup',
'megaport',
'mendeley',
'microsoft',
'mix',
'mixcloud',
'mizuni',
'modx',
'monero',
'napster',
'neos',
'nimblr',
'nintendo-switch',
'node',
'node-js',
'npm',
'ns8',
'nutritionix',
'odnoklassniki',
'odnoklassniki-square',
'old-republic',
'opencart',
'openid',
'opera',
'optin-monster',
'osi',
'page4',
'pagelines',
'palfed',
'patreon',
'paypal',
'penny-arcade',
'periscope',
'phabricator',
'phoenix-framework',
'phoenix-squadron',
'php',
'pied-piper',
'pied-piper-alt',
'pied-piper-hat',
'pied-piper-pp',
'pinterest',
'pinterest-p',
'pinterest-square',
'playstation',
'product-hunt',
'pushed',
'python',
'qq',
'quinscape',
'quora',
'r-project',
'raspberry-pi',
'ravelry',
'react',
'reacteurope',
'readme',
'rebel',
'red-river',
'reddit',
'reddit-alien',
'reddit-square',
'redhat',
'renren',
'replyd',
'researchgate',
'resolving',
'rev',
'rocketchat',
'rockrms',
'safari',
'salesforce',
'sass',
'schlix',
'scribd',
'searchengin',
'sellcast',
'sellsy',
'servicestack',
'shirtsinbulk',
'shopware',
'simplybuilt',
'sistrix',
'sith',
'sketch',
'skyatlas',
'skype',
'slack',
'slack-hash',
'slideshare',
'snapchat',
'snapchat-ghost',
'snapchat-square',
'soundcloud',
'sourcetree',
'speakap',
'speaker-deck',
'spotify',
'squarespace',
'stack-exchange',
'stack-overflow',
'staylinked',
'steam',
'steam-square',
'steam-symbol',
'sticker-mule',
'strava',
'stripe',
'stripe-s',
'studiovinari',
'stumbleupon',
'stumbleupon-circle',
'superpowers',
'supple',
'suse',
'symfony',
'teamspeak',
'telegram',
'telegram-plane',
'tencent-weibo',
'the-red-yeti',
'themeco',
'themeisle',
'think-peaks',
'trade-federation',
'trello',
'tripadvisor',
'tumblr',
'tumblr-square',
'twitch',
'twitter',
'twitter-square',
'typo3',
'uber',
'ubuntu',
'uikit',
'uniregistry',
'untappd',
'ups',
'usb',
'usps',
'ussunnah',
'vaadin',
'viacoin',
'viadeo',
'viadeo-square',
'viber',
'vimeo',
'vimeo-square',
'vimeo-v',
'vine',
'vk',
'vnv',
'vuejs',
'waze',
'weebly',
'weibo',
'weixin',
'whatsapp',
'whatsapp-square',
'whmcs',
'wikipedia-w',
'windows',
'wix',
'wizards-of-the-coast',
'wolf-pack-battalion',
'wordpress',
'wordpress-simple',
'wpbeginner',
'wpexplorer',
'wpforms',
'wpressr',
'xbox',
'xing',
'xing-square',
'y-combinator',
'yahoo',
'yammer',
'yandex',
'yandex-international',
'yarn',
'yelp',
'yoast',
'youtube',
'youtube-square',
'zhihu'
);
if (in_array($icon, $fab_icons)) {
return 'fab';
} else {
return 'fas';
}
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\View\Helper;
use Cake\View\Helper;
use Cake\Utility\Hash;
class HashHelper extends Helper
{
public function extract($target, $extraction_string)
{
return Hash::extract($target, $extraction_string);
}
}

View File

@ -0,0 +1,23 @@
<?php
echo $this->element('genericElements/Form/genericForm', array(
'form' => $this->Form,
'data' => array(
'entity' => $alignment,
'title' => __('Add new alignment for {0} #{1}', \Cake\Utility\Inflector::singularize(h($scope)), h($source_id)),
'description' => __('Alignments indicate that an individual belongs to an organisation in one way or another. The type of relationship is defined by the type field.'),
'model' => 'Organisations',
'fields' => array(
array(
'field' => ($scope === 'individuals' ? 'organisation_id' : 'individual_id'),
'options' => ($scope === 'individuals' ? $organisations : $individuals),
'type' => 'select'
),
array(
'field' => 'type'
)
),
'submit' => array(
'action' => $this->request->getParam('action')
)
)
));

View File

@ -0,0 +1,30 @@
<?php
$primaryIdentifiers = ['individual' => 'email', 'organisation' => 'name'];
echo $this->element('genericElements/Form/genericForm', array(
'form' => $this->Form,
'data' => array(
'entity' => $encryptionKey,
'title' => __('Add new encryption key for {0} #{1} ({2})', h($owner_type), h($owner_id), h($owner[$primaryIdentifiers[$owner_type]])),
'description' => __('Alignments indicate that an individual belongs to an organisation in one way or another. The type of relationship is defined by the type field.'),
'model' => 'Organisations',
'fields' => array(
array(
'field' => 'uuid',
'type' => 'uuid'
),
array(
'field' => 'type',
'options' => array('pgp' => 'PGP', 'smime' => 'S/MIME')
),
array(
'field' => 'encryption_key',
'label' => __('Public key'),
'type' => 'textarea',
'rows' => 8
)
),
'submit' => array(
'action' => $this->request->getParam('action')
)
)
));

View File

@ -0,0 +1,38 @@
<?php
/**
* @var \App\View\AppView $this
*/
use Cake\Core\Configure;
use Cake\Error\Debugger;
$this->layout = 'error';
if (Configure::read('debug')) :
$this->layout = 'dev_error';
$this->assign('title', $message);
$this->assign('templateName', 'error400.php');
$this->start('file');
?>
<?php if (!empty($error->queryString)) : ?>
<p class="notice">
<strong>SQL Query: </strong>
<?= h($error->queryString) ?>
</p>
<?php endif; ?>
<?php if (!empty($error->params)) : ?>
<strong>SQL Query Params: </strong>
<?php Debugger::dump($error->params) ?>
<?php endif; ?>
<?= $this->element('auto_table_warning') ?>
<?php
$this->end();
endif;
?>
<h2><?= h($message) ?></h2>
<p class="error">
<strong><?= __d('cake', 'Error') ?>: </strong>
<?= __d('cake', 'The requested address {0} was not found on this server.', "<strong>'{$url}'</strong>") ?>
</p>

View File

@ -0,0 +1,42 @@
<?php
/**
* @var \App\View\AppView $this
*/
use Cake\Core\Configure;
use Cake\Error\Debugger;
$this->layout = 'error';
if (Configure::read('debug')) :
$this->layout = 'dev_error';
$this->assign('title', $message);
$this->assign('templateName', 'error500.php');
$this->start('file');
?>
<?php if (!empty($error->queryString)) : ?>
<p class="notice">
<strong>SQL Query: </strong>
<?= h($error->queryString) ?>
</p>
<?php endif; ?>
<?php if (!empty($error->params)) : ?>
<strong>SQL Query Params: </strong>
<?php Debugger::dump($error->params) ?>
<?php endif; ?>
<?php if ($error instanceof Error) : ?>
<strong>Error in: </strong>
<?= sprintf('%s, line %s', str_replace(ROOT, 'ROOT', $error->getFile()), $error->getLine()) ?>
<?php endif; ?>
<?php
echo $this->element('auto_table_warning');
$this->end();
endif;
?>
<h2><?= __d('cake', 'An Internal Error Has Occurred') ?></h2>
<p class="error">
<strong><?= __d('cake', 'Error') ?>: </strong>
<?= h($message) ?>
</p>

View File

@ -0,0 +1,35 @@
<?php
$modelForForm = 'Individuals';
echo $this->element('genericElements/Form/genericForm', array(
'form' => $this->Form,
'data' => array(
'entity' => $individual,
'title' => __('Add new individual'),
'description' => __('Individuals are natural persons. They are meant to describe the basic information about an individual that may or may not be a user of this community. Users in genral require an individual object to identify the person behind them - however, no user account is required to store information about an individual. Individuals can have affiliations to organisations and broods as well as cryptographic keys, using which their messages can be verified and which can be used to securely contact them.'),
'model' => 'Organisations',
'fields' => array(
array(
'field' => 'email'
),
array(
'field' => 'first_name'
),
array(
'field' => 'last_name'
),
array(
'field' => 'position'
),
array(
'field' => 'uuid',
'label' => 'UUID',
'type' => 'uuid'
)
),
'submit' => array(
'action' => $this->request->getParam('action')
)
)
));
?>
</div>

View File

@ -0,0 +1,74 @@
<?php
echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'data' => $data,
'top_bar' => [
'pull' => 'right',
'children' => [
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'value'
]
]
],
'fields' => [
[
'name' => '#',
'sort' => 'id',
'data_path' => 'id',
],
[
'name' => __('Email'),
'sort' => 'email',
'data_path' => 'email',
],
[
'name' => __('First Name'),
'sort' => 'first_name',
'data_path' => 'first_name',
],
[
'name' => __('Last Name'),
'sort' => 'last_name',
'data_path' => 'last_name',
],
[
'name' => __('Alignments'),
'data_path' => 'alignments',
'element' => 'alignments',
'scope' => $alignmentScope
],
[
'name' => __('UUID'),
'sort' => 'uuid',
'data_path' => 'uuid',
'placeholder' => __('Leave empty to auto generate')
],
],
'title' => __('ContactDB Individuals Index'),
'description' => __('A list of individuals known by your Cerebrate instance. This list can get populated either directly, by adding new individuals or by fetching them from trusted remote sources. Additionally, users created for the platform will always have an individual identity.'),
'pull' => 'right',
'actions' => [
[
'url' => '/individuals/view',
'url_params_data_paths' => ['id'],
'icon' => 'eye'
],
[
'url' => '/individuals/edit',
'url_params_data_paths' => ['id'],
'icon' => 'edit'
],
[
'onclick' => 'populateAndLoadModal(\'/individuals/delete/[onclick_params_data_path]\');',
'onclick_params_data_path' => 'id',
'icon' => 'trash'
]
]
]
]);
echo '</div>';
?>

View File

@ -0,0 +1,41 @@
<?php
echo $this->element(
'/genericElements/SingleViews/single_view',
[
'title' => __('Individual View'),
'data' => $individual,
'fields' => [
[
'key' => __('ID'),
'path' => 'id'
],
[
'key' => __('Email'),
'path' => 'email'
],
[
'key' => __('UUID'),
'path' => 'uuid'
],
[
'key' => __('First Name'),
'path' => 'first_name'
],
[
'key' => __('Last Name'),
'path' => 'last_name'
],
[
'key' => __('Position'),
'path' => 'position'
],
[
'key' => __('Alignments'),
'type' => 'alignment',
'path' => 'alignments',
'scope' => 'individuals'
]
],
'children' => []
]
);

View File

@ -0,0 +1,42 @@
<?php
$modelForForm = 'Organisations';
echo $this->element('genericElements/Form/genericForm', array(
'form' => $this->Form,
'data' => array(
'entity' => $organisation,
'title' => __('Add new organisation'),
'description' => __('Organisations can be equivalent to legal entities or specific individual teams within such entities. Their purpose is to relate individuals to their affiliations and for release control of information using the Trust Circles.'),
'model' => 'Organisations',
'fields' => array(
array(
'field' => 'name'
),
array(
'field' => 'description',
'type' => 'textarea'
),
array(
'field' => 'uuid',
'label' => 'UUID',
'type' => 'uuid'
),
array(
'field' => 'URL'
),
array(
'field' => 'nationality'
),
array(
'field' => 'sector'
),
array(
'field' => 'type'
)
),
'submit' => array(
'action' => $this->request->getParam('action')
)
)
));
?>
</div>

View File

@ -0,0 +1,84 @@
<?php
echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'data' => $data,
'top_bar' => [
'pull' => 'right',
'children' => [
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'value'
]
]
],
'fields' => [
[
'name' => '#',
'sort' => 'id',
'class' => 'short',
'data_path' => 'id',
],
[
'name' => __('Name'),
'class' => 'short',
'data_path' => 'name',
],
[
'name' => __('UUID'),
'sort' => 'uuid',
'class' => 'short',
'data_path' => 'uuid',
],
[
'name' => __('Members'),
'data_path' => 'alignments',
'element' => 'count_summary',
'url' => '/individuals/index/?organisation_id={{url_data}}',
'url_data_path' => 'id'
],
[
'name' => __('URL'),
'sort' => 'url',
'class' => 'short',
'data_path' => 'url',
],
[
'name' => __('Nationality'),
'data_path' => 'nationality',
],
[
'name' => __('Sector'),
'data_path' => 'sector',
],
[
'name' => __('Type'),
'data_path' => 'type',
]
],
'title' => __('ContactDB Organisation Index'),
'description' => __('A list of organisations known by your Cerebrate instance. This list can get populated either directly, by adding new organisations or by fetching them from trusted remote sources.'),
'pull' => 'right',
'actions' => [
[
'url' => '/organisations/view',
'url_params_data_paths' => ['id'],
'icon' => 'eye'
],
[
'url' => '/organisations/edit',
'url_params_data_paths' => ['id'],
'icon' => 'edit'
],
[
'onclick' => 'populateAndLoadModal(\'/organisations/delete/[onclick_params_data_path]\');',
'onclick_params_data_path' => 'id',
'icon' => 'trash'
]
]
]
]);
echo '</div>';
?>

View File

@ -0,0 +1,49 @@
<?php
echo $this->element(
'/genericElements/SingleViews/single_view',
array(
'title' => __('Organisation View'),
'data' => $organisation,
'fields' => array(
[
'key' => __('ID'),
'path' => 'id'
],
[
'key' => __('Name'),
'path' => 'name'
],
[
'key' => __('UUID'),
'path' => 'uuid'
],
[
'key' => __('URL'),
'path' => 'url'
],
[
'key' => __('Nationality'),
'path' => 'nationality'
],
[
'key' => __('Sector'),
'path' => 'sector'
],
[
'key' => __('Type'),
'path' => 'type'
],
[
'key' => __('Contacts'),
'path' => 'contacts'
],
[
'key' => __('Alignments'),
'type' => 'alignment',
'path' => 'alignments',
'scope' => 'organisations'
]
),
'children' => []
)
);

View File

@ -0,0 +1,222 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
* @var \App\View\AppView $this
*/
use Cake\Cache\Cache;
use Cake\Core\Configure;
use Cake\Core\Plugin;
use Cake\Datasource\ConnectionManager;
use Cake\Error\Debugger;
use Cake\Http\Exception\NotFoundException;
$this->disableAutoLayout();
if (!Configure::read('debug')) :
throw new NotFoundException(
'Please replace templates/Pages/home.php with your own version or re-enable debug mode.'
);
endif;
$cakeDescription = 'CakePHP: the rapid development PHP framework';
?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
<?= $cakeDescription ?>:
<?= $this->fetch('title') ?>
</title>
<?= $this->Html->meta('icon') ?>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.css">
<?= $this->Html->css('milligram.min.css') ?>
<?= $this->Html->css('cake.css') ?>
<?= $this->Html->css('home.css') ?>
<?= $this->fetch('meta') ?>
<?= $this->fetch('css') ?>
<?= $this->fetch('script') ?>
</head>
<body>
<header>
<div class="container text-center">
<a href="https://cakephp.org/" target="_blank">
<img alt="CakePHP" src="https://cakephp.org/v2/img/logos/CakePHP_Logo.svg" width="350" />
</a>
<h1>
Welcome to CakePHP <?php echo Configure::version() ?> Strawberry
</h1>
</div>
</header>
<main class="main">
<div class="container">
<div class="content">
<div class="row">
<div class="column">
<div class="message default text-center">
<small>Please be aware that this page will not be shown if you turn off debug mode unless you replace templates/Pages/home.php with your own version.</small>
</div>
<!-- <div id="url-rewriting-warning" class="alert url-rewriting">
<ul>
<li class="bullet problem">
URL rewriting is not properly configured on your server.<br />
1) <a target="_blank" href="https://book.cakephp.org/4/en/installation.html#url-rewriting">Help me configure it</a><br />
2) <a target="_blank" href="https://book.cakephp.org/4/en/development/configuration.html#general-configuration">I don't / can't use URL rewriting</a>
</li>
</ul>
</div> -->
<?php Debugger::checkSecurityKeys(); ?>
</div>
</div>
<div class="row">
<div class="column">
<h4>Environment</h4>
<ul>
<?php if (version_compare(PHP_VERSION, '7.2.0', '>=')) : ?>
<li class="bullet success">Your version of PHP is 7.2.0 or higher (detected <?php echo PHP_VERSION ?>).</li>
<?php else : ?>
<li class="bullet problem">Your version of PHP is too low. You need PHP 7.2.0 or higher to use CakePHP (detected <?php echo PHP_VERSION ?>).</li>
<?php endif; ?>
<?php if (extension_loaded('mbstring')) : ?>
<li class="bullet success">Your version of PHP has the mbstring extension loaded.</li>
<?php else : ?>
<li class="bullet problem">Your version of PHP does NOT have the mbstring extension loaded.</li>
<?php endif; ?>
<?php if (extension_loaded('openssl')) : ?>
<li class="bullet success">Your version of PHP has the openssl extension loaded.</li>
<?php elseif (extension_loaded('mcrypt')) : ?>
<li class="bullet success">Your version of PHP has the mcrypt extension loaded.</li>
<?php else : ?>
<li class="bullet problem">Your version of PHP does NOT have the openssl or mcrypt extension loaded.</li>
<?php endif; ?>
<?php if (extension_loaded('intl')) : ?>
<li class="bullet success">Your version of PHP has the intl extension loaded.</li>
<?php else : ?>
<li class="bullet problem">Your version of PHP does NOT have the intl extension loaded.</li>
<?php endif; ?>
</ul>
</div>
<div class="column">
<h4>Filesystem</h4>
<ul>
<?php if (is_writable(TMP)) : ?>
<li class="bullet success">Your tmp directory is writable.</li>
<?php else : ?>
<li class="bullet problem">Your tmp directory is NOT writable.</li>
<?php endif; ?>
<?php if (is_writable(LOGS)) : ?>
<li class="bullet success">Your logs directory is writable.</li>
<?php else : ?>
<li class="bullet problem">Your logs directory is NOT writable.</li>
<?php endif; ?>
<?php $settings = Cache::getConfig('_cake_core_'); ?>
<?php if (!empty($settings)) : ?>
<li class="bullet success">The <em><?php echo $settings['className'] ?>Engine</em> is being used for core caching. To change the config edit config/app.php</li>
<?php else : ?>
<li class="bullet problem">Your cache is NOT working. Please check the settings in config/app.php</li>
<?php endif; ?>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="column">
<h4>Database</h4>
<?php
try {
$connection = ConnectionManager::get('default');
$connected = $connection->connect();
} catch (Exception $connectionError) {
$connected = false;
$errorMsg = $connectionError->getMessage();
if (method_exists($connectionError, 'getAttributes')) :
$attributes = $connectionError->getAttributes();
if (isset($errorMsg['message'])) :
$errorMsg .= '<br />' . $attributes['message'];
endif;
endif;
}
?>
<ul>
<?php if ($connected) : ?>
<li class="bullet success">CakePHP is able to connect to the database.</li>
<?php else : ?>
<li class="bullet problem">CakePHP is NOT able to connect to the database.<br /><?php echo $errorMsg ?></li>
<?php endif; ?>
</ul>
</div>
<div class="column">
<h4>DebugKit</h4>
<ul>
<?php if (Plugin::isLoaded('DebugKit')) : ?>
<li class="bullet success">DebugKit is loaded.</li>
<?php else : ?>
<li class="bullet problem">DebugKit is NOT loaded. You need to either install pdo_sqlite, or define the "debug_kit" connection name.</li>
<?php endif; ?>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="column links">
<h3>Getting Started</h3>
<a target="_blank" href="https://book.cakephp.org/4/en/">CakePHP Documentation</a>
<a target="_blank" href="https://book.cakephp.org/4/en/tutorials-and-examples/cms/installation.html">The 20 min CMS Tutorial</a>
</div>
</div>
<hr>
<div class="row">
<div class="column links">
<h3>Help and Bug Reports</h3>
<a target="_blank" href="irc://irc.freenode.net/cakephp">irc.freenode.net #cakephp</a>
<a target="_blank" href="http://cakesf.herokuapp.com/">Slack</a>
<a target="_blank" href="https://github.com/cakephp/cakephp/issues">CakePHP Issues</a>
<a target="_blank" href="http://discourse.cakephp.org/">CakePHP Forum</a>
</div>
</div>
<hr>
<div class="row">
<div class="column links">
<h3>Docs and Downloads</h3>
<a target="_blank" href="https://api.cakephp.org/">CakePHP API</a>
<a target="_blank" href="https://bakery.cakephp.org">The Bakery</a>
<a target="_blank" href="https://book.cakephp.org/4/en/">CakePHP Documentation</a>
<a target="_blank" href="https://plugins.cakephp.org">CakePHP plugins repo</a>
<a target="_blank" href="https://github.com/cakephp/">CakePHP Code</a>
<a target="_blank" href="https://github.com/FriendsOfCake/awesome-cakephp">CakePHP Awesome List</a>
<a target="_blank" href="https://www.cakephp.org">CakePHP</a>
</div>
</div>
<hr>
<div class="row">
<div class="column links">
<h3>Training and Certification</h3>
<a target="_blank" href="https://cakefoundation.org/">Cake Software Foundation</a>
<a target="_blank" href="https://training.cakephp.org/">CakePHP Training</a>
</div>
</div>
</div>
</div>
</main>
</body>
</html>

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,20 @@
<?php
/**
* @var \App\View\AppView $this
* @var array $params
* @var string $message
*/
$class = 'message';
if (!empty($params['class'])) {
$class .= ' ' . $params['class'];
}
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="alert <?= h($class) ?> alert-dismissible fade show" role="alert">
<?= $message ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>

View File

@ -0,0 +1,16 @@
<?php
/**
* @var \App\View\AppView $this
* @var array $params
* @var string $message
*/
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<?= $message ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>

View File

@ -0,0 +1,16 @@
<?php
/**
* @var \App\View\AppView $this
* @var array $params
* @var string $message
*/
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= $message ?>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>

View File

@ -0,0 +1,8 @@
<?php
$params['div'] = false;
$temp = $form->control($fieldData['field'], $params);
if (!empty($fieldData['hidden'])) {
$temp = '<span class="hidden">' . $temp . '</span>';
}
echo $temp;
?>

View File

@ -0,0 +1,9 @@
<?php
$params['div'] = false;
$params['class'] .= ' form-control';
$temp = $form->control($fieldData['field'], $params);
if (!empty($fieldData['hidden'])) {
$temp = '<span class="hidden">' . $temp . '</span>';
}
echo $temp;
?>

View File

@ -0,0 +1,42 @@
<?php
$random = Cake\Utility\Security::randomString(8);
$params['div'] = false;
$this->Form->setTemplates([
'inputContainer' => '{{content}}',
'inputContainerError' => '{{content}}',
'formGroup' => '{{input}}',
]);
$label = $fieldData['label'];
$temp = sprintf(
'<div class="form-group row ">
<div class="col-sm-2 col-form-label">%s</div>
<div class="col-sm-10">
<div class="input-group">
%s<span class="input-group-append">%s</span>
</div>
</div>
</div>',
h($label),
$form->control($fieldData['field'], $params),
sprintf(
'<span id="uuid-gen-%s" class="btn btn-secondary">%s</span>',
$random,
__('Generate')
)
);
echo $temp;
?>
<script type="text/javascript">
$(document).ready(function() {
$('#uuid-gen-<?= h($random) ?>').on('click', function() {
$.ajax({
success:function (data, textStatus) {
$('#uuid').val(data["uuid"]);
},
type: "get",
cache: false,
url: "/organisations/generateUUID",
});
});
});
</script>

View File

@ -0,0 +1,47 @@
<?php
if (!is_array($fieldDesc)) {
$fieldDesc = array('info' => $fieldDesc);
$default = 'info';
} else {
if (!empty($field['options'])) {
if (isset($this->request->data[$modelForForm][$field['field']])) {
$default = $this->request->data[$modelForForm][$field['field']];
} else {
reset($field['options']);
$default = key($field['options']);
}
} else {
reset($fieldDesc);
$fieldDesc = array('info' => key($fieldDesc));
$default = 'info';
}
}
echo sprintf(
'<span id = "%sInfoPopover" class="icon-info-sign" data-toggle="popover" data-trigger="hover"></span>',
h($field['field'])
);
?>
<script type="text/javascript">
$(document).ready(function() {
$('#<?php echo h($field['field']); ?>InfoPopover').popover({
html: true,
content: function() {
var tempSelector = '#<?php echo h($modelForForm . \Cake\Utility\Inflector::camelize($field['field'])); ?>';
if ($(tempSelector)[0].nodeName === "SELECT" && Object.keys(fieldDesc).length > 1) {
return $('<div>').append(
$('<span>').attr('class', 'blue bold').text($(tempSelector +" option:selected").text())
).append(
$('<span>').text(': ' + fieldDesc[$(tempSelector).val()])
);
} else {
return $('<div>').append(
$('<span>').attr('class', 'blue bold').text('<?php echo h(\Cake\Utility\Inflector::humanize($field['field'])); ?>')
).append(
$('<span>').text(": " + fieldDesc["info"])
);
}
}
});
var fieldDesc = <?php echo json_encode($fieldDesc); ?>;
});
</script>

View File

@ -0,0 +1,160 @@
<?php
/*
* Generic form builder
*
* Simply pass a JSON with the following keys set:
* - model: The model used to create the form (such as Attribute, Event)
* - fields: an array with each element generating an input field
* - field is the actual field name (such as org_id, name, etc) which is required
* - optional fields: default, type, options, placeholder, label - these are passed directly to $this->Form->input(),
* - requirements: boolean, if false is passed the field is skipped
* - metafields: fields that are outside of the scope of the form itself
- use these to define dynamic form fields, or anything that will feed into the regular fields via JS population
* - submit: The submit button itself. By default it will simply submit to the form as defined via the 'model' field
*/
$modelForForm = empty($data['model']) ?
h(\Cake\Utility\Inflector::singularize(\Cake\Utility\Inflector::classify($this->request->getParam('controller')))) :
h($data['model']);
$fieldsString = '';
$simpleFieldWhitelist = array(
'default', 'type', 'options', 'placeholder', 'label', 'empty', 'rows', 'div', 'required'
);
$fieldsArrayForPersistence = array();
if (empty($data['url'])) {
$data['url'] = ["controller" => $this->request->getParam('controller'), "action" => $this->request->getParam('url')];
}
$formRandomValue = Cake\Utility\Security::randomString(8);
$formCreate = $this->Form->create($data['entity'], ['id' => 'form-' . $formRandomValue]);
$default_template = [
'inputContainer' => '<div class="form-group row">{{content}}</div>',
'inputContainerError' => '<div class="form-group row has-error">{{content}}</div>',
'label' => '{{text}}',
'input' => '<input type="{{type}}" name="{{name}}"{{attrs}} />',
'textarea' => '<textarea name="{{name}}" {{attrs}}>{{value}}</textarea>',
'select' => '<select name="{{name}}" {{attrs}}>{{content}}</select>',
'checkbox' => '<input type="checkbox" name="{{name}}" value="{{value}}"{{attrs}}>',
'checkboxFormGroup' => '{{label}}',
'checkboxWrapper' => '<div class="checkbox">{{label}}</div>',
'formGroup' => '<div class="col-sm-2 col-form-label" {{attrs}}>{{label}}</div><div class="col-sm-10">{{input}}</div>',
'nestingLabel' => '{{hidden}}<div class="col-sm-2 col-form-label">{{text}}</div><div class="col-sm-10">{{input}}</div>',
];
if (!empty($data['fields'])) {
foreach ($data['fields'] as $fieldData) {
// we reset the template each iteration as individual fields might override the defaults.
$this->Form->setTemplates($default_template);
if (isset($fieldData['requirements']) && !$fieldData['requirements']) {
continue;
}
if (is_array($fieldData)) {
if (empty($fieldData['type'])) {
$fieldData['type'] = 'text';
}
$fieldTemplate = 'genericField';
if (file_exists(ROOT . '/templates/element/genericElements/Form/Fields/' . $fieldData['type'] . 'Field.php')) {
$fieldTemplate = $fieldData['type'] . 'Field';
}
if (empty($fieldData['label'])) {
$fieldData['label'] = \Cake\Utility\Inflector::humanize($fieldData['field']);
}
if (!empty($fieldDesc[$fieldData['field']])) {
$fieldData['label'] .= $this->element(
'genericElements/Form/formInfo', array(
'field' => $fieldData,
'fieldDesc' => $fieldDesc[$fieldData['field']],
'modelForForm' => $modelForForm
)
);
}
$params = array();
if (!empty($fieldData['class'])) {
if (is_array($fieldData['class'])) {
$class = implode(' ', $fieldData['class']);
} else {
$class = $fieldData['class'];
}
$params['class'] = $class;
} else {
$params['class'] = '';
}
if (empty($fieldData['type']) || $fieldData['type'] !== 'checkbox' ) {
$params['class'] .= ' form-control';
}
//$params['class'] = sprintf('form-control %s', $params['class']);
foreach ($simpleFieldWhitelist as $f) {
if (!empty($fieldData[$f])) {
$params[$f] = $fieldData[$f];
}
}
$temp = $this->element('genericElements/Form/Fields/' . $fieldTemplate, array(
'fieldData' => $fieldData,
'params' => $params,
'form' => $this->Form
));
if (!empty($fieldData['hidden'])) {
$temp = '<span class="hidden">' . $temp . '</span>';
}
$fieldsString .= $temp;
$fieldsArrayForPersistence []= $modelForForm . \Cake\Utility\Inflector::camelize($fieldData['field']);
} else {
$fieldsString .= $fieldData;
}
}
}
$metaFieldString = '';
if (!empty($data['metaFields'])) {
foreach ($data['metaFields'] as $metaField) {
$metaFieldString .= $metaField;
}
}
$submitButtonData = array('model' => $modelForForm, 'formRandomValue' => $formRandomValue);
if (!empty($data['submit'])) {
$submitButtonData = array_merge($submitButtonData, $data['submit']);
}
if (!empty($data['ajaxSubmit'])) {
$submitButtonData['ajaxSubmit'] = $ajaxSubmit;
}
$ajaxFlashMessage = '';
if ($ajax) {
$ajaxFlashMessage = sprintf(
'<div id="flashContainer"><div id="main-view-container">%s</div></div>',
$this->Flash->render()
);
}
$formEnd = $this->Form->end();
if (!empty($ajax)) {
echo $this->element('genericElements/genericModal', array(
'title' => empty($data['title']) ? h(\Cake\Utility\Inflector::humanize($this->request->params['action'])) . ' ' . $modelForForm : h($data['title']),
'body' => sprintf(
'%s%s%s%s%s%s',
empty($data['description']) ? '' : sprintf(
'<div class="pb-2">%s</div>',
$data['description']
),
$ajaxFlashMessage,
$formCreate,
$fieldsString,
$formEnd,
$metaFieldString
),
'actionButton' => $this->element('genericElements/Form/submitButton', $submitButtonData),
'class' => 'modal-lg'
));
} else {
echo sprintf(
'%s<h2>%s</h2>%s%s%s%s%s%s%s%s',
empty($ajax) ? '<div class="col-8">' : '',
empty($data['title']) ? h(\Cake\Utility\Inflector::humanize($this->request->params['action'])) . ' ' . $modelForForm : h($data['title']),
$formCreate,
$ajaxFlashMessage,
empty($data['description']) ? '' : sprintf(
'<div class="pb-3">%s</div>',
$data['description']
),
$fieldsString,
$this->element('genericElements/Form/submitButton', $submitButtonData),
$formEnd,
$metaFieldString,
empty($ajax) ? '</div>' : ''
);
}
?>

View File

@ -0,0 +1,17 @@
<?php
if ($ajax) {
echo sprintf(
'%s',
sprintf(
'<button id="submitButton" class="btn btn-primary" onClick="%s">%s</button>',
"$('#form-" . h($formRandomValue) . "').submit()",
__('Submit')
)
);
} else {
echo $this->Form->button(empty($text) ? __('Submit') : h($text), [
'class' => 'btn btn-' . (empty($type) ? 'primary' : h($type)),
'type' => 'submit'
]);
}
?>

View File

@ -0,0 +1,96 @@
<?php
/*
* Create the action field in a table
* - pass a list of actions that will be added as separate icons to the field
* - each action can have the following fields setTLDs
* - url: url to query
* - url_params_data_paths: add dynamic URL elements such as an id to the URL. Can be an array with each value added in a separate param
* - title: title of the action. Automatically generates aria labels too
* - postLink: convert the button into a POST request
* - postLinkConfirm: As the user to confirm the POST before submission with the given message
* - onclick: custom onclick action instead of a simple GET/POST request
* - onclick_params_data_path: pass a data path param to the onclick field. requires [onclick_params_data_path] in the onclick field
* as a needle for replacement
* - icon: FA icon (added using the helper, knowing the fa domain is not needed, just add the short name such as "edit")
* - requirement evaluates to true/false
* - complex_requirement - add complex requirements via lambda functions:
* - function($row, $options): the lambda function. $row contain the row data
* - options: array of options. datapaths described in the datapath keyname will be extracted and replaced with the actual row value
*/
echo '<td class="action-links text-right">';
foreach ($actions as $action) {
if (isset($action['requirement']) && !$action['requirement']) {
continue;
}
if (isset($action['complex_requirement'])) {
if (isset($action['complex_requirement']['options']['datapath'])) {
foreach ($action['complex_requirement']['options']['datapath'] as $name => $path) {
$action['complex_requirement']['options']['datapath'][$name] = empty($this->Hash->extract($row, $path)[0]) ? null : $this->Hash->extract($row, $path)[0];
}
}
$options = isset($action['complex_requirement']['options']) ? $action['complex_requirement']['options'] : array();
$requirementMet = $action['complex_requirement']['function']($row, $options);
if (!$requirementMet) {
continue;
}
}
$url_param_data_paths = '';
$url = empty($action['url']) ? '#' : h($action['url']);
if (!empty($action['url_params_data_paths'])) {
if (is_array($action['url_params_data_paths'])) {
$temp = array();
foreach ($action['url_params_data_paths'] as $path) {
$temp[] = h(Cake\Utility\Hash::extract($row, $path)[0]);
}
$url_param_data_paths = implode('/', $temp);
} else {
$url_param_data_paths = h(Cake\Utility\Hash::extract($row, $action['url_params_data_paths'])[0]);
}
$url .= '/' . $url_param_data_paths;
}
if (!empty($action['url_named_params_data_paths'])) {
if (is_array($action['url_named_params_data_paths'])) {
$temp = array();
foreach ($action['url_named_params_data_paths'] as $namedParam => $path) {
$temp[] = sprintf('%s:%s', h($namedParam), h($this->Hash->extract($row, $path)[0]));
}
$url_param_data_paths = implode('/', $temp);
}
$url .= '/' . $url_param_data_paths;
}
if (!empty($action['url_extension'])) {
$url .= '.' . $action['url_extension'];
}
if (isset($action['postLink'])) {
echo $this->Form->postLink(
'',
$url,
array(
'class' => $this->FontAwesome->getClass($action['icon']) . ' black ' . (empty($action['class']) ? '' : h($action['class'])),
'title' => empty($action['title']) ? '' : h($action['title']),
'aria-label' => empty($action['aria-label']) ? '' : h($action['aria-label']),
),
empty($action['postLinkConfirm'])? '' : $action['postLinkConfirm']
);
} else {
if (!empty($action['onclick']) && !empty($action['onclick_params_data_path'])) {
$action['onclick'] = str_replace(
'[onclick_params_data_path]',
h(Cake\Utility\Hash::extract($row, $action['onclick_params_data_path'])[0]),
$action['onclick']
);
}
echo sprintf(
'<a href="%s" title="%s" aria-label="%s" %s %s><i class="black %s"></i></a> ',
$url,
empty($action['title']) ? '' : h($action['title']),
empty($action['title']) ? '' : h($action['title']),
empty($action['dbclickAction']) ? '' : 'class="dblclickActionElement"',
empty($action['onclick']) ? '' : sprintf('onClick="%s"', $action['onclick']),
$this->FontAwesome->getClass($action['icon'])
);
}
}
echo '</td>';
?>

View File

@ -0,0 +1,43 @@
<?php
$raw_alignments = $this->Hash->extract($row, $field['data_path']);
$alignments = '';
if ($field['scope'] === 'individuals') {
foreach ($raw_alignments as $alignment) {
$alignments .= sprintf(
'<div><span class="font-weight-bold">%s</span> @ %s <a href="#" class="fas fa-trash text-black" onClick="%s"></a></div>',
h($alignment['type']),
sprintf(
'<a href="/organisations/view/%s">%s</a>',
h($alignment['organisation']['id']),
h($alignment['organisation']['name'])
),
sprintf(
"populateAndLoadModal(%s);",
sprintf(
"'/alignments/delete/%s'",
$alignment['id']
)
)
);
}
} else if ($field['scope'] === 'organisations') {
foreach ($raw_alignments as $alignment) {
$alignments .= sprintf(
'<div>[<span class="font-weight-bold">%s</span>] %s <a href="#" class="fas fa-trash text-black" onClick="%s"></a></div>',
h($alignment['type']),
sprintf(
'<a href="/individuals/view/%s">%s</a>',
h($alignment['individual']['id']),
h($alignment['individual']['email'])
),
sprintf(
"populateAndLoadModal(%s);",
sprintf(
"'/alignments/delete/%s'",
$alignment['id']
)
)
);
}
}
echo $alignments;

View File

@ -0,0 +1,4 @@
<?php
echo h($arrayData[$this->Hash->extract($row, $field['data_path'])[0]]);
?>

View File

@ -0,0 +1,57 @@
<?php
$rules_raw = array();
$typeOptions = array(
'OR' => array(
'colour' => 'green',
'text' => 'allowed'
),
'NOT' => array(
'colour' => 'red',
'text' => 'blocked'
)
);
if (
!empty($this->Hash->extract($row, $field['data_path'])[0]) &&
!empty($field['rule_path'][0]) &&
!empty($this->Hash->extract($row, $field['rule_path'])[0])
) {
$rules = $this->Hash->extract($row, $field['rule_path'])[0];
$rules = json_decode($rules, true);
foreach ($rules as $rule => $rule_data) {
if (is_array($rule_data)) {
foreach ($rule_data as $boolean => $values) {
if (!empty($values)) {
if (is_array($values)) {
$values = implode(', ', $values);
}
$rules_raw[] = sprintf(
'<span class=\'bold\'>%s %s</span>: <span class=\'%s\'>%s</span>',
h(\Cake\Utility\Inflector::humanize($rule)),
$typeOptions[$boolean]['text'],
$typeOptions[$boolean]['colour'],
h($values)
);
}
}
} else if (!empty($rule_data)){
$rules_raw[] = sprintf(
'<span class=\'bold\'>%s</span>: <span class=\'green\'>%s</span>',
h(\Cake\Utility\Inflector::humanize($rule)),
h($rule_data)
);
}
}
$rules_raw = implode('<br />', $rules_raw);
}
echo sprintf(
'<i class="black fa fa-%s"></i>%s',
(!empty($this->Hash->extract($row, $field['data_path'])[0])) ? 'check' : 'times',
empty($rules_raw) ? '' :
sprintf(
' <span data-toggle="popover" title="%s" data-content="%s">(%s)</span>',
__('Filter rules'),
$rules_raw,
__('Rules')
)
);
?>

View File

@ -0,0 +1,32 @@
<?php
$timestamp = $this->Hash->extract($row, $field['data_path'])[0];
$enabled = isset($field['enabled_path']) ? $this->Hash->extract($row, $field['enabled_path'])[0] : true;
if (!empty($timestamp)):
$units = array('m', 'h', 'd');
$intervals = array(60, 60, 24);
$unit = 's';
$last = time() - $timestamp;
foreach ($units as $k => $v) {
if ($last > $intervals[$k]) {
$unit = $v;
$last = floor($last / $intervals[$k]);
} else {
break;
}
}
$ageString = __('Age: ') . $last . $unit;
else:
$ageString = __('Not cached');
endif;
echo sprintf(
'<span class="%s">%s</span>%s',
empty($timestamp) ? 'red bold' : '',
h($ageString),
(!$enabled || !$isSiteAdmin) ? '' : sprintf(
' <a href="%s" aria-label="%s" title="%s"><span class="black fa fa-memory"></span></a>',
$baseurl . '/feeds/cacheFeeds/' . h($primary),
__('Cache feed'),
__('Cache feed')
)
);
?>

View File

@ -0,0 +1,20 @@
<?php
$correlations = $this->Hash->extract($row, $field['data_path']);
$scope_to_url = array(
'event' => $baseurl . '/events/view'
);
$correlations_html = array();
foreach ($correlations as $id => $name) {
$correlations_html[] = sprintf(
'<a href="%s" title="%s">%s</a>',
sprintf(
'%s/%s',
$scope_to_url[empty($scope) ? 'event' : $scope],
h($id)
),
h($name),
h($id)
);
}
echo implode(' ', $correlations_html);
?>

View File

@ -0,0 +1,25 @@
<?php
/*
'name' => __('Members'),
'data_path' => 'alignments',
'element' => 'count_summary',
'url' => '/individuals/index/?organisation_id={{url_data}}',
'url_data_path' => 'id'
*/
$data = $this->Hash->extract($row, $field['data_path']);
if (!empty($field['url_data_path'])) {
$url_data_path = $this->Hash->extract($row, $field['url_data_path'])[0];
}
if (!empty($field['url']) && count($data) > 0) {
if (!empty($url_data_path)) {
$field['url'] = str_replace('{{url_data}}', $url_data_path, $field['url']);
}
echo sprintf(
'<a href="%s">%s</a>',
h($field['url']),
count($data)
);
} else {
echo count($data);
}
?>

View File

@ -0,0 +1,26 @@
<?php
$data = $this->Hash->extract($row, $field['data_path']);
if (is_array($data)) {
if (count($data) > 1) {
$data = implode(', ', $data);
} else {
if (count($data) > 0) {
$data = $data[0];
} else {
$data = '';
}
}
}
$data = h($data);
if (is_numeric($data)) {
$data = date('Y-m-d H:i:s', $data);
}
if (!empty($field['onClick'])) {
$data = sprintf(
'<span onClick="%s">%s</span>',
$field['onClick'],
$data
);
}
echo $data;
?>

View File

@ -0,0 +1,15 @@
<?php
$distributionLevel = ($this->Hash->extract($row, $field['data_path'])[0]);
echo sprintf(
'<span class="%s bold">%s</span>',
$distributionLevel == 0 ? 'red' : '',
$distributionLevel != 4 ? $distributionLevels[$distributionLevel] :
sprintf(
'<a href="%s/sharing_groups/view/%s">%s</a>',
$baseurl,
h($row['SharingGroup']['id']),
h($row['SharingGroup']['name'])
)
);
?>

View File

@ -0,0 +1,37 @@
<?php
$data = $this->Hash->extract($row, $field['data_path']);
if (is_array($data)) {
if (count($data) > 1) {
$data = implode(', ', $data);
} else {
if (count($data) > 0) {
$data = $data[0];
} else {
$data = '';
}
}
}
if (is_bool($data)) {
$data = sprintf(
'<i class="black fa fa-%s"></i>',
$data ? 'check' : 'times'
);
$data = '';
} else {
$data = h($data);
if (!empty($field['privacy'])) {
$data = sprintf(
'<span class="privacy-value" data-hidden-value="%s">****************************************</span> <i class="privacy-toggle fas fa-eye useCursorPointer"></i>',
$data
);
}
}
if (!empty($field['onClick'])) {
$data = sprintf(
'<span onClick="%s">%s</span>',
$field['onClick'],
$data
);
}
echo $data;
?>

View File

@ -0,0 +1,5 @@
<?php
echo sprintf(
'<i class="black %s"></i>',
$this->FontAwesome->getClass($this->Hash->extract($row, $field['data_path'])[0])
);

View File

@ -0,0 +1,16 @@
<?php
$data = h($this->Hash->extract($row, $field['data_path']));
// I feed dirty for this...
if (is_array($data) && count($data) === 1 && isset($data[0])) {
$data = $data[0];
}
echo sprintf(
'<div class="json_container_%s"></div>',
h($k)
);
?>
<script type="text/javascript">
$(document).ready(function() {
$('.json_container_<?php echo h($k);?>').html(syntaxHighlightJson(<?php echo json_encode($data); ?>, 4));
});
</script>

View File

@ -0,0 +1,47 @@
<?php
/**
* - url: url to reference. Can have `%s` in it to be replaced by `data_path` extracted value.
* - url_params_data_paths: add dynamic URL elements such as an id to the URL. Can be an array with each value added in a separate param. Used if `url` does not have a `%s` marker
*/
$data_elements = $this->Hash->extract($row, $field['data_path']);
$url_param_data_paths = '';
$urlWithData = empty($field['url']) ? '#' : h($field['url']);
if (!empty($field['url_params_data_paths'])) {
if (is_array($field['url_params_data_paths'])) {
$temp = array();
foreach ($field['url_params_data_paths'] as $path) {
$temp[] = h($this->Hash->extract($row, $path)[0]);
}
$url_param_data_paths = implode('/', $temp);
} else {
$url_param_data_paths = h($this->Hash->extract($row, $field['url_params_data_paths'])[0]);
}
$urlWithData .= '/' . $url_param_data_paths;
}
$links = array();
foreach ($data_elements as $data) {
if (!empty($data['name'])) {
$field['title'] = $data['name'];
}
if (!empty($data['url'])) {
$data = $data['url'];
}
if (isset($field['url']) && strpos($field['url'], '%s') !== false) {
$url = sprintf(
$field['url'],
$data
);
} elseif (!empty($field['url_params_data_paths'])) {
$url = $urlWithData;
} else {
$url = $data;
}
$links[] = sprintf(
'<a href="%s" title="%s">%s</a>',
h($url),
empty($field['title']) ? h($data) : h($field['title']),
empty($field['title']) ? h($data) : h($field['title'])
);
}
echo implode('<br />', $links);
?>

View File

@ -0,0 +1,16 @@
<?php
$data = $this->Hash->extract($row, $field['data_path']);
foreach ($data as $key => $element) {
if (!is_numeric($key)) {
$data[$key] = sprintf(
'<span>%s</span>: %s',
h($key),
h($element)
);
} else {
$data[$key] = h($element);
}
}
$data = implode('<br />', $data);
echo $data;
?>

View File

@ -0,0 +1,38 @@
<?php
$orgs = $this->Hash->extract($row, $field['data_path']);
if (!isset($field['fields']['allow_picture'])) {
$field['fields']['allow_picture'] = true;
}
if (!isset($field['fields']['default_org'])) {
$field['fields']['default_org'] = '';
}
if (!empty($orgs)) {
if (!isset($orgs[0])) {
$orgs = array($orgs);
}
$count = count($orgs);
$i = 0;
foreach ($orgs as $org) {
$i++;
if (!empty($org['id']) || !empty($org['name'])) {
if ($field['fields']['allow_picture'] && !empty($org['id'])) {
echo $this->OrgImg->getOrgImg(array('name' => $org['name'], 'id' => $org['id'], 'size' => 24));
} else {
echo sprintf(
'<a href="%s/organisations/view/%s">%s</a>',
$baseurl,
empty($org['id']) ? h($org['uuid']) : h($org['id']),
h($org['name'])
);
}
if ($i < $count) {
echo '<br />';
}
} else {
if ($field['fields']['allow_picture']) {
echo $this->OrgImg->getOrgImg(array('name' => $field['fields']['default_org'], 'size' => 24));
}
}
}
}
?>

View File

@ -0,0 +1,22 @@
<?php
$roles = $this->Hash->extract($row, $field['data_path']);
if (!empty($roles)) {
if (!isset($roles[0])) {
$roles = array($roles);
}
$count = count($roles);
$i = 0;
foreach ($roles as $role) {
$i++;
echo sprintf(
'<a href="%s/roles/view/%s">%s</a>',
$baseurl,
h($role['id']),
h($role['name'])
);
if ($i < $count) {
echo '<br />';
}
}
}
?>

View File

@ -0,0 +1,20 @@
<?php
$data = array();
if (!empty($field['data'])) {
foreach ($field['data'] as $dataField => $dataValue) {
$value = '';
if (!empty($dataValue['value'])) {
$value = $dataValue['value'];
}
if (!empty($dataValue['value_path']) && !empty($this->Hash->extract($row, $dataValue['value_path'])[0])) {
$value = $this->Hash->extract($row, $dataValue['value_path'])[0];
}
$data[] = 'data-' . h($dataField) . '="' . h($value) . '"';
}
}
echo sprintf(
'<input class="select_attribute select" type="checkbox" data-rowid="%s" %s>',
h($k),
empty($data) ? '' : implode(' ', $data)
);
?>

View File

@ -0,0 +1,17 @@
<?php
$self_registration_flag = $this->Hash->extract($row, $field['data_path_requirement']);
if (empty($self_registration_flag[0])) {
echo '<i class="black fa fa-times"></i>';
} else {
$url = $this->Hash->extract($row, $field['data_path'])[0];
echo sprintf(
'<i class="black fa fa-%s"></i>%s',
(!empty($self_registration_flag[0])) ? 'check' : 'times',
(empty($self_registration_flag[0])) ? '' :
sprintf(
' (<a href="%s/users/register">' . __('click here') . '</a>)',
h($url)
)
);
}
?>

Some files were not shown because too many files have changed in this diff Show More