diff --git a/index.php b/index.php
index 4e14dc3..582efa6 100644
--- a/index.php
+++ b/index.php
@@ -77,21 +77,12 @@
:
-
:
diff --git a/lang/de-DE.ini b/lang/de-DE.ini
index f03a812..357c483 100644
--- a/lang/de-DE.ini
+++ b/lang/de-DE.ini
@@ -12,19 +12,11 @@ EFFORT="Aufwand"
EFFORT_REMARK="(in Stunden)"
RECURRENCE="Wiederholung"
DONT_REPEAT="Nicht wiederholen"
-REPEAT_EVERY_THREE_DAYS="Alle 3 Tage"
-REPEAT_WEEKLY="Wöchentlich"
-REPEAT_BIWEEKLY="Zwei-Wöchentlich"
-REPEAT_MONTHLY="Monatlich"
-REPEAT_BIMONTHLY="Zwei-Monatlich"
-REPEAT_QUARTERLY="Einmal pro Quartal"
-REPEAT_THIRDOFYEARLY="Einmal pro Jahresdrittel"
-REPEAT_HALFYEARLY="Halbjährlich"
-REPEAT_YEARLY="Jährlich"
-REPEAT_BIYEARLY="Alle zwei Jahre"
-REPEAT_THREEYEARLY="Alle drei Jahre"
-REPEAT_FOURYEARLY="Alle vier Jahre"
-REPEAT_FIVEYEARLY="Alle fünf Jahre"
+RECURRENCE_EVERY="Alle"
+REPEAT_UNIT_DAYS="Tag(e)"
+REPEAT_UNIT_WEEKS="Woche(n)"
+REPEAT_UNIT_MONTHS="Monat(e)"
+REPEAT_UNIT_YEARS="Jahr(e)"
RECURRENCE_ANCHOR="Beginnt ab"
REPEAT_ANCHOR_COMPLETIONDATE="Fertigstellungs-Datum"
REPEAT_ANCHOR_ENDDATE="Eingetragenes End-Datum"
@@ -122,6 +114,7 @@ DOTODAY="Heute erledigen"
EDITED_TAG_SUCCESSFUL="Tag erfolgreich geändert!"
INVALID_EFFORT="Ungültiger Aufwand!"
INVALID_RECURRENCE_MODE="Ungültige Wiederholung!"
+INVALID_RECURRENCE_INTERVAL="Ungültiges Wiederholungs-Intervall!"
INVALID_RECURRENCE_ANCHOR="Ungültiger Wiederholungs-Bezugspunkt!"
DATABASE_ERROR="Datenbankfehler!"
METHOD_NOT_ALLOWED="Methode nicht erlaubt!"
diff --git a/lang/en-US.ini b/lang/en-US.ini
index a42210c..639460b 100644
--- a/lang/en-US.ini
+++ b/lang/en-US.ini
@@ -12,19 +12,11 @@ EFFORT="Effort"
EFFORT_REMARK="(in hours)"
RECURRENCE="Recurrence"
DONT_REPEAT="Don't repeat"
-REPEAT_EVERY_THREE_DAYS="Every 3 days"
-REPEAT_WEEKLY="Weekly"
-REPEAT_BIWEEKLY="Bi-weekly"
-REPEAT_MONTHLY="Monthly"
-REPEAT_BIMONTHLY="Bi-monthly"
-REPEAT_QUARTERLY="Once per quarter"
-REPEAT_THIRDOFYEARLY="Once per a third of the year"
-REPEAT_HALFYEARLY="Once every half year"
-REPEAT_YEARLY="Once per year"
-REPEAT_BIYEARLY="Every two years"
-REPEAT_THREEYEARLY="Every three years"
-REPEAT_FOURYEARLY="Every four years"
-REPEAT_FIVEYEARLY="Every five years"
+RECURRENCE_EVERY="Every"
+REPEAT_UNIT_DAYS="day(s)"
+REPEAT_UNIT_WEEKS="week(s)"
+REPEAT_UNIT_MONTHS="month(s)"
+REPEAT_UNIT_YEARS="year(s)"
RECURRENCE_ANCHOR="Starts with"
REPEAT_ANCHOR_COMPLETIONDATE="Completion date"
REPEAT_ANCHOR_ENDDATE="Assigned end date"
@@ -122,6 +114,7 @@ DOTODAY="Do today"
EDITED_TAG_SUCCESSFUL="Tag changed successfully!"
INVALID_EFFORT="Invalid effort!"
INVALID_RECURRENCE_MODE="Invalid recurrence mode!"
+INVALID_RECURRENCE_INTERVAL="Invalid recurrence interval!"
INVALID_RECURRENCE_ANCHOR="Invalid recurrence anchor!"
DATABASE_ERROR="Database error!"
METHOD_NOT_ALLOWED="Method not allowed!"
diff --git a/queries/reactivate-temp.php b/queries/reactivate-temp.php
index 54a5d29..7b6e6e4 100644
--- a/queries/reactivate-temp.php
+++ b/queries/reactivate-temp.php
@@ -1,4 +1,5 @@
fetch_array()[0];
@@ -7,18 +8,17 @@
{
// new due date: recurrence interval after completion (anchor 0) or after due date (anchor 1);
// start date keeps its distance to the due date (or equals the due date if there is none)
+ $nextDue = recurrenceNextSql("IF(recurrenceAnchor=0, completionDate, dueDate)");
dbExec($db, "INSERT INTO todo ".
"(creationDate, description, startDate, completed, ".
- "dueDate, notes, version, recurrenceMode, recurrenceAnchor, list_id) ".
+ "dueDate, notes, version, recurrenceMode, recurrenceInterval, recurrenceAnchor, list_id) ".
"SELECT ?, description, ".
- "COALESCE(DATE_SUB(DATE_ADD(IF(recurrenceAnchor=0, completionDate, dueDate), ".
- "INTERVAL recurrenceMode DAY), INTERVAL DATEDIFF(dueDate, startDate) DAY), ".
- "DATE_ADD(IF(recurrenceAnchor=0, completionDate, dueDate), INTERVAL recurrenceMode DAY)), ".
+ "COALESCE(DATE_SUB(".$nextDue.", INTERVAL DATEDIFF(dueDate, startDate) DAY), ".$nextDue."), ".
"0, ". // completed
- "DATE_ADD(IF(recurrenceAnchor=0, completionDate, dueDate), INTERVAL recurrenceMode DAY), ".
+ $nextDue.", ".
"notes, ".
"1, ". // version
- "recurrenceMode, recurrenceAnchor, list_id ".
+ "recurrenceMode, recurrenceInterval, recurrenceAnchor, list_id ".
"FROM reviving WHERE id=?",
array($creationDate, $toReactivate->id));
diff --git a/queries/reactivate.php b/queries/reactivate.php
index b7c76ab..36841c8 100644
--- a/queries/reactivate.php
+++ b/queries/reactivate.php
@@ -2,26 +2,17 @@
// recurring events reactivation
// currently called from query.php
// would theoretically be enough to do this once per day or so:
+ require_once(__DIR__."/recurrence.php");
$sql = "CREATE TEMPORARY TABLE reviving AS ".
"SELECT * FROM todo t WHERE completed=1 and ".
"recurrenceMode != 0 AND ((".
"recurrenceAnchor = 0 AND ".
- "DATEDIFF(".
- "DATE_ADD(".
- "completionDate, ".
- "INTERVAL recurrenceMode DAY".
- "),".
- "UTC_DATE()".
- ") < GREATEST(DATEDIFF(dueDate, startDate), 4) ".
- ") OR (".
- "recurrenceAnchor = 1 AND ".
- "DATEDIFF(".
- "DATE_ADD(".
- "dueDate, ".
- "INTERVAL recurrenceMode DAY".
- "),".
- "UTC_DATE()".
- ") < GREATEST(DATEDIFF(dueDate, startDate), 4) ".
+ "DATEDIFF(".recurrenceNextSql("completionDate").", UTC_DATE()) ".
+ "< GREATEST(DATEDIFF(dueDate, startDate), 4) ".
+ ") OR (".
+ "recurrenceAnchor = 1 AND ".
+ "DATEDIFF(".recurrenceNextSql("dueDate").", UTC_DATE()) ".
+ "< GREATEST(DATEDIFF(dueDate, startDate), 4) ".
")) AND NOT EXISTS (SELECT 1 FROM recurringCopied r WHERE r.todo_id=t.id);";
$qResult = dbQueryOrDie($db, $sql);
require("reactivate-temp.php");
diff --git a/queries/recurrence.php b/queries/recurrence.php
new file mode 100644
index 0000000..94d67dc
--- /dev/null
+++ b/queries/recurrence.php
@@ -0,0 +1,14 @@
+ 'DAY', 2 => 'WEEK', 3 => 'MONTH', 4 => 'YEAR');
+const MAX_RECURRENCE_INTERVAL = 999;
+
+// SQL expression: $baseExpr plus the recurrence interval of the todo row
+function recurrenceNextSql($baseExpr)
+{
+ $sql = "CASE recurrenceMode ";
+ foreach (RECURRENCE_UNITS as $mode => $unit) {
+ $sql .= "WHEN ".$mode." THEN DATE_ADD(".$baseExpr.", INTERVAL recurrenceInterval ".$unit.") ";
+ }
+ return $sql."END";
+}
diff --git a/queries/todo-list-query.php b/queries/todo-list-query.php
index bc6b93e..20d801a 100644
--- a/queries/todo-list-query.php
+++ b/queries/todo-list-query.php
@@ -3,7 +3,7 @@
function todoListQuery($incomplete = true)
{
return "SELECT todo.id, description as todo, dueDate as due, startDate as start, effort, ".
- "completed, notes, version, recurrenceMode, recurrenceAnchor, completionDate, ".
+ "completed, notes, version, recurrenceMode, recurrenceInterval, recurrenceAnchor, completionDate, ".
"creationDate, deleted, ".
"GROUP_CONCAT( DISTINCT name ORDER BY name SEPARATOR ',') as tags, list_id ".
"FROM todo ".
diff --git a/queries/update.php b/queries/update.php
index fd22de2..ecb50bd 100644
--- a/queries/update.php
+++ b/queries/update.php
@@ -4,6 +4,7 @@
require("db.php");
require("date.php");
require("tags.php");
+ require("recurrence.php");
$id = (int)postParam('id');
$todo = encodeInput(postParam('todo'));
$due = postParam('due');
@@ -13,6 +14,7 @@
$tags = explode(",", postParam('tags'));
$version = (int)postParam('version');
$recurrenceMode = (int)postParam('recurrenceMode');
+ $recurrenceInterval = (int)postParam('recurrenceInterval');
$recurrenceAnchor = (int)postParam('recurrenceAnchor');
$list_id = (int)postParam('list_id');
requireOwnTodo($db, $id);
@@ -39,11 +41,14 @@
echo TodoLang::_("INVALID_EFFORT");
die;
}
- // recurrence interval in days, at most 10 years:
- if ($recurrenceMode < 0 || $recurrenceMode > 3650) {
+ if (!isset(RECURRENCE_UNITS[$recurrenceMode]) && $recurrenceMode != 0) {
echo TodoLang::_("INVALID_RECURRENCE_MODE");
die;
}
+ if ($recurrenceInterval < 1 || $recurrenceInterval > MAX_RECURRENCE_INTERVAL) {
+ echo TodoLang::_("INVALID_RECURRENCE_INTERVAL");
+ die;
+ }
if ($recurrenceAnchor != 0 && $recurrenceAnchor != 1) {
echo TodoLang::_("INVALID_RECURRENCE_ANCHOR");
die;
@@ -56,6 +61,7 @@
"notes=?, ".
"version=?, ".
"recurrenceMode=?, ".
+ "recurrenceInterval=?, ".
"recurrenceAnchor=?, ".
"list_id=? ".
"WHERE id=? AND version=?";
@@ -64,7 +70,7 @@
($start == '') ? NULL : $start,
$effort,
($notes == '') ? NULL : $notes,
- $version+1, $recurrenceMode, $recurrenceAnchor, $list_id,
+ $version+1, $recurrenceMode, $recurrenceInterval, $recurrenceAnchor, $list_id,
$id, $version));
$affectedRows = $stmt->affected_rows;
diff --git a/sql/install.sql b/sql/install.sql
index f29fc82..a6326d4 100644
--- a/sql/install.sql
+++ b/sql/install.sql
@@ -10,6 +10,7 @@ CREATE TABLE `todo` (
`notes` varchar(10000) DEFAULT '',
`version` int(11) NOT NULL DEFAULT 1,
`recurrenceMode` int(11) NOT NULL DEFAULT 0,
+ `recurrenceInterval` int(11) NOT NULL DEFAULT 1,
`recurrenceAnchor` int(1) NOT NULL DEFAULT 0,
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`list_id` int(11) NOT NULL DEFAULT '0',
@@ -47,7 +48,7 @@ CREATE TABLE `settings` (
PRIMARY KEY(`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-INSERT INTO `settings` (`key`, `value`) VALUES ("db.version", "11");
+INSERT INTO `settings` (`key`, `value`) VALUES ("db.version", "12");
-- default list of the default user (todo.list_id defaults to 0):
SET SESSION sql_mode = CONCAT(@@sql_mode, ',NO_AUTO_VALUE_ON_ZERO');
diff --git a/sql/update-012.sql b/sql/update-012.sql
new file mode 100644
index 0000000..a5cbbfd
--- /dev/null
+++ b/sql/update-012.sql
@@ -0,0 +1,24 @@
+-- recurrenceMode used to be an interval in days; it is now the unit
+-- (0 = none, 1 = days, 2 = weeks, 3 = months, 4 = years) and recurrenceInterval the count
+ALTER TABLE `todo` ADD `recurrenceInterval` int(11) NOT NULL DEFAULT 1 AFTER `recurrenceMode`;
+
+-- recurrenceInterval first: it is computed from the old recurrenceMode value
+UPDATE `todo` SET
+ `recurrenceInterval` = CASE
+ WHEN `recurrenceMode` = 0 THEN 1
+ WHEN `recurrenceMode` IN (30, 60) THEN `recurrenceMode` / 30
+ WHEN `recurrenceMode` = 91 THEN 3
+ WHEN `recurrenceMode` = 121 THEN 4
+ WHEN `recurrenceMode` = 182 THEN 6
+ WHEN `recurrenceMode` = 1805 THEN 5
+ WHEN `recurrenceMode` % 365 = 0 THEN `recurrenceMode` / 365
+ WHEN `recurrenceMode` % 7 = 0 THEN `recurrenceMode` / 7
+ ELSE `recurrenceMode` END,
+ `recurrenceMode` = CASE
+ WHEN `recurrenceMode` = 0 THEN 0
+ WHEN `recurrenceMode` IN (30, 60, 91, 121, 182) THEN 3
+ WHEN `recurrenceMode` = 1805 OR `recurrenceMode` % 365 = 0 THEN 4
+ WHEN `recurrenceMode` % 7 = 0 THEN 2
+ ELSE 1 END;
+
+UPDATE `settings` SET `value` = "12" WHERE `key` = "db.version";
diff --git a/tests/api.test.js b/tests/api.test.js
index f61d84e..506f7ec 100644
--- a/tests/api.test.js
+++ b/tests/api.test.js
@@ -4,7 +4,7 @@ const { sql, sqlRows, resetDb, session, post, get } = require('./lib');
const todoFields = (over) => Object.assign({
todo: 'item', due: '', start: '', effort: 1, notes: '', tags: '',
- recurrenceMode: 0, recurrenceAnchor: 0, list_id: 0
+ recurrenceMode: 0, recurrenceInterval: 1, recurrenceAnchor: 0, list_id: 0
}, over);
test.beforeEach(resetDb);
@@ -42,7 +42,7 @@ test('reactivating a recurring entry copies stored values verbatim (no second-or
const payload = ', NULL, 0, NULL, (SELECT CONCAT(user(), 0x20, version())), 1, 7, 1, 0)# ';
const id = (await post('enter.php', { todo: 'x\\', due: '', start: '', tags: 't', list_id: 0 }, sess)).text;
assert.equal((await post('update.php', todoFields({ id, version: 1, todo: 'x\\', notes: payload,
- recurrenceMode: 7, recurrenceAnchor: 1, tags: 't' }), sess)).text, '1');
+ recurrenceMode: 1, recurrenceInterval: 7, recurrenceAnchor: 1, tags: 't' }), sess)).text, '1');
assert.equal((await post('complete.php', { id, completed: 1, version: 2 }, sess)).text, '1');
assert.equal((await post('reactivate-one.php', { id }, sess)).text, 'Reactivated entry...');
const rows = sqlRows('SELECT description, notes, completed FROM todo ORDER BY id');
@@ -55,7 +55,7 @@ test('due recurring entries are reactivated when loading the list', async () =>
const sess = await session();
const id = (await post('enter.php', { todo: 'weekly', due: '2026-01-10', start: '2026-01-08', tags: 'w', list_id: 0 }, sess)).text;
await post('update.php', todoFields({ id, version: 1, todo: 'weekly', due: '2026-01-10', start: '2026-01-08',
- recurrenceMode: 7, recurrenceAnchor: 1, tags: 'w' }), sess);
+ recurrenceMode: 2, recurrenceInterval: 1, recurrenceAnchor: 1, tags: 'w' }), sess);
await post('complete.php', { id, completed: 1, version: 2 }, sess);
const res = await get('query-todos.php?list_id=0&age=10000&incomplete=true');
const items = JSON.parse(res.text);
@@ -66,6 +66,21 @@ test('due recurring entries are reactivated when loading the list', async () =>
assert.equal(copy.tags, 'w');
});
+test('recurrence supports arbitrary intervals in days, weeks, months and years', async () => {
+ const sess = await session();
+ const cases = [[1, 10, '2026-01-20'], [2, 3, '2026-01-31'], [3, 2, '2026-03-10'], [4, 10, '2036-01-10']];
+ for (const [mode, interval, expectedDue] of cases) {
+ sql('DELETE FROM recurringCopied; DELETE FROM todo_tags; DELETE FROM todo');
+ const id = (await post('enter.php', { todo: 'r', due: '2026-01-10', start: '2026-01-10', tags: '', list_id: 0 }, sess)).text;
+ assert.equal((await post('update.php', todoFields({ id, version: 1, todo: 'r', due: '2026-01-10',
+ start: '2026-01-10', recurrenceMode: mode, recurrenceInterval: interval, recurrenceAnchor: 1 }), sess)).text, '1');
+ await post('complete.php', { id, completed: 1, version: 2 }, sess);
+ assert.equal((await post('reactivate-one.php', { id }, sess)).text, 'Reactivated entry...');
+ assert.deepEqual(sqlRows('SELECT DATE(dueDate), recurrenceMode, recurrenceInterval FROM todo WHERE completed=0'),
+ [[expectedDue, String(mode), String(interval)]]);
+ }
+});
+
test('lists and todos of other users can neither be read nor changed', async () => {
const sess = await session();
const denied = 'Access denied: this list or entry does not belong to you!';
@@ -99,6 +114,9 @@ test('invalid input is rejected', async () => {
[{ due: '2026-01-01', start: '2026-01-05' }, 'Due date is earlier than start date!'],
[{ effort: 10000 }, 'Invalid effort!'],
[{ recurrenceMode: -5 }, 'Invalid recurrence mode!'],
+ [{ recurrenceMode: 5 }, 'Invalid recurrence mode!'],
+ [{ recurrenceMode: 2, recurrenceInterval: 0 }, 'Invalid recurrence interval!'],
+ [{ recurrenceMode: 2, recurrenceInterval: 1000 }, 'Invalid recurrence interval!'],
[{ recurrenceAnchor: 2 }, 'Invalid recurrence anchor!'],
];
for (const [over, msg] of bad) {
diff --git a/todo.css b/todo.css
index 4368414..321807a 100644
--- a/todo.css
+++ b/todo.css
@@ -157,6 +157,14 @@ ul.ui-autocomplete {
width: 300px;
box-sizing: border-box;
}
+#modify_recurrenceInterval {
+ width: 70px;
+ box-sizing: border-box;
+ margin-right: 6px;
+}
+#modify_recurrenceMode {
+ width: 224px;
+}
.tagify {
--tag-hide-transition: 0s;
--readonly-striped: 0;
@@ -383,6 +391,12 @@ ul.ui-autocomplete {
#tag_dialog .tagify {
width: 100%;
}
+ #modify_recurrenceInterval {
+ width: 80px;
+ }
+ #modify_recurrenceMode {
+ width: calc(100% - 90px);
+ }
/* larger touch targets */
.addButton, .editButton, .deleteButton, .undeleteButton, .todayButton, .refreshButton, .reactivateButton, .mergeButton {
width: 32px;
diff --git a/todo.js b/todo.js
index 2ee841a..a18bcd2 100644
--- a/todo.js
+++ b/todo.js
@@ -14,7 +14,7 @@ var listsData = {user_id: 0};
function Todo(id, todo, due, start, effort,
completed, notes, tags, deleted,
- version, recurrenceMode, recurrenceAnchor,
+ version, recurrenceMode, recurrenceInterval, recurrenceAnchor,
completionDate, creationDate, list_id) {
this.id = parseInt(id);
this.todo = todo;
@@ -27,6 +27,7 @@ function Todo(id, todo, due, start, effort,
this.deleted = parseInt(deleted);
this.version = parseInt(version);
this.recurrenceMode = parseInt(recurrenceMode);
+ this.recurrenceInterval = parseInt(recurrenceInterval);
this.recurrenceAnchor = parseInt(recurrenceAnchor);
this.completionDate = completionDate;
this.creationDate = creationDate;
@@ -38,7 +39,7 @@ function copyTodo(item)
return new Todo(
item.id, item.todo, item.due, item.start, item.effort,
item.completed, item.notes, item.tags, item.deleted,
- item.version, item.recurrenceMode, item.recurrenceAnchor,
+ item.version, item.recurrenceMode, item.recurrenceInterval, item.recurrenceAnchor,
item.completionDate, item.creationDate, item.list_id
);
}
@@ -163,6 +164,7 @@ function modifyLocally(item) {
itemList[index].tags = item.tags;
itemList[index].version = item.version;
itemList[index].recurrenceMode = item.recurrenceMode;
+ itemList[index].recurrenceInterval = item.recurrenceInterval;
itemList[index].recurrenceAnchor = item.recurrenceAnchor;
renderTable();
}
@@ -399,10 +401,12 @@ function toggleRecurrenceAnchor(e)
if (val == 0)
{
$('.recurrence_dependent').hide();
+ $('#modify_recurrenceInterval').hide();
}
else
{
$('.recurrence_dependent').show();
+ $('#modify_recurrenceInterval').show();
}
}
@@ -441,6 +445,7 @@ function fillModifyForm(id) {
$('#modify_recurrenceMode option:selected').prop('selected', false);
$('#modify_recurrenceMode option[value="'+item.recurrenceMode+'"]').prop('selected', true);
+ $('#modify_recurrenceInterval').val(item.recurrenceInterval);
$('#modify_recurrenceAnchor option:selected').prop('selected', false);
$('#modify_recurrenceAnchor option[value="'+item.recurrenceAnchor+'"]').prop('selected', true);
@@ -547,9 +552,14 @@ function setListener(id) {
}
-function getRecurrenceString(recurrenceMode)
+function getRecurrenceString(recurrenceMode, recurrenceInterval)
{
- return $('#modify_recurrenceMode option[value="'+recurrenceMode+'"]').text();
+ var unit = $('#modify_recurrenceMode option[value="'+recurrenceMode+'"]').text();
+ if (recurrenceMode == 0)
+ {
+ return unit;
+ }
+ return $T('RECURRENCE_EVERY')+' '+recurrenceInterval+' '+unit;
}
@@ -686,6 +696,7 @@ function reload() {
itemList[i].completed = parseInt(itemList[i].completed);
itemList[i].version = parseInt(itemList[i].version);
itemList[i].recurrenceMode = parseInt(itemList[i].recurrenceMode);
+ itemList[i].recurrenceInterval = parseInt(itemList[i].recurrenceInterval);
itemList[i].recurrenceAnchor = parseInt(itemList[i].recurrenceAnchor);
}
renderTable();
@@ -812,6 +823,7 @@ function storeItem() {
0, // deleted items cannot be modified
itemList[idx].version,
$('#modify_recurrenceMode').val(),
+ $('#modify_recurrenceInterval').val(),
$('#modify_recurrenceAnchor').val(),
itemList[idx].completionDate,
itemList[idx].creationDate,
@@ -856,7 +868,7 @@ function enter() {
due = null;
}
var stuff = new Todo(-1, todo, due, start, 1 /* effort */ ,
- 0, '', tags, 0, 1, 0 /* recurrenceMode */, 0 /* recurrenceAnchor */,
+ 0, '', tags, 0, 1, 0 /* recurrenceMode */, 1 /* recurrenceInterval */, 0 /* recurrenceAnchor */,
null, formatDate(getUTCDate(), true),
reloadData.list_id);
addItem(stuff);
@@ -898,7 +910,7 @@ function getTodoTitleHtml(it, lineNr, tagbasename, spanCssClass, baseElem, check
var hasTags = it.tags != null && it.tags != '';
var createDate = parseDate(it.creationDate);
// label of the recurrence option (DOM text), goes into an HTML attribute:
- var repetition = escapeHtml(getRecurrenceString(it.recurrenceMode));
+ var repetition = escapeHtml(getRecurrenceString(it.recurrenceMode, it.recurrenceInterval));
var complDate = parseDate(it.completionDate);
line = '<'+baseElem+' class="'+spanCssClass+'" title="'+$T('CREATED')+': '+formatDate(createDate, true)+
'; '+$T('RECURRENCE')+': '+repetition+