diff --git a/src/.htaccess b/src/.htaccess
index 194f78e..76b8d44 100644
--- a/src/.htaccess
+++ b/src/.htaccess
@@ -1,12 +1,16 @@
-# For Apache
+# For REST API in Apache
#
# RewriteEngine On
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^api/(.*)$ api.php/$1 [L,QSA]
#
+#
+# Allow from all
+#
-# For Nginx set something like this:
+
+# In Nginx set something like this:
# location /api/ {
# rewrite ^/api/(.*) /api.php/$1 last;
# }
diff --git a/src/api.php b/src/api.php
index 5eae879..cb6a8aa 100644
--- a/src/api.php
+++ b/src/api.php
@@ -32,6 +32,7 @@ $endpoints = array(
'GET' => [ ListsController::class , 'getId' ],
'PUT' => [ ListsController::class , 'putId' ],
'DELETE' => [ ListsController::class , 'deleteId' ],
+ 'POST' => [ ListsController::class , 'putId' ], //compatibility
],
'/tasks' => [
'GET' => [ TasksController::class , 'get' ],
@@ -41,6 +42,7 @@ $endpoints = array(
'/tasks/(-?\d+)' => [
'PUT' => [ TasksController::class , 'putId' ],
'DELETE' => [ TasksController::class , 'deleteId' ],
+ 'POST' => [ TasksController::class , 'putId' ], //compatibility
],
'/tasks/parseTitle' => [
'POST' => [ TasksController::class , 'postTitleParse' ],
@@ -60,6 +62,7 @@ $endpoints = array(
'/ext-settings/(.+)' => [
'GET' => [ ExtSettingsController::class , 'get' ],
'PUT' => [ ExtSettingsController::class , 'put' ],
+ 'POST' => [ ExtSettingsController::class , 'put' ], //compatibility
]
);
diff --git a/src/content/mytinytodo_api.js b/src/content/mytinytodo_api.js
index 57a5fe5..af97a1a 100644
--- a/src/content/mytinytodo_api.js
+++ b/src/content/mytinytodo_api.js
@@ -1,6 +1,6 @@
/*
This file is a part of myTinyTodo.
- (C) Copyright 2010,2020,2022 Max Pozdeev
+ (C) Copyright 2010,2020-2023 Max Pozdeev
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
@@ -9,10 +9,12 @@
"use strict";
var mtt;
+var useREST = true;
function MytinytodoAjaxApi(amtt)
{
mtt = amtt;
+ useREST = false;
}
window.MytinytodoAjaxApi = MytinytodoAjaxApi;
@@ -22,7 +24,9 @@ MytinytodoAjaxApi.prototype =
/* required method */
request: function(action, params, callback)
{
- if (!this[action]) throw "Unknown ApiDriver action: " + action;
+ if (!this[action] || typeof this[action] !== 'function') {
+ throw "Unknown ApiDriver action: " + action;
+ }
this[action] (params, function(json){
if (json.denied) mtt.errorDenied();
@@ -50,7 +54,7 @@ MytinytodoAjaxApi.prototype =
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
- action: 'simple',
+ action: 'newSimple',
list: params.list,
title: params.title,
tag: params.tag,
@@ -68,7 +72,7 @@ MytinytodoAjaxApi.prototype =
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
- action: 'full',
+ action: 'newFull',
list: params.list,
title: params.title,
note: params.note,
@@ -87,7 +91,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks/' + encodeURIComponent(params.id),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'edit',
@@ -107,7 +111,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks/' + encodeURIComponent(params.id),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'note',
@@ -123,7 +127,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks/' + encodeURIComponent(params.id),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'complete',
@@ -139,7 +143,11 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks/' + encodeURIComponent(params.id),
- method: 'DELETE',
+ method: useREST ? 'DELETE' : 'POST',
+ contentType : 'application/json', // contentType and data are required if method is POST
+ data: JSON.stringify({
+ action: 'delete',
+ }),
success: callback,
dataType: 'json'
});
@@ -150,7 +158,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks/' + encodeURIComponent(params.id),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'priority',
@@ -165,7 +173,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks',
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'order',
@@ -190,7 +198,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'tasks/' + encodeURIComponent(params.id),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'move',
@@ -232,6 +240,7 @@ MytinytodoAjaxApi.prototype =
method: 'POST',
contentType : 'application/json',
data: JSON.stringify({
+ action: 'new',
name: params.name,
}),
success: callback,
@@ -244,7 +253,11 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'DELETE',
+ method: useREST ? 'DELETE' : 'POST',
+ contentType : 'application/json', // contentType and data are required if method is POST
+ data: JSON.stringify({
+ action: 'delete',
+ }),
success: callback,
dataType: 'json'
});
@@ -254,7 +267,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'rename',
@@ -269,7 +282,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'sort',
@@ -285,7 +298,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'publish',
@@ -300,7 +313,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'enableFeedKey',
@@ -315,7 +328,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'showNotes',
@@ -330,7 +343,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'hide',
@@ -345,7 +358,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists',
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'order',
@@ -360,7 +373,7 @@ MytinytodoAjaxApi.prototype =
{
$.ajax({
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
- method: 'PUT',
+ method: useREST ? 'PUT' : 'POST',
contentType : 'application/json',
data: JSON.stringify({
action: 'clearCompleted',
diff --git a/src/includes/api/ListsController.php b/src/includes/api/ListsController.php
index 6440a7e..cd2dbc1 100644
--- a/src/includes/api/ListsController.php
+++ b/src/includes/api/ListsController.php
@@ -40,7 +40,7 @@ class ListsController extends ApiController {
/**
- * Create new list
+ * Create new list and Actions with all lists
* Code 201 on success
* @return void
* @throws Exception
@@ -48,15 +48,12 @@ class ListsController extends ApiController {
function post()
{
checkWriteAccess();
- $id = DBCore::default()->createListWithName($this->req->jsonBody['name'] ?? '');
- $db = DBConnection::instance();
- $t = array();
- $t['total'] = 1;
- $r = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$id");
- $oo = $this->prepareList($r, true);
- MTTNotificationCenter::postNotification(MTTNotification::didCreateList, $oo);
- $t['list'][] = $oo;
- $this->response->data = $t;
+ $action = $this->req->jsonBody['action'] ?? '';
+ switch ($action) {
+ case 'order': $this->response->data = $this->changeListOrder(); break; //compatibility
+ case 'new':
+ default: $this->response->data = $this->createList();
+ }
}
/**
@@ -105,32 +102,13 @@ class ListsController extends ApiController {
function deleteId($id)
{
checkWriteAccess();
- $db = DBConnection::instance();
- $t = array();
- $t['total'] = 0;
- $id = (int)$id;
- $list = null;
- if (MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteList)) {
- $list = $this->getListRowById($id);
- }
- $db->ex("BEGIN");
- $db->ex("DELETE FROM {$db->prefix}lists WHERE id=$id");
- $t['total'] = $db->affected();
- if ($t['total']) {
- $db->ex("DELETE FROM {$db->prefix}tag2task WHERE list_id=$id");
- $db->ex("DELETE FROM {$db->prefix}todolist WHERE list_id=$id");
- }
- $db->ex("COMMIT");
- if ($t['total'] && MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteList)) {
- MTTNotificationCenter::postNotification(MTTNotification::didDeleteList, $list);
- }
- $this->response->data = $t;
+ $this->response->data = $this->deleteList($id);
}
/**
* Edit some properties of List
- * Actions: rename
+ * Actions: rename, ...
* @param mixed $id
* @return void
* @throws Exception
@@ -149,6 +127,7 @@ class ListsController extends ApiController {
case 'showNotes': $this->response->data = $this->showNotes($id); break;
case 'hide': $this->response->data = $this->hideList($id); break;
case 'clearCompleted': $this->response->data = $this->clearCompleted($id); break;
+ case 'delete': $this->response->data = $this->deleteList($id); break; //compatibility
default: $this->response->data = ['total' => 0];
}
}
@@ -214,6 +193,23 @@ class ListsController extends ApiController {
);
}
+ private function createList(): ?array
+ {
+ $t = array();
+ $t['total'] = 0;
+ $id = DBCore::default()->createListWithName($this->req->jsonBody['name'] ?? '');
+ if (!$id) {
+ return $t;
+ }
+ $db = DBConnection::instance();
+ $t['total'] = 1;
+ $r = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$id");
+ $oo = $this->prepareList($r, true);
+ MTTNotificationCenter::postNotification(MTTNotification::didCreateList, $oo);
+ $t['list'][] = $oo;
+ return $t;
+ }
+
private function renameList(int $id): ?array
{
$db = DBConnection::instance();
@@ -371,4 +367,27 @@ class ListsController extends ApiController {
return $t;
}
+ private function deleteList(int $id)
+ {
+ $db = DBConnection::instance();
+ $t = array();
+ $t['total'] = 0;
+ $id = (int)$id;
+ $list = null;
+ if (MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteList)) {
+ $list = $this->getListRowById($id);
+ }
+ $db->ex("BEGIN");
+ $db->ex("DELETE FROM {$db->prefix}lists WHERE id=$id");
+ $t['total'] = $db->affected();
+ if ($t['total']) {
+ $db->ex("DELETE FROM {$db->prefix}tag2task WHERE list_id=$id");
+ $db->ex("DELETE FROM {$db->prefix}todolist WHERE list_id=$id");
+ }
+ $db->ex("COMMIT");
+ if ($t['total'] && MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteList)) {
+ MTTNotificationCenter::postNotification(MTTNotification::didDeleteList, $list);
+ }
+ return $t;
+ }
}
diff --git a/src/includes/api/TasksController.php b/src/includes/api/TasksController.php
index 8593764..446300a 100644
--- a/src/includes/api/TasksController.php
+++ b/src/includes/api/TasksController.php
@@ -135,22 +135,27 @@ class TasksController extends ApiController {
/**
* Create new task
- * action: simple or full
+ * action: newSimple or newFull
* @return void
* @throws Exception
*/
function post()
{
- $listId = (int)($this->req->jsonBody['list'] ?? 0);
- checkWriteAccess($listId);
$action = $this->req->jsonBody['action'] ?? '';
- if ($action == 'full') {
- $this->response->data = $this->fullNewTaskInList($listId);
+ if ($action == 'order') { //compatibility
+ checkWriteAccess();
+ $this->response->data = $this->changeTaskOrder();
}
else {
- $this->response->data = $this->newTaskInList($listId);
+ $listId = (int)($this->req->jsonBody['list'] ?? 0);
+ checkWriteAccess($listId);
+ if ($action == 'newFull') {
+ $this->response->data = $this->fullNewTaskInList($listId);
+ }
+ else {
+ $this->response->data = $this->newTaskInList($listId);
+ }
}
-
}
/**
@@ -178,25 +183,7 @@ class TasksController extends ApiController {
function deleteId($id)
{
checkWriteAccess();
- $id = (int)$id;
- $task = null;
- if (MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteTask)) {
- $task = $this->getTaskRowById($id);
- }
- $db = DBConnection::instance();
- $db->ex("BEGIN");
- $db->ex("DELETE FROM {$db->prefix}tag2task WHERE task_id=$id");
- //TODO: delete unused tags?
- $db->dq("DELETE FROM {$db->prefix}todolist WHERE id=$id");
- $deleted = $db->affected();
- $db->ex("COMMIT");
- if ($deleted && MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteTask)) {
- MTTNotificationCenter::postNotification(MTTNotification::didDeleteTask, $task);
- }
- $t = array();
- $t['total'] = $deleted;
- $t['list'][] = array('id' => $id);
- $this->response->data = $t;
+ $this->response->data = $this->deleteTask((int)$id);
}
/**
@@ -222,6 +209,7 @@ class TasksController extends ApiController {
case 'note': $this->response->data = $this->editNote($id); break;
case 'move': $this->response->data = $this->moveTask($id); break;
case 'priority': $this->response->data = $this->priorityTask($id); break;
+ case 'delete': $this->response->data = $this->deleteTask($id); break; //compatibility
default: $this->response->data = ['total' => 0];
}
}
@@ -557,6 +545,29 @@ class TasksController extends ApiController {
return $t;
}
+ private function deleteTask(int $id)
+ {
+ $id = (int)$id;
+ $task = null;
+ if (MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteTask)) {
+ $task = $this->getTaskRowById($id);
+ }
+ $db = DBConnection::instance();
+ $db->ex("BEGIN");
+ $db->ex("DELETE FROM {$db->prefix}tag2task WHERE task_id=$id");
+ //TODO: delete unused tags?
+ $db->dq("DELETE FROM {$db->prefix}todolist WHERE id=$id");
+ $deleted = $db->affected();
+ $db->ex("COMMIT");
+ if ($deleted && MTTNotificationCenter::hasObserversForNotification(MTTNotification::didDeleteTask)) {
+ MTTNotificationCenter::postNotification(MTTNotification::didDeleteTask, $task);
+ }
+ $t = array();
+ $t['total'] = $deleted;
+ $t['list'][] = array('id' => $id);
+ return $t;
+ }
+
private function getUserListsSimple(bool $readOnly = false): array
{
$db = DBConnection::instance();
diff --git a/src/includes/class.dbcore.php b/src/includes/class.dbcore.php
index 313d93e..8179f46 100644
--- a/src/includes/class.dbcore.php
+++ b/src/includes/class.dbcore.php
@@ -170,10 +170,13 @@ class DBCore
return $data;
}
- function createListWithName(string $name): int
+ function createListWithName(string $name): ?int
{
$db = DBConnection::instance();
$name = str_replace( ['"',"'",'<','>','&'], '', trim($name) );
+ if ($name == '') {
+ return null;
+ }
$ow = 1 + (int)$db->sq("SELECT MAX(ow) FROM {$db->prefix}lists");
$time = time();
$db->dq("INSERT INTO {$db->prefix}lists (uuid,name,ow,d_created,d_edited,taskview) VALUES (?,?,?,?,?,?)",