mirror of
https://github.com/maxpozdeev/mytinytodo.git
synced 2026-03-11 08:55:27 +00:00
* implement feed key to allow to get an export or rss of password-protected lists (just add &key=YOUR_KEY to the url)
This commit is contained in:
parent
4d481d6013
commit
46de0c09de
10 changed files with 167 additions and 46 deletions
|
|
@ -180,7 +180,7 @@ function checkWriteAccess(?int $listId = null)
|
|||
jsonExit( array('total'=>0, 'list'=>array(), 'denied'=>1) );
|
||||
}
|
||||
|
||||
function haveWriteAccess(?int $listId = null)
|
||||
function haveWriteAccess(?int $listId = null) : bool
|
||||
{
|
||||
if (is_readonly()) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -940,9 +940,9 @@ var mytinytodo = window.mytinytodo = _mtt = {
|
|||
|
||||
urlForFeed: function(list)
|
||||
{
|
||||
var l = list || curList;
|
||||
if (l === undefined) return '';
|
||||
return _mtt.mttUrl + 'feed.php?list='+l.id;
|
||||
list = list || curList;
|
||||
if (list === undefined) return '';
|
||||
return _mtt.mttUrl + 'feed.php?list=' + list.id;
|
||||
},
|
||||
|
||||
urlForSettings: function(json = 0)
|
||||
|
|
@ -1018,6 +1018,35 @@ function publishCurList()
|
|||
});
|
||||
};
|
||||
|
||||
function enableFeedKeyInCurList()
|
||||
{
|
||||
if (!curList) return false;
|
||||
_mtt.db.request('enableFeedKey', {
|
||||
list: curList.id,
|
||||
enable: (curList.feedKey === undefined || curList.feedKey === '') ? 1 : 0
|
||||
}, function(json){
|
||||
if (!parseInt(json.total)) return;
|
||||
var item = json.list[0];
|
||||
curList.feedKey = item.feedKey;
|
||||
if (curList.feedKey) {
|
||||
$('#btnFeedKey').addClass('mtt-item-checked');
|
||||
$('#btnShowFeedKey').removeClass('mtt-item-disabled');
|
||||
alert(curList.feedKey);
|
||||
}
|
||||
else {
|
||||
$('#btnFeedKey').removeClass('mtt-item-checked');
|
||||
$('#btnShowFeedKey').addClass('mtt-item-disabled');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function showFeedKeyInCurList()
|
||||
{
|
||||
if (!curList) return false;
|
||||
if (curList.feedKey === undefined || curList.feedKey === '') return false;
|
||||
alert(curList.feedKey);
|
||||
};
|
||||
|
||||
|
||||
function loadTasks(opts)
|
||||
{
|
||||
|
|
@ -1504,6 +1533,8 @@ function listMenuClick(el, menu)
|
|||
case 'btnRenameList': renameCurList(); break;
|
||||
case 'btnDeleteList': deleteCurList(); break;
|
||||
case 'btnPublish': publishCurList(); break;
|
||||
case 'btnFeedKey': enableFeedKeyInCurList(); break;
|
||||
case 'btnShowFeedKey': showFeedKeyInCurList(); break;
|
||||
case 'btnHideList': hideList(curList.id); break;
|
||||
case 'btnExportCSV': return true;
|
||||
case 'btnExportICAL': return true;
|
||||
|
|
@ -2258,7 +2289,7 @@ function cmenuOnListHidden(list)
|
|||
|
||||
function tabmenuOnListSelected(list)
|
||||
{
|
||||
if(list.published) {
|
||||
if (list.published) {
|
||||
$('#btnPublish').addClass('mtt-item-checked');
|
||||
$('#btnRssFeed').removeClass('mtt-item-disabled');
|
||||
}
|
||||
|
|
@ -2266,8 +2297,20 @@ function tabmenuOnListSelected(list)
|
|||
$('#btnPublish').removeClass('mtt-item-checked');
|
||||
$('#btnRssFeed').addClass('mtt-item-disabled');
|
||||
}
|
||||
if(list.showCompl) $('#btnShowCompleted').addClass('mtt-item-checked');
|
||||
else $('#btnShowCompleted').removeClass('mtt-item-checked');
|
||||
if (list.showCompl) {
|
||||
$('#btnShowCompleted').addClass('mtt-item-checked');
|
||||
}
|
||||
else {
|
||||
$('#btnShowCompleted').removeClass('mtt-item-checked');
|
||||
}
|
||||
if (list.feedKey !== undefined && list.feedKey !== '') {
|
||||
$('#btnFeedKey').addClass('mtt-item-checked');
|
||||
$('#btnShowFeedKey').removeClass('mtt-item-disabled');
|
||||
}
|
||||
else {
|
||||
$('#btnFeedKey').removeClass('mtt-item-checked');
|
||||
$('#btnShowFeedKey').addClass('mtt-item-disabled');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -296,6 +296,21 @@ MytinytodoAjaxApi.prototype =
|
|||
});
|
||||
},
|
||||
|
||||
enableFeedKey: function(params, callback)
|
||||
{
|
||||
$.ajax({
|
||||
url: mtt.apiUrl + 'lists/' + encodeURIComponent(params.list),
|
||||
method: 'PUT',
|
||||
contentType : 'application/json',
|
||||
data: JSON.stringify({
|
||||
action: 'enableFeedKey',
|
||||
enable: params.enable,
|
||||
}),
|
||||
success: callback,
|
||||
dataType: 'json'
|
||||
});
|
||||
},
|
||||
|
||||
setShowNotesInList: function(params, callback)
|
||||
{
|
||||
$.ajax({
|
||||
|
|
|
|||
|
|
@ -9,14 +9,19 @@
|
|||
//$dontStartSession = 1;
|
||||
require_once('./init.php');
|
||||
|
||||
$onlyPublishedList = false;
|
||||
if(!have_write_access()) $onlyPublishedList = true;
|
||||
|
||||
$listId = (int)_get('list');
|
||||
$db = DBConnection::instance();
|
||||
$listData = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$listId ". ($onlyPublishedList ? "AND published=1" : "") );
|
||||
if(!$listData) {
|
||||
die("No such list or access denied");
|
||||
$listData = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$listId");
|
||||
if ( $listData && !is_logged() && !$listData['published'] ) {
|
||||
$extra = json_decode($listData['extra'] ?? '', true, 10, JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
$feedKey = (string)$extra['feedKey'] ?? '';
|
||||
$inFeedKey = trim(_get('key'));
|
||||
if ($feedKey == '' || $feedKey != $inFeedKey) {
|
||||
die("Access denied.");
|
||||
}
|
||||
}
|
||||
if (!$listData) {
|
||||
die("No list found.");
|
||||
}
|
||||
|
||||
$sqlSort = "ORDER BY compl ASC, ";
|
||||
|
|
@ -37,13 +42,6 @@ if($format == 'ical') printICal($listData, $data);
|
|||
else printCSV($listData, $data);
|
||||
|
||||
|
||||
function have_write_access()
|
||||
{
|
||||
if(is_logged()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function printCSV($listData, $data)
|
||||
{
|
||||
$s = "\xEF\xBB\xBF". "Completed;Priority;Task;Notes;Tags;Due;DateCreated;DateCompleted\n";
|
||||
|
|
|
|||
13
src/feed.php
13
src/feed.php
|
|
@ -15,11 +15,16 @@ $lang = Lang::instance();
|
|||
$listId = (int)_get('list');
|
||||
$db = DBConnection::instance();
|
||||
$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.");
|
||||
if ( $listData && need_auth() && !$listData['published'] ) {
|
||||
$extra = json_decode($listData['extra'] ?? '', true, 10, JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
$feedKey = (string)$extra['feedKey'] ?? '';
|
||||
$inFeedKey = trim(_get('key'));
|
||||
if ($feedKey == '' || $feedKey != $inFeedKey) {
|
||||
die("Access denied!<br> List is not published.");
|
||||
}
|
||||
}
|
||||
if(!$listData) {
|
||||
die("No list found");
|
||||
if (!$listData) {
|
||||
die("No list found.");
|
||||
}
|
||||
|
||||
$data = array();
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class ListsController extends ApiController {
|
|||
check_token();
|
||||
$t = array();
|
||||
$t['total'] = 0;
|
||||
if (!is_logged()) {
|
||||
$haveWriteAccess = haveWriteAccess();
|
||||
if (!$haveWriteAccess) {
|
||||
$sqlWhere = 'WHERE published=1';
|
||||
}
|
||||
else {
|
||||
|
|
@ -31,7 +32,7 @@ class ListsController extends ApiController {
|
|||
while ($r = $q->fetchAssoc())
|
||||
{
|
||||
$t['total']++;
|
||||
$t['list'][] = $this->prepareList($r);
|
||||
$t['list'][] = $this->prepareList($r, $haveWriteAccess);
|
||||
}
|
||||
return $t;
|
||||
}
|
||||
|
|
@ -60,7 +61,7 @@ class ListsController extends ApiController {
|
|||
$id = $db->lastInsertId();
|
||||
$t['total'] = 1;
|
||||
$r = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$id");
|
||||
$t['list'][] = $this->prepareList($r);
|
||||
$t['list'][] = $this->prepareList($r, true);
|
||||
return $t;
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ class ListsController extends ApiController {
|
|||
if (!$r) {
|
||||
return null;
|
||||
}
|
||||
$t = $this->prepareList($r);
|
||||
$t = $this->prepareList($r, haveWriteAccess());
|
||||
return $t;
|
||||
}
|
||||
|
||||
|
|
@ -142,6 +143,7 @@ class ListsController extends ApiController {
|
|||
case 'rename': return $this->renameList($id); break;
|
||||
case 'sort': return $this->sortList($id); break;
|
||||
case 'publish': return $this->publishList($id); break;
|
||||
case 'enableFeedKey': return $this->enableFeedKey($id); break;
|
||||
case 'showNotes': return $this->showNotes($id); break;
|
||||
case 'hide': return $this->hideList($id); break;
|
||||
case 'clearCompleted': return $this->clearCompleted($id); break;
|
||||
|
|
@ -172,12 +174,23 @@ class ListsController extends ApiController {
|
|||
'showCompl' => $showCompleted,
|
||||
'showNotes' => 0,
|
||||
'hidden' => $hidden,
|
||||
'feedKey' => '',
|
||||
);
|
||||
}
|
||||
|
||||
private function prepareList($row)
|
||||
private function prepareList($row, bool $haveWriteAccess)
|
||||
{
|
||||
$taskview = (int)$row['taskview'];
|
||||
$feedKey = '';
|
||||
if ($haveWriteAccess) {
|
||||
$extra = json_decode($row['extra'] ?? '', true, 10, JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
if ($extra === false) {
|
||||
error_log("Failed to decodes JSON data of list extra listId=". (int)$row['id'] . ": " . json_last_error_msg());
|
||||
$extra = [];
|
||||
}
|
||||
$feedKey = (string)$extra['feedKey'] ?? '';
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $row['id'],
|
||||
'name' => htmlarray($row['name']),
|
||||
|
|
@ -186,6 +199,7 @@ class ListsController extends ApiController {
|
|||
'showCompl' => $taskview & 1 ? 1 : 0,
|
||||
'showNotes' => $taskview & 2 ? 1 : 0,
|
||||
'hidden' => $taskview & 4 ? 1 : 0,
|
||||
'feedKey' => $feedKey,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +216,7 @@ class ListsController extends ApiController {
|
|||
$db->dq("UPDATE {$db->prefix}lists SET name=?,d_edited=? WHERE id=$id", array($name, time()) );
|
||||
$t['total'] = $db->affected();
|
||||
$r = $db->sqa("SELECT * FROM {$db->prefix}lists WHERE id=$id");
|
||||
$t['list'][] = $this->prepareList($r);
|
||||
$t['list'][] = $this->prepareList($r, true);
|
||||
return $t;
|
||||
}
|
||||
|
||||
|
|
@ -250,6 +264,33 @@ class ListsController extends ApiController {
|
|||
return ['total'=>1];
|
||||
}
|
||||
|
||||
private function enableFeedKey(int $listId)
|
||||
{
|
||||
$db = DBConnection::instance();
|
||||
$flag = (int)($this->req->jsonBody['enable'] ?? 0);
|
||||
$json = $db->sq("SELECT extra FROM {$db->prefix}lists WHERE id=$listId") ?? '';
|
||||
$extra = strlen($json) > 0 ? json_decode($json, true, 10, JSON_INVALID_UTF8_SUBSTITUTE) : [];
|
||||
if ($extra === false) {
|
||||
error_log("Failed to decodes JSON data of list extra listId=$listId: " . json_last_error_msg());
|
||||
$extra = [];
|
||||
}
|
||||
if ($flag == 0) {
|
||||
$extra['feedKey'] = '';
|
||||
}
|
||||
else {
|
||||
$extra['feedKey'] = randomString();
|
||||
}
|
||||
$json = json_encode($extra, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$db->ex("UPDATE {$db->prefix}lists SET extra=?,d_edited=? WHERE id=$listId", array($json, time()));
|
||||
return [
|
||||
'total' => 1,
|
||||
'list' => [[
|
||||
'id' => $listId,
|
||||
'feedKey' => $extra['feedKey']
|
||||
]]
|
||||
];
|
||||
}
|
||||
|
||||
private function showNotes(int $listId)
|
||||
{
|
||||
$db = DBConnection::instance();
|
||||
|
|
|
|||
|
|
@ -175,3 +175,15 @@ function isValidSignature(string $signature, string $id, string $key, string $sa
|
|||
if ( hash_equals($signature, idSignature($id, $key, $salt)) ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function randomString(int $len = 16) : string
|
||||
{
|
||||
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$a = [];
|
||||
$max = strlen($chars) - 1;
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$a[]= $chars[random_int(0, $max)];
|
||||
}
|
||||
return implode('', $a);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,14 +123,16 @@
|
|||
"list_new": "New list",
|
||||
"list_rename": "Rename list",
|
||||
"list_delete": "Delete list",
|
||||
"list_publish": "Publish list",
|
||||
"list_showcompleted": "Show completed tasks",
|
||||
"list_clearcompleted": "Clear completed tasks",
|
||||
"list_select": "Select list",
|
||||
"list_export": "Export",
|
||||
"list_export_csv": "CSV",
|
||||
"list_export_ical": "iCalendar",
|
||||
"list_rssfeed": "RSS Feed",
|
||||
"list_share": "Share",
|
||||
"list_publish": "Publish list",
|
||||
"list_enable_feedkey": "Enable Feed Key",
|
||||
"list_show_feedkey": "Show Feed Key",
|
||||
"list_rssfeed": "RSS Feed",
|
||||
"list_export_to_csv": "Export to CSV",
|
||||
"list_export_to_ical": "Export to iCalendar",
|
||||
"list_hide": "Hide list",
|
||||
"alltags": "All tags:",
|
||||
"alltags_show": "Show all",
|
||||
|
|
|
|||
|
|
@ -123,14 +123,16 @@
|
|||
"list_new": "Новый список",
|
||||
"list_rename": "Переименовать список",
|
||||
"list_delete": "Удалить список",
|
||||
"list_publish": "Опубликовать список",
|
||||
"list_showcompleted": "Показать завершенные задачи",
|
||||
"list_clearcompleted": "Удалить завершенные задачи",
|
||||
"list_select": "Выбрать список",
|
||||
"list_export": "Экспортировать",
|
||||
"list_export_csv": "CSV",
|
||||
"list_export_ical": "iCalendar",
|
||||
"list_rssfeed": "RSS-лента",
|
||||
"list_share": "Поделиться",
|
||||
"list_publish": "Опубликовать список",
|
||||
"list_enable_feedkey": "Включить доступ по ключу",
|
||||
"list_show_feedkey": "Показать ключ",
|
||||
"list_rssfeed": "RSS-лента",
|
||||
"list_export_to_csv": "Экспортировать в CSV",
|
||||
"list_export_to_ical": "Экспортировать в iCalendar",
|
||||
"list_hide": "Скрыть список",
|
||||
"alltags": "Все теги:",
|
||||
"alltags_show": "Показать все",
|
||||
|
|
|
|||
|
|
@ -221,11 +221,9 @@ $().ready(function(){
|
|||
<li class="mtt-need-list mtt-need-real-list" id="btnRenameList"><?php _e('list_rename');?></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnDeleteList"><?php _e('list_delete');?></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnClearCompleted"><?php _e('list_clearcompleted');?></li>
|
||||
<li class="mtt-need-list mtt-need-real-list mtt-menu-indicator" submenu="listexportmenucontainer"><div class="submenu-icon"></div><?php _e('list_export'); ?></li>
|
||||
<li class="mtt-need-list" id="btnHideList"><?php _e('list_hide');?></li>
|
||||
<li class="mtt-menu-delimiter"></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnPublish"><div class="menu-icon"></div><?php _e('list_publish');?></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnRssFeed"><div class="menu-icon"></div><a href="#"><?php _e('list_rssfeed');?></a></li>
|
||||
<li class="mtt-need-list mtt-need-real-list mtt-menu-indicator" submenu="listsharemenucontainer"><div class="submenu-icon"></div><?php _e('list_share'); ?></li>
|
||||
<li class="mtt-menu-delimiter mtt-need-real-list"></li>
|
||||
<li class="mtt-need-list mtt-need-real-list sort-item" id="sortByHand"><div class="menu-icon"></div><?php _e('sortByHand');?> <span class="mtt-sort-direction"></span></li>
|
||||
<li class="mtt-need-list sort-item" id="sortByDateCreated"><div class="menu-icon"></div><?php _e('sortByDateCreated');?> <span class="mtt-sort-direction"></span></li>
|
||||
|
|
@ -237,10 +235,15 @@ $().ready(function(){
|
|||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="listexportmenucontainer" class="mtt-menu-container" style="display:none">
|
||||
<div id="listsharemenucontainer" class="mtt-menu-container" style="display:none">
|
||||
<ul>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnExportCSV"><a href="#"><?php _e('list_export_csv');?></a></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnExportICAL"><a href="#"><?php _e('list_export_ical');?></a></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnPublish"><div class="menu-icon"></div><?php _e('list_publish');?></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnRssFeed"><div class="menu-icon"></div><a href="#"><?php _e('list_rssfeed');?></a></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnExportCSV"><a href="#"><?php _e('list_export_to_csv');?></a></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnExportICAL"><a href="#"><?php _e('list_export_to_ical');?></a></li>
|
||||
<li class="mtt-menu-delimiter"></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnFeedKey"><div class="menu-icon"></div><?php _e('list_enable_feedkey');?></li>
|
||||
<li class="mtt-need-list mtt-need-real-list" id="btnShowFeedKey"><div class="menu-icon"></div><?php _e('list_show_feedkey');?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue