convert indention to spaces in php files

This commit is contained in:
Max Pozdeev 2022-02-06 23:37:02 +03:00
parent 69f3702a2e
commit acfb316e9a
21 changed files with 2787 additions and 2785 deletions

View file

@ -11,8 +11,13 @@ tab_width = 4
trim_trailing_whitespace = true
insert_final_newline = true
[*.php]
indent_style = space
indent_size = 4
insert_final_newline = false
# temporary. spaces will be used later.
[*.{php,js}]
[*.js]
indent_style = tab
tab_width = 4
@ -20,9 +25,6 @@ tab_width = 4
indent_style = space
indent_size = 2
[*.php]
insert_final_newline = false
[*.md]
trim_trailing_whitespace = false
insert_final_newline = false

View file

@ -4,7 +4,7 @@
// PHP 5.4 is required
if ( !isset($argv) || !isset($argv[1]) ) {
die("Usage: buildzip.php <path_to_repo> [-o source.zip] [-v VERSION]\n");
die("Usage: buildzip.php <path_to_repo> [-o source.zip] [-v VERSION]\n");
}
$repo = $argv[1];
@ -15,28 +15,28 @@ $ver = 0;
while ($arg = next($argv))
{
if ($arg == '-o') {
$zipfile = next($argv);
}
elseif ($arg == '-v') {
$ver = next($argv);
}
if ($arg == '-o') {
$zipfile = next($argv);
}
elseif ($arg == '-v') {
$ver = next($argv);
}
}
deleteTreeIfDir($dir);
$out = `git clone $repo $dir 2>&1`;
if (!is_dir($dir)) {
die("Error while clone: $out\n");
die("Error while clone: $out\n");
}
print "> Repository was cloned to temp dir: $dir\n";
#get current version number if not specified
if (!$ver)
{
chdir($dir);
$fh = fopen('version.txt', 'r') or die("Cant open version.txt\n");
$ver = trim(fgets($fh, 100));
fclose($fh);
chdir($dir);
$fh = fopen('version.txt', 'r') or die("Cant open version.txt\n");
$ver = trim(fgets($fh, 100));
fclose($fh);
}
chdir($dir. DIRECTORY_SEPARATOR. 'src');
$rev = trim(`git show --format=format:%H --summary`);
@ -62,9 +62,9 @@ unlink('./content/lang/en-rtl.json');
# save only 2 languages
$dh = opendir('./content/lang/') or die("Cant opendir lang\n");
while (false !== ($f = readdir($dh))) {
if (!in_array($f, ['.', '..', '.htaccess', 'en.json', 'ru.json'])) {
unlink('./content/lang/'. $f);
}
if (!in_array($f, ['.', '..', '.htaccess', 'en.json', 'ru.json'])) {
unlink('./content/lang/'. $f);
}
}
closedir($dh);
*/
@ -75,7 +75,7 @@ rename('src', 'mytinytodo') or die("Cant rename 'src'\n");
`zip -9 -r mytinytodo.zip mytinytodo`; #OS dep.!!!
if (!file_exists('mytinytodo.zip')) {
die("Failed to pack files (no output zip file)\n");
die("Failed to pack files (no output zip file)\n");
}
$zipfile = str_replace('@VERSION', $ver, $zipfile);
@ -83,7 +83,7 @@ $zipfile = str_replace('@REV', $rev, $zipfile);
chdir($curdir);
if ( ! rename("$dir/mytinytodo.zip", $zipfile) ) {
die("Failed to move mytinytodo.zip to $zipfile");
die("Failed to move mytinytodo.zip to $zipfile");
}
deleteTreeIfDir($dir);
@ -98,23 +98,23 @@ echo("> Build is stored in $zipfile\n");
function deleteTreeIfDir($dir)
{
if ( is_dir($dir) ) {
switch (PHP_OS) {
case 'Darwin':
system("rm -rf $dir");
break;
case 'Windows':
system("rmdir /s /q $dir");
break;
default:
die("Unknown system ". PHP_OS. "\n");
}
}
if ( is_dir($dir) ) {
switch (PHP_OS) {
case 'Darwin':
system("rm -rf $dir");
break;
case 'Windows':
system("rmdir /s /q $dir");
break;
default:
die("Unknown system ". PHP_OS. "\n");
}
}
}
function replaceVer($filename, $ver)
{
$s = @file_get_contents($filename) or die("Cant open $filename\n");
$s = str_replace('@VERSION', $ver, $s);
@file_put_contents($filename, $s) or die("Cant write $filename\n");
$s = @file_get_contents($filename) or die("Cant open $filename\n");
$s = str_replace('@VERSION', $ver, $s);
@file_put_contents($filename, $s) or die("Cant write $filename\n");
}

File diff suppressed because it is too large Load diff

View file

@ -3,17 +3,17 @@
// 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'));
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'));
define("MTT_DB_TYPE", "mysql");
define("MTT_DB_HOST", getenv('MTT_DB_HOST'));
define("MTT_DB_NAME", getenv('MTT_DB'));
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'));
}
else if (getenv('MTT_DB_TYPE') == 'sqlite') {
define("MTT_DB_TYPE", "sqlite");
define("MTT_DB_PREFIX", "");
define("MTT_DB_TYPE", "sqlite");
define("MTT_DB_PREFIX", "");
}
define("MTT_SALT", "Random text");

View file

@ -1,9 +1,9 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2010-2011,2019-2021 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2010-2011,2019-2021 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
//$dontStartSession = 1;
@ -15,7 +15,7 @@ if(!have_write_access()) $onlyPublishedList = true;
$listId = (int)_get('list');
$listData = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$listId ". ($onlyPublishedList ? "AND published=1" : "") );
if(!$listData) {
die("No such list or access denied");
die("No such list or access denied");
}
$sqlSort = "ORDER BY compl ASC, ";
@ -27,7 +27,7 @@ $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;
$data[] = $r;
}
$format = _get('format');
@ -38,124 +38,124 @@ else printCSV($listData, $data);
function have_write_access()
{
if(is_logged()) return true;
return false;
if(is_logged()) return true;
return false;
}
function printCSV($listData, $data)
{
$s = "\xEF\xBB\xBF". "Completed;Priority;Task;Notes;Tags;Due;DateCreated;DateCompleted\n";
foreach($data as $r)
{
$s .= ($r['compl']?'1':'0'). ';'.
$r['prio']. ';'. escape_csv($r['title']). ';'.
escape_csv($r['note']). ';'.
escape_csv($r['tags']). ';'.
$r['duedate']. ';'.
date('Y-m-d H:i:s O',$r['d_created']). ';'.
($r['d_completed'] ? date('Y-m-d H:i:s O',$r['d_completed']) :''). "\n";
}
header('Content-type: text/csv; charset=utf-8');
header('Content-disposition: attachment; filename=list_'.(int)$listData['id'].'.csv');
print $s;
$s = "\xEF\xBB\xBF". "Completed;Priority;Task;Notes;Tags;Due;DateCreated;DateCompleted\n";
foreach($data as $r)
{
$s .= ($r['compl']?'1':'0'). ';'.
$r['prio']. ';'. escape_csv($r['title']). ';'.
escape_csv($r['note']). ';'.
escape_csv($r['tags']). ';'.
$r['duedate']. ';'.
date('Y-m-d H:i:s O',$r['d_created']). ';'.
($r['d_completed'] ? date('Y-m-d H:i:s O',$r['d_completed']) :''). "\n";
}
header('Content-type: text/csv; charset=utf-8');
header('Content-disposition: attachment; filename=list_'.(int)$listData['id'].'.csv');
print $s;
}
function escape_csv($v)
{
//escape formulas
$nf = '';
$trimmed = ltrim($v);
$trimmed = ltrim($v);
if (strlen($trimmed) > 0 && in_array(substr($trimmed, 0, 1), array('=', '+', '-', '@'))) {
$nf = "'";
}
return '"'. $nf. str_replace('"', '""', $v). '"';
return '"'. $nf. str_replace('"', '""', $v). '"';
}
function printICal($listData, $data)
{
$mttToIcalPrio = array("1" => 5, "2" => 1);
$s = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nMETHOD:PUBLISH\r\nCALSCALE:GREGORIAN\r\nPRODID:-//myTinyTodo//iCalendar Export v1.4//EN\r\n".
"X-WR-CALNAME:". $listData['name']. "\r\nX-MTT-TIMEZONE:".Config::get('timezone')."\r\n";
# to-do
foreach($data as $r)
{
$a = array();
$a[] = "BEGIN:VTODO";
$a[] = "UID:". $r['uuid'];
$a[] = "CREATED:". gmdate('Ymd\THis\Z', $r['d_created']);
$a[] = "DTSTAMP:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = "LAST-MODIFIED:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = utf8chunks("SUMMARY:". $r['title']);
if($r['duedate']) {
$dda = explode('-', $r['duedate']);
$a[] = "DUE;VALUE=DATE:".sprintf("%u%02u%02u", $dda[0], $dda[1], $dda[2]);
}
# Apple's iCal priorities: low-9, medium-5, high-1
if($r['prio'] > 0 && isset($mttToIcalPrio[$r['prio']])) $a[] = "PRIORITY:". $mttToIcalPrio[$r['prio']];
$a[] = "X-MTT-PRIORITY:". $r['prio'];
$mttToIcalPrio = array("1" => 5, "2" => 1);
$s = "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nMETHOD:PUBLISH\r\nCALSCALE:GREGORIAN\r\nPRODID:-//myTinyTodo//iCalendar Export v1.4//EN\r\n".
"X-WR-CALNAME:". $listData['name']. "\r\nX-MTT-TIMEZONE:".Config::get('timezone')."\r\n";
# to-do
foreach($data as $r)
{
$a = array();
$a[] = "BEGIN:VTODO";
$a[] = "UID:". $r['uuid'];
$a[] = "CREATED:". gmdate('Ymd\THis\Z', $r['d_created']);
$a[] = "DTSTAMP:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = "LAST-MODIFIED:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = utf8chunks("SUMMARY:". $r['title']);
if($r['duedate']) {
$dda = explode('-', $r['duedate']);
$a[] = "DUE;VALUE=DATE:".sprintf("%u%02u%02u", $dda[0], $dda[1], $dda[2]);
}
# Apple's iCal priorities: low-9, medium-5, high-1
if($r['prio'] > 0 && isset($mttToIcalPrio[$r['prio']])) $a[] = "PRIORITY:". $mttToIcalPrio[$r['prio']];
$a[] = "X-MTT-PRIORITY:". $r['prio'];
$descr = array();
if($r['tags'] != '') $descr[] = Lang::instance()->get('tags'). ": ". str_replace(',', ', ', $r['tags']);
if($r['note'] != '') $descr[] = Lang::instance()->get('note'). ": ". $r['note'];
if($descr) $a[] = utf8chunks("DESCRIPTION:". str_replace("\n", '\\n', implode("\n",$descr)));
$descr = array();
if($r['tags'] != '') $descr[] = Lang::instance()->get('tags'). ": ". str_replace(',', ', ', $r['tags']);
if($r['note'] != '') $descr[] = Lang::instance()->get('note'). ": ". $r['note'];
if($descr) $a[] = utf8chunks("DESCRIPTION:". str_replace("\n", '\\n', implode("\n",$descr)));
if($r['compl']) {
$a[] = "STATUS:COMPLETED"; #used in Sunbird
$a[] = "COMPLETED:". gmdate('Ymd\THis\Z', $r['d_completed']);
#$a[] = "PERCENT-COMPLETE:100"; #used in Sunbird
}
if($r['tags'] != '') $a[] = utf8chunks("X-MTT-TAGS:". $r['tags']);
$a[] = "END:VTODO\r\n";
$s .= implode("\r\n", $a);
}
# events
foreach($data as $r)
{
if(!$r['duedate'] || $r['compl']) continue; # skip tasks completed and without duedate
$a = array();
$a[] = "BEGIN:VEVENT";
$a[] = "UID:_". $r['uuid']; # do not duplicate VTODO UID
$a[] = "CREATED:". gmdate('Ymd\THis\Z', $r['d_created']);
$a[] = "DTSTAMP:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = "LAST-MODIFIED:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = utf8chunks("SUMMARY:". $r['title']);
if($r['prio'] > 0 && isset($mttToIcalPrio[$r['prio']])) $a[] = "PRIORITY:". $mttToIcalPrio[$r['prio']];
$dda = explode('-', $r['duedate']);
$a[] = "DTSTART;VALUE=DATE:".sprintf("%u%02u%02u", $dda[0], $dda[1], $dda[2]);
$a[] = "DTEND;VALUE=DATE:".date('Ymd', mktime(1,1,1,$dda[1],$dda[2],$dda[0]) + 86400);
$descr = array();
if($r['tags'] != '') $descr[] = Lang::instance()->get('tags'). ": ". str_replace(',', ', ', $r['tags']);
if($r['note'] != '') $descr[] = Lang::instance()->get('note'). ": ". $r['note'];
if($descr) $a[] = utf8chunks("DESCRIPTION:". str_replace("\n", '\\n', implode("\n",$descr)));
$a[] = "END:VEVENT\r\n";
$s .= implode("\r\n", $a);
}
$s .= "END:VCALENDAR\r\n";
header('Content-type: text/calendar; charset=utf-8');
header('Content-disposition: attachment; filename=list_'.(int)$listData['id'].'.ics');
print $s;
if($r['compl']) {
$a[] = "STATUS:COMPLETED"; #used in Sunbird
$a[] = "COMPLETED:". gmdate('Ymd\THis\Z', $r['d_completed']);
#$a[] = "PERCENT-COMPLETE:100"; #used in Sunbird
}
if($r['tags'] != '') $a[] = utf8chunks("X-MTT-TAGS:". $r['tags']);
$a[] = "END:VTODO\r\n";
$s .= implode("\r\n", $a);
}
# events
foreach($data as $r)
{
if(!$r['duedate'] || $r['compl']) continue; # skip tasks completed and without duedate
$a = array();
$a[] = "BEGIN:VEVENT";
$a[] = "UID:_". $r['uuid']; # do not duplicate VTODO UID
$a[] = "CREATED:". gmdate('Ymd\THis\Z', $r['d_created']);
$a[] = "DTSTAMP:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = "LAST-MODIFIED:". gmdate('Ymd\THis\Z', $r['d_edited']);
$a[] = utf8chunks("SUMMARY:". $r['title']);
if($r['prio'] > 0 && isset($mttToIcalPrio[$r['prio']])) $a[] = "PRIORITY:". $mttToIcalPrio[$r['prio']];
$dda = explode('-', $r['duedate']);
$a[] = "DTSTART;VALUE=DATE:".sprintf("%u%02u%02u", $dda[0], $dda[1], $dda[2]);
$a[] = "DTEND;VALUE=DATE:".date('Ymd', mktime(1,1,1,$dda[1],$dda[2],$dda[0]) + 86400);
$descr = array();
if($r['tags'] != '') $descr[] = Lang::instance()->get('tags'). ": ". str_replace(',', ', ', $r['tags']);
if($r['note'] != '') $descr[] = Lang::instance()->get('note'). ": ". $r['note'];
if($descr) $a[] = utf8chunks("DESCRIPTION:". str_replace("\n", '\\n', implode("\n",$descr)));
$a[] = "END:VEVENT\r\n";
$s .= implode("\r\n", $a);
}
$s .= "END:VCALENDAR\r\n";
header('Content-type: text/calendar; charset=utf-8');
header('Content-disposition: attachment; filename=list_'.(int)$listData['id'].'.ics');
print $s;
}
function utf8chunks($text, $chunklen=75, $delimiter="\r\n\t")
{
if($text == '') return '';
preg_match_all('/./u', $text, $m);
$chars = $m[0];
$a = array();
$s = '';
$max = count($chars);
for($i=0; $i<$max; $i++)
{
$ch = $chars[$i];
if(strlen($s) + strlen($ch) > $chunklen) { # line should be not more than $chunklen bytes
$a[] = $s;
$s = $ch;
}
else $s .= $ch;
}
if($s != '') $a[] = $s;
return implode($delimiter, $a);
if($text == '') return '';
preg_match_all('/./u', $text, $m);
$chars = $m[0];
$a = array();
$s = '';
$max = count($chars);
for($i=0; $i<$max; $i++)
{
$ch = $chars[$i];
if(strlen($s) + strlen($ch) > $chunklen) { # line should be not more than $chunklen bytes
$a[] = $s;
$s = $ch;
}
else $s .= $ch;
}
if($s != '') $a[] = $s;
return implode($delimiter, $a);
}
?>

View file

@ -1,9 +1,9 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2020-2021 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2020-2021 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
$dontStartSession = 1;
@ -16,10 +16,10 @@ $listId = (int)_get('list');
$listData = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$listId");
if (need_auth() && (!$listData || !$listData['published'])) {
die("Access denied!<br> List is not published.");
die("Access denied!<br> List is not published.");
}
if(!$listData) {
die("No list found");
die("No list found");
}
$data = array();
@ -27,26 +27,26 @@ $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', 'AND compl=1' );
}
elseif($feedType == 'modified') {
$listData['_feed_descr'] = $lang->get('feed_modified_tasks');
fillData( $data, $listId, 'd_edited', '' );
$listData['_feed_descr'] = $lang->get('feed_modified_tasks');
fillData( $data, $listId, 'd_edited', '' );
}
elseif($feedType == 'current') {
$listData['_feed_descr'] = $lang->get('feed_new_tasks');
fillData( $data, $listId, 'd_created', 'AND compl=0' );
$listData['_feed_descr'] = $lang->get('feed_new_tasks');
fillData( $data, $listId, 'd_created', 'AND 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' );
$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' );
}
else {
$listData['_feed_descr'] = $lang->get('feed_new_tasks');
$feedType = 'tasks';
fillData( $data, $listId, 'd_created', '' );
$listData['_feed_descr'] = $lang->get('feed_new_tasks');
$feedType = 'tasks';
fillData( $data, $listId, 'd_created', '' );
}
$listData['_feed_title'] = sprintf($lang->get('feed_title'), $listData['name']) . ' - '. $listData['_feed_descr'];
@ -59,86 +59,86 @@ printRss($data, $listData);
function fillData(&$data, $listId, $field, $sqlWhere )
{
$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())
{
if ($r['prio'] > 0) {
$r['prio'] = '+'.$r['prio'];
}
$a = array();
$a[] = $lang->get('task'). ": ". $r['title'];
if ($r['prio']) {
$a[] = $lang->get('priority'). ": $r[prio]";
}
if ($r['duedate'] != '') {
$ad = explode('-', $r['duedate']);
$a[] = $lang->get('due'). ": ".formatDate3(Config::get('dateformat'), (int)$ad[0], (int)$ad[1], (int)$ad[2], $lang);
}
if ($r['tags'] != '') {
$a[] = $lang->get('tags'). ": ". str_replace(',', ', ', $r['tags']);
}
if ($r['compl']) {
$a[] = $lang->get('taskdate_completed'). ": ". timestampToDatetime($r['d_completed']);
}
$r['title'] = htmlspecialchars( $r['title'] );
$r['note'] = noteMarkup($r['note'], true);
$r['_descr'] = implode("<br/>", htmlarray($a)). "<br/><br/>". $r['note'];
$r['_title'] = "#". (int)$r['id']. ": ". $r['title'];
$r['_d'] = gmdate('r', $r[$field]);
$r['_field'] = $field;
$data[] = $r;
}
$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())
{
if ($r['prio'] > 0) {
$r['prio'] = '+'.$r['prio'];
}
$a = array();
$a[] = $lang->get('task'). ": ". $r['title'];
if ($r['prio']) {
$a[] = $lang->get('priority'). ": $r[prio]";
}
if ($r['duedate'] != '') {
$ad = explode('-', $r['duedate']);
$a[] = $lang->get('due'). ": ".formatDate3(Config::get('dateformat'), (int)$ad[0], (int)$ad[1], (int)$ad[2], $lang);
}
if ($r['tags'] != '') {
$a[] = $lang->get('tags'). ": ". str_replace(',', ', ', $r['tags']);
}
if ($r['compl']) {
$a[] = $lang->get('taskdate_completed'). ": ". timestampToDatetime($r['d_completed']);
}
$r['title'] = htmlspecialchars( $r['title'] );
$r['note'] = noteMarkup($r['note'], true);
$r['_descr'] = implode("<br/>", htmlarray($a)). "<br/><br/>". $r['note'];
$r['_title'] = "#". (int)$r['id']. ": ". $r['title'];
$r['_d'] = gmdate('r', $r[$field]);
$r['_field'] = $field;
$data[] = $r;
}
}
function printRss($data, $listData)
{
$lang = Lang::instance();
$link = get_mttinfo('url'). "?list=". (int)$listData['id'];
$buildDate = gmdate('r');
$lang = Lang::instance();
$link = get_mttinfo('url'). "?list=". (int)$listData['id'];
$buildDate = gmdate('r');
$s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".
"<rss version=\"2.0\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n".
"<channel>\n".
"<title>$listData[_feed_title]</title>\n".
"<link>$link</link>\n".
"<atom:link href=\"${listData['_feed_link']}\" rel=\"self\" type=\"application/rss+xml\"/>\n".
"<description>$listData[_feed_descr]</description>\n".
"<lastBuildDate>$buildDate</lastBuildDate>\n\n";
$s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".
"<rss version=\"2.0\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n".
"<channel>\n".
"<title>$listData[_feed_title]</title>\n".
"<link>$link</link>\n".
"<atom:link href=\"${listData['_feed_link']}\" rel=\"self\" type=\"application/rss+xml\"/>\n".
"<description>$listData[_feed_descr]</description>\n".
"<lastBuildDate>$buildDate</lastBuildDate>\n\n";
foreach($data as $v)
{
$guid = $listData['_feed_type']. '-'. $listData['id']. '-'. $v['id']. '-'. $v[$v['_field']];
$itemLink = $link. "&amp;task=". (int)$v['id'];
foreach($data as $v)
{
$guid = $listData['_feed_type']. '-'. $listData['id']. '-'. $v['id']. '-'. $v[$v['_field']];
$itemLink = $link. "&amp;task=". (int)$v['id'];
$status = '';
if ( $listData['_feed_type'] == 'status' ) {
if ( $v['_field'] == 'd_created' ) {
$status = $lang->get('feed_status_new');
}
elseif ( $v['_field'] == 'd_edited' ) {
$status = $lang->get('feed_status_updated');
}
elseif ( $v['_field'] == 'd_completed' ) {
$status = $lang->get('feed_status_completed');
}
}
if ( $status !='' ) $status = "[$status] ";
$status = '';
if ( $listData['_feed_type'] == 'status' ) {
if ( $v['_field'] == 'd_created' ) {
$status = $lang->get('feed_status_new');
}
elseif ( $v['_field'] == 'd_edited' ) {
$status = $lang->get('feed_status_updated');
}
elseif ( $v['_field'] == 'd_completed' ) {
$status = $lang->get('feed_status_completed');
}
}
if ( $status !='' ) $status = "[$status] ";
$s .= "<item>\n".
"<title>". $status. $v['title']. "</title>\n".
"<link>". $itemLink. "</link>\n".
"<pubDate>". $v['_d']. "</pubDate>\n".
"<description><![CDATA[". $v['_descr']. "]]></description>\n".
"<guid isPermaLink=\"false\">$guid</guid>\n".
"</item>\n\n";
}
$s .= "<item>\n".
"<title>". $status. $v['title']. "</title>\n".
"<link>". $itemLink. "</link>\n".
"<pubDate>". $v['_d']. "</pubDate>\n".
"<description><![CDATA[". $v['_descr']. "]]></description>\n".
"<guid isPermaLink=\"false\">$guid</guid>\n".
"</item>\n\n";
}
$s .= "</channel>\n</rss>";
$s .= "</channel>\n</rss>";
header("Content-type: text/xml; charset=utf-8");
print $s;
header("Content-type: text/xml; charset=utf-8");
print $s;
}
?>

View file

@ -1,309 +1,309 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
class Config
{
/** @var bool */
public static $noDatabase = false;
/** @var bool */
public static $noDatabase = false;
/** @var array[] */
private static $dbparams = array(
# Database type: sqlite or mysql
'db.type' => array('default'=>'sqlite', 'type'=>'s'),
/** @var array[] */
private static $dbparams = array(
# Database type: sqlite or mysql
'db.type' => array('default'=>'sqlite', 'type'=>'s'),
# Specific database api
'db.driver' => array('default'=>'', 'type'=>'s'),
# Specific database api
'db.driver' => array('default'=>'', 'type'=>'s'),
# Mysql connection settings
'db.host' => array('default'=>'localhost', 'type'=>'s'),
'db.user' => array('default'=>'mtt', 'type'=>'s'),
'db.password' => array('default'=>'mtt', 'type'=>'s'),
'db.name' => array('default'=>'mytinytodo', 'type'=>'s'),
# Mysql connection settings
'db.host' => array('default'=>'localhost', 'type'=>'s'),
'db.user' => array('default'=>'mtt', 'type'=>'s'),
'db.password' => array('default'=>'mtt', 'type'=>'s'),
'db.name' => array('default'=>'mytinytodo', 'type'=>'s'),
# Prefix for table names
'db.prefix' => array('default'=>'', 'type'=>'s')
);
# Prefix for table names
'db.prefix' => array('default'=>'', 'type'=>'s')
);
/** @var array[] */
private static $convert = array(
'mysql.host' => 'db.host',
'mysql.user' => 'db.user',
'mysql.password' => 'db.password',
'mysql.db' => 'db.name',
'db' => 'db.type',
'prefix' => 'db.prefix'
);
/** @var array[] */
private static $convert = array(
'mysql.host' => 'db.host',
'mysql.user' => 'db.user',
'mysql.password' => 'db.password',
'mysql.db' => 'db.name',
'db' => 'db.type',
'prefix' => 'db.prefix'
);
/** @var array[] */
public static $params = array(
# These two parameters are used when mytinytodo index.php called not from installation directory
# 'url' - URL where index.php is called from (ex.: http://site.com/todo.php)
# 'mtt_url' - directory URL where mytinytodo is installed (with trailing slash) (ex.: http://site.com/lib/mytinytodo/)
'url' => array('default'=>'', 'type'=>'s'),
'mtt_url' => array('default'=>'', 'type'=>'s'),
/** @var array[] */
public static $params = array(
# These two parameters are used when mytinytodo index.php called not from installation directory
# 'url' - URL where index.php is called from (ex.: http://site.com/todo.php)
# 'mtt_url' - directory URL where mytinytodo is installed (with trailing slash) (ex.: http://site.com/lib/mytinytodo/)
'url' => array('default'=>'', 'type'=>'s'),
'mtt_url' => array('default'=>'', 'type'=>'s'),
# Top title
'title' => array('default'=>'', 'type'=>'s'),
# Top title
'title' => array('default'=>'', 'type'=>'s'),
# Language pack
'lang' => array('default'=>'en', 'type'=>'s'),
# Language pack
'lang' => array('default'=>'en', 'type'=>'s'),
# Password to protect your tasks from modification,
# leave empty that everyone could read/write todolist
'password' => array('default'=>'', 'type'=>'s'),
# Password to protect your tasks from modification,
# leave empty that everyone could read/write todolist
'password' => array('default'=>'', 'type'=>'s'),
# Smart Syntax enabled flag
'smartsyntax' => array('default'=>1, 'type'=>'i'),
# Smart Syntax enabled flag
'smartsyntax' => array('default'=>1, 'type'=>'i'),
# Default Time zone
'timezone' => array('default'=>'UTC', 'type'=>'s'),
# Default Time zone
'timezone' => array('default'=>'UTC', 'type'=>'s'),
# To disable auto adding selected tag set value to 0
'autotag' => array('default'=>1, 'type'=>'i'),
# To disable auto adding selected tag set value to 0
'autotag' => array('default'=>1, 'type'=>'i'),
# duedate calendar format: 1 => y-m-d (default), 2 => m/d/y, 3 => d.m.y
'duedateformat' => array('default'=>1, 'type'=>'i'),
# duedate calendar format: 1 => y-m-d (default), 2 => m/d/y, 3 => d.m.y
'duedateformat' => array('default'=>1, 'type'=>'i'),
# First day of week: 0-Sunday, 1-Monday, 2-Tuesday, .. 6-Saturday
'firstdayofweek' => array('default'=>1, 'type'=>'i', 'options'=>array(0,1,2,3,4,5,6)),
# First day of week: 0-Sunday, 1-Monday, 2-Tuesday, .. 6-Saturday
'firstdayofweek' => array('default'=>1, 'type'=>'i', 'options'=>array(0,1,2,3,4,5,6)),
# Date/time formats
'clock' => array('default'=>24, 'type'=>'i', 'options'=>array(12,24)),
'dateformat' => array('default'=>'j M Y', 'type'=>'s'),
'dateformat2' => array('default'=>'n/j/y', 'type'=>'s'),
'dateformatshort' => array('default'=>'j M', 'type'=>'s'),
'template' => array('default'=>'default', 'type'=>'s'),
# Date/time formats
'clock' => array('default'=>24, 'type'=>'i', 'options'=>array(12,24)),
'dateformat' => array('default'=>'j M Y', 'type'=>'s'),
'dateformat2' => array('default'=>'n/j/y', 'type'=>'s'),
'dateformatshort' => array('default'=>'j M', 'type'=>'s'),
'template' => array('default'=>'default', 'type'=>'s'),
# Show task date in list
'showdate' => array('default'=>0, 'type'=>'i'),
# Show task date in list
'showdate' => array('default'=>0, 'type'=>'i'),
# Use Markdown syntax for notes. Set to 'v1' to use old v1.6 syntax.
'markup' => array('default'=>'markdown', 'type'=>'s'),
);
# Use Markdown syntax for notes. Set to 'v1' to use old v1.6 syntax.
'markup' => array('default'=>'markdown', 'type'=>'s'),
);
/** @var mixed[] */
private static $config = array();
/** @var mixed[] */
private static $config = array();
/**
*
* @param mixed[] $config
* @return void
*/
public static function loadConfigV14(array $config)
{
foreach ($config as $key => $val) {
if (isset(self::$convert[$key])) {
$key = self::$convert[$key];
}
elseif ($key == 'mysqli' && (int)$val != 0) {
$key = 'db.driver';
$val = 'mysqli';
}
elseif ($key == 'password' && $val != '') {
$val = passwordHash($val); // in v1.7 password is hashed
}
// if (!isset(self::$dbparams[$key])) {
// throw new Exception("Unknown key: $key");
// }
self::$config[$key] = $val;
}
}
/**
*
* @param mixed[] $config
* @return void
*/
public static function loadConfigV14(array $config)
{
foreach ($config as $key => $val) {
if (isset(self::$convert[$key])) {
$key = self::$convert[$key];
}
elseif ($key == 'mysqli' && (int)$val != 0) {
$key = 'db.driver';
$val = 'mysqli';
}
elseif ($key == 'password' && $val != '') {
$val = passwordHash($val); // in v1.7 password is hashed
}
// if (!isset(self::$dbparams[$key])) {
// throw new Exception("Unknown key: $key");
// }
self::$config[$key] = $val;
}
}
/**
*
* @return void
* @throws Exception
*/
public static function load()
{
if (self::$noDatabase) {
return;
}
$j = self::requestDefaultDomain();
foreach ($j as $key=>$val) {
// Ignore params for database config
if ( !isset(self::$dbparams[$key]) ) {
self::$config[$key] = $val;
}
}
}
/**
*
* @return void
* @throws Exception
*/
public static function load()
{
if (self::$noDatabase) {
return;
}
$j = self::requestDefaultDomain();
foreach ($j as $key=>$val) {
// Ignore params for database config
if ( !isset(self::$dbparams[$key]) ) {
self::$config[$key] = $val;
}
}
}
/**
*
* @param string $key
* @return mixed
*/
public static function get($key)
{
if (isset(self::$config[$key])) return self::$config[$key];
elseif (isset(self::$params[$key])) return self::$params[$key]['default'];
elseif (isset(self::$dbparams[$key])) return self::$dbparams[$key]['default'];
else return null;
}
/**
*
* @param string $key
* @return mixed
*/
public static function get($key)
{
if (isset(self::$config[$key])) return self::$config[$key];
elseif (isset(self::$params[$key])) return self::$params[$key]['default'];
elseif (isset(self::$dbparams[$key])) return self::$dbparams[$key]['default'];
else return null;
}
/**
*
* @param string $key
* @return string|null
*/
public static function getUrl($key)
{
$url = '';
if ( isset(self::$config[$key]) ) $url = self::$config[$key];
else if( isset(self::$params[$key]) ) $url = self::$params[$key]['default'];
else return null;
return str_replace( ["\r","\n"], '', $url );
}
/**
*
* @param string $key
* @return string|null
*/
public static function getUrl($key)
{
$url = '';
if ( isset(self::$config[$key]) ) $url = self::$config[$key];
else if( isset(self::$params[$key]) ) $url = self::$params[$key]['default'];
else return null;
return str_replace( ["\r","\n"], '', $url );
}
/**
*
* @param string $key
* @param mixed $value
* @return void
* @throws Exception
*/
public static function set($key, $value)
{
if ($key == "db.prefix" && $value != "" && !preg_match("/^[a-zA-Z0-9_]+$/", $value)) {
throw new Exception("Incorrect table prefix. Can contain only latin letters, digits and underscore character.");
}
self::$config[$key] = $value;
}
/**
*
* @param string $key
* @param mixed $value
* @return void
* @throws Exception
*/
public static function set($key, $value)
{
if ($key == "db.prefix" && $value != "" && !preg_match("/^[a-zA-Z0-9_]+$/", $value)) {
throw new Exception("Incorrect table prefix. Can contain only latin letters, digits and underscore character.");
}
self::$config[$key] = $value;
}
/**
*
* @return void
* @throws Exception
*/
public static function save()
{
$j = array();
foreach (self::$params as $param => $v)
{
if ( !isset(self::$config[$param]) ) $val = $v['default'];
elseif ( isset($v['options']) && !in_array(self::$config[$param], $v['options'])) $val = $v['default'];
else $val = self::$config[$param];
/**
*
* @return void
* @throws Exception
*/
public static function save()
{
$j = array();
foreach (self::$params as $param => $v)
{
if ( !isset(self::$config[$param]) ) $val = $v['default'];
elseif ( isset($v['options']) && !in_array(self::$config[$param], $v['options'])) $val = $v['default'];
else $val = self::$config[$param];
if ($v['type']=='i') $val = (int)$val;
else $val = strval($val);
if ($v['type']=='i') $val = (int)$val;
else $val = strval($val);
$j[$param] = $val;
}
self::saveDomain('config.json', $j);
}
$j[$param] = $val;
}
self::saveDomain('config.json', $j);
}
/**
*
* @param string $key
* @return array
* @throws Exception
*/
public static function requestDomain(string $key)
{
$db = DBConnection::instance();
$json = $db->sq("SELECT param_value FROM {$db->prefix}settings WHERE param_key = ?", array($key));
if (!$json) return array();
$j = json_decode($json, true);
return $j;
}
/**
*
* @param string $key
* @return array
* @throws Exception
*/
public static function requestDomain(string $key)
{
$db = DBConnection::instance();
$json = $db->sq("SELECT param_value FROM {$db->prefix}settings WHERE param_key = ?", array($key));
if (!$json) return array();
$j = json_decode($json, true);
return $j;
}
/**
*
* @return array
* @throws Exception
*/
public static function requestDefaultDomain()
{
return self::requestDomain('config.json');
}
/**
*
* @return array
* @throws Exception
*/
public static function requestDefaultDomain()
{
return self::requestDomain('config.json');
}
/**
*
* @param string $key
* @param array $array
* @return void
* @throws Exception
*/
public static function saveDomain($key, $array)
{
$json = json_encode($array, JSON_PRETTY_PRINT);
$db = DBConnection::instance();
$keyExists = $db->sq("SELECT COUNT(param_key) FROM {$db->prefix}settings WHERE param_key = ?", array($key) );
if ($keyExists) {
$db->ex("UPDATE {$db->prefix}settings SET param_value = ? WHERE param_key = ?", array($json,$key) );
}
else {
$db->ex("INSERT INTO {$db->prefix}settings (param_key,param_value) VALUES (?,?)", array($key,$json) );
}
}
/**
*
* @param string $key
* @param array $array
* @return void
* @throws Exception
*/
public static function saveDomain($key, $array)
{
$json = json_encode($array, JSON_PRETTY_PRINT);
$db = DBConnection::instance();
$keyExists = $db->sq("SELECT COUNT(param_key) FROM {$db->prefix}settings WHERE param_key = ?", array($key) );
if ($keyExists) {
$db->ex("UPDATE {$db->prefix}settings SET param_value = ? WHERE param_key = ?", array($json,$key) );
}
else {
$db->ex("INSERT INTO {$db->prefix}settings (param_key,param_value) VALUES (?,?)", array($key,$json) );
}
}
public static function defineDbConstants()
{
define("MTT_DB_TYPE", self::get('db.type'));
define("MTT_DB_HOST", self::get('db.host'));
define("MTT_DB_USER", self::get('db.user'));
define("MTT_DB_PASSWORD", self::get('db.password'));
define("MTT_DB_NAME", self::get('db.name'));
define("MTT_DB_PREFIX", self::get('db.prefix'));
if ( self::get('db.driver') != '' ) {
define("MTT_DB_DRIVER", self::get('db.driver'));
}
}
public static function defineDbConstants()
{
define("MTT_DB_TYPE", self::get('db.type'));
define("MTT_DB_HOST", self::get('db.host'));
define("MTT_DB_USER", self::get('db.user'));
define("MTT_DB_PASSWORD", self::get('db.password'));
define("MTT_DB_NAME", self::get('db.name'));
define("MTT_DB_PREFIX", self::get('db.prefix'));
if ( self::get('db.driver') != '' ) {
define("MTT_DB_DRIVER", self::get('db.driver'));
}
}
public static function dbConfigAsFileContents(): string
{
$a = array();
$a[] = "<?php\n";
$a[] = "// myTinyTodo Database connection configuration\n";
$a[] = self::prepareDbDefine("MTT_DB_TYPE", self::get('db.type')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_HOST", self::get('db.host')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_USER", self::get('db.user')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_PASSWORD", self::get('db.password')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_NAME", self::get('db.name')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_PREFIX", self::get('db.prefix')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_DRIVER", self::get('db.driver')) . "\n";
$a[] = self::prepareDbDefine("MTT_SALT", defined('MTT_SALT') ? MTT_SALT : generateUUID()) . "\n";
return implode("\n", $a);
}
public static function dbConfigAsFileContents(): string
{
$a = array();
$a[] = "<?php\n";
$a[] = "// myTinyTodo Database connection configuration\n";
$a[] = self::prepareDbDefine("MTT_DB_TYPE", self::get('db.type')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_HOST", self::get('db.host')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_USER", self::get('db.user')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_PASSWORD", self::get('db.password')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_NAME", self::get('db.name')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_PREFIX", self::get('db.prefix')) . "\n";
$a[] = self::prepareDbDefine("MTT_DB_DRIVER", self::get('db.driver')) . "\n";
$a[] = self::prepareDbDefine("MTT_SALT", defined('MTT_SALT') ? MTT_SALT : generateUUID()) . "\n";
return implode("\n", $a);
}
private static function prepareDbDefine(string $key, string $value): string
{
if (!preg_match("/^[a-zA-Z0-9_]+$/", $key)) {
throw new Exception("Unexpected constant name: ". $key);
}
if (preg_match('~\R~', $value)) {
throw new Exception("Unexpected constant value: ". $value);
}
return "define(\"$key\", \"". str_replace(
array("\\", "'", "\""),
array("\\\\", "\\'", "\\\""),
$value )
. "\");";
}
private static function prepareDbDefine(string $key, string $value): string
{
if (!preg_match("/^[a-zA-Z0-9_]+$/", $key)) {
throw new Exception("Unexpected constant name: ". $key);
}
if (preg_match('~\R~', $value)) {
throw new Exception("Unexpected constant value: ". $value);
}
return "define(\"$key\", \"". str_replace(
array("\\", "'", "\""),
array("\\\\", "\\'", "\\\""),
$value )
. "\");";
}
public static function saveDbConfig()
{
$contents = self::dbConfigAsFileContents();
$f = fopen(MTTPATH. 'config.php', 'w');
if ($f === false) throw new Exception("Error while saving config file");
fwrite($f, $contents);
fclose($f);
public static function saveDbConfig()
{
$contents = self::dbConfigAsFileContents();
$f = fopen(MTTPATH. 'config.php', 'w');
if ($f === false) throw new Exception("Error while saving config file");
fwrite($f, $contents);
fclose($f);
//Reset Zend OPcache
//opcache_get_status() sometimes crashes
if (function_exists("opcache_invalidate") && 0 != (int)opcache_get_configuration()["directives"]["opcache.enable"]) {
opcache_invalidate(MTTPATH. 'config.php', true);
}
}
//Reset Zend OPcache
//opcache_get_status() sometimes crashes
if (function_exists("opcache_invalidate") && 0 != (int)opcache_get_configuration()["directives"]["opcache.enable"]) {
opcache_invalidate(MTTPATH. 'config.php', true);
}
}
}
?>

View file

@ -1,192 +1,192 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
// ---------------------------------------------------------------------------- //
class DatabaseResult_Mysql extends DatabaseResult_Abstract
{
/** @var PDOStatement */
private $q;
/** @var PDOStatement */
private $q;
private $affected;
private $affected;
function __construct($dbh, $query, $resultless = 0)
{
// use with DELETE, INSERT, UPDATE
if ($resultless)
{
$this->affected = $dbh->exec($query); //throws PDOException
}
// SELECT
else
{
$this->q = $dbh->query($query); //throws PDOException
$this->affected = $this->q->rowCount();
}
}
function __construct($dbh, $query, $resultless = 0)
{
// use with DELETE, INSERT, UPDATE
if ($resultless)
{
$this->affected = $dbh->exec($query); //throws PDOException
}
// SELECT
else
{
$this->q = $dbh->query($query); //throws PDOException
$this->affected = $this->q->rowCount();
}
}
function fetchRow()
{
return $this->q->fetch(PDO::FETCH_NUM);
}
function fetchRow()
{
return $this->q->fetch(PDO::FETCH_NUM);
}
function fetchAssoc()
{
return $this->q->fetch(PDO::FETCH_ASSOC);
}
function fetchAssoc()
{
return $this->q->fetch(PDO::FETCH_ASSOC);
}
function rowsAffected()
{
return $this->affected;
}
function rowsAffected()
{
return $this->affected;
}
}
// ---------------------------------------------------------------------------- //
class Database_Mysql extends Database_Abstract
{
/** @var PDO */
private $dbh;
/** @var PDO */
private $dbh;
private $affected = null;
var $lastQuery;
private $dbname;
var $prefix = '';
private $affected = null;
var $lastQuery;
private $dbname;
var $prefix = '';
function __construct()
{
}
function __construct()
{
}
function connect($params)
{
$host = $params['host'];
$user = $params['user'];
$pass = $params['password'];
$db = $params['db'];
$options = array(
PDO::MYSQL_ATTR_FOUND_ROWS => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->dbname = $db;
function connect($params)
{
$host = $params['host'];
$user = $params['user'];
$pass = $params['password'];
$db = $params['db'];
$options = array(
PDO::MYSQL_ATTR_FOUND_ROWS => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->dbname = $db;
$this->dbh = new PDO("mysql:host=$host;dbname=$db", $user, $pass, $options);
return true;
}
$this->dbh = new PDO("mysql:host=$host;dbname=$db", $user, $pass, $options);
return true;
}
/*
Returns single row of SELECT query as indexed array (FETCH_NUM).
Returns single field value if resulting array has only one field.
*/
function sq($query, $p = NULL)
{
$q = $this->_dq($query, $p);
/*
Returns single row of SELECT query as indexed array (FETCH_NUM).
Returns single field value if resulting array has only one field.
*/
function sq($query, $p = NULL)
{
$q = $this->_dq($query, $p);
$res = $q->fetchRow();
if ($res === false) return NULL;
$res = $q->fetchRow();
if ($res === false) return NULL;
if (sizeof($res) > 1) return $res;
else return $res[0];
}
if (sizeof($res) > 1) return $res;
else return $res[0];
}
/*
Returns single row of SELECT query as dictionary array (FETCH_ASSOC).
Returns single field value if resulting array has only one field.
*/
function sqa($query, $p = NULL)
{
$q = $this->_dq($query, $p);
/*
Returns single row of SELECT query as dictionary array (FETCH_ASSOC).
Returns single field value if resulting array has only one field.
*/
function sqa($query, $p = NULL)
{
$q = $this->_dq($query, $p);
$res = $q->fetchAssoc();
if ($res === false) return NULL;
$res = $q->fetchAssoc();
if ($res === false) return NULL;
if (sizeof($res) > 1) return $res;
else return $res[0];
}
if (sizeof($res) > 1) return $res;
else return $res[0];
}
function dq($query, $p = NULL) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
}
function dq($query, $p = NULL) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
{
$dbr = $this->_dq($query, $p, true);
return $this->affected();
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
{
$dbr = $this->_dq($query, $p, true);
return $this->affected();
}
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
$m = explode('?', $query);
$m = explode('?', $query);
if (sizeof($p) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i<sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
}
$query .= $m[$i];
}
$this->lastQuery = $query;
$dbr = new DatabaseResult_Mysql($this->dbh, $query, $resultless);
$this->affected = $dbr->rowsAffected();
return $dbr;
}
if (sizeof($p) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i<sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
}
$query .= $m[$i];
}
$this->lastQuery = $query;
$dbr = new DatabaseResult_Mysql($this->dbh, $query, $resultless);
$this->affected = $dbr->rowsAffected();
return $dbr;
}
function affected()
{
return $this->affected;
}
function affected()
{
return $this->affected;
}
function quote($s)
{
return '\''. addslashes($s). '\'';
}
function quote($s)
{
return '\''. addslashes($s). '\'';
}
function quoteForLike($format, $s)
{
$s = str_replace(array('%','_'), array('\%','\_'), addslashes($s));
return '\''. sprintf($format, $s). '\'';
}
function quoteForLike($format, $s)
{
$s = str_replace(array('%','_'), array('\%','\_'), addslashes($s));
return '\''. sprintf($format, $s). '\'';
}
function lastInsertId($name = null)
{
return $this->dbh->lastInsertId();
}
function lastInsertId($name = null)
{
return $this->dbh->lastInsertId();
}
function tableExists($table)
{
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
array($this->dbname, $table) );
if ($r === false || $r === null) return false;
return true;
}
function tableExists($table)
{
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
array($this->dbname, $table) );
if ($r === false || $r === null) return false;
return true;
}
function tableFieldExists($table, $field): bool
{
$table = str_replace('`', '\\`', addslashes($table));
$q = $this->dq("DESCRIBE `$table`");
while ($r = $q->fetchRow()) {
if ($r[0] == $field) return true;
}
return false;
}
function tableFieldExists($table, $field): bool
{
$table = str_replace('`', '\\`', addslashes($table));
$q = $this->dq("DESCRIBE `$table`");
while ($r = $q->fetchRow()) {
if ($r[0] == $field) return true;
}
return false;
}
}
?>

View file

@ -1,157 +1,157 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
// ---------------------------------------------------------------------------- //
class DatabaseResult_Mysql extends DatabaseResult_Abstract
{
/** @var mysqli_result */
private $q;
/** @var mysqli_result */
private $q;
function __construct(mysqli $dbh, $query, $resultless = 0)
{
$this->q = $dbh->query($query); //throws mysqli_sql_exception
}
function __construct(mysqli $dbh, $query, $resultless = 0)
{
$this->q = $dbh->query($query); //throws mysqli_sql_exception
}
function fetchRow()
{
return $this->q->fetch_row();
}
function fetchRow()
{
return $this->q->fetch_row();
}
function fetchAssoc()
{
return $this->q->fetch_assoc();
}
function fetchAssoc()
{
return $this->q->fetch_assoc();
}
}
// ---------------------------------------------------------------------------- //
class Database_Mysql extends Database_Abstract
{
/** @var mysqli */
private $dbh;
/** @var mysqli */
private $dbh;
private $dbname;
var $prefix = '';
private $dbname;
var $prefix = '';
function __construct()
{
// enable throwing exceptions
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
}
function __construct()
{
// enable throwing exceptions
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
}
function connect($params)
{
$host = $params['host'];
$user = $params['user'];
$pass = $params['password'];
$db = $params['db'];
$this->dbname = $db;
$this->dbh = new mysqli($host, $user, $pass, $db); //throws mysqli_sql_exception
return true;
}
function connect($params)
{
$host = $params['host'];
$user = $params['user'];
$pass = $params['password'];
$db = $params['db'];
$this->dbname = $db;
$this->dbh = new mysqli($host, $user, $pass, $db); //throws mysqli_sql_exception
return true;
}
function lastInsertId($name = null)
{
return $this->dbh->insert_id;
}
function lastInsertId($name = null)
{
return $this->dbh->insert_id;
}
function sq($query, $p = NULL)
{
$q = $this->_dq($query, $p);
function sq($query, $p = NULL)
{
$q = $this->_dq($query, $p);
$res = $q->fetchRow();
if ($res === false || $res === null) return NULL;
$res = $q->fetchRow();
if ($res === false || $res === null) return NULL;
if (sizeof($res) > 1) return $res;
else return $res[0];
}
if (sizeof($res) > 1) return $res;
else return $res[0];
}
function sqa($query, $p = NULL)
{
$q = $this->_dq($query, $p);
function sqa($query, $p = NULL)
{
$q = $this->_dq($query, $p);
$res = $q->fetchAssoc();
if ($res === false || $res === null) return NULL;
$res = $q->fetchAssoc();
if ($res === false || $res === null) return NULL;
return $res;
}
return $res;
}
function dq($query, $p = NULL) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
}
function dq($query, $p = NULL) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
{
$dbr = $this->_dq($query, $p, 1);
return $this->affected();
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
{
$dbr = $this->_dq($query, $p, 1);
return $this->affected();
}
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
$m = explode('?', $query);
$m = explode('?', $query);
if (sizeof($p) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i < sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
}
$query .= $m[$i];
}
$this->lastQuery = $query;
return new DatabaseResult_Mysql($this->dbh, $query, $resultless);
}
if (sizeof($p) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i < sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
}
$query .= $m[$i];
}
$this->lastQuery = $query;
return new DatabaseResult_Mysql($this->dbh, $query, $resultless);
}
function affected()
{
return $this->dbh->affected_rows;
}
function affected()
{
return $this->dbh->affected_rows;
}
function quote($s)
{
return '\''. addslashes($s). '\'';
}
function quote($s)
{
return '\''. addslashes($s). '\'';
}
function quoteForLike($format, $s)
{
$s = str_replace(array('%','_'), array('\%','\_'), addslashes($s));
return '\''. sprintf($format, $s). '\'';
}
function quoteForLike($format, $s)
{
$s = str_replace(array('%','_'), array('\%','\_'), addslashes($s));
return '\''. sprintf($format, $s). '\'';
}
function tableExists($table)
{
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
array($this->dbname, $table) );
if ($r === false || $r === null) return false;
return true;
}
function tableExists($table)
{
$r = $this->sq("SELECT 1 FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?",
array($this->dbname, $table) );
if ($r === false || $r === null) return false;
return true;
}
function tableFieldExists($table, $field): bool
{
$table = str_replace('`', '\\`', addslashes($table));
$q = $this->dq("DESCRIBE `$table`");
while ($r = $q->fetchRow()) {
if ($r[0] == $field) return true;
}
return false;
}
function tableFieldExists($table, $field): bool
{
$table = str_replace('`', '\\`', addslashes($table));
$q = $this->dq("DESCRIBE `$table`");
while ($r = $q->fetchRow()) {
if ($r[0] == $field) return true;
}
return false;
}
}
?>

View file

@ -1,185 +1,185 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009,2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009,2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
class DatabaseResult_Sqlite3 extends DatabaseResult_Abstract
{
/** @var PDOStatement */
private $q;
/** @var PDOStatement */
private $q;
private $affected;
private $affected;
function __construct($dbh, $query, $resultless = 0)
{
// use with DELETE, INSERT, UPDATE
if ($resultless)
{
$this->affected = $dbh->exec($query); //throws PDOException
}
// SELECT
else
{
$this->q = $dbh->query($query); //throws PDOException
$this->affected = $this->q->rowCount();
}
}
function __construct($dbh, $query, $resultless = 0)
{
// use with DELETE, INSERT, UPDATE
if ($resultless)
{
$this->affected = $dbh->exec($query); //throws PDOException
}
// SELECT
else
{
$this->q = $dbh->query($query); //throws PDOException
$this->affected = $this->q->rowCount();
}
}
function fetchRow()
{
return $this->q->fetch(PDO::FETCH_NUM);
}
function fetchRow()
{
return $this->q->fetch(PDO::FETCH_NUM);
}
function fetchAssoc()
{
return $this->q->fetch(PDO::FETCH_ASSOC);
}
function fetchAssoc()
{
return $this->q->fetch(PDO::FETCH_ASSOC);
}
function rowsAffected()
{
return $this->affected;
}
function rowsAffected()
{
return $this->affected;
}
}
class Database_Sqlite3 extends Database_Abstract
{
/** @var PDO */
private $dbh;
/** @var PDO */
private $dbh;
private $affected = null;
var $lastQuery;
var $prefix = '';
private $affected = null;
var $lastQuery;
var $prefix = '';
function __construct()
{
}
function __construct()
{
}
function connect($params)
{
$filename = $params['filename'];
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->dbh = new PDO("sqlite:$filename", null, null, $options); //throws PDOException
return true;
}
function connect($params)
{
$filename = $params['filename'];
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->dbh = new PDO("sqlite:$filename", null, null, $options); //throws PDOException
return true;
}
/*
SELECT queries for single row
*/
function sq($query, $p = NULL)
{
$q = $this->_dq($query, $p);
/*
SELECT queries for single row
*/
function sq($query, $p = NULL)
{
$q = $this->_dq($query, $p);
$res = $q->fetchRow();
if ($res === false) return NULL;
$res = $q->fetchRow();
if ($res === false) return NULL;
if (sizeof($res) > 1) return $res;
else return $res[0];
}
if (sizeof($res) > 1) return $res;
else return $res[0];
}
/*
SELECT queries for single row
*/
function sqa($query, $p = NULL)
{
$q = $this->_dq($query, $p);
/*
SELECT queries for single row
*/
function sqa($query, $p = NULL)
{
$q = $this->_dq($query, $p);
$res = $q->fetchAssoc();
if ($res === false) return NULL;
return $res;
}
$res = $q->fetchAssoc();
if ($res === false) return NULL;
return $res;
}
/*
SELECT queries for multiple rows
*/
function dq($query, $p = NULL) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
}
/*
SELECT queries for multiple rows
*/
function dq($query, $p = NULL) : DatabaseResult_Abstract
{
return $this->_dq($query, $p);
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
{
$dbr = $this->_dq($query, $p, 1);
return $this->affected();
}
/*
for resultless queries like INSERT,UPDATE,DELETE
*/
function ex($query, $p = NULL)
{
$dbr = $this->_dq($query, $p, 1);
return $this->affected();
}
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
private function _dq($query, $p = NULL, $resultless = 0) : DatabaseResult_Abstract
{
if (!isset($p)) $p = array();
elseif (!is_array($p)) $p = array($p);
$m = explode('?', $query);
$m = explode('?', $query);
if (sizeof($p) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i<sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
}
$query .= $m[$i];
}
$this->lastQuery = $query;
$dbr = new DatabaseResult_Sqlite3($this->dbh, $query, $resultless);
$this->affected = $dbr->rowsAffected();
return $dbr;
}
if (sizeof($p) > 0)
{
if (sizeof($m) < sizeof($p)+1) {
throw new Exception("params to set MORE than query params");
}
if (sizeof($m) > sizeof($p)+1) {
throw new Exception("params to set LESS than query params");
}
$query = "";
for ($i=0; $i<sizeof($m)-1; $i++) {
$query .= $m[$i]. (is_null($p[$i]) ? 'NULL' : $this->quote($p[$i]));
}
$query .= $m[$i];
}
$this->lastQuery = $query;
$dbr = new DatabaseResult_Sqlite3($this->dbh, $query, $resultless);
$this->affected = $dbr->rowsAffected();
return $dbr;
}
function affected()
{
return $this->affected;
}
function affected()
{
return $this->affected;
}
function quote($s)
{
return $this->dbh->quote($s);
}
function quote($s)
{
return $this->dbh->quote($s);
}
function quoteForLike($format, $s)
{
$s = str_replace(array('\\','%','_'), array('\\\\','\%','\_'), $s);
return $this->dbh->quote(sprintf($format, $s)). " ESCAPE '\'";
}
function quoteForLike($format, $s)
{
$s = str_replace(array('\\','%','_'), array('\\\\','\%','\_'), $s);
return $this->dbh->quote(sprintf($format, $s)). " ESCAPE '\'";
}
function lastInsertId($name = null)
{
return $this->dbh->lastInsertId();
}
function lastInsertId($name = null)
{
return $this->dbh->lastInsertId();
}
function tableExists($table)
{
$exists = $this->sq("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", $table);
if ($exists == "1") {
return true;
}
$exists = $this->sq("SELECT 1 FROM sqlite_temp_master WHERE type='table' AND name=?", $table);
if ($exists == "1") {
return true;
}
return false;
}
function tableExists($table)
{
$exists = $this->sq("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", $table);
if ($exists == "1") {
return true;
}
$exists = $this->sq("SELECT 1 FROM sqlite_temp_master WHERE type='table' AND name=?", $table);
if ($exists == "1") {
return true;
}
return false;
}
function tableFieldExists($table, $field): bool
{
$q = $this->dq("PRAGMA table_info(". $this->quote($table). ")");
while ($r = $q->fetchRow()) {
if ($r[1] == $field) return true;
}
return false;
}
function tableFieldExists($table, $field): bool
{
$q = $this->dq("PRAGMA table_info(". $this->quote($table). ")");
while ($r = $q->fetchRow()) {
if ($r[1] == $field) return true;
}
return false;
}
}
?>

View file

@ -1,67 +1,67 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2021,2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2021,2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
class DBConnection
{
protected static $instance;
protected static $instance;
public static function init(Database_Abstract $instance) : Database_Abstract
{
self::$instance = $instance;
return $instance;
}
public static function init(Database_Abstract $instance) : Database_Abstract
{
self::$instance = $instance;
return $instance;
}
public static function instance() : Database_Abstract
{
public static function instance() : Database_Abstract
{
if (!isset(self::$instance)) {
throw new Exception("DBConnection is not initialized");
throw new Exception("DBConnection is not initialized");
}
return self::$instance;
}
return self::$instance;
}
public static function setPrefix($prefix)
{
$db = self::instance();
$db->setPrefix($prefix);
}
public static function setPrefix($prefix)
{
$db = self::instance();
$db->setPrefix($prefix);
}
}
abstract class Database_Abstract
{
var $lastQuery = null;
var $prefix = ''; //TODO: make private
abstract function connect($params);
abstract function sq($query, $p = NULL);
abstract function sqa($query, $p = NULL);
abstract function dq($query, $p = NULL) : DatabaseResult_Abstract;
abstract function ex($query, $p = NULL);
abstract function affected();
abstract function quote($s);
abstract function quoteForLike($format, $s);
abstract function lastInsertId($name = null);
abstract function tableExists($table);
abstract function tableFieldExists($table, $field): bool;
var $lastQuery = null;
var $prefix = ''; //TODO: make private
abstract function connect($params);
abstract function sq($query, $p = NULL);
abstract function sqa($query, $p = NULL);
abstract function dq($query, $p = NULL) : DatabaseResult_Abstract;
abstract function ex($query, $p = NULL);
abstract function affected();
abstract function quote($s);
abstract function quoteForLike($format, $s);
abstract function lastInsertId($name = null);
abstract function tableExists($table);
abstract function tableFieldExists($table, $field): bool;
function prefix(): string {
return $this->prefix;
}
function prefix(): string {
return $this->prefix;
}
function setPrefix(string $prefix) {
if ($prefix != '' && !preg_match("/^[a-zA-Z0-9_]+$/", $prefix)) {
throw new Exception("Incorrect table prefix");
}
$this->prefix = $prefix;
}
function setPrefix(string $prefix) {
if ($prefix != '' && !preg_match("/^[a-zA-Z0-9_]+$/", $prefix)) {
throw new Exception("Incorrect table prefix");
}
$this->prefix = $prefix;
}
}
abstract class DatabaseResult_Abstract
{
abstract function fetchRow();
abstract function fetchAssoc();
abstract function fetchRow();
abstract function fetchAssoc();
}

View file

@ -1,9 +1,9 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
@ -11,68 +11,68 @@
class DBCore
{
/** @var Database_Abstract $db */
protected $db;
/** @var Database_Abstract $db */
protected $db;
/** @var DBCore $defaultdb */
protected static $defaultInstance;
/** @var DBCore $defaultdb */
protected static $defaultInstance;
/**
*
* @param Database_Abstract $db Value of DBConnection::instance() or similar
* @return void
*/
public function __construct(Database_Abstract $db) {
$this->db = $db;
}
/**
*
* @param Database_Abstract $db Value of DBConnection::instance() or similar
* @return void
*/
public function __construct(Database_Abstract $db) {
$this->db = $db;
}
/**
*
* @return Database_Abstract
* @throws Exception
*/
public function connection()
{
if (!isset($this->db)) {
throw new Exception("DBConnection is not set");
}
return $this->db;
}
/**
*
* @return DBCore
* @throws Exception
*/
public static function defaultInstance() : DBCore
{
if (!isset(self::$defaultInstance)) {
throw new Exception("DBCore defaultInstance is not initialized");
/**
*
* @return Database_Abstract
* @throws Exception
*/
public function connection()
{
if (!isset($this->db)) {
throw new Exception("DBConnection is not set");
}
return self::$defaultInstance;
}
return $this->db;
}
/**
*
* @param DBCore $instance
* @return void
*/
public static function setDefaultInstance(DBCore $instance)
{
self::$defaultInstance = $instance;
}
/**
*
* @return DBCore
* @throws Exception
*/
public static function defaultInstance() : DBCore
{
if (!isset(self::$defaultInstance)) {
throw new Exception("DBCore defaultInstance is not initialized");
}
return self::$defaultInstance;
}
/**
*
* @param int $id
* @return int
*/
public function getListIdByTaskId(int $id): int
{
$db = $this->db;
$listId = (int)$db->sq("SELECT list_id FROM {$db->prefix}todolist WHERE id=". (int)$id);
return $listId;
}
/**
*
* @param DBCore $instance
* @return void
*/
public static function setDefaultInstance(DBCore $instance)
{
self::$defaultInstance = $instance;
}
/**
*
* @param int $id
* @return int
*/
public function getListIdByTaskId(int $id): int
{
$db = $this->db;
$listId = (int)$db->sq("SELECT list_id FROM {$db->prefix}todolist WHERE id=". (int)$id);
return $listId;
}
}

View file

@ -1,165 +1,165 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
/*
myTinyTodo language class
myTinyTodo language class
*/
class Lang
{
protected static $instance;
protected static $langDir = MTTLANG;
protected $code = 'en';
protected $default = 'en';
protected $strings;
protected static $instance;
protected static $langDir = MTTLANG;
protected $code = 'en';
protected $default = 'en';
protected $strings;
public static function instance()
{
public static function instance()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
return self::$instance;
}
public static function loadLangOrDie($code, $die = 1)
{
$lang = self::instance();
public static function loadLangOrDie($code, $die = 1)
{
$lang = self::instance();
//check if json file exists
if ( self::langExists($code) ) {
$jsonString = file_get_contents( self::$langDir. "{$code}.json" );
$lang->loadJsonString($code, $jsonString);
}
else if ( $die == 0 ) {
//notice?
$lang->code = $lang->default; //sure?
$lang->loadDefaultStrings();
}
else if ( $die == 1 ) {
die("Language file not found (". htmlspecialchars($code). ".json)");
}
}
//check if json file exists
if ( self::langExists($code) ) {
$jsonString = file_get_contents( self::$langDir. "{$code}.json" );
$lang->loadJsonString($code, $jsonString);
}
else if ( $die == 0 ) {
//notice?
$lang->code = $lang->default; //sure?
$lang->loadDefaultStrings();
}
else if ( $die == 1 ) {
die("Language file not found (". htmlspecialchars($code). ".json)");
}
}
public static function loadLang($code)
{
self::loadLangOrDie($code, 0);
}
public static function loadLang($code)
{
self::loadLangOrDie($code, 0);
}
public static function langExists($code)
{
return file_exists(self::$langDir. $code. '.json');
}
public static function langExists($code)
{
return file_exists(self::$langDir. $code. '.json');
}
function loadJsonString($code, $jsonString)
{
$this->code = $code;
$json = json_decode($jsonString, true);
function loadJsonString($code, $jsonString)
{
$this->code = $code;
$json = json_decode($jsonString, true);
//load default language
if ( $code != $this->default ) {
$this->loadDefaultStrings();
$this->strings = array_replace($this->strings, $json);
}
else {
$this->strings = $json;
}
}
//load default language
if ( $code != $this->default ) {
$this->loadDefaultStrings();
$this->strings = array_replace($this->strings, $json);
}
else {
$this->strings = $json;
}
}
function loadDefaultStrings()
{
if ( ! self::langExists($this->default) ) {
die("Default language file not found (". htmlspecialchars($this->default). ".json)");
}
$defStr = file_get_contents($this->langDir(). "{$this->default}.json");
$this->strings = json_decode($defStr, true);
}
function loadDefaultStrings()
{
if ( ! self::langExists($this->default) ) {
die("Default language file not found (". htmlspecialchars($this->default). ".json)");
}
$defStr = file_get_contents($this->langDir(). "{$this->default}.json");
$this->strings = json_decode($defStr, true);
}
function get($key)
{
if ( isset($this->strings[$key]) ) {
return $this->strings[$key];
}
return $key;
}
function get($key)
{
if ( isset($this->strings[$key]) ) {
return $this->strings[$key];
}
return $key;
}
function rtl()
{
if ( isset($this->strings['_rtl']) ) {
return intval($this->strings['_rtl']);
}
return 0;
}
function rtl()
{
if ( isset($this->strings['_rtl']) ) {
return intval($this->strings['_rtl']);
}
return 0;
}
/* minimal number of translated strings to use in js front-end */
function jsStrings()
{
$a = array();
$a['daysMin'] = $this->get('days_min');
$a['daysLong'] = $this->get('days_long');
$a['monthsShort'] = $this->get('months_short');
$a['monthsLong'] = $this->get('months_long');
/* minimal number of translated strings to use in js front-end */
function jsStrings()
{
$a = array();
$a['daysMin'] = $this->get('days_min');
$a['daysLong'] = $this->get('days_long');
$a['monthsShort'] = $this->get('months_short');
$a['monthsLong'] = $this->get('months_long');
$this->fillWithValues($a, [
'confirmDelete',
'confirmLeave',
'actionNoteSave',
'actionNoteCancel',
'error',
'denied',
'listNotFound',
'noPublicLists',
'invalidpass',
'addList',
'addListDefault',
'renameList',
'deleteList',
'clearCompleted',
'settingsSaved',
'tags',
'tasks',
'f_past',
'f_today',
'f_soon',
'alltasks',
'set_header'
]);
$a['_rtl'] = $this->rtl() ? 1 : 0;
$this->fillWithValues($a, [
'confirmDelete',
'confirmLeave',
'actionNoteSave',
'actionNoteCancel',
'error',
'denied',
'listNotFound',
'noPublicLists',
'invalidpass',
'addList',
'addListDefault',
'renameList',
'deleteList',
'clearCompleted',
'settingsSaved',
'tags',
'tasks',
'f_past',
'f_today',
'f_soon',
'alltasks',
'set_header'
]);
$a['_rtl'] = $this->rtl() ? 1 : 0;
return $a;
}
return $a;
}
function makeJS($pretty = 0)
{
$a = $this->jsStrings();
$opts = JSON_UNESCAPED_UNICODE;
if ($pretty) {
$opts |= JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES;
}
return json_encode($a, $opts);
}
function makeJS($pretty = 0)
{
$a = $this->jsStrings();
$opts = JSON_UNESCAPED_UNICODE;
if ($pretty) {
$opts |= JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES;
}
return json_encode($a, $opts);
}
protected function fillWithValues(array &$a, array $keys)
{
foreach ( $keys as $key ) {
$a[$key] = $this->get($key);
}
}
protected function fillWithValues(array &$a, array $keys)
{
foreach ( $keys as $key ) {
$a[$key] = $this->get($key);
}
}
function langDir()
{
return self::$langDir;
}
function langDir()
{
return self::$langDir;
}
function langCode()
{
return $this->code;
}
function langCode()
{
return $this->code;
}
}

View file

@ -1,105 +1,105 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
class MTTSessionHandler implements SessionHandlerInterface
{
/**
* @var Database_Abstract
*/
private $db;
/**
* @var Database_Abstract
*/
private $db;
/**
* @param string $path
* @param string $name
* @return bool
* @throws Exception
*/
public function open($path, $name): bool
{
$this->db = DBConnection::instance();
return true;
}
/**
* @param string $path
* @param string $name
* @return bool
* @throws Exception
*/
public function open($path, $name): bool
{
$this->db = DBConnection::instance();
return true;
}
/** @return bool */
public function close(): bool
/** @return bool */
public function close(): bool
{
return true;
}
/**
* @param string $id
* @return string
* @throws Exception
*/
#[\ReturnTypeWillChange]
public function read($id)
{
// read session data if not expired
$time = time();
$expire = $time;
$r = $this->db->sq("SELECT data,last_access FROM {$this->db->prefix}sessions WHERE id = ? AND expires >= $expire", $id);
if ( is_null($r) ) return '';
/**
* @param string $id
* @return string
* @throws Exception
*/
#[\ReturnTypeWillChange]
public function read($id)
{
// read session data if not expired
$time = time();
$expire = $time;
$r = $this->db->sq("SELECT data,last_access FROM {$this->db->prefix}sessions WHERE id = ? AND expires >= $expire", $id);
if ( is_null($r) ) return '';
// update last access time and set expires in 14 days
// refresh once in a second
if ( $r[1] < $time ) {
$expire = $time + 14 * 86400;
$this->db->ex("UPDATE {$this->db->prefix}sessions SET last_access=?,expires=? WHERE id = ?",
array($time, $expire, $id) );
}
return $r[0];
}
// update last access time and set expires in 14 days
// refresh once in a second
if ( $r[1] < $time ) {
$expire = $time + 14 * 86400;
$this->db->ex("UPDATE {$this->db->prefix}sessions SET last_access=?,expires=? WHERE id = ?",
array($time, $expire, $id) );
}
return $r[0];
}
/**
* @param string $id
* @param string $data
* @return bool
* @throws Exception
*/
public function write($id, $data): bool
{
$exists = $this->db->sq("SELECT COUNT(*) FROM {$this->db->prefix}sessions WHERE id = ?", $id);
if (!$exists) {
// Create new session with 14 days lifetime
$expire = time() + 14 * 86400;
$this->db->ex("INSERT INTO {$this->db->prefix}sessions (id,data,expires) VALUES (?,?,?)",
array($id, $data, $expire) );
}
else {
// Update existing session
$this->db->ex("UPDATE {$this->db->prefix}sessions SET data = ? WHERE id = ?",
array($data, $id) );
}
return true;
}
/**
* @param string $id
* @param string $data
* @return bool
* @throws Exception
*/
public function write($id, $data): bool
{
$exists = $this->db->sq("SELECT COUNT(*) FROM {$this->db->prefix}sessions WHERE id = ?", $id);
if (!$exists) {
// Create new session with 14 days lifetime
$expire = time() + 14 * 86400;
$this->db->ex("INSERT INTO {$this->db->prefix}sessions (id,data,expires) VALUES (?,?,?)",
array($id, $data, $expire) );
}
else {
// Update existing session
$this->db->ex("UPDATE {$this->db->prefix}sessions SET data = ? WHERE id = ?",
array($data, $id) );
}
return true;
}
/**
* @param string $id
* @return bool
* @throws Exception
*/
public function destroy($id): bool
{
$this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE id = ?", $id);
return true;
}
/**
* @param string $id
* @return bool
* @throws Exception
*/
public function destroy($id): bool
{
$this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE id = ?", $id);
return true;
}
/**
* @param int $max_lifetime
* @return int|false
*/
#[\ReturnTypeWillChange]
public function gc($max_lifetime)
{
// We ignore php runtime 'session.gc_maxlifetime'
$expire = time();
$affected = $this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE expires < $expire");
return $affected;
}
/**
* @param int $max_lifetime
* @return int|false
*/
#[\ReturnTypeWillChange]
public function gc($max_lifetime)
{
// We ignore php runtime 'session.gc_maxlifetime'
$expire = time();
$affected = $this->db->ex("DELETE FROM {$this->db->prefix}sessions WHERE expires < $expire");
return $affected;
}
}

View file

@ -1,123 +1,123 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2010,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009-2010,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
function htmlarray($a, $exclude=null)
{
htmlarray_ref($a, $exclude);
return $a;
htmlarray_ref($a, $exclude);
return $a;
}
function htmlarray_ref(&$a, $exclude=null)
{
if(!$a) return;
if(!is_array($a)) {
$a = htmlspecialchars($a);
return;
}
reset($a);
if($exclude && !is_array($exclude)) $exclude = array($exclude);
foreach($a as $k=>$v)
{
if(is_array($v)) $a[$k] = htmlarray($v, $exclude);
elseif(!$exclude) $a[$k] = htmlspecialchars($v);
elseif(!in_array($k, $exclude)) $a[$k] = htmlspecialchars($v);
}
return;
if(!$a) return;
if(!is_array($a)) {
$a = htmlspecialchars($a);
return;
}
reset($a);
if($exclude && !is_array($exclude)) $exclude = array($exclude);
foreach($a as $k=>$v)
{
if(is_array($v)) $a[$k] = htmlarray($v, $exclude);
elseif(!$exclude) $a[$k] = htmlspecialchars($v);
elseif(!in_array($k, $exclude)) $a[$k] = htmlspecialchars($v);
}
return;
}
function _post($param,$defvalue = '')
{
if(!isset($_POST[$param])) {
return $defvalue;
}
else {
return $_POST[$param];
}
if(!isset($_POST[$param])) {
return $defvalue;
}
else {
return $_POST[$param];
}
}
function _get($param,$defvalue = '')
{
if(!isset($_GET[$param])) {
return $defvalue;
}
else {
return $_GET[$param];
}
if(!isset($_GET[$param])) {
return $defvalue;
}
else {
return $_GET[$param];
}
}
function _server($param, $defvalue = '')
{
if ( !isset($_SERVER[$param]) ) {
return $defvalue;
}
else {
return $_SERVER[$param];
}
if ( !isset($_SERVER[$param]) ) {
return $defvalue;
}
else {
return $_SERVER[$param];
}
}
function formatDate3($format, $ay, $am, $ad, $lang)
{
# F - month long, M - month short
# m - month 2-digit, n - month 1-digit
# d - day 2-digit, j - day 1-digit
$ml = $lang->get('months_long');
$ms = $lang->get('months_short');
$Y = $ay;
$YC = 100 * floor($Y/100); //...1900,2000,2100...
if ($YC == 2000) $y = $Y < $YC+10 ? '0'.($Y-$YC) : $Y-$YC;
else $y = $Y;
$n = $am;
$m = $n < 10 ? '0'.$n : $n;
$F = $ml[$am-1];
$M = $ms[$am-1];
$j = $ad;
$d = $j < 10 ? '0'.$j : $j;
return strtr($format, array('Y'=>$Y, 'y'=>$y, 'F'=>$F, 'M'=>$M, 'n'=>$n, 'm'=>$m, 'd'=>$d, 'j'=>$j));
# F - month long, M - month short
# m - month 2-digit, n - month 1-digit
# d - day 2-digit, j - day 1-digit
$ml = $lang->get('months_long');
$ms = $lang->get('months_short');
$Y = $ay;
$YC = 100 * floor($Y/100); //...1900,2000,2100...
if ($YC == 2000) $y = $Y < $YC+10 ? '0'.($Y-$YC) : $Y-$YC;
else $y = $Y;
$n = $am;
$m = $n < 10 ? '0'.$n : $n;
$F = $ml[$am-1];
$M = $ms[$am-1];
$j = $ad;
$d = $j < 10 ? '0'.$j : $j;
return strtr($format, array('Y'=>$Y, 'y'=>$y, 'F'=>$F, 'M'=>$M, 'n'=>$n, 'm'=>$m, 'd'=>$d, 'j'=>$j));
}
function getRequestUri()
{
// Do not use HTTP_X_REWRITE_URL due to CVE-2018-14773
// SCRIPT_NAME or PATH_INFO ?
if (isset($_SERVER['REQUEST_URI'])) {
return $_SERVER['REQUEST_URI'];
}
else if (isset($_SERVER['ORIG_PATH_INFO'])) // IIS 5.0 CGI
{
$uri = $_SERVER['ORIG_PATH_INFO']; //has no query
if (!empty($_SERVER['QUERY_STRING'])) $uri .= '?'. $_SERVER['QUERY_STRING'];
return $uri;
}
// Do not use HTTP_X_REWRITE_URL due to CVE-2018-14773
// SCRIPT_NAME or PATH_INFO ?
if (isset($_SERVER['REQUEST_URI'])) {
return $_SERVER['REQUEST_URI'];
}
else if (isset($_SERVER['ORIG_PATH_INFO'])) // IIS 5.0 CGI
{
$uri = $_SERVER['ORIG_PATH_INFO']; //has no query
if (!empty($_SERVER['QUERY_STRING'])) $uri .= '?'. $_SERVER['QUERY_STRING'];
return $uri;
}
}
function url_dir($url, $onlyPath = 1)
{
if (false !== $p = strpos($url, '?')) {
$url = substr($url, 0, $p); # to avoid parse errors on strange query strings
}
if ($onlyPath) {
$url = parse_url($url, PHP_URL_PATH);
}
if ($url == '') {
return '/';
}
if (substr($url, -1) == '/') {
return $url;
}
if (false !== $p = strrpos($url, '/')) {
return substr($url, 0, $p+1);
}
return '/';
if (false !== $p = strpos($url, '?')) {
$url = substr($url, 0, $p); # to avoid parse errors on strange query strings
}
if ($onlyPath) {
$url = parse_url($url, PHP_URL_PATH);
}
if ($url == '') {
return '/';
}
if (substr($url, -1) == '/') {
return $url;
}
if (false !== $p = strrpos($url, '/')) {
return substr($url, 0, $p+1);
}
return '/';
}
function removeNewLines($s)
{
return str_replace( ["\r","\n"], '', $s );
return str_replace( ["\r","\n"], '', $s );
}
/**
@ -126,21 +126,21 @@ function removeNewLines($s)
*/
function generateUUID(): string
{
$uuid = bin2hex(random_bytes(16));
return sprintf('%08s-%04s-4%03s-%04x-%012s',
substr($uuid, 0, 8),
substr($uuid, 8, 4),
// $uuid[14] = 4
substr($uuid, 13, 3),
hexdec(substr($uuid, 16, 4)) & 0x3fff | 0x8000,
substr($uuid, 20, 12)
$uuid = bin2hex(random_bytes(16));
return sprintf('%08s-%04s-4%03s-%04x-%012s',
substr($uuid, 0, 8),
substr($uuid, 8, 4),
// $uuid[14] = 4
substr($uuid, 13, 3),
hexdec(substr($uuid, 16, 4)) & 0x3fff | 0x8000,
substr($uuid, 20, 12)
);
}
function passwordHash(string $p): string
{
if ($p == '') return '';
return 'sha256:'. hash('sha256', $p);
if ($p == '') return '';
return 'sha256:'. hash('sha256', $p);
}
/**
@ -151,26 +151,26 @@ function passwordHash(string $p): string
*/
function isPasswordEqualsToHash(string $p, string $hash): bool
{
if ($hash == '' && $p == '') return true;
if ($hash == '' || $p == '') return false;
if ( false !== $pos = strpos($hash, ':') ) {
$algo = substr($hash, 0, $pos);
if ($algo != 'sha256') throw new Exception("Unsupported algo of password hash");
if ( hash_equals($hash, passwordHash($p)) ) return true;
}
return false;
if ($hash == '' && $p == '') return true;
if ($hash == '' || $p == '') return false;
if ( false !== $pos = strpos($hash, ':') ) {
$algo = substr($hash, 0, $pos);
if ($algo != 'sha256') throw new Exception("Unsupported algo of password hash");
if ( hash_equals($hash, passwordHash($p)) ) return true;
}
return false;
}
function idSignature(string $id, string $key, string $salt): string
{
$secret = $key.$salt;
return hash_hmac('sha256', $id, $secret);
$secret = $key.$salt;
return hash_hmac('sha256', $id, $secret);
}
function isSignatureOk(string $signature, string $id, string $key, string $salt): bool
{
if ( hash_equals($signature, idSignature($id, $key, $salt)) ) return true;
return false;
if ( hash_equals($signature, idSignature($id, $key, $salt)) ) return true;
return false;
}
?>

View file

@ -1,9 +1,9 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2021-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
require_once(MTTINC. 'parsedown/Parsedown.php');
@ -12,76 +12,76 @@ require_once(MTTINC. 'parsedown/MTTParsedown.php');
function noteMarkup($note, $toExternal = false)
{
if ($note === null) {
$note = '';
}
if (Config::get('markup') == 'v1') {
return mttMarkup_v1($note);
}
return markdownToHtml($note, $toExternal);
if ($note === null) {
$note = '';
}
if (Config::get('markup') == 'v1') {
return mttMarkup_v1($note);
}
return markdownToHtml($note, $toExternal);
}
// Markdown converter (Parsedown)
function markdownToHtml($s, $toExternal = false)
{
$parser = MTTParsedown::instance();
$parser->setToExternal($toExternal);
$parser->setSafeMode(true);
//$parser->setBreaksEnabled(true);
return $parser->text($s);
$parser = MTTParsedown::instance();
$parser->setToExternal($toExternal);
$parser->setSafeMode(true);
//$parser->setBreaksEnabled(true);
return $parser->text($s);
}
// Convert note's raw text to html with allowed elements (b,i,u,s and raw urls)
function mttMarkup_v1($s)
{
//hide allowed elements from escaping
$c1 = chr(1);
$c2 = chr(2);
$s = preg_replace("~<b>([\s\S]*?)</b>~i", "${c1}b${c2}\$1${c1}/b${c2}", $s);
$s = preg_replace("~<i>([\s\S]*?)</i>~i", "${c1}i${c2}\$1${c1}/i${c2}", $s);
$s = preg_replace("~<u>([\s\S]*?)</u>~i", "${c1}u${c2}\$1${c1}/u${c2}", $s);
$s = preg_replace("~<s>([\s\S]*?)</s>~i", "${c1}s${c2}\$1${c1}/s${c2}", $s);
$s = htmlspecialchars($s, ENT_QUOTES); //escape all elements, except above
$s = str_replace( [$c1, $c2], ['<','>'], $s ); //unhide
$s = nl2br($s);
//hide allowed elements from escaping
$c1 = chr(1);
$c2 = chr(2);
$s = preg_replace("~<b>([\s\S]*?)</b>~i", "${c1}b${c2}\$1${c1}/b${c2}", $s);
$s = preg_replace("~<i>([\s\S]*?)</i>~i", "${c1}i${c2}\$1${c1}/i${c2}", $s);
$s = preg_replace("~<u>([\s\S]*?)</u>~i", "${c1}u${c2}\$1${c1}/u${c2}", $s);
$s = preg_replace("~<s>([\s\S]*?)</s>~i", "${c1}s${c2}\$1${c1}/s${c2}", $s);
$s = htmlspecialchars($s, ENT_QUOTES); //escape all elements, except above
$s = str_replace( [$c1, $c2], ['<','>'], $s ); //unhide
$s = nl2br($s);
// make links from text starting with 'www.'
$s = preg_replace(
"/(^|\s|>)(www\.([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="http://$2" target="_blank">$2</a>$4' ,
$s
);
// make links from text starting with 'www.'
$s = preg_replace(
"/(^|\s|>)(www\.([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="http://$2" target="_blank">$2</a>$4' ,
$s
);
// make link from text starting with protocol like 'http://'
$s = preg_replace(
"/(^|\s|>)([a-z]+:\/\/([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="$2" target="_blank">$2</a>$4' ,
$s
);
// make link from text starting with protocol like 'http://'
$s = preg_replace(
"/(^|\s|>)([a-z]+:\/\/([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="$2" target="_blank">$2</a>$4' ,
$s
);
return $s;
return $s;
}
// Convert raw title to html with allowed urls
function titleMarkup($title)
{
//escape all unsafe
$title = htmlspecialchars($title, ENT_QUOTES);
//escape all unsafe
$title = htmlspecialchars($title, ENT_QUOTES);
// make links from text starting with 'www.'
$title = preg_replace(
"/(^|\s|>)(www\.([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="http://$2" target="_blank">$2</a>$4' ,
$title
);
// make links from text starting with 'www.'
$title = preg_replace(
"/(^|\s|>)(www\.([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="http://$2" target="_blank">$2</a>$4' ,
$title
);
// make link from text starting with protocol like 'http://'
$title = preg_replace(
"/(^|\s|>)([a-z]+:\/\/([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="$2" target="_blank">$2</a>$4' ,
$title
);
return $title;
// make link from text starting with protocol like 'http://'
$title = preg_replace(
"/(^|\s|>)([a-z]+:\/\/([\w\#$%&~\/.\-\+;:=,\?\[\]@]+?))(,|\.|:|)?(?=\s|&quot;|&lt;|&gt;|\"|<|>|$)/iu" ,
'$1<a href="$2" target="_blank">$2</a>$4' ,
$title
);
return $title;
}

View file

@ -1,41 +1,41 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
class MTTParsedown extends Parsedown
{
protected $toExternal;
protected $toExternal;
function __construct()
{
$this->toExternal = false;
$this->toExternal = false;
$this->InlineTypes['#'][]= 'TaskId';
$this->inlineMarkerList .= '#';
}
public function setToExternal(bool $v)
{
$this->toExternal = $v;
}
public function setToExternal(bool $v)
{
$this->toExternal = $v;
}
protected function inlineTaskId($excerpt)
{
if (preg_match('/^#(\d+)/', $excerpt['text'], $matches))
{
$attrs = array(
'href' => get_mttinfo('url'). '?task='. $matches[1],
'target' => '_blank',
);
if (!$this->toExternal) {
$attrs['class'] = 'mtt-link-to-task';
$attrs['target-id'] = $matches[1];
}
$attrs = array(
'href' => get_mttinfo('url'). '?task='. $matches[1],
'target' => '_blank',
);
if (!$this->toExternal) {
$attrs['class'] = 'mtt-link-to-task';
$attrs['target-id'] = $matches[1];
}
return array(
// How many characters to advance the Parsedown's

View file

@ -1,26 +1,26 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2010,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009-2010,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
require_once('./init.php');
//Parse query string
if ( isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '' ) {
parseRoute($_SERVER['QUERY_STRING']);
parseRoute($_SERVER['QUERY_STRING']);
}
$lang = Lang::instance();
if ($lang->rtl()) {
Config::set('rtl', 1);
Config::set('rtl', 1);
}
if (!is_int(Config::get('firstdayofweek')) || Config::get('firstdayofweek')<0 || Config::get('firstdayofweek')>6) {
Config::set('firstdayofweek', 1);
Config::set('firstdayofweek', 1);
}
define('TEMPLATEPATH', MTTTHEMES. Config::get('template'). '/');
@ -32,51 +32,51 @@ require(TEMPLATEPATH. 'index.php');
function parseRoute($queryString)
{
parse_str($queryString, $q);
if (isset($q['list'])) {
$hash = ($q['list'] == 'alltasks') ? ['alltasks'] : ['list', (int)$q['list']];
unset($q['list']);
redirectWithHashRoute($hash, $q);
}
else if (isset($q['task'])) {
$listId = (int)DBCore::defaultInstance()->getListIdByTaskId((int)$q['task']);
if ($listId > 0) {
$h = [ 'list', $listId, 'search', '#'. (int)$q['task']];
redirectWithHashRoute($h);
}
// TODO: not found
}
parse_str($queryString, $q);
if (isset($q['list'])) {
$hash = ($q['list'] == 'alltasks') ? ['alltasks'] : ['list', (int)$q['list']];
unset($q['list']);
redirectWithHashRoute($hash, $q);
}
else if (isset($q['task'])) {
$listId = (int)DBCore::defaultInstance()->getListIdByTaskId((int)$q['task']);
if ($listId > 0) {
$h = [ 'list', $listId, 'search', '#'. (int)$q['task']];
redirectWithHashRoute($h);
}
// TODO: not found
}
}
function redirectWithHashRoute(array $hash, array $q = [])
{
$url = get_unsafe_mttinfo('url');
$query = http_build_query($q);
if ($query != '') $url .= "?$query";
if (count($hash) > 0) {
$encodedHash = implode("/", array_map("rawurlencode", $hash));
$url .= "#$encodedHash";
}
header("Location: ". $url);
exit;
$url = get_unsafe_mttinfo('url');
$query = http_build_query($q);
if ($query != '') $url .= "?$query";
if (count($hash) > 0) {
$encodedHash = implode("/", array_map("rawurlencode", $hash));
$url .= "#$encodedHash";
}
header("Location: ". $url);
exit;
}
function js_options()
{
$a = array(
"token" => htmlspecialchars(access_token()),
"title" => get_unsafe_mttinfo('title'),
"lang" => Lang::instance()->jsStrings(),
"mttUrl" => get_mttinfo('mtt_url'),
"homeUrl" => get_mttinfo('url'),
"needAuth" => need_auth() ? true : false,
"isLogged" => is_logged() ? true : false,
"showdate" => Config::get('showdate') ? true : false,
"duedatepickerformat" => htmlspecialchars(Config::get('dateformat2')),
"firstdayofweek" => (int) Config::get('firstdayofweek'),
"calendarIcon" => get_mttinfo('template_url'). 'images/calendar.svg',
"autotag" => Config::get('autotag') ? true : false,
"markdown" => Config::get('markup') == 'v1' ? false : true
);
echo json_encode($a, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$a = array(
"token" => htmlspecialchars(access_token()),
"title" => get_unsafe_mttinfo('title'),
"lang" => Lang::instance()->jsStrings(),
"mttUrl" => get_mttinfo('mtt_url'),
"homeUrl" => get_mttinfo('url'),
"needAuth" => need_auth() ? true : false,
"isLogged" => is_logged() ? true : false,
"showdate" => Config::get('showdate') ? true : false,
"duedatepickerformat" => htmlspecialchars(Config::get('dateformat2')),
"firstdayofweek" => (int) Config::get('firstdayofweek'),
"calendarIcon" => get_mttinfo('template_url'). 'images/calendar.svg',
"autotag" => Config::get('autotag') ? true : false,
"markdown" => Config::get('markup') == 'v1' ? false : true
);
echo json_encode($a, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}

View file

@ -1,15 +1,15 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2019-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
define('MTT_VERSION', '@VERSION');
##### MyTinyTodo requires php 7.0 and above! #####
if (version_compare(PHP_VERSION, '7.0.0') < 0) {
die("PHP 7.0+ is required");
die("PHP 7.0+ is required");
}
if(!defined('MTTPATH')) define('MTTPATH', dirname(__FILE__) .'/');
@ -19,15 +19,15 @@ if(!defined('MTTLANG')) define('MTTLANG', MTTCONTENT. 'lang/');
if(!defined('MTTTHEMES')) define('MTTTHEMES', MTTCONTENT. 'themes/');
if (getenv('MTT_ENABLE_DEBUG') == 'YES') {
define('MTT_DEBUG', true);
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
define('MTT_DEBUG', true);
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
}
else {
//ini_set('display_errors', '0');
//ini_set('log_errors', '1');
define('MTT_DEBUG', false);
//ini_set('display_errors', '0');
//ini_set('log_errors', '1');
define('MTT_DEBUG', false);
}
requireConfig();
@ -40,32 +40,32 @@ require_once(MTTINC. 'class.config.php');
# MySQL Database Connection
if (MTT_DB_TYPE == 'mysql')
{
if (defined('MTT_DB_DRIVER') && MTT_DB_DRIVER == 'mysqli') require_once(MTTINC. 'class.db.mysqli.php');
else require_once(MTTINC. 'class.db.mysql.php');
$db = DBConnection::init(new Database_Mysql);
try {
$db->connect( array(
'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());
}
$db->dq("SET NAMES utf8mb4");
if (defined('MTT_DB_DRIVER') && MTT_DB_DRIVER == 'mysqli') require_once(MTTINC. 'class.db.mysqli.php');
else require_once(MTTINC. 'class.db.mysql.php');
$db = DBConnection::init(new Database_Mysql);
try {
$db->connect( array(
'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());
}
$db->dq("SET NAMES utf8mb4");
}
# SQLite3 Database
elseif (MTT_DB_TYPE == 'sqlite')
{
require_once(MTTINC. 'class.db.sqlite3.php');
$db = DBConnection::init(new Database_Sqlite3);
$db->connect( array( 'filename' => MTTPATH. 'db/todolist.db' ) );
require_once(MTTINC. 'class.db.sqlite3.php');
$db = DBConnection::init(new Database_Sqlite3);
$db->connect( array( 'filename' => MTTPATH. 'db/todolist.db' ) );
}
else {
die("Incorrect database connection config");
die("Incorrect database connection config");
}
DBConnection::setPrefix(MTT_DB_PREFIX);
@ -81,7 +81,7 @@ if( isset($_COOKIE['lang']) ) $forceLang = $_COOKIE['lang'];
//else if ( isset($_GET['lang']) ) $forceLang = $_GET['lang'];
if ( $forceLang != '' && preg_match("/^[a-z-]+$/i", $forceLang) ) {
Config::set('lang', $forceLang); //TODO: special for demo, do not change config
Config::set('lang', $forceLang); //TODO: special for demo, do not change config
}
require_once(MTTINC. 'class.lang.php');
@ -90,131 +90,131 @@ Lang::loadLang( Config::get('lang') );
$_mttinfo = array();
if (need_auth() && !isset($dontStartSession)) {
setup_and_start_session();
setup_and_start_session();
}
function requireConfig()
{
$exists = file_exists(MTTPATH. 'config.php');
$defined = false;
if ($exists) {
require_once(MTTPATH. 'config.php');
$defined = defined('MTT_DB_TYPE');
}
# It seems not installed
if (!$defined) {
die("Not installed. Run <a href=setup.php>setup.php</a> first.");
}
$exists = file_exists(MTTPATH. 'config.php');
$defined = false;
if ($exists) {
require_once(MTTPATH. 'config.php');
$defined = defined('MTT_DB_TYPE');
}
# It seems not installed
if (!$defined) {
die("Not installed. Run <a href=setup.php>setup.php</a> first.");
}
}
function need_auth(): bool
{
return (Config::get('password') != '') ? true : false;
return (Config::get('password') != '') ? true : false;
}
function is_logged(): bool
{
if ( !need_auth() ) return true;
if ( !isset($_SESSION['logged']) || !isset($_SESSION['sign']) ) return false;
if ( !(int)$_SESSION['logged'] ) return false;
return isSignatureOk($_SESSION['sign'], session_id(), Config::get('password'), defined('MTT_SALT') ? MTT_SALT : '');
if ( !need_auth() ) return true;
if ( !isset($_SESSION['logged']) || !isset($_SESSION['sign']) ) return false;
if ( !(int)$_SESSION['logged'] ) return false;
return isSignatureOk($_SESSION['sign'], session_id(), Config::get('password'), defined('MTT_SALT') ? MTT_SALT : '');
}
function is_readonly(): bool
{
if ( !is_logged() ) return true;
return false;
if ( !is_logged() ) return true;
return false;
}
function access_token(): string
{
if (!need_auth()) return '';
if (!isset($_SESSION)) return '';
if (!isset($_SESSION['token'])) return '';
return $_SESSION['token'];
if (!need_auth()) return '';
if (!isset($_SESSION)) return '';
if (!isset($_SESSION['token'])) return '';
return $_SESSION['token'];
}
function check_token()
{
$token = access_token();
if ($token == '') return true;
if (!isset($_SERVER)) return true;
if (!isset($_SERVER['HTTP_MTT_TOKEN']) || $_SERVER['HTTP_MTT_TOKEN'] != $token) {
die("Access denied! Try to reload the page.");
}
$token = access_token();
if ($token == '') return true;
if (!isset($_SERVER)) return true;
if (!isset($_SERVER['HTTP_MTT_TOKEN']) || $_SERVER['HTTP_MTT_TOKEN'] != $token) {
die("Access denied! Try to reload the page.");
}
}
function setup_and_start_session()
{
require_once(MTTINC. 'class.sessionhandler.php');
session_set_save_handler(new MTTSessionHandler());
require_once(MTTINC. 'class.sessionhandler.php');
session_set_save_handler(new MTTSessionHandler());
ini_set('session.use_cookies', true);
ini_set('session.use_only_cookies', true);
ini_set('session.use_cookies', true);
ini_set('session.use_only_cookies', true);
$lifetime = 5184000; # 60 days session cookie lifetime
$path = url_dir(Config::get('url')=='' ? getRequestUri() : Config::getUrl('url'));
$samesite = 'lax';
$lifetime = 5184000; # 60 days session cookie lifetime
$path = url_dir(Config::get('url')=='' ? getRequestUri() : Config::getUrl('url'));
$samesite = 'lax';
if (PHP_VERSION_ID < 70300) {
# this is a known samesite flag workaround, was fixed in 7.3
session_set_cookie_params($lifetime, $path. '; samesite='.$samesite, null, null, true);
} else {
session_set_cookie_params(Array(
'lifetime' => $lifetime,
'path' => $path,
'httponly' => true,
'samesite' => $samesite
));
}
session_name('mtt-session');
session_start();
if (PHP_VERSION_ID < 70300) {
# this is a known samesite flag workaround, was fixed in 7.3
session_set_cookie_params($lifetime, $path. '; samesite='.$samesite, null, null, true);
} else {
session_set_cookie_params(Array(
'lifetime' => $lifetime,
'path' => $path,
'httponly' => true,
'samesite' => $samesite
));
}
session_name('mtt-session');
session_start();
}
function timestampToDatetime($timestamp)
{
$format = Config::get('dateformat') .' '. (Config::get('clock') == 12 ? 'g:i A' : 'H:i');
return formatTime($format, $timestamp);
$format = Config::get('dateformat') .' '. (Config::get('clock') == 12 ? 'g:i A' : 'H:i');
return formatTime($format, $timestamp);
}
function formatTime($format, $timestamp=0)
{
$lang = Lang::instance();
if($timestamp == 0) $timestamp = time();
$newformat = strtr($format, array('F'=>'%1', 'M'=>'%2'));
$adate = explode(',', date('n,'.$newformat, $timestamp), 2);
$s = $adate[1];
if($newformat != $format)
{
$am = (int)$adate[0];
$ml = $lang->get('months_long');
$ms = $lang->get('months_short');
$F = $ml[$am-1];
$M = $ms[$am-1];
$s = strtr($s, array('%1'=>$F, '%2'=>$M));
}
return $s;
$lang = Lang::instance();
if($timestamp == 0) $timestamp = time();
$newformat = strtr($format, array('F'=>'%1', 'M'=>'%2'));
$adate = explode(',', date('n,'.$newformat, $timestamp), 2);
$s = $adate[1];
if($newformat != $format)
{
$am = (int)$adate[0];
$ml = $lang->get('months_long');
$ms = $lang->get('months_short');
$F = $ml[$am-1];
$M = $ms[$am-1];
$s = strtr($s, array('%1'=>$F, '%2'=>$M));
}
return $s;
}
function _e($s)
{
echo Lang::instance()->get($s);
echo Lang::instance()->get($s);
}
function __($s)
{
return Lang::instance()->get($s);
return Lang::instance()->get($s);
}
function mttinfo($v)
{
echo get_mttinfo($v);
echo get_mttinfo($v);
}
function get_mttinfo($v)
{
return htmlspecialchars( get_unsafe_mttinfo($v) );
return htmlspecialchars( get_unsafe_mttinfo($v) );
}
/*
@ -223,70 +223,70 @@ function get_mttinfo($v)
*/
function get_unsafe_mttinfo($v)
{
global $_mttinfo;
if (isset($_mttinfo[$v])) {
return $_mttinfo[$v];
}
switch($v)
{
case 'template_url':
$_mttinfo['template_url'] = get_unsafe_mttinfo('mtt_url'). 'content/themes/'. Config::get('template') . '/';
return $_mttinfo['template_url'];
case 'includes_url':
$_mttinfo['includes_url'] = get_unsafe_mttinfo('mtt_url'). 'includes/';
return $_mttinfo['includes_url'];
case 'url':
/* full url to homepage: directory with root index.php or custom index file in the root. */
/* ex: http://my.site/mytinytodo/ or https://my.site/mytinytodo/home_for_2nd_theme.php */
/* Should not contain a query string. Have to be set in config if custom port is used or wrong detection. */
$_mttinfo['url'] = Config::getUrl('url');
if ($_mttinfo['url'] == '') {
$is_https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false;
$_mttinfo['url'] = ($is_https ? 'https://' : 'http://'). $_SERVER['HTTP_HOST']. url_dir(getRequestUri());
}
return $_mttinfo['url'];
case 'mtt_url':
/* Directory with ajax.php. No need to set if you use default directory structure. */
$_mttinfo['mtt_url'] = Config::getUrl('mtt_url'); // need to have a trailing slash
if ($_mttinfo['mtt_url'] == '') {
$_mttinfo['mtt_url'] = url_dir( get_unsafe_mttinfo('url'), 0 );
}
return $_mttinfo['mtt_url'];
case 'title':
$_mttinfo['title'] = (Config::get('title') != '') ? Config::get('title') : __('My Tiny Todolist');
return $_mttinfo['title'];
case 'version':
if (MTT_VERSION != '@'.'VERSION') {
$_mttinfo['version'] = MTT_VERSION;
return $_mttinfo['version'];
}
return time(); //force no-cache for dev needs
}
global $_mttinfo;
if (isset($_mttinfo[$v])) {
return $_mttinfo[$v];
}
switch($v)
{
case 'template_url':
$_mttinfo['template_url'] = get_unsafe_mttinfo('mtt_url'). 'content/themes/'. Config::get('template') . '/';
return $_mttinfo['template_url'];
case 'includes_url':
$_mttinfo['includes_url'] = get_unsafe_mttinfo('mtt_url'). 'includes/';
return $_mttinfo['includes_url'];
case 'url':
/* full url to homepage: directory with root index.php or custom index file in the root. */
/* ex: http://my.site/mytinytodo/ or https://my.site/mytinytodo/home_for_2nd_theme.php */
/* Should not contain a query string. Have to be set in config if custom port is used or wrong detection. */
$_mttinfo['url'] = Config::getUrl('url');
if ($_mttinfo['url'] == '') {
$is_https = (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false;
$_mttinfo['url'] = ($is_https ? 'https://' : 'http://'). $_SERVER['HTTP_HOST']. url_dir(getRequestUri());
}
return $_mttinfo['url'];
case 'mtt_url':
/* Directory with ajax.php. No need to set if you use default directory structure. */
$_mttinfo['mtt_url'] = Config::getUrl('mtt_url'); // need to have a trailing slash
if ($_mttinfo['mtt_url'] == '') {
$_mttinfo['mtt_url'] = url_dir( get_unsafe_mttinfo('url'), 0 );
}
return $_mttinfo['mtt_url'];
case 'title':
$_mttinfo['title'] = (Config::get('title') != '') ? Config::get('title') : __('My Tiny Todolist');
return $_mttinfo['title'];
case 'version':
if (MTT_VERSION != '@'.'VERSION') {
$_mttinfo['version'] = MTT_VERSION;
return $_mttinfo['version'];
}
return time(); //force no-cache for dev needs
}
}
function reset_mttinfo($key)
{
global $_mttinfo;
unset( $_mttinfo[$key] );
global $_mttinfo;
unset( $_mttinfo[$key] );
}
function jsonExit($data)
{
header('Content-type: application/json; charset=utf-8');
echo json_encode($data);
exit;
header('Content-type: application/json; charset=utf-8');
echo json_encode($data);
exit;
}
function logAndDie($userText, $errText = null)
{
$errText === null ? error_log($userText) : error_log($errText);
if (ini_get('display_errors')) {
echo htmlspecialchars($userText);
}
else {
echo "Error! See details in error log.";
}
exit(1);
$errText === null ? error_log($userText) : error_log($errText);
if (ini_get('display_errors')) {
echo htmlspecialchars($userText);
}
else {
echo "Error! See details in error log.";
}
exit(1);
}
?>

View file

@ -1,9 +1,9 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
require_once('./init.php');
@ -11,81 +11,81 @@ require_once('./init.php');
$lang = Lang::instance();
if ( !is_logged() ) {
die("Access denied!<br/> Disable password protection or Log in.");
die("Access denied!<br/> Disable password protection or Log in.");
}
if(isset($_POST['save']))
{
check_token();
check_token();
$t = array();
$langs = getLangs();
Config::$params['lang']['options'] = array_keys($langs);
Config::set('lang', _post('lang'));
$t = array();
$langs = getLangs();
Config::$params['lang']['options'] = array_keys($langs);
Config::set('lang', _post('lang'));
// in Demo mode we can set only language by cookies
if(defined('MTTDEMO')) {
setcookie('lang', Config::get('lang'), 0, url_dir(Config::get('url')=='' ? getRequestUri() : Config::getUrl('url')));
$t['saved'] = 1;
jsonExit($t);
}
// in Demo mode we can set only language by cookies
if(defined('MTTDEMO')) {
setcookie('lang', Config::get('lang'), 0, url_dir(Config::get('url')=='' ? getRequestUri() : Config::getUrl('url')));
$t['saved'] = 1;
jsonExit($t);
}
if (isset($_POST['password']) && $_POST['password'] != '') Config::set('password', passwordHash($_POST['password'])) ;
elseif (!_post('allowpassword')) Config::set('password', '');
if (isset($_POST['password']) && $_POST['password'] != '') Config::set('password', passwordHash($_POST['password'])) ;
elseif (!_post('allowpassword')) Config::set('password', '');
Config::set('smartsyntax', (int)_post('smartsyntax'));
// Do not set invalid timezone
try {
$tz = trim(_post('timezone'));
$testTZ = new DateTimeZone($tz); //will throw Exception on invalid timezone
Config::set('timezone', $tz);
}
catch (Exception $e) {
}
Config::set('autotag', (int)_post('autotag'));
Config::set('markup', (int)_post('markdown') == 0 ? 'v1' : 'markdown');
Config::set('firstdayofweek', (int)_post('firstdayofweek'));
Config::set('clock', (int)_post('clock'));
Config::set('dateformat', removeNewLines(_post('dateformat')) );
Config::set('dateformat2', removeNewLines(_post('dateformat2')) );
Config::set('dateformatshort', removeNewLines(_post('dateformatshort')) );
Config::set('title', removeNewLines(trim(_post('title'))) );
Config::set('showdate', (int)_post('showdate'));
Config::save();
$t['saved'] = 1;
jsonExit($t);
Config::set('smartsyntax', (int)_post('smartsyntax'));
// Do not set invalid timezone
try {
$tz = trim(_post('timezone'));
$testTZ = new DateTimeZone($tz); //will throw Exception on invalid timezone
Config::set('timezone', $tz);
}
catch (Exception $e) {
}
Config::set('autotag', (int)_post('autotag'));
Config::set('markup', (int)_post('markdown') == 0 ? 'v1' : 'markdown');
Config::set('firstdayofweek', (int)_post('firstdayofweek'));
Config::set('clock', (int)_post('clock'));
Config::set('dateformat', removeNewLines(_post('dateformat')) );
Config::set('dateformat2', removeNewLines(_post('dateformat2')) );
Config::set('dateformatshort', removeNewLines(_post('dateformatshort')) );
Config::set('title', removeNewLines(trim(_post('title'))) );
Config::set('showdate', (int)_post('showdate'));
Config::save();
$t['saved'] = 1;
jsonExit($t);
}
function _c($key)
{
return Config::get($key);
return Config::get($key);
}
function getLangs($withContents = 0)
{
$langDir = Lang::instance()->langDir();
$langDir = Lang::instance()->langDir();
if ( ! $h = opendir($langDir) ) {
return false;
}
return false;
}
$a = array();
while ( false !== ($file = readdir($h)) )
{
if ( preg_match('/(.+)\.json$/', $file, $m) ) {
$jsonText = file_get_contents($langDir. $file);
if (false === $jsonText) {
die("false ");
continue;
}
$a[$m[1]] = $m[1];
{
if ( preg_match('/(.+)\.json$/', $file, $m) ) {
$jsonText = file_get_contents($langDir. $file);
if (false === $jsonText) {
die("false ");
continue;
}
$a[$m[1]] = $m[1];
$j = json_decode($jsonText, true);
if ( isset($j['_header']['language']) && isset($j['_header']['original_name']) ) {
$a[$m[1]]= [
'name' => $j['_header']['original_name'],
'title' => $j['_header']['language']
];
}
}
$j = json_decode($jsonText, true);
if ( isset($j['_header']['language']) && isset($j['_header']['original_name']) ) {
$a[$m[1]]= [
'name' => $j['_header']['original_name'],
'title' => $j['_header']['language']
];
}
}
}
closedir($h);
return $a;
@ -94,13 +94,13 @@ function getLangs($withContents = 0)
function selectOptions($a, $value, $default=null)
{
if(!$a) return '';
$s = '';
if($default !== null && !isset($a[$value])) $value = $default;
foreach($a as $k=>$v) {
$s .= '<option value="'.htmlspecialchars($k).'" '.($k===$value?'selected="selected"':'').'>'.htmlspecialchars($v).'</option>';
}
return $s;
if(!$a) return '';
$s = '';
if($default !== null && !isset($a[$value])) $value = $default;
foreach($a as $k=>$v) {
$s .= '<option value="'.htmlspecialchars($k).'" '.($k===$value?'selected="selected"':'').'>'.htmlspecialchars($v).'</option>';
}
return $s;
}
/**
@ -110,15 +110,15 @@ function selectOptions($a, $value, $default=null)
*/
function selectOptionsA($a, $key, $default=null)
{
if(!$a) return '';
$s = '';
if($default !== null && !isset($a[$key])) $key = $default;
foreach($a as $k=>$v) {
$s .= '<option value="'.htmlspecialchars($k).'" '.($k===$key?'selected="selected"':'').
(isset($v['title']) ? ' title="'.htmlspecialchars($v['title']).'"' : '').
'>'.htmlspecialchars($v['name']).'</option>';
}
return $s;
if(!$a) return '';
$s = '';
if($default !== null && !isset($a[$key])) $key = $default;
foreach($a as $k=>$v) {
$s .= '<option value="'.htmlspecialchars($k).'" '.($k===$key?'selected="selected"':'').
(isset($v['title']) ? ' title="'.htmlspecialchars($v['title']).'"' : '').
'>'.htmlspecialchars($v['name']).'</option>';
}
return $s;
}
function timezoneIdentifiers()
@ -138,10 +138,10 @@ header('Content-type:text/html; charset=utf-8');
<?php
if (isset($_GET['json'])) {
$j = Config::requestDefaultDomain();
if ($j['password'] != '') $j['password'] = "<not empty>";
$j = json_encode($j, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
if (isset($_GET['json'])) {
$j = Config::requestDefaultDomain();
if ($j['password'] != '') $j['password'] = "<not empty>";
$j = json_encode($j, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
?>
<div class="mtt-settings-table">
<div class="tr">
@ -150,8 +150,8 @@ header('Content-type:text/html; charset=utf-8');
</div>
</div>
<?php
exit;
}
exit;
}
?>
<div id="settings_msg" style="display:none"></div>
@ -221,7 +221,7 @@ header('Content-type:text/html; charset=utf-8');
<input name="dateformat" size="8" value="<?php echo htmlspecialchars(_c('dateformat'));?>" />
<select onchange="if(this.value!=0) this.form.dateformat.value=this.value;">
<?php echo selectOptions(array('F j, Y'=>formatTime('F j, Y'), 'M d, Y'=>formatTime('M d, Y'), 'j M Y'=>formatTime('j M Y'), 'd F Y'=>formatTime('d F Y'),
'n/j/Y'=>formatTime('n/j/Y'), 'd.m.Y'=>formatTime('d.m.Y'), 'j. F Y'=>formatTime('j. F Y'), 0=>__('set_custom')), _c('dateformat'), 0); ?>
'n/j/Y'=>formatTime('n/j/Y'), 'd.m.Y'=>formatTime('d.m.Y'), 'j. F Y'=>formatTime('j. F Y'), 0=>__('set_custom')), _c('dateformat'), 0); ?>
</select>
</div></div>

View file

@ -1,19 +1,19 @@
<?php
/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
This file is a part of myTinyTodo.
(C) Copyright 2009-2011,2020-2022 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
// Can be used to upgrade database from myTinyTodo v1.4 or later
$lastVer = '1.7';
if (getenv('MTT_ENABLE_DEBUG') == 'YES') {
set_exception_handler('debugExceptionHandler');
set_exception_handler('debugExceptionHandler');
}
else {
set_exception_handler('myExceptionHandler');
set_exception_handler('myExceptionHandler');
}
if (!defined('MTTPATH')) define('MTTPATH', dirname(__FILE__) .'/');
@ -35,141 +35,141 @@ echo "<big><b>myTinyTodo @VERSION Setup</b></big><br><br>";
if (!$configExists && $oldConfigExists)
{
// First we need to migrate database config
require_once(MTTPATH. 'db/config.php');
if (isset($config['password']) && $config['password'] != '') {
if (!isset($_POST['configpassword']) || $_POST['configpassword'] != $config['password']) {
exitMessage("Enter current password to continue. <form method=post><input type=password name=configpassword> <input type=submit value=' Continue '></form>");
}
}
Config::loadConfigV14($config);
tryToSaveDBConfig();
$configExists = true;
// First we need to migrate database config
require_once(MTTPATH. 'db/config.php');
if (isset($config['password']) && $config['password'] != '') {
if (!isset($_POST['configpassword']) || $_POST['configpassword'] != $config['password']) {
exitMessage("Enter current password to continue. <form method=post><input type=password name=configpassword> <input type=submit value=' Continue '></form>");
}
}
Config::loadConfigV14($config);
tryToSaveDBConfig();
$configExists = true;
}
if ($configExists)
{
// No need to migrate database config
require_once(MTTPATH. 'config.php');
$db = testConnect($error);
if (!$db) {
exitMessage( "Database connection config file seems to be incorrect. You can remove config.php or edit it manually and then reload setup.<br><br>".
"<b>Error:</b> ". htmlspecialchars($error) );
}
// Config file v1.7 already exists and set up correctly
$dbtype = MTT_DB_TYPE;
// No need to migrate database config
require_once(MTTPATH. 'config.php');
$db = testConnect($error);
if (!$db) {
exitMessage( "Database connection config file seems to be incorrect. You can remove config.php or edit it manually and then reload setup.<br><br>".
"<b>Error:</b> ". htmlspecialchars($error) );
}
// Config file v1.7 already exists and set up correctly
$dbtype = MTT_DB_TYPE;
// Determine current installed db version
$ver = databaseVersion($db);
// Determine current installed db version
$ver = databaseVersion($db);
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.");
}
}
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.");
}
}
}
if ($ver == '')
{
$install = trim(_post('install'));
$install = trim(_post('install'));
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>
<input type=hidden name=install value=create><input type=submit value=' Next '></form>");
}
elseif ($install == '')
{
# Specify database type and connection settings to save.
exitMessage("
<form method=post>Select database type to use:<br><br>
<input type=hidden name=install value=config>
<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>
<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>
<tr><td>Password:</td><td><input type=password name=db_password></td></tr>
<tr><td>Table prefix:</td><td><input name=db_prefix value='mtt_'></td></tr>
</table></div><br><input type=submit value=' Next '></form>
");
}
elseif ($install == 'config')
{
# Save configuration
$dbtype = ($_POST['db_type'] == 'mysql') ? 'mysql' : 'sqlite';
Config::set('db.type', $dbtype);
if ($dbtype == 'mysql') {
Config::set('db.host', _post('db_host'));
Config::set('db.name', _post('db_name'));
Config::set('db.user', _post('db_user'));
Config::set('db.password', _post('db_password'));
Config::set('db.prefix', trim(_post('db_prefix')));
}
Config::defineDbConstants();
$db = testConnect($error);
if (!$db) {
exitMessage("Database connection error: ". htmlspecialchars($error));
}
if (defined('MTT_DB_DRIVER')) {
Config::set('db.driver', MTT_DB_DRIVER);
}
tryToSaveDBConfig();
exitMessage("This will create myTinyTodo database <br> <form method=post><input type=hidden name=install value=create><input type=submit value=' Install '></form>");
}
elseif ($install == 'create')
{
# install database
try {
createAllTables($db, $dbtype);
} catch (Exception $e) {
exitMessage("<b>Error:</b> ". htmlarray($e->getMessage()));
}
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>
<input type=hidden name=install value=create><input type=submit value=' Next '></form>");
}
elseif ($install == '')
{
# Specify database type and connection settings to save.
exitMessage("
<form method=post>Select database type to use:<br><br>
<input type=hidden name=install value=config>
<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>
<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>
<tr><td>Password:</td><td><input type=password name=db_password></td></tr>
<tr><td>Table prefix:</td><td><input name=db_prefix value='mtt_'></td></tr>
</table></div><br><input type=submit value=' Next '></form>
");
}
elseif ($install == 'config')
{
# Save configuration
$dbtype = ($_POST['db_type'] == 'mysql') ? 'mysql' : 'sqlite';
Config::set('db.type', $dbtype);
if ($dbtype == 'mysql') {
Config::set('db.host', _post('db_host'));
Config::set('db.name', _post('db_name'));
Config::set('db.user', _post('db_user'));
Config::set('db.password', _post('db_password'));
Config::set('db.prefix', trim(_post('db_prefix')));
}
Config::defineDbConstants();
$db = testConnect($error);
if (!$db) {
exitMessage("Database connection error: ". htmlspecialchars($error));
}
if (defined('MTT_DB_DRIVER')) {
Config::set('db.driver', MTT_DB_DRIVER);
}
tryToSaveDBConfig();
exitMessage("This will create myTinyTodo database <br> <form method=post><input type=hidden name=install value=create><input type=submit value=' Install '></form>");
}
elseif ($install == 'create')
{
# install database
try {
createAllTables($db, $dbtype);
} catch (Exception $e) {
exitMessage("<b>Error:</b> ". htmlarray($e->getMessage()));
}
# create default list
$db->ex( "INSERT INTO {$db->prefix}lists (uuid,name,d_created,taskview) VALUES (?,?,?,?)", array(generateUUID(), 'Todo', time(), 1) );
# create default list
$db->ex( "INSERT INTO {$db->prefix}lists (uuid,name,d_created,taskview) VALUES (?,?,?,?)", array(generateUUID(), 'Todo', time(), 1) );
Config::save();
}
else {
exitMessage("Unknown action");
}
Config::save();
}
else {
exitMessage("Unknown action");
}
}
elseif ($ver == $lastVer)
{
exitMessage("Installed version does not require database update.");
exitMessage("Installed version does not require database update.");
}
else
{
if (!in_array($ver, array('1.4'))) {
exitMessage(htmlspecialchars("Can not update. Unsupported database version ($ver)."));
}
if (!in_array($ver, array('1.4'))) {
exitMessage(htmlspecialchars("Can not update. Unsupported database version ($ver)."));
}
if (!isset($_POST['update'])) {
exitMessage(htmlspecialchars("Update database v$ver to v$lastVer"). "<br><br>
<form name=frm method=post><input type=hidden name=update value=1>
<input type=submit value=' Update '>
</form>");
}
if (!isset($_POST['update'])) {
exitMessage(htmlspecialchars("Update database v$ver to v$lastVer"). "<br><br>
<form name=frm method=post><input type=hidden name=update value=1>
<input type=submit value=' Update '>
</form>");
}
# update process
if ($ver == '1.4')
{
update_14_17($db, $dbtype);
}
# update process
if ($ver == '1.4')
{
update_14_17($db, $dbtype);
}
}
echo "Done<br><br> <b>Attention!</b> Delete this file for security reasons. <br><br> Go to <a href='". htmlspecialchars(url_dir(getRequestUri())). "'>homepage</a>.";
@ -178,358 +178,358 @@ printFooter();
function createAllTables($db, $dbtype)
{
if ($dbtype == 'mysql') {
createMysqlTables($db);
}
else {
createSqliteTables($db);
}
if ($dbtype == 'mysql') {
createMysqlTables($db);
}
else {
createSqliteTables($db);
}
}
function createMysqlTables($db)
{
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}lists (
`id` INT UNSIGNED NOT NULL auto_increment,
`uuid` CHAR(36) NOT NULL default '',
`ow` INT NOT NULL default 0,
`name` VARCHAR(50) NOT NULL default '',
`d_created` INT UNSIGNED NOT NULL default 0,
`d_edited` INT UNSIGNED NOT NULL default 0,
`sorting` TINYINT UNSIGNED NOT NULL default 0,
`published` TINYINT UNSIGNED NOT NULL default 0,
`taskview` INT UNSIGNED NOT NULL default 0,
PRIMARY KEY(`id`),
UNIQUE KEY(`uuid`)
`id` INT UNSIGNED NOT NULL auto_increment,
`uuid` CHAR(36) NOT NULL default '',
`ow` INT NOT NULL default 0,
`name` VARCHAR(50) NOT NULL default '',
`d_created` INT UNSIGNED NOT NULL default 0,
`d_edited` INT UNSIGNED NOT NULL default 0,
`sorting` TINYINT UNSIGNED NOT NULL default 0,
`published` TINYINT UNSIGNED NOT NULL default 0,
`taskview` INT UNSIGNED NOT NULL default 0,
PRIMARY KEY(`id`),
UNIQUE KEY(`uuid`)
) CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}todolist (
`id` INT UNSIGNED NOT NULL auto_increment,
`uuid` CHAR(36) NOT NULL default '',
`list_id` INT UNSIGNED NOT NULL default 0,
`d_created` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`d_completed` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`d_edited` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`compl` TINYINT UNSIGNED NOT NULL default 0,
`title` VARCHAR(250) NOT NULL,
`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`),
UNIQUE KEY(`uuid`)
`id` INT UNSIGNED NOT NULL auto_increment,
`uuid` CHAR(36) NOT NULL default '',
`list_id` INT UNSIGNED NOT NULL default 0,
`d_created` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`d_completed` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`d_edited` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`compl` TINYINT UNSIGNED NOT NULL default 0,
`title` VARCHAR(250) NOT NULL,
`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`),
UNIQUE KEY(`uuid`)
) CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}tags (
`id` INT UNSIGNED NOT NULL auto_increment,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY(`id`),
UNIQUE KEY `name` (`name`)
`id` INT UNSIGNED NOT NULL auto_increment,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY(`id`),
UNIQUE KEY `name` (`name`)
) CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}tag2task (
`tag_id` INT UNSIGNED NOT NULL,
`task_id` INT UNSIGNED NOT NULL,
`list_id` INT UNSIGNED NOT NULL,
KEY(`tag_id`),
KEY(`task_id`),
KEY(`list_id`) /* for tagcloud */
`tag_id` INT UNSIGNED NOT NULL,
`task_id` INT UNSIGNED NOT NULL,
`list_id` INT UNSIGNED NOT NULL,
KEY(`tag_id`),
KEY(`task_id`),
KEY(`list_id`) /* for tagcloud */
) CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci ");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}settings (
`param_key` VARCHAR(100) NOT NULL default '',
`param_value` TEXT,
UNIQUE KEY `param_key` (`param_key`)
`param_key` VARCHAR(100) NOT NULL default '',
`param_value` TEXT,
UNIQUE KEY `param_key` (`param_key`)
) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}sessions (
`id` VARCHAR(64) NOT NULL default '', /* upto 64 bytes for sha256 */
`data` TEXT,
`last_access` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`expires` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
UNIQUE KEY `id` (`id`)
`id` VARCHAR(64) NOT NULL default '', /* upto 64 bytes for sha256 */
`data` TEXT,
`last_access` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
`expires` INT UNSIGNED NOT NULL default 0, /* time() timestamp */
UNIQUE KEY `id` (`id`)
) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ");
}
function createSqliteTables($db)
{
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}lists (
id INTEGER PRIMARY KEY,
uuid CHAR(36) NOT NULL,
ow INTEGER NOT NULL default 0,
name VARCHAR(50) 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,
published TINYINT UNSIGNED NOT NULL default 0,
taskview INTEGER UNSIGNED NOT NULL default 0
id INTEGER PRIMARY KEY,
uuid CHAR(36) NOT NULL,
ow INTEGER NOT NULL default 0,
name VARCHAR(50) 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,
published TINYINT UNSIGNED NOT NULL default 0,
taskview INTEGER UNSIGNED NOT NULL default 0
) ");
$db->ex("CREATE UNIQUE INDEX lists_uuid ON {$db->prefix}lists (uuid)");
$db->ex("CREATE UNIQUE INDEX lists_uuid ON {$db->prefix}lists (uuid)");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}todolist (
id INTEGER PRIMARY KEY,
uuid CHAR(36) NOT NULL,
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,
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
id INTEGER PRIMARY KEY,
uuid CHAR(36) NOT NULL,
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,
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)");
$db->ex("CREATE UNIQUE INDEX todo_uuid ON {$db->prefix}todolist (uuid)");
$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(
$db->ex(
"CREATE TABLE {$db->prefix}tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(50) NOT NULL COLLATE NOCASE
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(50) NOT NULL COLLATE NOCASE
) ");
$db->ex("CREATE UNIQUE INDEX tags_name ON {$db->prefix}tags (name COLLATE NOCASE)");
$db->ex("CREATE UNIQUE INDEX tags_name ON {$db->prefix}tags (name COLLATE NOCASE)");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}tag2task (
tag_id INTEGER NOT NULL,
task_id INTEGER NOT NULL,
list_id INTEGER NOT NULL
tag_id INTEGER NOT NULL,
task_id INTEGER NOT NULL,
list_id INTEGER NOT NULL
) ");
$db->ex("CREATE INDEX tag2task_tag_id ON {$db->prefix}tag2task (tag_id)");
$db->ex("CREATE INDEX tag2task_task_id ON {$db->prefix}tag2task (task_id)");
$db->ex("CREATE INDEX tag2task_list_id ON {$db->prefix}tag2task (list_id)"); /* for tagcloud */
$db->ex("CREATE INDEX tag2task_tag_id ON {$db->prefix}tag2task (tag_id)");
$db->ex("CREATE INDEX tag2task_task_id ON {$db->prefix}tag2task (task_id)");
$db->ex("CREATE INDEX tag2task_list_id ON {$db->prefix}tag2task (list_id)"); /* for tagcloud */
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}settings (
param_key VARCHAR(100) NOT NULL default '',
param_value TEXT
param_key VARCHAR(100) NOT NULL default '',
param_value TEXT
) ");
$db->ex("CREATE UNIQUE INDEX settings_key ON {$db->prefix}settings (param_key COLLATE NOCASE)");
$db->ex("CREATE UNIQUE INDEX settings_key ON {$db->prefix}settings (param_key COLLATE NOCASE)");
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}sessions (
id VARCHAR(64) NOT NULL default '',
data TEXT,
last_access INTEGER UNSIGNED NOT NULL default 0,
expires INTEGER UNSIGNED NOT NULL default 0
id VARCHAR(64) NOT NULL default '',
data TEXT,
last_access INTEGER UNSIGNED NOT NULL default 0,
expires INTEGER UNSIGNED NOT NULL default 0
) ");
$db->ex("CREATE UNIQUE INDEX sessions_id ON {$db->prefix}sessions (id COLLATE NOCASE)");
$db->ex("CREATE UNIQUE INDEX sessions_id ON {$db->prefix}sessions (id COLLATE NOCASE)");
}
function databaseVersion(Database_Abstract $db): string
{
if ( !$db ) return '';
if ( !$db->tableExists($db->prefix.'todolist') ) return '';
$v = '1.0';
if ( !$db->tableExists($db->prefix.'tags') ) return $v;
$v = '1.1';
if ( !$db->tableFieldExists($db->prefix.'todolist', 'duedate') ) return $v;
$v = '1.2';
if ( !$db->tableExists($db->prefix.'lists') ) return $v;
$v = '1.3.0';
if ( !$db->tableFieldExists($db->prefix.'todolist', 'd_completed') ) return $v;
$v = '1.3.1';
if ( !$db->tableFieldExists($db->prefix.'todolist', 'd_edited') ) return $v;
$v = '1.4';
if ( !$db->tableExists($db->prefix.'settings') ) return $v;
$v = '1.7';
return $v;
if ( !$db ) return '';
if ( !$db->tableExists($db->prefix.'todolist') ) return '';
$v = '1.0';
if ( !$db->tableExists($db->prefix.'tags') ) return $v;
$v = '1.1';
if ( !$db->tableFieldExists($db->prefix.'todolist', 'duedate') ) return $v;
$v = '1.2';
if ( !$db->tableExists($db->prefix.'lists') ) return $v;
$v = '1.3.0';
if ( !$db->tableFieldExists($db->prefix.'todolist', 'd_completed') ) return $v;
$v = '1.3.1';
if ( !$db->tableFieldExists($db->prefix.'todolist', 'd_edited') ) return $v;
$v = '1.4';
if ( !$db->tableExists($db->prefix.'settings') ) return $v;
$v = '1.7';
return $v;
}
function exitMessage($s)
{
echo $s;
printFooter();
exit;
echo $s;
printFooter();
exit;
}
function printFooter()
{
echo "</body></html>";
echo "</body></html>";
}
function tryToSaveDbConfig()
{
if (!file_exists(MTTPATH.'config.php')) {
@touch(MTTPATH.'config.php');
}
if (!is_writable(MTTPATH.'config.php')) {
exitMessage("Database connection config file ('config.php') is not writable. You need to edit it manually, set contents to this and run setup once more. <br><br> \n".
"<textarea id='contents' style='width:90%; min-height:300px;'>\n".
htmlspecialchars(Config::dbConfigAsFileContents()).
"</textarea>\n".
"<script type='text/javascript'>document.getElementById('contents').select();</script>"
);
}
Config::saveDbConfig();
if (!file_exists(MTTPATH.'config.php')) {
@touch(MTTPATH.'config.php');
}
if (!is_writable(MTTPATH.'config.php')) {
exitMessage("Database connection config file ('config.php') is not writable. You need to edit it manually, set contents to this and run setup once more. <br><br> \n".
"<textarea id='contents' style='width:90%; min-height:300px;'>\n".
htmlspecialchars(Config::dbConfigAsFileContents()).
"</textarea>\n".
"<script type='text/javascript'>document.getElementById('contents').select();</script>"
);
}
Config::saveDbConfig();
}
function testConnect(&$error)
{
$db = null;
try
{
if (!defined('MTT_DB_TYPE')) throw new Exception("MTT_DB_TYPE is not defined");
$db = null;
try
{
if (!defined('MTT_DB_TYPE')) throw new Exception("MTT_DB_TYPE is not defined");
if (MTT_DB_TYPE == 'mysql')
{
$hasPDO = false;
$hasMysqli = false;
if (defined('PDO::MYSQL_ATTR_FOUND_ROWS')) {
$hasPDO = true;
}
if (function_exists("mysqli_connect")) {
$hasMysqli = true;
}
if (MTT_DB_TYPE == 'mysql')
{
$hasPDO = false;
$hasMysqli = false;
if (defined('PDO::MYSQL_ATTR_FOUND_ROWS')) {
$hasPDO = true;
}
if (function_exists("mysqli_connect")) {
$hasMysqli = true;
}
$driver = '';
if (defined('MTT_DB_DRIVER')) {
// forced to use specific mysql interface
if ( in_array(MTT_DB_DRIVER, ['mysqli', 'pdo', '']) ) {
$driver = MTT_DB_DRIVER;
if ($driver == '') $driver = 'pdo'; // default
}
else {
throw new Exception("Unknown database driver");
}
}
$driver = '';
if (defined('MTT_DB_DRIVER')) {
// forced to use specific mysql interface
if ( in_array(MTT_DB_DRIVER, ['mysqli', 'pdo', '']) ) {
$driver = MTT_DB_DRIVER;
if ($driver == '') $driver = 'pdo'; // default
}
else {
throw new Exception("Unknown database driver");
}
}
if ($driver == '') {
// auto-detect driver
if ($hasPDO) $driver = 'pdo';
else if ($hasMysqli) $driver = 'mysqli';
}
if ($driver == '') {
// auto-detect driver
if ($hasPDO) $driver = 'pdo';
else if ($hasMysqli) $driver = 'mysqli';
}
if ($driver == 'mysqli') {
if ($hasMysqli) {
require_once(MTTINC. 'class.db.mysqli.php');
if (!defined('MTT_DB_DRIVER')) define('MTT_DB_DRIVER', 'mysqli');
}
else {
throw new Exception("Required PHP extension 'MySQLi' is not installed.");
}
}
else {
if ($hasPDO) {
require_once(MTTINC. 'class.db.mysql.php');
if (!defined('MTT_DB_DRIVER')) define('MTT_DB_DRIVER', ''); // set pdo?
}
else {
throw new Exception("Required PHP extension 'PDO_MySQL' is not installed.");
}
}
if ($driver == 'mysqli') {
if ($hasMysqli) {
require_once(MTTINC. 'class.db.mysqli.php');
if (!defined('MTT_DB_DRIVER')) define('MTT_DB_DRIVER', 'mysqli');
}
else {
throw new Exception("Required PHP extension 'MySQLi' is not installed.");
}
}
else {
if ($hasPDO) {
require_once(MTTINC. 'class.db.mysql.php');
if (!defined('MTT_DB_DRIVER')) define('MTT_DB_DRIVER', ''); // set pdo?
}
else {
throw new Exception("Required PHP extension 'PDO_MySQL' is not installed.");
}
}
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");
}
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_Mysql;
$db->connect( array(
'host' => MTT_DB_HOST,
'user' => MTT_DB_USER,
'password' => MTT_DB_PASSWORD,
'db' => MTT_DB_NAME
));
}
else if (MTT_DB_TYPE == 'sqlite')
{
if (false === $f = @fopen(MTTPATH. 'db/todolist.db', 'a+')) {
throw new Exception("database file is not readable/writable");
}
else {
fclose($f);
}
if (!is_writable(MTTPATH. 'db/')) {
throw new Exception("database directory ('db') is not writable");
}
require_once(MTTINC. 'class.db.sqlite3.php');
$db = new Database_Sqlite3;
$db->connect( array( 'filename' => MTTPATH. 'db/todolist.db' ) );
}
else {
new Exception("Unsupported database type");
}
$db = new Database_Mysql;
$db->connect( array(
'host' => MTT_DB_HOST,
'user' => MTT_DB_USER,
'password' => MTT_DB_PASSWORD,
'db' => MTT_DB_NAME
));
}
else if (MTT_DB_TYPE == 'sqlite')
{
if (false === $f = @fopen(MTTPATH. 'db/todolist.db', 'a+')) {
throw new Exception("database file is not readable/writable");
}
else {
fclose($f);
}
if (!is_writable(MTTPATH. 'db/')) {
throw new Exception("database directory ('db') is not writable");
}
require_once(MTTINC. 'class.db.sqlite3.php');
$db = new Database_Sqlite3;
$db->connect( array( 'filename' => MTTPATH. 'db/todolist.db' ) );
}
else {
new Exception("Unsupported database type");
}
if (!defined('MTT_DB_PREFIX')) define('MTT_DB_PREFIX', '');
$db->setPrefix(MTT_DB_PREFIX);
}
catch(Exception $e) {
//if (MTT_DEBUG) throw $e;
$error = $e->getMessage();
return null;
}
$error = '';
return $db;
if (!defined('MTT_DB_PREFIX')) define('MTT_DB_PREFIX', '');
$db->setPrefix(MTT_DB_PREFIX);
}
catch(Exception $e) {
//if (MTT_DEBUG) throw $e;
$error = $e->getMessage();
return null;
}
$error = '';
return $db;
}
function debugExceptionHandler($e)
{
echo '<br><b>Error ('. htmlspecialchars(get_class($e)) .'):</b> \''. htmlspecialchars($e->getMessage()) .'\' in <i>'. htmlspecialchars($e->getFile() .':'. $e->getLine()). '</i>'.
"\n<pre>". htmlspecialchars($e->getTraceAsString()) . "</pre>\n";
exit;
echo '<br><b>Error ('. htmlspecialchars(get_class($e)) .'):</b> \''. htmlspecialchars($e->getMessage()) .'\' in <i>'. htmlspecialchars($e->getFile() .':'. $e->getLine()). '</i>'.
"\n<pre>". htmlspecialchars($e->getTraceAsString()) . "</pre>\n";
exit;
}
function myExceptionHandler($e)
{
echo '<br><b>Error:</b> '. htmlspecialchars($e->getMessage()) ;
exit;
echo '<br><b>Error:</b> '. htmlspecialchars($e->getMessage()) ;
exit;
}
### update v1.4 to v1.7 ##########
function update_14_17(Database_Abstract $db, $dbtype)
{
$db->ex("BEGIN");
$db->ex("BEGIN");
if($dbtype=='mysql')
{
# convert charset to utf8mb4
if($dbtype=='mysql')
{
# convert charset to utf8mb4
$db->ex("ALTER TABLE {$db->prefix}lists CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}todolist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}tags CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}tag2task CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}lists CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}todolist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}tags CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
$db->ex("ALTER TABLE {$db->prefix}tag2task CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
# create settings table
# create settings table
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}settings (
`param_key` VARCHAR(100) NOT NULL default '',
`param_value` TEXT,
UNIQUE KEY `param_key` (`param_key`)
) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ");
# create sessions table
# create sessions table
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}sessions (
`id` VARCHAR(64) NOT NULL default '',
`data` TEXT,
@ -538,20 +538,20 @@ UNIQUE KEY `param_key` (`param_key`)
UNIQUE KEY `id` (`id`)
) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ");
}
}
else #sqlite
{
$db->ex(
else #sqlite
{
$db->ex(
"CREATE TABLE {$db->prefix}settings (
param_key VARCHAR(100) NOT NULL default '',
param_value TEXT
) ");
$db->ex("CREATE UNIQUE INDEX settings_key ON {$db->prefix}settings (param_key COLLATE NOCASE)");
$db->ex("CREATE UNIQUE INDEX settings_key ON {$db->prefix}settings (param_key COLLATE NOCASE)");
# sessions
# sessions
$db->ex(
$db->ex(
"CREATE TABLE {$db->prefix}sessions (
id VARCHAR(100) NOT NULL default '',
data TEXT,
@ -559,12 +559,12 @@ UNIQUE KEY `id` (`id`)
expires INTEGER UNSIGNED NOT NULL default 0
) ");
$db->ex("CREATE UNIQUE INDEX sessions_id ON {$db->prefix}sessions (id COLLATE NOCASE)");
}
$db->ex("CREATE UNIQUE INDEX sessions_id ON {$db->prefix}sessions (id COLLATE NOCASE)");
}
$db->ex("COMMIT");
$db->ex("COMMIT");
Config::save();
Config::saveDbConfig();
Config::save();
Config::saveDbConfig();
}
### end of 1.7 #####