mirror of
https://github.com/maxpozdeev/mytinytodo.git
synced 2026-03-11 08:55:27 +00:00
Merge branch 'v1.8'
This commit is contained in:
commit
4b4e47df6a
39 changed files with 1189 additions and 203 deletions
|
|
@ -3,7 +3,8 @@
|
|||
"vendor-dir": "src/includes/vendor"
|
||||
},
|
||||
"require": {
|
||||
"erusev/parsedown": "^1.7"
|
||||
"erusev/parsedown": "^1.7",
|
||||
"symfony/polyfill-intl-normalizer": "^1.27"
|
||||
},
|
||||
"require-dev": {
|
||||
"league/commonmark": "^2.3"
|
||||
|
|
|
|||
180
composer.lock
generated
180
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "18661f8c2155d955e7962271812bbed0",
|
||||
"content-hash": "cb6818ea27ab475ab65854fa66fd68bd",
|
||||
"packages": [
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
|
|
@ -55,21 +55,105 @@
|
|||
"source": "https://github.com/erusev/parsedown/tree/1.7.x"
|
||||
},
|
||||
"time": "2019-12-30T22:54:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-normalizer",
|
||||
"version": "v1.27.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
|
||||
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for intl's Normalizer class and related functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"intl",
|
||||
"normalizer",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-11-03T14:55:06+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "dflydev/dot-access-data",
|
||||
"version": "v3.0.1",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dflydev/dflydev-dot-access-data.git",
|
||||
"reference": "0992cc19268b259a39e86f296da5f0677841f42c"
|
||||
"reference": "f41715465d65213d644d3141a6a93081be5d3549"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/0992cc19268b259a39e86f296da5f0677841f42c",
|
||||
"reference": "0992cc19268b259a39e86f296da5f0677841f42c",
|
||||
"url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549",
|
||||
"reference": "f41715465d65213d644d3141a6a93081be5d3549",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -80,7 +164,7 @@
|
|||
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
|
||||
"scrutinizer/ocular": "1.6.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"vimeo/psalm": "^3.14"
|
||||
"vimeo/psalm": "^4.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
|
@ -129,22 +213,22 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
|
||||
"source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.1"
|
||||
"source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2"
|
||||
},
|
||||
"time": "2021-08-13T13:06:58+00:00"
|
||||
"time": "2022-10-27T11:44:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/commonmark",
|
||||
"version": "2.3.4",
|
||||
"version": "2.3.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/commonmark.git",
|
||||
"reference": "155ec1c95626b16fda0889cf15904d24890a60d5"
|
||||
"reference": "a36bd2be4f5387c0f3a8792a0d76b7d68865abbf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/155ec1c95626b16fda0889cf15904d24890a60d5",
|
||||
"reference": "155ec1c95626b16fda0889cf15904d24890a60d5",
|
||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/a36bd2be4f5387c0f3a8792a0d76b7d68865abbf",
|
||||
"reference": "a36bd2be4f5387c0f3a8792a0d76b7d68865abbf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -164,15 +248,15 @@
|
|||
"erusev/parsedown": "^1.0",
|
||||
"ext-json": "*",
|
||||
"github/gfm": "0.29.0",
|
||||
"michelf/php-markdown": "^1.4",
|
||||
"michelf/php-markdown": "^1.4 || ^2.0",
|
||||
"nyholm/psr7": "^1.5",
|
||||
"phpstan/phpstan": "^0.12.88 || ^1.0.0",
|
||||
"phpunit/phpunit": "^9.5.5",
|
||||
"phpstan/phpstan": "^1.8.2",
|
||||
"phpunit/phpunit": "^9.5.21",
|
||||
"scrutinizer/ocular": "^1.8.1",
|
||||
"symfony/finder": "^5.3",
|
||||
"symfony/finder": "^5.3 | ^6.0",
|
||||
"symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0",
|
||||
"unleashedtech/php-coding-standard": "^3.1",
|
||||
"vimeo/psalm": "^4.7.3"
|
||||
"unleashedtech/php-coding-standard": "^3.1.1",
|
||||
"vimeo/psalm": "^4.24.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/yaml": "v2.3+ required if using the Front Matter extension"
|
||||
|
|
@ -237,7 +321,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-07-17T16:25:47+00:00"
|
||||
"time": "2022-11-03T17:29:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/config",
|
||||
|
|
@ -323,25 +407,25 @@
|
|||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
"version": "v1.2.2",
|
||||
"version": "v1.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/schema.git",
|
||||
"reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df"
|
||||
"reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/9a39cef03a5b34c7de64f551538cbba05c2be5df",
|
||||
"reference": "9a39cef03a5b34c7de64f551538cbba05c2be5df",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
|
||||
"reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nette/utils": "^2.5.7 || ^3.1.5 || ^4.0",
|
||||
"php": ">=7.1 <8.2"
|
||||
"php": ">=7.1 <8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3 || ^2.4",
|
||||
"phpstan/phpstan-nette": "^0.12",
|
||||
"phpstan/phpstan-nette": "^1.0",
|
||||
"tracy/tracy": "^2.7"
|
||||
},
|
||||
"type": "library",
|
||||
|
|
@ -379,26 +463,26 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/schema/issues",
|
||||
"source": "https://github.com/nette/schema/tree/v1.2.2"
|
||||
"source": "https://github.com/nette/schema/tree/v1.2.3"
|
||||
},
|
||||
"time": "2021-10-15T11:40:02+00:00"
|
||||
"time": "2022-10-13T01:24:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v3.2.7",
|
||||
"version": "v3.2.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99"
|
||||
"reference": "02a54c4c872b99e4ec05c4aec54b5a06eb0f6368"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99",
|
||||
"reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/02a54c4c872b99e4ec05c4aec54b5a06eb0f6368",
|
||||
"reference": "02a54c4c872b99e4ec05c4aec54b5a06eb0f6368",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2 <8.2"
|
||||
"php": ">=7.2 <8.3"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/di": "<3.0.6"
|
||||
|
|
@ -464,9 +548,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v3.2.7"
|
||||
"source": "https://github.com/nette/utils/tree/v3.2.8"
|
||||
},
|
||||
"time": "2022-01-24T11:29:14+00:00"
|
||||
"time": "2022-09-12T23:36:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/event-dispatcher",
|
||||
|
|
@ -520,16 +604,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.1.1",
|
||||
"version": "v3.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918"
|
||||
"reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
|
||||
"reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3",
|
||||
"reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -538,7 +622,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.1-dev"
|
||||
"dev-main": "3.3-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
|
|
@ -567,7 +651,7 @@
|
|||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -583,20 +667,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-25T11:15:52+00:00"
|
||||
"time": "2022-11-25T10:21:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.26.0",
|
||||
"version": "v1.27.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
||||
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -605,7 +689,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.26-dev"
|
||||
"dev-main": "1.27-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
|
|
@ -650,7 +734,7 @@
|
|||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -666,7 +750,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-05-10T07:21:04+00:00"
|
||||
"time": "2022-11-03T14:55:06+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
|||
23
docker/dev/_nginx/Dockerfile-angie-alpine
Normal file
23
docker/dev/_nginx/Dockerfile-angie-alpine
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
FROM alpine:latest
|
||||
|
||||
RUN set -x \
|
||||
&& apk add --no-cache ca-certificates curl \
|
||||
&& curl -o /etc/apk/keys/angie-signing.rsa https://angie.software/keys/angie-signing.rsa \
|
||||
&& echo "https://download.angie.software/angie/alpine/v$(egrep -o '[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \
|
||||
&& apk add --no-cache angie \
|
||||
&& rm /etc/apk/keys/angie-signing.rsa \
|
||||
&& ln -sf /dev/stdout /var/log/angie/access.log \
|
||||
&& ln -sf /dev/stderr /var/log/angie/error.log
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["angie", "-g", "daemon off;"]
|
||||
|
||||
|
||||
RUN mv /etc/angie/http.d/default.conf /etc/angie/http.d/default.conf.bak \
|
||||
&& mkdir /var/log/nginx \
|
||||
&& ln -sf /var/log/angie/access.log /var/log/nginx/access.log
|
||||
|
||||
# config from nginx
|
||||
COPY default.conf /etc/angie/http.d/default.conf
|
||||
|
||||
VOLUME /var/www/html
|
||||
5
docker/dev/_nginx/Dockerfile-nginx-alpine
Normal file
5
docker/dev/_nginx/Dockerfile-nginx-alpine
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
FROM nginx:alpine
|
||||
|
||||
COPY default.conf /etc/nginx/conf.d/
|
||||
|
||||
VOLUME /var/www/html
|
||||
|
|
@ -3,7 +3,7 @@ log_format mtt '$remote_addr - $remote_user [$time_local] "$request" $status $
|
|||
access_log off;
|
||||
|
||||
upstream fpm {
|
||||
server php-fpm:9000;
|
||||
server fpm:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
|
|
|
|||
1
docker/dev/mtt-php72-angie-postgres10/.env
Normal file
1
docker/dev/mtt-php72-angie-postgres10/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
PLATFORM_NAME=mtt-dev-php72-angie-postgres10
|
||||
17
docker/dev/mtt-php72-angie-postgres10/Dockerfile-fpm
Normal file
17
docker/dev/mtt-php72-angie-postgres10/Dockerfile-fpm
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
FROM php:7.2-fpm-alpine
|
||||
|
||||
RUN apk add --update-cache libpq postgresql-dev && \
|
||||
docker-php-ext-install pdo_mysql pdo_pgsql && \
|
||||
apk del postgresql-dev && rm -rf /var/cache/apk/* && \
|
||||
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" && \
|
||||
mkdir /var/www/phpinfo && \
|
||||
echo "<?php phpinfo();" > /var/www/phpinfo/phpinfo.php && \
|
||||
curl -o /usr/local/bin/phpunit -fL "https://phar.phpunit.de/phpunit-8.phar" && \
|
||||
chmod +x /usr/local/bin/phpunit && \
|
||||
ln -s html /var/www/src
|
||||
|
||||
#COPY php-mtt.ini /usr/local/etc/php/conf.d/
|
||||
#COPY php-opcache.ini /usr/local/etc/php/conf.d/
|
||||
COPY php-fpm-www.conf /usr/local/etc/php-fpm.d/www.conf
|
||||
|
||||
VOLUME /var/www/html
|
||||
58
docker/dev/mtt-php72-angie-postgres10/compose.yml
Normal file
58
docker/dev/mtt-php72-angie-postgres10/compose.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
version: "3.9"
|
||||
|
||||
networks:
|
||||
network:
|
||||
name: "${PLATFORM_NAME}-network"
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: ../_nginx
|
||||
dockerfile: Dockerfile-angie-alpine
|
||||
image: mtt-dev/angie:alpine
|
||||
container_name: ${PLATFORM_NAME}-web
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- ../../../src:/var/www/html
|
||||
depends_on:
|
||||
- fpm
|
||||
networks:
|
||||
- network
|
||||
|
||||
fpm:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-fpm
|
||||
image: mtt-dev/php:7.2-fpm-alpine
|
||||
container_name: ${PLATFORM_NAME}-fpm
|
||||
environment:
|
||||
- MTT_ENABLE_DEBUG=YES
|
||||
- MTT_DB_TYPE=postgres
|
||||
- MTT_API_USE_PATH_INFO=YES
|
||||
- MTT_DB_HOST=db
|
||||
- MTT_DB_NAME=mtt
|
||||
- MTT_DB_USER=mtt
|
||||
- MTT_DB_PASSWORD=mtt
|
||||
- MTT_DB_PREFIX=mtt_
|
||||
volumes:
|
||||
- ../../../src:/var/www/html
|
||||
- ../../../tests:/var/www/tests
|
||||
- ./php-mtt.ini:/usr/local/etc/php/conf.d/php-mtt.ini
|
||||
- ./php-opcache.ini:/usr/local/etc/php/conf.d/php-opcache.ini
|
||||
networks:
|
||||
- network
|
||||
|
||||
db:
|
||||
# do not use alpine image due to missing locales
|
||||
image: postgres:10.0
|
||||
container_name: ${PLATFORM_NAME}-db
|
||||
userns_mode: keep-id
|
||||
environment:
|
||||
POSTGRES_PASSWORD: mtt
|
||||
POSTGRES_USER: mtt
|
||||
POSTGRES_DB: mtt
|
||||
volumes:
|
||||
- ../_postgres10/db_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- network
|
||||
12
docker/dev/mtt-php72-angie-postgres10/php-fpm-www.conf
Normal file
12
docker/dev/mtt-php72-angie-postgres10/php-fpm-www.conf
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[www]
|
||||
user = www-data
|
||||
group = www-data
|
||||
listen = 127.0.0.1:9000
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
pm.max_requests = 500
|
||||
|
||||
access.format = "%R - %u [%t] \"%m %r\" %s %f"
|
||||
1
docker/dev/mtt-php72-apache-postgres10/.env
Normal file
1
docker/dev/mtt-php72-apache-postgres10/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
PLATFORM_NAME=mtt-dev-php72-apache-postgres10
|
||||
45
docker/dev/mtt-php72-apache-postgres10/compose.yml
Normal file
45
docker/dev/mtt-php72-apache-postgres10/compose.yml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
version: "3.9"
|
||||
|
||||
networks:
|
||||
network:
|
||||
name: "${PLATFORM_NAME}-network"
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: ../mtt-php72-apache/
|
||||
dockerfile: Dockerfile-web
|
||||
image: mtt-dev/php:7.2-apache
|
||||
container_name: ${PLATFORM_NAME}-web
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
- MTT_ENABLE_DEBUG=YES
|
||||
- MTT_DB_TYPE=postgres
|
||||
- MTT_DB_HOST=db
|
||||
- MTT_DB_NAME=mtt
|
||||
- MTT_DB_USER=mtt
|
||||
- MTT_DB_PASSWORD=mtt
|
||||
- MTT_DB_PREFIX=mtt_
|
||||
volumes:
|
||||
- ../../../src:/var/www/html
|
||||
- ../mtt-php72-apache/php-mtt.ini:/usr/local/etc/php/conf.d/php-mtt.ini
|
||||
- ../mtt-php72-apache/php-opcache.ini:/usr/local/etc/php/conf.d/php-opcache.ini
|
||||
depends_on:
|
||||
- db
|
||||
networks:
|
||||
- network
|
||||
|
||||
db:
|
||||
# do not use alpine image due to missing locales
|
||||
image: postgres:10.0
|
||||
container_name: ${PLATFORM_NAME}-db
|
||||
userns_mode: keep-id
|
||||
environment:
|
||||
POSTGRES_PASSWORD: mtt
|
||||
POSTGRES_USER: mtt
|
||||
POSTGRES_DB: mtt
|
||||
volumes:
|
||||
- ../_postgres10/db_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- network
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
FROM php:7.2-apache
|
||||
FROM php:7.2-apache-buster
|
||||
|
||||
RUN docker-php-ext-install mysqli && \
|
||||
RUN apt-get update && apt-get install -y libpq5 libpq-dev && \
|
||||
docker-php-ext-install mysqli pdo_pgsql && \
|
||||
apt-get remove -y libpq-dev && apt-get -y autoremove && \
|
||||
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" && \
|
||||
mkdir /var/www/phpinfo && \
|
||||
echo "<?php\nphpinfo();" > /var/www/phpinfo/phpinfo.php && \
|
||||
|
|
@ -12,5 +14,5 @@ RUN docker-php-ext-install mysqli && \
|
|||
ln -s html /var/www/src
|
||||
|
||||
|
||||
#COPY php-mtt.ini /usr/local/etc/php/conf.d/
|
||||
#COPY php-opcache.ini /usr/local/etc/php/conf.d/
|
||||
COPY php-mtt.ini /usr/local/etc/php/conf.d/
|
||||
COPY php-opcache.ini /usr/local/etc/php/conf.d/
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ services:
|
|||
dockerfile: Dockerfile-fpm
|
||||
image: mtt-dev/php:7.4-fpm
|
||||
container_name: ${PLATFORM_NAME}-fpm
|
||||
hostname: php-fpm
|
||||
environment:
|
||||
- MTT_ENABLE_DEBUG=YES
|
||||
- MTT_DB_TYPE=sqlite
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
PLATFORM_NAME=mtt-dev-php81-nginx
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
FROM php:8.1-fpm
|
||||
|
||||
RUN docker-php-ext-install mysqli && \
|
||||
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" && \
|
||||
mkdir /var/www/phpinfo && \
|
||||
echo "<?php\nphpinfo();" > /var/www/phpinfo/phpinfo.php
|
||||
|
||||
#COPY php-mtt.ini /usr/local/etc/php/conf.d/
|
||||
#COPY php-opcache.ini /usr/local/etc/php/conf.d/
|
||||
|
||||
VOLUME /var/www/html
|
||||
1
docker/dev/mtt-php82-angie/.env
Normal file
1
docker/dev/mtt-php82-angie/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
PLATFORM_NAME=mtt-dev-php82-angie-alpine
|
||||
39
docker/dev/mtt-php82-angie/compose.yml
Normal file
39
docker/dev/mtt-php82-angie/compose.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
version: "3.9"
|
||||
|
||||
networks:
|
||||
network:
|
||||
name: "${PLATFORM_NAME}-network"
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: ../_nginx
|
||||
dockerfile: Dockerfile-angie-alpine
|
||||
image: mtt-dev/angie:alpine
|
||||
container_name: ${PLATFORM_NAME}-web
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- ../../../src:/var/www/html
|
||||
depends_on:
|
||||
- fpm
|
||||
networks:
|
||||
- network
|
||||
|
||||
fpm:
|
||||
build:
|
||||
context: ../mtt-php82-nginx
|
||||
dockerfile: Dockerfile-fpm
|
||||
image: mtt-dev/php:8.2-fpm-alpine
|
||||
container_name: ${PLATFORM_NAME}-fpm
|
||||
environment:
|
||||
- MTT_ENABLE_DEBUG=YES
|
||||
- MTT_DB_TYPE=sqlite
|
||||
- MTT_API_USE_PATH_INFO=YES
|
||||
volumes:
|
||||
- ../../../src:/var/www/html
|
||||
- ../../../tests:/var/www/tests
|
||||
- ../mtt-php82-nginx/php-mtt.ini:/usr/local/etc/php/conf.d/php-mtt.ini
|
||||
- ../mtt-php82-nginx/php-opcache.ini:/usr/local/etc/php/conf.d/php-opcache.ini
|
||||
networks:
|
||||
- network
|
||||
1
docker/dev/mtt-php82-nginx/.env
Normal file
1
docker/dev/mtt-php82-nginx/.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
PLATFORM_NAME=mtt-dev-php82-nginx-alpine
|
||||
15
docker/dev/mtt-php82-nginx/Dockerfile-fpm
Normal file
15
docker/dev/mtt-php82-nginx/Dockerfile-fpm
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
FROM php:8.2-fpm-alpine
|
||||
|
||||
RUN docker-php-ext-install pdo_mysql && \
|
||||
mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" && \
|
||||
mkdir /var/www/phpinfo && \
|
||||
echo "<?php phpinfo();" > /var/www/phpinfo/phpinfo.php && \
|
||||
curl -o /usr/local/bin/phpunit -fL "https://phar.phpunit.de/phpunit-10.phar" && \
|
||||
chmod +x /usr/local/bin/phpunit && \
|
||||
ln -s html /var/www/src
|
||||
|
||||
#COPY php-mtt.ini /usr/local/etc/php/conf.d/
|
||||
#COPY php-opcache.ini /usr/local/etc/php/conf.d/
|
||||
COPY php-fpm-www.conf /usr/local/etc/php-fpm.d/www.conf
|
||||
|
||||
VOLUME /var/www/html
|
||||
|
|
@ -8,8 +8,8 @@ services:
|
|||
web:
|
||||
build:
|
||||
context: ../_nginx
|
||||
dockerfile: Dockerfile-nginx
|
||||
image: mtt-dev/nginx
|
||||
dockerfile: Dockerfile-nginx-alpine
|
||||
image: mtt-dev/nginx:alpine
|
||||
container_name: ${PLATFORM_NAME}-web
|
||||
ports:
|
||||
- "8080:80"
|
||||
|
|
@ -24,12 +24,12 @@ services:
|
|||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile-fpm
|
||||
image: mtt-dev/php:8.1-fpm
|
||||
image: mtt-dev/php:8.2-fpm-alpine
|
||||
container_name: ${PLATFORM_NAME}-fpm
|
||||
hostname: php-fpm
|
||||
environment:
|
||||
- MTT_ENABLE_DEBUG=YES
|
||||
- MTT_DB_TYPE=sqlite
|
||||
- MTT_API_USE_PATH_INFO=YES
|
||||
volumes:
|
||||
- ../../../src:/var/www/html
|
||||
- ./php-mtt.ini:/usr/local/etc/php/conf.d/php-mtt.ini
|
||||
12
docker/dev/mtt-php82-nginx/php-fpm-www.conf
Normal file
12
docker/dev/mtt-php82-nginx/php-fpm-www.conf
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[www]
|
||||
user = www-data
|
||||
group = www-data
|
||||
listen = 127.0.0.1:9000
|
||||
pm = dynamic
|
||||
pm.max_children = 5
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
pm.max_requests = 500
|
||||
|
||||
access.format = "%R - %u [%t] \"%m %r\" %s %f"
|
||||
10
docker/dev/mtt-php82-nginx/php-mtt.ini
Normal file
10
docker/dev/mtt-php82-nginx/php-mtt.ini
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
; Since PHP 5.4 E_STRICT is included in E_ALL
|
||||
error_reporting = E_ALL
|
||||
|
||||
log_errors = On
|
||||
display_errors = On
|
||||
display_startup_errors = On
|
||||
|
||||
cgi.fix_pathinfo=0
|
||||
session.gc_divisor = 100
|
||||
42
docker/dev/mtt-php82-nginx/php-opcache.ini
Normal file
42
docker/dev/mtt-php82-nginx/php-opcache.ini
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
;; opcache.ini
|
||||
|
||||
;; check /usr/local/etc/php/conf.d if exists docker-php-ext-opcache.ini
|
||||
|
||||
; To disable opcache just comment this line
|
||||
zend_extension=opcache.so
|
||||
|
||||
[opcache]
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
|
||||
; JIT will work only if opcache is enabled and jit_buffer_size is not zero.
|
||||
; To disable JIT set the buffer size to 0.
|
||||
opcache.jit_buffer_size=64M
|
||||
|
||||
; JIT mode. Read more at https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit
|
||||
; Default is tracing.
|
||||
opcache.jit=tracing
|
||||
|
||||
; The size of the shared memory storage used by OPcache, in megabytes. The minimum permissible value is "8", which is enforced if a smaller value is set.
|
||||
; Default is 128.
|
||||
opcache.memory_consumption=128
|
||||
|
||||
; The maximum number of keys (and therefore scripts) in the OPcache hash table.
|
||||
; The actual value used will be the first number in the set of prime numbers
|
||||
; { 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 }
|
||||
; that is greater than or equal to the configured value.
|
||||
; The minimum value is 200. The maximum value is 1000000. Values outside of this range are clamped to the permissible range.
|
||||
; Default is 10000.
|
||||
opcache.max_accelerated_files=1000
|
||||
|
||||
; How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.
|
||||
; This configuration directive is ignored if opcache.validate_timestamps is disabled.
|
||||
; Default is 2.
|
||||
opcache.revalidate_freq=2
|
||||
|
||||
; If enabled, OPcache will check for updated scripts every opcache.revalidate_freq seconds.
|
||||
; When this directive is disabled, you must reset OPcache manually via opcache_reset(), opcache_invalidate()
|
||||
; or by restarting the Web server for changes to the filesystem to take effect.
|
||||
; Default is 1.
|
||||
;; Override in docker-compose
|
||||
opcache.validate_timestamps=1
|
||||
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
// Rename it to config.php before using in docker.
|
||||
|
||||
if (getenv('MTT_DB_TYPE') == 'mysql') {
|
||||
define("MTT_DB_TYPE", "mysql");
|
||||
define("MTT_DB_HOST", getenv('MTT_DB_HOST'));
|
||||
define("MTT_DB_NAME", getenv('MTT_DB_NAME'));
|
||||
define("MTT_DB_USER", getenv('MTT_DB_USER'));
|
||||
define("MTT_DB_PASSWORD", getenv('MTT_DB_PASSWORD'));
|
||||
define("MTT_DB_PREFIX", getenv('MTT_DB_PREFIX'));
|
||||
define("MTT_DB_DRIVER", getenv('MTT_DB_DRIVER'));
|
||||
if (getenv('MTT_DB_TYPE') == 'mysql' || getenv('MTT_DB_TYPE') == 'postgres') {
|
||||
define("MTT_DB_TYPE", getenv('MTT_DB_TYPE'));
|
||||
define("MTT_DB_HOST", getenv('MTT_DB_HOST') ?: "undefined_host");
|
||||
define("MTT_DB_NAME", getenv('MTT_DB_NAME') ?: "undefined_db");
|
||||
define("MTT_DB_USER", getenv('MTT_DB_USER') ?: "undefined_user");
|
||||
define("MTT_DB_PASSWORD", getenv('MTT_DB_PASSWORD') ?: "");
|
||||
define("MTT_DB_PREFIX", getenv('MTT_DB_PREFIX') ?: "mtt_");
|
||||
define("MTT_DB_DRIVER", getenv('MTT_DB_DRIVER') ?: "");
|
||||
}
|
||||
else if (getenv('MTT_DB_TYPE') == 'sqlite') {
|
||||
define("MTT_DB_TYPE", "sqlite");
|
||||
|
|
@ -17,3 +17,7 @@ else if (getenv('MTT_DB_TYPE') == 'sqlite') {
|
|||
}
|
||||
|
||||
define("MTT_SALT", "Put Random Text Here");
|
||||
//define("MTT_DISABLE_EXT", 1);
|
||||
if (getenv('MTT_API_USE_PATH_INFO')) {
|
||||
define("MTT_API_USE_PATH_INFO", 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,22 +24,14 @@ if (!$listData) {
|
|||
die("No list found.");
|
||||
}
|
||||
|
||||
$sqlSort = "ORDER BY compl ASC, ";
|
||||
if($listData['sorting'] == 1) $sqlSort .= "prio DESC, ddn ASC, duedate ASC, ow ASC";
|
||||
elseif($listData['sorting'] == 2) $sqlSort .= "ddn ASC, duedate ASC, prio DESC, ow ASC";
|
||||
else $sqlSort .= "ow ASC";
|
||||
$data = DBCore::default()->getTasksByListId($listId, '', (int)$listData['sorting']);
|
||||
|
||||
$data = array();
|
||||
$q = $db->dq("SELECT *, duedate IS NULL AS ddn FROM {$db->prefix}todolist WHERE list_id=$listId $sqlSort");
|
||||
while($r = $q->fetchAssoc())
|
||||
{
|
||||
$data[] = $r;
|
||||
if (_get('format') == 'ical') {
|
||||
printICal($listData, $data);
|
||||
}
|
||||
else {
|
||||
printCSV($listData, $data);
|
||||
}
|
||||
|
||||
$format = _get('format');
|
||||
|
||||
if($format == 'ical') printICal($listData, $data);
|
||||
else printCSV($listData, $data);
|
||||
|
||||
|
||||
function printCSV(array $listData, array $data)
|
||||
|
|
|
|||
15
src/feed.php
15
src/feed.php
|
|
@ -32,7 +32,7 @@ $feedType = _get('feed');
|
|||
|
||||
if($feedType == 'completed') {
|
||||
$listData['_feed_descr'] = $lang->get('feed_completed_tasks');
|
||||
fillData( $data, $listId, 'd_completed', 'AND compl=1' );
|
||||
fillData( $data, $listId, 'd_completed', 'compl=1' );
|
||||
}
|
||||
elseif($feedType == 'modified') {
|
||||
$listData['_feed_descr'] = $lang->get('feed_modified_tasks');
|
||||
|
|
@ -40,13 +40,13 @@ elseif($feedType == 'modified') {
|
|||
}
|
||||
elseif($feedType == 'current') {
|
||||
$listData['_feed_descr'] = $lang->get('feed_new_tasks');
|
||||
fillData( $data, $listId, 'd_created', 'AND compl=0' );
|
||||
fillData( $data, $listId, 'd_created', 'compl=0' );
|
||||
}
|
||||
elseif($feedType == 'status') {
|
||||
$listData['_feed_descr'] = $lang->get('feed_tasks');
|
||||
fillData( $data, $listId, 'd_created', '' );
|
||||
fillData( $data, $listId, 'd_edited', 'AND compl=0 AND d_edited > d_created' );
|
||||
fillData( $data, $listId, 'd_completed', 'AND compl=1' );
|
||||
fillData( $data, $listId, 'd_edited', 'compl=0 AND d_edited > d_created' );
|
||||
fillData( $data, $listId, 'd_completed', 'compl=1' );
|
||||
}
|
||||
else {
|
||||
$listData['_feed_descr'] = $lang->get('feed_new_tasks');
|
||||
|
|
@ -64,15 +64,14 @@ printRss($data, $listData);
|
|||
|
||||
function fillData(array &$data, int $listId, string $field, string $sqlWhere )
|
||||
{
|
||||
$tasks = DBCore::default()->getTasksByListId($listId, $sqlWhere, "$field DESC", 100);
|
||||
$lang = Lang::instance();
|
||||
$db = DBConnection::instance();
|
||||
$q = $db->dq("SELECT * FROM {$db->prefix}todolist WHERE list_id=$listId $sqlWhere ORDER BY $field DESC LIMIT 100");
|
||||
while ($r = $q->fetchAssoc())
|
||||
foreach ($tasks as $r)
|
||||
{
|
||||
if ($r['prio'] > 0) {
|
||||
$r['prio'] = '+'.$r['prio'];
|
||||
}
|
||||
$a = array();
|
||||
$a = array(); //for _descr
|
||||
$a[] = $lang->get('task'). ": ". $r['title'];
|
||||
if ($r['prio']) {
|
||||
$a[] = $lang->get('priority'). ": $r[prio]";
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@ class TagsController extends ApiController {
|
|||
checkReadAccess($listId);
|
||||
$db = DBConnection::instance();
|
||||
|
||||
$sqlWhere = ($listId == -1) ? "" : "WHERE list_id=$listId";
|
||||
$q = $db->dq("SELECT name,tag_id,COUNT(tag_id) AS tags_count
|
||||
FROM {$db->prefix}tag2task INNER JOIN {$db->prefix}tags ON tag_id=id
|
||||
$sqlWhere = ($listId == -1) ? "" : "WHERE list_id = $listId";
|
||||
$q = $db->dq("SELECT name, tag_id, COUNT(tag_id) AS tags_count
|
||||
FROM {$db->prefix}tag2task INNER JOIN {$db->prefix}tags ON tag_id = id
|
||||
$sqlWhere
|
||||
GROUP BY (tag_id)
|
||||
GROUP BY tag_id, name
|
||||
ORDER BY tags_count DESC");
|
||||
$at = array();
|
||||
$ac = array();
|
||||
|
|
@ -72,10 +72,12 @@ class TagsController extends ApiController {
|
|||
$db = DBConnection::instance();
|
||||
$begin = trim(_get('q'));
|
||||
$limit = 8;
|
||||
$q = $db->dq("SELECT name,id FROM {$db->prefix}tags
|
||||
$q = $db->dq("SELECT name, tag_id AS id FROM {$db->prefix}tags
|
||||
INNER JOIN {$db->prefix}tag2task ON id=tag_id
|
||||
WHERE list_id=$listId AND ". $db->like('name', '%s%%', $begin). "
|
||||
GROUP BY tag_id ORDER BY name LIMIT $limit");
|
||||
GROUP BY tag_id, name
|
||||
ORDER BY name
|
||||
LIMIT $limit");
|
||||
$t = array();
|
||||
while ($r = $q->fetchRow()) {
|
||||
$t[] = $r[0];
|
||||
|
|
|
|||
|
|
@ -22,14 +22,15 @@ class TasksController extends ApiController {
|
|||
checkReadAccess($listId);
|
||||
$db = DBConnection::instance();
|
||||
|
||||
$sqlWhere = $inner = $sqlWhereListId = $sqlInnerWhereListId = '';
|
||||
$sqlWhere = $sqlWhereListId = $sqlInnerWhereListId = '';
|
||||
if ($listId == -1) {
|
||||
$userLists = $this->getUserListsSimple();
|
||||
$sqlWhereListId = "{$db->prefix}todolist.list_id IN (". implode(',', array_keys($userLists)). ") ";
|
||||
$sqlInnerWhereListId = "list_id IN (". implode(',', array_keys($userLists)). ") ";
|
||||
$userListsIds = implode(',', array_keys($userLists));
|
||||
$sqlWhereListId = "todo.list_id IN ($userListsIds) ";
|
||||
$sqlInnerWhereListId = "list_id IN ($userListsIds) ";
|
||||
}
|
||||
else {
|
||||
$sqlWhereListId = "{$db->prefix}todolist.list_id=". $listId;
|
||||
$sqlWhereListId = "todo.list_id=". $listId;
|
||||
$sqlInnerWhereListId = "list_id=$listId ";
|
||||
}
|
||||
if (_get('compl') == 0) {
|
||||
|
|
@ -52,20 +53,18 @@ class TasksController extends ApiController {
|
|||
}
|
||||
}
|
||||
|
||||
// Include tags: All
|
||||
if (sizeof($tagIds) > 1) {
|
||||
$inner .= "INNER JOIN (SELECT task_id, COUNT(tag_id) AS c FROM {$db->prefix}tag2task WHERE $sqlInnerWhereListId AND tag_id IN (".
|
||||
implode(',',$tagIds). ") GROUP BY task_id) AS t2t ON id=t2t.task_id";
|
||||
$sqlWhere .= " AND c=". sizeof($tagIds);
|
||||
}
|
||||
elseif ($tagIds) {
|
||||
$inner .= "INNER JOIN {$db->prefix}tag2task ON id=task_id";
|
||||
$sqlWhere .= " AND tag_id = {$tagIds[0]}";
|
||||
// Include tags
|
||||
if ($tagIds) {
|
||||
$sqlWhere .= " AND todo.id IN (SELECT task_id FROM {$db->prefix}tag2task WHERE tag_id IN (". implode(',',$tagIds). ")";
|
||||
if (count($tagIds) > 1) {
|
||||
$sqlWhere .= " GROUP BY task_id HAVING COUNT(tag_id)=". count($tagIds);
|
||||
}
|
||||
$sqlWhere .= ")";
|
||||
}
|
||||
|
||||
// Exclude tags
|
||||
if (sizeof($tagExIds) > 0) {
|
||||
$sqlWhere .= " AND {$db->prefix}todolist.id NOT IN (SELECT DISTINCT task_id FROM {$db->prefix}tag2task WHERE $sqlInnerWhereListId AND tag_id IN (".
|
||||
if (count($tagExIds) > 0) {
|
||||
$sqlWhere .= " AND todo.id NOT IN (SELECT DISTINCT task_id FROM {$db->prefix}tag2task WHERE $sqlInnerWhereListId AND tag_id IN (".
|
||||
implode(',',$tagExIds). "))";
|
||||
}
|
||||
//no optimization for single exTag
|
||||
|
|
@ -74,7 +73,7 @@ class TasksController extends ApiController {
|
|||
$s = trim(_get('s'));
|
||||
if ($s != '') {
|
||||
if (preg_match("|^#(\d+)$|", $s, $m)) {
|
||||
$sqlWhere .= " AND {$db->prefix}todolist.id = ". (int)$m[1];
|
||||
$sqlWhere .= " AND todo.id = ". (int)$m[1];
|
||||
}
|
||||
else {
|
||||
$sqlWhere .= " AND (". $db->like("title", "%%%s%%", $s). " OR ". $db->like("note", "%%%s%%", $s). ")";
|
||||
|
|
@ -98,7 +97,24 @@ class TasksController extends ApiController {
|
|||
$t = array();
|
||||
$t['total'] = 0;
|
||||
$t['list'] = array();
|
||||
$q = $db->dq("SELECT *, duedate IS NULL AS ddn FROM {$db->prefix}todolist $inner WHERE $sqlWhereListId $sqlWhere $sqlSort");
|
||||
|
||||
$groupConcat = '';
|
||||
if ($db::DBTYPE == DBConnection::DBTYPE_POSTGRES) {
|
||||
$groupConcat = "array_to_string(array_agg(tags.id), ',') AS tags_ids, string_agg(tags.name, ',') AS tags";
|
||||
}
|
||||
else {
|
||||
$groupConcat = "GROUP_CONCAT(tags.id) AS tags_ids, GROUP_CONCAT(tags.name) AS tags";
|
||||
}
|
||||
$q = $db->dq("
|
||||
SELECT todo.*, todo.duedate IS NULL AS ddn, $groupConcat
|
||||
FROM {$db->prefix}todolist AS todo
|
||||
LEFT JOIN {$db->prefix}tag2task AS t2t ON todo.id = t2t.task_id
|
||||
LEFT JOIN {$db->prefix}tags AS tags ON t2t.tag_id = tags.id
|
||||
WHERE $sqlWhereListId $sqlWhere
|
||||
GROUP BY todo.id
|
||||
$sqlSort
|
||||
");
|
||||
|
||||
while ($r = $q->fetchAssoc())
|
||||
{
|
||||
$t['total']++;
|
||||
|
|
@ -183,6 +199,11 @@ class TasksController extends ApiController {
|
|||
checkWriteAccess();
|
||||
$id = (int)$id;
|
||||
|
||||
if (!DBCore::default()->taskExists($id)) {
|
||||
$this->response->data = ['total' => 0];
|
||||
return;
|
||||
}
|
||||
|
||||
$action = $this->req->jsonBody['action'] ?? '';
|
||||
switch ($action) {
|
||||
case 'edit': $this->response->data = $this->editTask($id); break;
|
||||
|
|
@ -254,14 +275,12 @@ class TasksController extends ApiController {
|
|||
$aTags = $this->prepareTags($tags);
|
||||
if ($aTags) {
|
||||
$this->addTaskTags($id, $aTags['ids'], $listId);
|
||||
$db->ex("UPDATE {$db->prefix}todolist SET tags=?,tags_ids=? WHERE id=$id", array(implode(',',$aTags['tags']), implode(',',$aTags['ids'])));
|
||||
}
|
||||
}
|
||||
$db->ex("COMMIT");
|
||||
$r = $db->sqa("SELECT * FROM {$db->prefix}todolist WHERE id=$id");
|
||||
$oo = $this->prepareTaskRow($r);
|
||||
MTTNotificationCenter::postNotification(MTTNotification::didCreateTask, $oo);
|
||||
$t['list'][] = $oo;
|
||||
$task = $this->getTaskRowById($id);
|
||||
MTTNotificationCenter::postNotification(MTTNotification::didCreateTask, $task);
|
||||
$t['list'][] = $task;
|
||||
$t['total'] = 1;
|
||||
return $t;
|
||||
}
|
||||
|
|
@ -294,14 +313,12 @@ class TasksController extends ApiController {
|
|||
$aTags = $this->prepareTags($tags);
|
||||
if ($aTags) {
|
||||
$this->addTaskTags($id, $aTags['ids'], $listId);
|
||||
$db->ex("UPDATE {$db->prefix}todolist SET tags=?,tags_ids=? WHERE id=$id", array(implode(',',$aTags['tags']), implode(',',$aTags['ids'])));
|
||||
}
|
||||
}
|
||||
$db->ex("COMMIT");
|
||||
$r = $db->sqa("SELECT * FROM {$db->prefix}todolist WHERE id=$id");
|
||||
$oo = $this->prepareTaskRow($r);
|
||||
MTTNotificationCenter::postNotification(MTTNotification::didCreateTask, $oo);
|
||||
$t['list'][] = $oo;
|
||||
$task = $this->getTaskRowById($id);
|
||||
MTTNotificationCenter::postNotification(MTTNotification::didCreateTask, $task);
|
||||
$t['list'][] = $task;
|
||||
$t['total'] = 1;
|
||||
return $t;
|
||||
}
|
||||
|
|
@ -325,31 +342,26 @@ class TasksController extends ApiController {
|
|||
$db->ex("BEGIN");
|
||||
$db->ex("DELETE FROM {$db->prefix}tag2task WHERE task_id=$id");
|
||||
$aTags = $this->prepareTags($tags);
|
||||
if($aTags) {
|
||||
$tags = implode(',', $aTags['tags']);
|
||||
$tags_ids = implode(',',$aTags['ids']);
|
||||
if ($aTags) {
|
||||
$this->addTaskTags($id, $aTags['ids'], $listId);
|
||||
}
|
||||
$db->dq("UPDATE {$db->prefix}todolist SET title=?,note=?,prio=?,tags=?,tags_ids=?,duedate=?,d_edited=? WHERE id=$id",
|
||||
array($title, $note, $prio, $tags, $tags_ids, $duedate, time()) );
|
||||
$db->dq("UPDATE {$db->prefix}todolist SET title=?,note=?,prio=?,duedate=?,d_edited=? WHERE id=$id",
|
||||
array($title, $note, $prio, $duedate, time()) );
|
||||
$db->ex("COMMIT");
|
||||
$r = $db->sqa("SELECT * FROM {$db->prefix}todolist WHERE id=$id");
|
||||
if ($r) {
|
||||
$t['list'][] = $this->prepareTaskRow($r);
|
||||
$t['total'] = 1;
|
||||
}
|
||||
$task = $this->getTaskRowById($id);
|
||||
$t['list'][] = $task;
|
||||
$t['total'] = 1;
|
||||
return $t;
|
||||
}
|
||||
|
||||
private function moveTask(int $id): ?array
|
||||
{
|
||||
$db = DBConnection::instance();
|
||||
$fromId = (int)($this->req->jsonBody['from'] ?? 0);
|
||||
$toId = (int)($this->req->jsonBody['to'] ?? 0);
|
||||
$result = $this->doMoveTask($id, $toId);
|
||||
$t = array('total' => $result ? 1 : 0);
|
||||
if ($fromId == -1 && $result && $r = $db->sqa("SELECT * FROM {$db->prefix}todolist WHERE id=$id")) {
|
||||
$t['list'][] = $this->prepareTaskRow($r);
|
||||
if ($fromId == -1 && $result) {
|
||||
$t['list'][] = $this->getTaskRowById($id);
|
||||
}
|
||||
return $t;
|
||||
}
|
||||
|
|
@ -388,8 +400,7 @@ class TasksController extends ApiController {
|
|||
array($dateCompleted, $date) );
|
||||
$t = array();
|
||||
$t['total'] = 1;
|
||||
$r = $db->sqa("SELECT * FROM {$db->prefix}todolist WHERE id=$id");
|
||||
$t['list'][] = $this->prepareTaskRow($r);
|
||||
$t['list'][] = $this->getTaskRowById($id);
|
||||
return $t;
|
||||
}
|
||||
|
||||
|
|
@ -456,6 +467,15 @@ class TasksController extends ApiController {
|
|||
return $a;
|
||||
}
|
||||
|
||||
private function getTaskRowById(int $id): ?array
|
||||
{
|
||||
$r = DBCore::default()->getTaskById($id);
|
||||
if (!$r) {
|
||||
throw new Exception("Failed to fetch task data");
|
||||
}
|
||||
return $this->prepareTaskRow($r);
|
||||
}
|
||||
|
||||
private function prepareTaskRow(array $r): array
|
||||
{
|
||||
$lang = Lang::instance();
|
||||
|
|
@ -483,7 +503,7 @@ class TasksController extends ApiController {
|
|||
'date' => htmlarray($dCreated),
|
||||
'dateInt' => (int)$r['d_created'],
|
||||
'dateFull' => htmlarray($dCreatedFull),
|
||||
'dateInlineTitle' => htmlarray(sprintf($lang->get('taskdate_inline_created'), $dCreated)),
|
||||
'dateInlineTitle' => htmlarray(sprintf($lang->get('taskdate_inline_created'), $dCreated)), //TODO: move preparing of *inlineTitle to js
|
||||
'dateEdited' => htmlarray($dEdited),
|
||||
'dateEditedInt' => (int)$r['d_edited'],
|
||||
'dateEditedFull' => htmlarray($dEditedFull),
|
||||
|
|
@ -497,8 +517,8 @@ class TasksController extends ApiController {
|
|||
'note' => noteMarkup($r['note']),
|
||||
'noteText' => (string)$r['note'],
|
||||
'ow' => (int)$r['ow'],
|
||||
'tags' => htmlarray($r['tags']),
|
||||
'tags_ids' => htmlarray($r['tags_ids']),
|
||||
'tags' => htmlarray($r['tags'] ?? ''),
|
||||
'tags_ids' => htmlarray($r['tags_ids'] ?? ''),
|
||||
'duedate' => htmlarray($dueA['formatted']),
|
||||
'dueClass' => $dueA['class'],
|
||||
'dueStr' => htmlarray($dueA['str']),
|
||||
|
|
|
|||
208
src/includes/class.db.postgres.php
Normal file
208
src/includes/class.db.postgres.php
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
This file is a part of myTinyTodo.
|
||||
(C) Copyright 2023 Max Pozdeev <maxpozdeev@gmail.com>
|
||||
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
|
||||
*/
|
||||
|
||||
// ---------------------------------------------------------------------------- //
|
||||
class DatabaseResult_Postgres extends DatabaseResult_Abstract
|
||||
{
|
||||
/** @var PDOStatement */
|
||||
protected $q;
|
||||
|
||||
/** @var int */
|
||||
protected $affected;
|
||||
|
||||
/** @var string const */
|
||||
protected $schema = 'public';
|
||||
|
||||
function __construct(PDO $dbh, string $query, bool $resultless = false)
|
||||
{
|
||||
// use with DELETE, INSERT, UPDATE
|
||||
if ($resultless)
|
||||
{
|
||||
$this->affected = (int) $dbh->exec($query); //throws PDOException
|
||||
}
|
||||
// SELECT
|
||||
else
|
||||
{
|
||||
$this->q = $dbh->query($query); //throws PDOException
|
||||
$this->affected = $this->q->rowCount();
|
||||
}
|
||||
}
|
||||
|
||||
function fetchRow(): ?array
|
||||
{
|
||||
$res = $this->q->fetch(PDO::FETCH_NUM);
|
||||
if ($res === false || !is_array($res)) {
|
||||
return null;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function fetchAssoc(): ?array
|
||||
{
|
||||
$res = $this->q->fetch(PDO::FETCH_ASSOC);
|
||||
if ($res === false || !is_array($res)) {
|
||||
return null;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function rowsAffected(): int
|
||||
{
|
||||
return $this->affected;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------- //
|
||||
class Database_Postgres extends Database_Abstract
|
||||
{
|
||||
const DBTYPE = 'postgres';
|
||||
|
||||
/** @var PDO */
|
||||
protected $dbh;
|
||||
|
||||
/** @var int */
|
||||
protected $affected = 0;
|
||||
|
||||
protected $dbname;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
function connect(array $params): void
|
||||
{
|
||||
$host = $params['host'];
|
||||
$user = $params['user'];
|
||||
$pass = $params['password'];
|
||||
$db = $params['db'];
|
||||
$options = array(
|
||||
PDO::PGSQL_ATTR_DISABLE_PREPARES => 1,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||
);
|
||||
$this->dbname = $db;
|
||||
$this->dbh = new PDO("pgsql:host=$host;dbname=$db", $user, $pass, $options);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Returns single row of SELECT query as indexed array (FETCH_NUM).
|
||||
Returns single field value if resulting array has only one field.
|
||||
*/
|
||||
function sq(string $query, ?array $values = null)
|
||||
{
|
||||
$q = $this->_dq($query, $values);
|
||||
|
||||
$res = $q->fetchRow();
|
||||
if ($res === false || !is_array($res)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sizeof($res) > 1) return $res;
|
||||
else return $res[0];
|
||||
}
|
||||
|
||||
/*
|
||||
Returns single row of SELECT query as dictionary array (FETCH_ASSOC).
|
||||
*/
|
||||
function sqa(string $query, ?array $values = null): ?array
|
||||
{
|
||||
$q = $this->_dq($query, $values);
|
||||
$res = $q->fetchAssoc();
|
||||
if ($res === false || !is_array($res)){
|
||||
return null;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
function dq(string $query, ?array $values = null) : DatabaseResult_Abstract
|
||||
{
|
||||
return $this->_dq($query, $values);
|
||||
}
|
||||
|
||||
/*
|
||||
for resultless queries like INSERT,UPDATE,DELETE
|
||||
*/
|
||||
function ex(string $query, ?array $values = null): void
|
||||
{
|
||||
$this->_dq($query, $values, true);
|
||||
}
|
||||
|
||||
private function _dq(string $query, ?array $values = null, bool $resultless = false) : DatabaseResult_Abstract
|
||||
{
|
||||
if (null !== $values && sizeof($values) > 0)
|
||||
{
|
||||
$m = explode('?', $query);
|
||||
if (sizeof($m) < sizeof($values)+1) {
|
||||
throw new Exception("params to set MORE than query params");
|
||||
}
|
||||
if (sizeof($m) > sizeof($values)+1) {
|
||||
throw new Exception("params to set LESS than query params");
|
||||
}
|
||||
$query = "";
|
||||
for ($i=0; $i<sizeof($m)-1; $i++) {
|
||||
$query .= $m[$i]. $this->quote($values[$i]);
|
||||
}
|
||||
$query .= $m[$i];
|
||||
}
|
||||
$this->lastQuery = $query;
|
||||
$dbr = new DatabaseResult_Postgres($this->dbh, $query, $resultless);
|
||||
$this->affected = $dbr->rowsAffected();
|
||||
return $dbr;
|
||||
}
|
||||
|
||||
function affected(): int
|
||||
{
|
||||
return $this->affected;
|
||||
}
|
||||
|
||||
function quote($value): string
|
||||
{
|
||||
if (null === $value) {
|
||||
return 'null';
|
||||
}
|
||||
return $this->dbh->quote((string) $value);
|
||||
}
|
||||
|
||||
function quoteForLike(string $format, string $string): string
|
||||
{
|
||||
$string = str_replace(array('\\','%','_'), array('\\\\','\%','\_'), $string);
|
||||
return $this->dbh->quote(sprintf($format, $string)). " ESCAPE '\'";
|
||||
}
|
||||
|
||||
function like(string $column, string $format, string $string): string
|
||||
{
|
||||
$column = str_replace('"', '""', $column);
|
||||
return '"'. $column. '" ILIKE '. $this->quoteForLike($format, $string);
|
||||
}
|
||||
|
||||
function lastInsertId(?string $name = null): ?string
|
||||
{
|
||||
$ret = $this->dbh->lastInsertId();
|
||||
if (false === $ret) {
|
||||
return null;
|
||||
}
|
||||
return (string) $ret;
|
||||
}
|
||||
|
||||
function tableExists(string $table): bool
|
||||
{
|
||||
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE table_catalog = ? AND table_name = ?",
|
||||
array($this->dbname, $table) );
|
||||
if ($r === false || $r === null) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function tableFieldExists(string $table, string $field): bool
|
||||
{
|
||||
$r = $this->sq("SELECT 1 FROM information_schema.columns WHERE table_name = ? AND column_name = ? AND table_schema = ?",
|
||||
array($table, $field, $this->schema) );
|
||||
if ($r === false || $r === null) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -62,8 +62,16 @@ class Database_Sqlite3 extends Database_Abstract
|
|||
/** @var int */
|
||||
protected $affected = 0;
|
||||
|
||||
function __construct()
|
||||
/** @var bool */
|
||||
protected $useNormalizedUtf8 = true;
|
||||
|
||||
function __construct(array $params = null)
|
||||
{
|
||||
if (is_array($params)) {
|
||||
if (isset($params['useNormalizedUtf8'])) {
|
||||
$this->useNormalizedUtf8 = boolval($params['useNormalizedUtf8']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function connect(array $params): void
|
||||
|
|
@ -73,8 +81,10 @@ class Database_Sqlite3 extends Database_Abstract
|
|||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||
);
|
||||
$this->dbh = new PDO("sqlite:$filename", null, null, $options); //throws PDOException
|
||||
$this->dbh->sqliteCreateFunction('utf8_lower', [$this, 'utf8lower'], 1);
|
||||
$this->dbh->sqliteCreateCollation('UTF8CI', [$this, 'utf8ci']);
|
||||
$this->dbh->sqliteCreateFunction('utf8_lower', [$this, 'utf8_lower'], 1);
|
||||
$this->dbh->sqliteCreateFunction('utf8_normalized_lower', [$this, 'utf8_normalized_lower'], 1);
|
||||
$this->dbh->sqliteCreateCollation('UTF8CI', [$this, 'collate_utf8ci']);
|
||||
$this->dbh->sqliteCreateCollation('UTF8CI_NORMALIZED', [$this, 'collate_utf8ci_normalized']);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -170,7 +180,10 @@ class Database_Sqlite3 extends Database_Abstract
|
|||
function like(string $column, string $format, string $string): string
|
||||
{
|
||||
$column = str_replace('"', '""', $column);
|
||||
return 'utf8_lower("'. $column. '") LIKE '. $this->quoteForLike($format, mb_strtolower($string, 'UTF-8'));
|
||||
if ($this->useNormalizedUtf8) {
|
||||
return 'utf8_normalized_lower("'. $column. '") LIKE '. $this->quoteForLike($format, $this->utf8_normalized_lower($string));
|
||||
}
|
||||
return 'utf8_lower("'. $column. '") LIKE '. $this->quoteForLike($format, $this->utf8_lower($string));
|
||||
}
|
||||
|
||||
function lastInsertId(?string $name = null): ?string
|
||||
|
|
@ -204,14 +217,102 @@ class Database_Sqlite3 extends Database_Abstract
|
|||
return false;
|
||||
}
|
||||
|
||||
public function utf8lower($value)
|
||||
public function utf8_lower($value): string
|
||||
{
|
||||
if (is_null($value)) return '';
|
||||
return mb_strtolower((string)$value, 'UTF-8');
|
||||
}
|
||||
|
||||
public function utf8ci(string $str1, string $str2): int
|
||||
public function utf8_normalized_lower($value): string
|
||||
{
|
||||
if (is_null($value)) return '';
|
||||
$value = self::normalizeValue((string) $value);
|
||||
return mb_strtolower($value, 'UTF-8');
|
||||
}
|
||||
|
||||
public function collate_utf8ci(string $str1, string $str2): int
|
||||
{
|
||||
return strcmp(mb_strtolower($str1, 'UTF-8'), mb_strtolower($str2, 'UTF-8'));
|
||||
}
|
||||
|
||||
public function collate_utf8ci_normalized(string $str1, string $str2): int
|
||||
{
|
||||
$str1 = self::normalizeValue($str1);
|
||||
$str2 = self::normalizeValue($str2);
|
||||
return strcmp(mb_strtolower($str1, 'UTF-8'), mb_strtolower($str2, 'UTF-8'));
|
||||
}
|
||||
|
||||
public static function normalizeValue(string $str): string
|
||||
{
|
||||
$str = Normalizer::normalize($str, Normalizer::FORM_KD);
|
||||
|
||||
if (false === preg_match_all("/./u", $str, $m)) {
|
||||
$ea = error_get_last();
|
||||
$error = ($ea && isset($ea['message'])) ? $ea['message'] : "preg_match_all() failed";
|
||||
throw new Exception($error);
|
||||
}
|
||||
$chars = $m[0];
|
||||
static $map = [
|
||||
// https://en.wikipedia.org/wiki/Combining_character
|
||||
"\u{0300}" => '', "\u{0301}" => '', "\u{0302}" => '', "\u{0303}" => '', "\u{0304}" => '', "\u{0305}" => '', "\u{0306}" => '', "\u{0307}" => '',
|
||||
"\u{0308}" => '', "\u{0309}" => '', "\u{030a}" => '', "\u{030b}" => '', "\u{030c}" => '', "\u{030d}" => '', "\u{030e}" => '', "\u{030f}" => '',
|
||||
"\u{0310}" => '', "\u{0311}" => '', "\u{0312}" => '', "\u{0313}" => '', "\u{0314}" => '', "\u{0315}" => '', "\u{0316}" => '', "\u{0317}" => '',
|
||||
"\u{0318}" => '', "\u{0319}" => '', "\u{031a}" => '', "\u{031b}" => '', "\u{031c}" => '', "\u{031d}" => '', "\u{031e}" => '', "\u{031f}" => '',
|
||||
"\u{0320}" => '', "\u{0321}" => '', "\u{0322}" => '', "\u{0323}" => '', "\u{0324}" => '', "\u{0325}" => '', "\u{0326}" => '', "\u{0327}" => '',
|
||||
"\u{0328}" => '', "\u{0329}" => '', "\u{032a}" => '', "\u{032b}" => '', "\u{032c}" => '', "\u{032d}" => '', "\u{032e}" => '', "\u{032f}" => '',
|
||||
"\u{0330}" => '', "\u{0331}" => '', "\u{0332}" => '', "\u{0333}" => '', "\u{0334}" => '', "\u{0335}" => '', "\u{0336}" => '', "\u{0337}" => '',
|
||||
"\u{0338}" => '', "\u{0339}" => '', "\u{033a}" => '', "\u{033b}" => '', "\u{033c}" => '', "\u{033d}" => '', "\u{033e}" => '', "\u{033f}" => '',
|
||||
"\u{0340}" => '', "\u{0341}" => '', "\u{0342}" => '', "\u{0343}" => '', "\u{0344}" => '', "\u{0345}" => '', "\u{0346}" => '', "\u{0347}" => '',
|
||||
"\u{0348}" => '', "\u{0349}" => '', "\u{034a}" => '', "\u{034b}" => '', "\u{034c}" => '', "\u{034d}" => '', "\u{034e}" => '', "\u{034f}" => '',
|
||||
"\u{0350}" => '', "\u{0351}" => '', "\u{0352}" => '', "\u{0353}" => '', "\u{0354}" => '', "\u{0355}" => '', "\u{0356}" => '', "\u{0357}" => '',
|
||||
"\u{0358}" => '', "\u{0359}" => '', "\u{035a}" => '', "\u{035b}" => '', "\u{035c}" => '', "\u{035d}" => '', "\u{035e}" => '', "\u{035f}" => '',
|
||||
"\u{0360}" => '', "\u{0361}" => '', "\u{0362}" => '', "\u{0363}" => '', "\u{0364}" => '', "\u{0365}" => '', "\u{0366}" => '', "\u{0367}" => '',
|
||||
"\u{0368}" => '', "\u{0369}" => '', "\u{036a}" => '', "\u{036b}" => '', "\u{036c}" => '', "\u{036d}" => '', "\u{036e}" => '', "\u{036f}" => '',
|
||||
|
||||
"\u{1ab0}" => '', "\u{1ab1}" => '', "\u{1ab2}" => '', "\u{1ab3}" => '', "\u{1ab4}" => '', "\u{1ab5}" => '', "\u{1ab6}" => '', "\u{1ab7}" => '',
|
||||
"\u{1ab8}" => '', "\u{1ab9}" => '', "\u{1aba}" => '', "\u{1abb}" => '', "\u{1abc}" => '', "\u{1abd}" => '', "\u{1abe}" => '', "\u{1abf}" => '',
|
||||
"\u{1ac0}" => '', "\u{1ac1}" => '', "\u{1ac2}" => '', "\u{1ac3}" => '', "\u{1ac4}" => '', "\u{1ac5}" => '', "\u{1ac6}" => '', "\u{1ac7}" => '',
|
||||
"\u{1ac8}" => '', "\u{1ac9}" => '', "\u{1aca}" => '', "\u{1acb}" => '', "\u{1acc}" => '', "\u{1acd}" => '', "\u{1ace}" => '', "\u{1acf}" => '',
|
||||
"\u{1ad0}" => '', "\u{1ad1}" => '', "\u{1ad2}" => '', "\u{1ad3}" => '', "\u{1ad4}" => '', "\u{1ad5}" => '', "\u{1ad6}" => '', "\u{1ad7}" => '',
|
||||
"\u{1ad8}" => '', "\u{1ad9}" => '', "\u{1ada}" => '', "\u{1adb}" => '', "\u{1adc}" => '', "\u{1add}" => '', "\u{1ade}" => '', "\u{1adf}" => '',
|
||||
"\u{1ae0}" => '', "\u{1ae1}" => '', "\u{1ae2}" => '', "\u{1ae3}" => '', "\u{1ae4}" => '', "\u{1ae5}" => '', "\u{1ae6}" => '', "\u{1ae7}" => '',
|
||||
"\u{1ae8}" => '', "\u{1ae9}" => '', "\u{1aea}" => '', "\u{1aeb}" => '', "\u{1aec}" => '', "\u{1aed}" => '', "\u{1aee}" => '', "\u{1aef}" => '',
|
||||
"\u{1af0}" => '', "\u{1af1}" => '', "\u{1af2}" => '', "\u{1af3}" => '', "\u{1af4}" => '', "\u{1af5}" => '', "\u{1af6}" => '', "\u{1af7}" => '',
|
||||
"\u{1af8}" => '', "\u{1af9}" => '', "\u{1afa}" => '', "\u{1afb}" => '', "\u{1afc}" => '', "\u{1afd}" => '', "\u{1afe}" => '', "\u{1aff}" => '',
|
||||
|
||||
"\u{1dc0}" => '', "\u{1dc1}" => '', "\u{1dc2}" => '', "\u{1dc3}" => '', "\u{1dc4}" => '', "\u{1dc5}" => '', "\u{1dc6}" => '', "\u{1dc7}" => '',
|
||||
"\u{1dc8}" => '', "\u{1dc9}" => '', "\u{1dca}" => '', "\u{1dcb}" => '', "\u{1dcc}" => '', "\u{1dcd}" => '', "\u{1dce}" => '', "\u{1dcf}" => '',
|
||||
"\u{1dd0}" => '', "\u{1dd1}" => '', "\u{1dd2}" => '', "\u{1dd3}" => '', "\u{1dd4}" => '', "\u{1dd5}" => '', "\u{1dd6}" => '', "\u{1dd7}" => '',
|
||||
"\u{1dd8}" => '', "\u{1dd9}" => '', "\u{1dda}" => '', "\u{1ddb}" => '', "\u{1ddc}" => '', "\u{1ddd}" => '', "\u{1dde}" => '', "\u{1ddf}" => '',
|
||||
"\u{1de0}" => '', "\u{1de1}" => '', "\u{1de2}" => '', "\u{1de3}" => '', "\u{1de4}" => '', "\u{1de5}" => '', "\u{1de6}" => '', "\u{1de7}" => '',
|
||||
"\u{1de8}" => '', "\u{1de9}" => '', "\u{1dea}" => '', "\u{1deb}" => '', "\u{1dec}" => '', "\u{1ded}" => '', "\u{1dee}" => '', "\u{1def}" => '',
|
||||
"\u{1df0}" => '', "\u{1df1}" => '', "\u{1df2}" => '', "\u{1df3}" => '', "\u{1df4}" => '', "\u{1df5}" => '', "\u{1df6}" => '', "\u{1df7}" => '',
|
||||
"\u{1df8}" => '', "\u{1df9}" => '', "\u{1dfa}" => '', "\u{1dfb}" => '', "\u{1dfc}" => '', "\u{1dfd}" => '', "\u{1dfe}" => '', "\u{1dff}" => '',
|
||||
|
||||
"\u{20d0}" => '', "\u{20d1}" => '', "\u{20d2}" => '', "\u{20d3}" => '', "\u{20d4}" => '', "\u{20d5}" => '', "\u{20d6}" => '', "\u{20d7}" => '',
|
||||
"\u{20d8}" => '', "\u{20d9}" => '', "\u{20da}" => '', "\u{20db}" => '', "\u{20dc}" => '', "\u{20dd}" => '', "\u{20de}" => '', "\u{20df}" => '',
|
||||
"\u{20e0}" => '', "\u{20e1}" => '', "\u{20e2}" => '', "\u{20e3}" => '', "\u{20e4}" => '', "\u{20e5}" => '', "\u{20e6}" => '', "\u{20e7}" => '',
|
||||
"\u{20e8}" => '', "\u{20e9}" => '', "\u{20ea}" => '', "\u{20eb}" => '', "\u{20ec}" => '', "\u{20ed}" => '', "\u{20ee}" => '', "\u{20ef}" => '',
|
||||
"\u{20f0}" => '', "\u{20f1}" => '', "\u{20f2}" => '', "\u{20f3}" => '', "\u{20f4}" => '', "\u{20f5}" => '', "\u{20f6}" => '', "\u{20f7}" => '',
|
||||
"\u{20f8}" => '', "\u{20f9}" => '', "\u{20fa}" => '', "\u{20fb}" => '', "\u{20fc}" => '', "\u{20fd}" => '', "\u{20fe}" => '', "\u{20ff}" => '',
|
||||
|
||||
"\u{fe20}" => '', "\u{fe21}" => '', "\u{fe22}" => '', "\u{fe23}" => '', "\u{fe24}" => '', "\u{fe25}" => '', "\u{fe26}" => '', "\u{fe27}" => '',
|
||||
"\u{fe28}" => '', "\u{fe29}" => '', "\u{fe2a}" => '', "\u{fe2b}" => '', "\u{fe2c}" => '', "\u{fe2d}" => '', "\u{fe2e}" => '', "\u{fe2f}" => '',
|
||||
|
||||
'Æ' => 'AE', // "U+00c6"
|
||||
'æ' => 'ae', // "U+00e6"
|
||||
'Œ' => 'OE', // "U+0152"
|
||||
'œ' => 'oe', // "U+0153"
|
||||
];
|
||||
|
||||
$len = count($chars);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$unichar = $chars[$i];
|
||||
if (isset($map[$unichar])) {
|
||||
$chars[$i] = $map[$unichar];
|
||||
}
|
||||
}
|
||||
return implode('', $chars);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
class DBConnection
|
||||
{
|
||||
const DBTYPE_SQLITE = "sqlite";
|
||||
const DBTYPE_MYSQL = "mysql";
|
||||
const DBTYPE_POSTGRES = "postgres";
|
||||
|
||||
protected static $instance;
|
||||
|
||||
public static function init(Database_Abstract $instance) : Database_Abstract
|
||||
|
|
@ -33,6 +37,7 @@ class DBConnection
|
|||
|
||||
abstract class Database_Abstract
|
||||
{
|
||||
const DBTYPE = '';
|
||||
protected static $readonlyProps = ['prefix', 'lastQuery'];
|
||||
|
||||
/** @var string */
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class DBCore
|
|||
* @return DBCore
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function defaultInstance() : DBCore
|
||||
public static function default() : DBCore
|
||||
{
|
||||
if (!isset(self::$defaultInstance)) {
|
||||
throw new Exception("DBCore defaultInstance is not initialized");
|
||||
|
|
@ -74,5 +74,92 @@ class DBCore
|
|||
return $listId;
|
||||
}
|
||||
|
||||
|
||||
public function taskExists(int $id): bool
|
||||
{
|
||||
$db = $this->db;
|
||||
$count = (int) $db->sq("SELECT COUNT(*) FROM {$db->prefix}todolist WHERE id = $id");
|
||||
return ($count > 0) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
public function getTaskById(int $id): ?array
|
||||
{
|
||||
$db = $this->db;
|
||||
$groupConcat = '';
|
||||
if ($db::DBTYPE == DBConnection::DBTYPE_POSTGRES) {
|
||||
$groupConcat = "array_to_string(array_agg(tags.id), ',') AS tags_ids, string_agg(tags.name, ',') AS tags";
|
||||
}
|
||||
else {
|
||||
$groupConcat = "GROUP_CONCAT(tags.id) AS tags_ids, GROUP_CONCAT(tags.name) AS tags";
|
||||
}
|
||||
$r = $db->sqa("
|
||||
SELECT todo.*, $groupConcat
|
||||
FROM {$db->prefix}todolist AS todo
|
||||
LEFT JOIN {$db->prefix}tag2task AS t2t ON todo.id = t2t.task_id
|
||||
LEFT JOIN {$db->prefix}tags AS tags ON t2t.tag_id = tags.id
|
||||
WHERE todo.id = $id
|
||||
GROUP BY todo.id
|
||||
");
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $listId
|
||||
* @param string $sqlWhere
|
||||
* @param int|string $sort
|
||||
* @param null|int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getTasksByListId(int $listId, string $sqlWhere, /* int|string */ $sort, ?int $limit = null): array
|
||||
{
|
||||
$db = $this->db;
|
||||
|
||||
if ($sqlWhere != '') {
|
||||
$sqlWhere = "AND $sqlWhere";
|
||||
}
|
||||
|
||||
$sqlSort = '';
|
||||
if (is_int($sort)) {
|
||||
$sqlSort = "ORDER BY compl ASC, ";
|
||||
if ($sort == 1) $sqlSort .= "prio DESC, ddn ASC, duedate ASC, ow ASC"; // byPrio
|
||||
elseif ($sort == 101) $sqlSort .= "prio ASC, ddn DESC, duedate DESC, ow DESC"; // byPrio (reverse)
|
||||
elseif ($sort == 2) $sqlSort .= "ddn ASC, duedate ASC, prio DESC, ow ASC"; // byDueDate
|
||||
elseif ($sort == 102) $sqlSort .= "ddn DESC, duedate DESC, prio ASC, ow DESC"; // byDueDate (reverse)
|
||||
elseif ($sort == 3) $sqlSort .= "d_created ASC, prio DESC, ow ASC"; // byDateCreated
|
||||
elseif ($sort == 103) $sqlSort .= "d_created DESC, prio ASC, ow DESC"; // byDateCreated (reverse)
|
||||
elseif ($sort == 4) $sqlSort .= "d_edited ASC, prio DESC, ow ASC"; // byDateModified
|
||||
elseif ($sort == 104) $sqlSort .= "d_edited DESC, prio ASC, ow DESC"; // byDateModified (reverse)
|
||||
elseif ($sort == 5) $sqlSort .= "title ASC, prio DESC, ow ASC"; // byTitle
|
||||
elseif ($sort == 105) $sqlSort .= "title DESC, prio ASC, ow DESC"; // byTitle (reverse)
|
||||
else $sqlSort .= "ow ASC";
|
||||
}
|
||||
else if ($sort != '') {
|
||||
$sqlSort = "ORDER BY $sort";
|
||||
}
|
||||
|
||||
$sqlLimit = '';
|
||||
if (!is_null($limit)) {
|
||||
$sqlLimit = "LIMIT $limit";
|
||||
}
|
||||
|
||||
$q = $db->dq("
|
||||
SELECT todo.*, todo.duedate IS NULL AS ddn, GROUP_CONCAT(tags.id) AS tags_ids, GROUP_CONCAT(tags.name) AS tags
|
||||
FROM {$db->prefix}todolist AS todo
|
||||
LEFT JOIN {$db->prefix}tag2task AS t2t ON todo.id = t2t.task_id
|
||||
LEFT JOIN {$db->prefix}tags AS tags ON t2t.tag_id = tags.id
|
||||
WHERE todo.list_id = $listId $sqlWhere
|
||||
GROUP BY todo.id
|
||||
$sqlSort
|
||||
$sqlLimit
|
||||
");
|
||||
|
||||
$data = array();
|
||||
while ($r = $q->fetchAssoc()) {
|
||||
$data[] = $r;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class ApiRequest
|
|||
public $jsonBody;
|
||||
|
||||
function __construct() {
|
||||
if (defined('MTT_API_USE_PATH_INFO') && MTT_API_USE_PATH_INFO) {
|
||||
if (defined('MTT_API_USE_PATH_INFO')) {
|
||||
$this->path = $_SERVER['PATH_INFO'];
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ namespace mytinytodo;
|
|||
|
||||
class Version
|
||||
{
|
||||
const VERSION = '1.7.4';
|
||||
const DB_VERSION = '1.7';
|
||||
const VERSION = '1.8-dev';
|
||||
const DB_VERSION = '1.8';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ function parseRoute($queryString)
|
|||
redirectWithHashRoute($hash, $q);
|
||||
}
|
||||
else if (isset($q['task'])) {
|
||||
$listId = (int)DBCore::defaultInstance()->getListIdByTaskId((int)$q['task']);
|
||||
$listId = (int)DBCore::default()->getListIdByTaskId((int)$q['task']);
|
||||
if ($listId > 0) {
|
||||
$h = [ 'list', $listId, 'search', '#'. (int)$q['task']];
|
||||
redirectWithHashRoute($h);
|
||||
|
|
|
|||
43
src/init.php
43
src/init.php
|
|
@ -103,12 +103,12 @@ function configureDbConnection()
|
|||
}
|
||||
DBConnection::init($db);
|
||||
try {
|
||||
$db->connect( array(
|
||||
$db->connect([
|
||||
'host' => MTT_DB_HOST,
|
||||
'user' => MTT_DB_USER,
|
||||
'password' => MTT_DB_PASSWORD,
|
||||
'db' => MTT_DB_NAME,
|
||||
));
|
||||
]);
|
||||
}
|
||||
catch(Exception $e) {
|
||||
logAndDie("Failed to connect to mysql database: ". $e->getMessage());
|
||||
|
|
@ -116,12 +116,40 @@ function configureDbConnection()
|
|||
$db->dq("SET NAMES utf8mb4");
|
||||
}
|
||||
|
||||
# PostgreSQL Database
|
||||
else if (MTT_DB_TYPE == 'postgres')
|
||||
{
|
||||
require_once(MTTINC. 'class.db.postgres.php');
|
||||
$db = DBConnection::init(new Database_Postgres());
|
||||
try {
|
||||
$db->connect([
|
||||
'host' => MTT_DB_HOST,
|
||||
'user' => MTT_DB_USER,
|
||||
'password' => MTT_DB_PASSWORD,
|
||||
'db' => MTT_DB_NAME,
|
||||
]);
|
||||
}
|
||||
catch(Exception $e) {
|
||||
$errlog = "Failed to connect to PostgreSQL database: ". $e->getMessage();
|
||||
if (MTT_DEBUG) {
|
||||
logAndDie($errlog);
|
||||
}
|
||||
else {
|
||||
logAndDie("Failed to connect to database", $errlog);
|
||||
}
|
||||
}
|
||||
$db->dq("SET NAMES 'utf8'");
|
||||
}
|
||||
|
||||
# SQLite3 Database
|
||||
elseif (MTT_DB_TYPE == 'sqlite')
|
||||
{
|
||||
require_once(MTTINC. 'vendor/autoload.php');
|
||||
require_once(MTTINC. 'class.db.sqlite3.php');
|
||||
$db = DBConnection::init(new Database_Sqlite3);
|
||||
$db->connect( array( 'filename' => MTTPATH. 'db/todolist.db' ) );
|
||||
$db = DBConnection::init(new Database_Sqlite3());
|
||||
$db->connect([
|
||||
'filename' => MTTPATH. 'db/todolist.db'
|
||||
]);
|
||||
}
|
||||
else {
|
||||
die("Incorrect database connection config");
|
||||
|
|
@ -346,7 +374,12 @@ function get_unsafe_mttinfo($v)
|
|||
/* URL for API, like http://localhost/mytinytodo/api/. No need to set by default. */
|
||||
$_mttinfo['api_url'] = Config::getUrl('api_url'); // need to have a trailing slash
|
||||
if ($_mttinfo['api_url'] == '') {
|
||||
$_mttinfo['api_url'] = get_unsafe_mttinfo('mtt_uri'). 'api.php?_path=/';
|
||||
if (defined('MTT_API_USE_PATH_INFO')) {
|
||||
$_mttinfo['api_url'] = get_unsafe_mttinfo('mtt_uri'). 'api/';
|
||||
}
|
||||
else {
|
||||
$_mttinfo['api_url'] = get_unsafe_mttinfo('mtt_uri'). 'api.php?_path=/';
|
||||
}
|
||||
}
|
||||
return $_mttinfo['api_url'];
|
||||
case 'title':
|
||||
|
|
|
|||
249
src/setup.php
249
src/setup.php
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
// Can be used to upgrade database from myTinyTodo v1.4 or later
|
||||
$lastVer = '1.7';
|
||||
$lastVer = '1.8';
|
||||
|
||||
if (version_compare(PHP_VERSION, '7.2.0') < 0) {
|
||||
die("PHP 7.2 or above is required");
|
||||
|
|
@ -77,22 +77,28 @@ if ($configExists)
|
|||
// Determine current installed db version
|
||||
$ver = databaseVersion($db);
|
||||
|
||||
if ($ver == '1.4') {
|
||||
if ($ver == '') {
|
||||
// clean install
|
||||
// will not load settings from database in init.php
|
||||
Config::$noDatabase = true;
|
||||
}
|
||||
else if (version_compare($ver, '1.4') < 0) {
|
||||
// Very old or previously failed while install
|
||||
exitMessage(htmlspecialchars("Can not update. Unsupported database version ($ver)."));
|
||||
}
|
||||
else if ($ver == '1.4') {
|
||||
// Need to upgrade. Do not ask for old password
|
||||
require_once(MTTPATH. 'db/config.php');
|
||||
Config::loadConfigV14($config);
|
||||
unset($config);
|
||||
DBConnection::init($db);
|
||||
}
|
||||
else {
|
||||
if ($ver != '1.7') {
|
||||
Config::$noDatabase = true; //will not load settings from database in init.php
|
||||
}
|
||||
require_once('./init.php');
|
||||
if ( !is_logged() ) {
|
||||
die("Access denied!<br> Disable password protection or Log in.");
|
||||
}
|
||||
|
||||
require_once('./init.php');
|
||||
if ( !is_logged() ) {
|
||||
die("Access denied!<br> Disable password protection or Log in.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($ver == '')
|
||||
|
|
@ -102,7 +108,7 @@ if ($ver == '')
|
|||
if ($install == '' && $db !== null)
|
||||
{
|
||||
# We already have settings file and need to create tables.
|
||||
exitMessage("<form method=post>Click next to create tables in '". htmlspecialchars($dbtype). "' database.<br><br>
|
||||
exitMessage("<form method=post>Click next to create tables in ". htmlspecialchars(databaseTypeName($db)). " database.<br><br>
|
||||
<input type=hidden name=stoken value='$setupToken'>
|
||||
<input type=hidden name=install value=create>
|
||||
<input type=submit value=' Next '></form>");
|
||||
|
|
@ -114,9 +120,10 @@ if ($ver == '')
|
|||
<form method=post>Select database type to use:<br><br>
|
||||
<input type=hidden name=install value=config>
|
||||
<input type=hidden name=stoken value='$setupToken'>
|
||||
<label><input type=radio name=db_type value=sqlite checked=checked onclick=\"document.getElementById('mysqlsettings').style.display='none'\">SQLite</label><br><br>
|
||||
<label><input type=radio name=db_type value=mysql onclick=\"document.getElementById('mysqlsettings').style.display=''\">MySQL</label><br>
|
||||
<div id='mysqlsettings' style='display:none; margin-left:30px;'><br><table>
|
||||
<label><input type=radio name=db_type value=sqlite checked=checked onclick=\"document.getElementById('dbsettings').style.display='none'\"> SQLite</label><br><br>
|
||||
<label><input type=radio name=db_type value=mysql onclick=\"document.getElementById('dbsettings').style.display=''\"> MySQL</label><br><br>
|
||||
<label><input type=radio name=db_type value=postgres onclick=\"document.getElementById('dbsettings').style.display=''\"> PostgreSQL (beta)</label><br>
|
||||
<div id='dbsettings' style='display:none; margin-left:30px;'><br><table>
|
||||
<tr><td>Host:</td><td><input name=db_host value=localhost></td></tr>
|
||||
<tr><td>Database:</td><td><input name=db_name value=mytinytodo></td></tr>
|
||||
<tr><td>User:</td><td><input name=db_user value=mtt></td></tr>
|
||||
|
|
@ -129,9 +136,12 @@ if ($ver == '')
|
|||
{
|
||||
check_post_stoken();
|
||||
# Save configuration
|
||||
$dbtype = ($_POST['db_type'] == 'mysql') ? 'mysql' : 'sqlite';
|
||||
$dbtype = $_POST['db_type'] ?? '';
|
||||
if ($dbtype != 'mysql' && $dbtype != 'postgres' && $dbtype != 'sqlite') {
|
||||
exitMessage("Unknown database type $dbtype");
|
||||
}
|
||||
Config::set('db.type', $dbtype);
|
||||
if ($dbtype == 'mysql') {
|
||||
if ($dbtype == 'mysql' || $dbtype == 'postgres') {
|
||||
Config::set('db.host', _post('db_host'));
|
||||
Config::set('db.name', _post('db_name'));
|
||||
Config::set('db.user', _post('db_user'));
|
||||
|
|
@ -177,7 +187,7 @@ elseif ($ver == $lastVer)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!in_array($ver, array('1.4'))) {
|
||||
if (!in_array($ver, array('1.4','1.7'))) {
|
||||
exitMessage(htmlspecialchars("Can not update. Unsupported database version ($ver)."));
|
||||
}
|
||||
|
||||
|
|
@ -192,9 +202,12 @@ else
|
|||
|
||||
# update process
|
||||
check_post_stoken();
|
||||
if ($ver == '1.4')
|
||||
{
|
||||
if ($ver == '1.4') {
|
||||
update_14_17($db, $dbtype);
|
||||
update_17_18($db, $dbtype);
|
||||
}
|
||||
elseif ($ver == '1.7') {
|
||||
update_17_18($db, $dbtype);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,12 +250,15 @@ function createAllTables($db, $dbtype)
|
|||
if ($dbtype == 'mysql') {
|
||||
createMysqlTables($db);
|
||||
}
|
||||
else if ($dbtype == 'postgres') {
|
||||
createPostgresTables($db);
|
||||
}
|
||||
else {
|
||||
createSqliteTables($db);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ===== mysql ========================================================= */
|
||||
function createMysqlTables($db)
|
||||
{
|
||||
$db->ex(
|
||||
|
|
@ -275,8 +291,6 @@ function createMysqlTables($db)
|
|||
`note` TEXT,
|
||||
`prio` TINYINT NOT NULL default 0, /* priority -,0,+ */
|
||||
`ow` INT NOT NULL default 0, /* order weight */
|
||||
`tags` VARCHAR(600) NOT NULL default '', /* for fast access to task tags */
|
||||
`tags_ids` VARCHAR(250) NOT NULL default '', /* no more than 22 tags (x11 chars) */
|
||||
`duedate` DATE default NULL,
|
||||
PRIMARY KEY(`id`),
|
||||
KEY(`list_id`),
|
||||
|
|
@ -287,7 +301,7 @@ function createMysqlTables($db)
|
|||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}tags (
|
||||
`id` INT UNSIGNED NOT NULL auto_increment,
|
||||
`name` VARCHAR(50) NOT NULL,
|
||||
`name` VARCHAR(250) NOT NULL default '',
|
||||
PRIMARY KEY(`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ");
|
||||
|
|
@ -323,6 +337,78 @@ function createMysqlTables($db)
|
|||
}
|
||||
|
||||
|
||||
/* ===== postgres =============================================== */
|
||||
function createPostgresTables($db)
|
||||
{
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}lists (
|
||||
id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
uuid CHAR(36) NOT NULL default '',
|
||||
ow INTEGER NOT NULL default 0,
|
||||
name VARCHAR(250) NOT NULL default '',
|
||||
d_created INTEGER NOT NULL default 0,
|
||||
d_edited INTEGER NOT NULL default 0,
|
||||
sorting SMALLINT NOT NULL default 0,
|
||||
published SMALLINT NOT NULL default 0,
|
||||
taskview INTEGER NOT NULL default 0,
|
||||
extra TEXT
|
||||
) ");
|
||||
$db->ex("CREATE UNIQUE INDEX {$db->prefix}lists_uuid ON {$db->prefix}lists (uuid)");
|
||||
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}todolist (
|
||||
id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
uuid CHAR(36) NOT NULL default '',
|
||||
list_id INTEGER NOT NULL default 0,
|
||||
d_created INTEGER NOT NULL default 0,
|
||||
d_completed INTEGER NOT NULL default 0,
|
||||
d_edited INTEGER NOT NULL default 0,
|
||||
compl SMALLINT NOT NULL default 0,
|
||||
title VARCHAR(250) NOT NULL default '',
|
||||
note TEXT default NULL,
|
||||
prio SMALLINT NOT NULL default 0,
|
||||
ow INTEGER NOT NULL default 0,
|
||||
duedate DATE default NULL
|
||||
) ");
|
||||
$db->ex("CREATE INDEX {$db->prefix}todo_list_id ON {$db->prefix}todolist (list_id)");
|
||||
$db->ex("CREATE UNIQUE INDEX {$db->prefix}todo_uuid ON {$db->prefix}todolist (uuid)");
|
||||
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}tags (
|
||||
id INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
name VARCHAR(250) NOT NULL DEFAULT ''
|
||||
) ");
|
||||
$db->ex("CREATE UNIQUE INDEX {$db->prefix}tags_name ON {$db->prefix}tags (name)");
|
||||
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}tag2task (
|
||||
tag_id INTEGER NOT NULL,
|
||||
task_id INTEGER NOT NULL,
|
||||
list_id INTEGER NOT NULL
|
||||
) ");
|
||||
$db->ex("CREATE INDEX {$db->prefix}tag2task_tag_id ON {$db->prefix}tag2task (tag_id)");
|
||||
$db->ex("CREATE INDEX {$db->prefix}tag2task_task_id ON {$db->prefix}tag2task (task_id)");
|
||||
$db->ex("CREATE INDEX {$db->prefix}tag2task_list_id ON {$db->prefix}tag2task (list_id)");
|
||||
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}settings (
|
||||
param_key VARCHAR(250) NOT NULL default '',
|
||||
param_value TEXT
|
||||
) ");
|
||||
$db->ex("CREATE UNIQUE INDEX {$db->prefix}settings_key ON {$db->prefix}settings (param_key)");
|
||||
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}sessions (
|
||||
id VARCHAR(64) NOT NULL default '',
|
||||
data TEXT,
|
||||
last_access INTEGER NOT NULL default 0,
|
||||
expires INTEGER NOT NULL default 0
|
||||
) ");
|
||||
$db->ex("CREATE UNIQUE INDEX {$db->prefix}sessions_id ON {$db->prefix}sessions (id)");
|
||||
}
|
||||
|
||||
|
||||
/* ===== sqlite =============================================== */
|
||||
function createSqliteTables($db)
|
||||
{
|
||||
$db->ex(
|
||||
|
|
@ -330,7 +416,7 @@ function createSqliteTables($db)
|
|||
id INTEGER PRIMARY KEY,
|
||||
uuid CHAR(36) NOT NULL,
|
||||
ow INTEGER NOT NULL default 0,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(250) NOT NULL,
|
||||
d_created INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_edited INTEGER UNSIGNED NOT NULL default 0,
|
||||
sorting TINYINT UNSIGNED NOT NULL default 0,
|
||||
|
|
@ -344,18 +430,16 @@ function createSqliteTables($db)
|
|||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}todolist (
|
||||
id INTEGER PRIMARY KEY,
|
||||
uuid CHAR(36) NOT NULL,
|
||||
uuid CHAR(36) NOT NULL default '',
|
||||
list_id INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_created INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_completed INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_edited INTEGER UNSIGNED NOT NULL default 0,
|
||||
compl TINYINT UNSIGNED NOT NULL default 0,
|
||||
title VARCHAR(250) NOT NULL,
|
||||
note TEXT,
|
||||
title VARCHAR(250) NOT NULL default '' COLLATE UTF8CI,
|
||||
note TEXT COLLATE UTF8CI default NULL,
|
||||
prio TINYINT NOT NULL default 0,
|
||||
ow INTEGER NOT NULL default 0,
|
||||
tags VARCHAR(600) NOT NULL default '',
|
||||
tags_ids VARCHAR(250) NOT NULL default '',
|
||||
duedate DATE default NULL
|
||||
) ");
|
||||
$db->ex("CREATE INDEX todo_list_id ON {$db->prefix}todolist (list_id)");
|
||||
|
|
@ -365,9 +449,9 @@ function createSqliteTables($db)
|
|||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}tags (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(50) NOT NULL COLLATE NOCASE
|
||||
name VARCHAR(250) NOT NULL DEFAULT '' COLLATE UTF8CI
|
||||
) ");
|
||||
$db->ex("CREATE UNIQUE INDEX tags_name ON {$db->prefix}tags (name COLLATE NOCASE)");
|
||||
$db->ex("CREATE INDEX tags_name ON {$db->prefix}tags (name)"); //NB: unique in mysql
|
||||
|
||||
|
||||
$db->ex(
|
||||
|
|
@ -419,6 +503,8 @@ function databaseVersion(Database_Abstract $db): string
|
|||
$v = '1.4';
|
||||
if ( !$db->tableExists($db->prefix.'settings') ) return $v;
|
||||
$v = '1.7';
|
||||
if ( $db->tableFieldExists($db->prefix.'todolist', 'tags') ) return $v;
|
||||
$v = '1.8';
|
||||
return $v;
|
||||
}
|
||||
|
||||
|
|
@ -514,12 +600,31 @@ function testConnect(&$error)
|
|||
if (!defined($c)) throw new Exception("$c is not defined");
|
||||
}
|
||||
|
||||
$db->connect( array(
|
||||
$db->connect([
|
||||
'host' => MTT_DB_HOST,
|
||||
'user' => MTT_DB_USER,
|
||||
'password' => MTT_DB_PASSWORD,
|
||||
'db' => MTT_DB_NAME
|
||||
));
|
||||
]);
|
||||
}
|
||||
else if (MTT_DB_TYPE == 'postgres')
|
||||
{
|
||||
if (!defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')) {
|
||||
throw new Exception("Required PHP extension 'PDO_PostgreSQL' is not installed.");
|
||||
}
|
||||
require_once(MTTINC. 'class.db.postgres.php');
|
||||
|
||||
foreach (['MTT_DB_HOST', 'MTT_DB_USER', 'MTT_DB_PASSWORD', 'MTT_DB_NAME', 'MTT_DB_PREFIX'] as $c) {
|
||||
if (!defined($c)) throw new Exception("$c is not defined");
|
||||
}
|
||||
|
||||
$db = new Database_Postgres;
|
||||
$db->connect([
|
||||
'host' => MTT_DB_HOST,
|
||||
'user' => MTT_DB_USER,
|
||||
'password' => MTT_DB_PASSWORD,
|
||||
'db' => MTT_DB_NAME
|
||||
]);
|
||||
}
|
||||
else if (MTT_DB_TYPE == 'sqlite')
|
||||
{
|
||||
|
|
@ -534,7 +639,9 @@ function testConnect(&$error)
|
|||
}
|
||||
require_once(MTTINC. 'class.db.sqlite3.php');
|
||||
$db = new Database_Sqlite3;
|
||||
$db->connect( array( 'filename' => MTTPATH. 'db/todolist.db' ) );
|
||||
$db->connect([
|
||||
'filename' => MTTPATH. 'db/todolist.db'
|
||||
]);
|
||||
}
|
||||
else {
|
||||
throw new Exception("Unsupported database type: ". MTT_DB_TYPE);
|
||||
|
|
@ -565,6 +672,17 @@ function myExceptionHandler($e)
|
|||
exit;
|
||||
}
|
||||
|
||||
function databaseTypeName(Database_Abstract $db)
|
||||
{
|
||||
$s = get_class($db);
|
||||
switch ($s) {
|
||||
case "Database_Mysql": return "MySQL";
|
||||
case "Database_Postgres": return "PostgreSQL";
|
||||
case "Database_Sqlite3": return "SQLite";
|
||||
default: return $s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### update v1.4 to v1.7 ##########
|
||||
function update_14_17(Database_Abstract $db, $dbtype)
|
||||
|
|
@ -576,7 +694,7 @@ function update_14_17(Database_Abstract $db, $dbtype)
|
|||
$db->ex("ALTER TABLE {$db->prefix}lists ADD `extra` TEXT");
|
||||
|
||||
# increase the length of list and tag name
|
||||
# (not applicable to sqlite because it uses VARCHAR fields of eny length as TEXT)
|
||||
# (not applicable to sqlite because it uses VARCHAR fields of any length as TEXT)
|
||||
$db->ex("ALTER TABLE {$db->prefix}todolist CHANGE `tags` `tags` VARCHAR(2000) NOT NULL default '' ");
|
||||
$db->ex("ALTER TABLE {$db->prefix}tags CHANGE `name` `name` VARCHAR(250) NOT NULL default '' ");
|
||||
$db->ex("ALTER TABLE {$db->prefix}lists CHANGE `name` `name` VARCHAR(250) NOT NULL default '' ");
|
||||
|
|
@ -642,3 +760,64 @@ UNIQUE KEY `id` (`id`)
|
|||
Config::saveDbConfig();
|
||||
}
|
||||
### end of 1.7 #####
|
||||
|
||||
### update v1.7 to v1.8 ##########
|
||||
function update_17_18(Database_Abstract $db, $dbtype)
|
||||
{
|
||||
$db->ex("BEGIN");
|
||||
|
||||
if ($dbtype == 'sqlite')
|
||||
{
|
||||
// Use UTF8CI collate. Old sqlite does not support DROP COLUMN (before v3.35.0 2021-03-12)
|
||||
$db->ex("DROP INDEX todo_list_id");
|
||||
$db->ex("DROP INDEX todo_uuid");
|
||||
$db->ex("ALTER TABLE {$db->prefix}todolist RENAME TO {$db->prefix}todolist_old");
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}todolist (
|
||||
id INTEGER PRIMARY KEY,
|
||||
uuid CHAR(36) NOT NULL default '',
|
||||
list_id INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_created INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_completed INTEGER UNSIGNED NOT NULL default 0,
|
||||
d_edited INTEGER UNSIGNED NOT NULL default 0,
|
||||
compl TINYINT UNSIGNED NOT NULL default 0,
|
||||
title VARCHAR(250) NOT NULL default '' COLLATE UTF8CI,
|
||||
note TEXT COLLATE UTF8CI default NULL,
|
||||
prio TINYINT NOT NULL default 0,
|
||||
ow INTEGER NOT NULL default 0,
|
||||
duedate DATE default NULL )"
|
||||
);
|
||||
$db->ex("INSERT INTO {$db->prefix}todolist SELECT id,uuid,list_id,d_created,d_completed,d_edited,compl,title,note,prio,ow,duedate FROM {$db->prefix}todolist_old");
|
||||
$db->ex("CREATE INDEX todo_list_id ON {$db->prefix}todolist (list_id)");
|
||||
$db->ex("CREATE UNIQUE INDEX todo_uuid ON {$db->prefix}todolist (uuid)");
|
||||
$db->ex("DROP TABLE {$db->prefix}todolist_old");
|
||||
|
||||
$db->ex("DROP INDEX tags_name");
|
||||
$db->ex("ALTER TABLE {$db->prefix}tags RENAME TO {$db->prefix}tags_old");
|
||||
$db->ex(
|
||||
"CREATE TABLE {$db->prefix}tags (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(250) NOT NULL DEFAULT '' COLLATE UTF8CI )"
|
||||
);
|
||||
$db->ex("INSERT INTO {$db->prefix}tags SELECT * FROM {$db->prefix}tags_old");
|
||||
$db->ex("CREATE INDEX tags_name ON {$db->prefix}tags (name)");
|
||||
$db->ex("DROP TABLE {$db->prefix}tags_old");
|
||||
}
|
||||
else // mysql
|
||||
{
|
||||
$db->ex("ALTER TABLE {$db->prefix}todolist DROP COLUMN tags");
|
||||
$db->ex("ALTER TABLE {$db->prefix}todolist DROP COLUMN tags_ids");
|
||||
|
||||
// if mysql db was created in v1.7.x then tags.name field has length of 50 instead of 250
|
||||
$db->ex("ALTER TABLE {$db->prefix}tags CHANGE `name` `name` VARCHAR(250) NOT NULL default '' ");
|
||||
}
|
||||
|
||||
$db->ex("COMMIT");
|
||||
|
||||
if ($dbtype == 'sqlite') {
|
||||
$db->ex("VACUUM");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
### end of 1.8 #####
|
||||
|
|
|
|||
Loading…
Reference in a new issue