* add more compatibility for http api (able to use GET and POST requests only)

This commit is contained in:
maxpozdeev 2023-11-04 14:58:24 +03:00
parent b7e060e6db
commit edfe391e41
6 changed files with 133 additions and 80 deletions

View file

@ -1,12 +1,16 @@
# For Apache
# For REST API in Apache
#<IfModule mod_rewrite.c>
# RewriteEngine On
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^api/(.*)$ api.php/$1 [L,QSA]
#</IfModule>
#<Limit GET POST PUT DELETE>
# Allow from all
#</Limit>
# For Nginx set something like this:
# In Nginx set something like this:
# location /api/ {
# rewrite ^/api/(.*) /api.php/$1 last;
# }

View file

@ -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
]
);

View file

@ -1,6 +1,6 @@
/*
This file is a part of myTinyTodo.
(C) Copyright 2010,2020,2022 Max Pozdeev <maxpozdeev@gmail.com>
(C) Copyright 2010,2020-2023 Max Pozdeev <maxpozdeev@gmail.com>
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',

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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 (?,?,?,?,?,?)",