Skip to content

Commit 6314f1b

Browse files
authoredNov 22, 2023
Merge pull request #22 from Chaosmeister/master
fix checkboxhandling with nested checkboxes
2 parents 9791529 + d896ca8 commit 6314f1b

File tree

6 files changed

+127
-63
lines changed

6 files changed

+127
-63
lines changed
 

‎Assets/js/checkbox.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ KB.on('dom.ready', function () {
2323
else {
2424
link = '?controller=CheckboxController&action=toggle&plugin=MarkdownPlus';
2525
}
26-
26+
2727
KB.http.postJson(link, {
2828
'task_id': task_id,
29-
'number': e.target.getAttribute('number')
29+
'number': e.target.dataset.number
3030
});
3131
}
3232
}

‎Controller/CheckboxController.php

+72-46
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,29 @@
77

88
class CheckboxController extends BaseController
99
{
10-
private const regexCheckbox = '/\[[x, ]\]/m';
10+
/*
11+
# must match
12+
+ [x] 2
13+
* [ ] 3
14+
> - [ ] 1
15+
+ [x] 2
16+
* [ ] 3
17+
> > * * [ ] 3
18+
> * > * * [ ] 3
19+
[ ] test 8
20+
21+
# must not match:
22+
23+
- [x]1
24+
+ [ ]
25+
test [x]
26+
test [x]
27+
[] test 3
28+
[a] test
29+
[x](www.google.de) test4
30+
*/
31+
32+
private const regexCheckbox = '/^([+,\-,*,>, ] )*(\[[x, ]\] )/m';
1133

1234
private function findCheckBox($text, $number, &$offset)
1335
{
@@ -17,7 +39,9 @@ private function findCheckBox($text, $number, &$offset)
1739
if ($offset >= $number) {
1840
return array(
1941
'success' => true,
20-
'offset' => $matches[0][$count - $offset + $number - 1][1] + 1
42+
'offset' => end($matches) // the actual box [ ]/[x]
43+
[$count - $offset + $number - 1] // the requested checkbox
44+
[1] + 1 // the offset to the checkbox content
2145
);
2246
}
2347
return array('success' => false);
@@ -35,69 +59,71 @@ private function togglechar(&$string, $offset)
3559
public function toggle()
3660
{
3761
$values = $this->request->getJson();
38-
39-
$foundCheckboxes = 0;
40-
$number = intval($values['number']);
4162
$taskId = $values['task_id'];
4263

4364
if (isset($taskId)) {
4465
$task = $this->taskFinderModel->getById($taskId);
4566

46-
$text = $task['description'];
47-
$result = $this->findCheckBox($text, $number, $foundCheckboxes);
48-
if ($result['success']) {
49-
$this->togglechar($text, $result['offset']);
50-
$task['description'] = $text;
51-
$this->taskModificationModel->update($task);
52-
return;
53-
}
67+
if ($task) {
68+
$foundCheckboxes = 0;
69+
$number = intval($values['number']);
5470

55-
if (isset($this->container["definitionOfDoneModel"])) {
56-
foreach ($this->definitionOfDoneModel->getAll($taskId) as $subtask) {
57-
$dod = $this->definitionOfDoneModel->getById($subtask['id']);
71+
$text = $task['description'];
72+
$result = $this->findCheckBox($text, $number, $foundCheckboxes);
73+
if ($result['success']) {
74+
$this->togglechar($text, $result['offset']);
75+
$task['description'] = $text;
76+
$this->taskModificationModel->update($task);
77+
return;
78+
}
5879

59-
$result = $this->findCheckBox($dod['title'], $number, $foundCheckboxes);
60-
if ($result['success']) {
61-
$this->togglechar($dod['title'], $result['offset']);
62-
$this->definitionOfDoneModel->save($dod);
63-
return;
80+
if (isset($this->container["definitionOfDoneModel"])) {
81+
foreach ($this->definitionOfDoneModel->getAll($taskId) as $subtask) {
82+
$dod = $this->definitionOfDoneModel->getById($subtask['id']);
83+
84+
$result = $this->findCheckBox($dod['title'], $number, $foundCheckboxes);
85+
if ($result['success']) {
86+
$this->togglechar($dod['title'], $result['offset']);
87+
$this->definitionOfDoneModel->save($dod);
88+
return;
89+
}
90+
91+
$result = $this->findCheckBox($dod['text'], $number, $foundCheckboxes);
92+
if ($result['success']) {
93+
$this->togglechar($dod['text'], $result['offset']);
94+
$this->definitionOfDoneModel->save($dod);
95+
return;
96+
}
6497
}
98+
}
6599

66-
$result = $this->findCheckBox($dod['text'], $number, $foundCheckboxes);
67-
if ($result['success']) {
68-
$this->togglechar($dod['text'], $result['offset']);
69-
$this->definitionOfDoneModel->save($dod);
70-
return;
100+
if (isset($this->container["subtaskResultModel"])) {
101+
foreach ($this->subtaskModel->getAll($taskId) as $subtask) {
102+
$text = $this->subtaskResultModel->getById($subtask['id']);
103+
$result = $this->findCheckBox($text, $number, $foundCheckboxes);
104+
if ($result['success']) {
105+
$this->togglechar($text, $result['offset']);
106+
$this->subtaskResultModel->Save($subtask['id'], $text);
107+
return;
108+
}
71109
}
72110
}
73-
}
74111

75-
if (isset($this->container["subtaskResultModel"])) {
76-
foreach ($this->subtaskModel->getAll($taskId) as $subtask) {
77-
$text = $this->subtaskResultModel->getById($subtask['id']);
112+
$commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
113+
114+
foreach ($this->commentModel->getAll($taskId, $commentSortingDirection) as $comment) {
115+
$text = $comment['comment'];
116+
78117
$result = $this->findCheckBox($text, $number, $foundCheckboxes);
118+
79119
if ($result['success']) {
80120
$this->togglechar($text, $result['offset']);
81-
$this->subtaskResultModel->Save($subtask['id'], $text);
121+
$comment['comment'] = $text;
122+
$this->commentModel->update($comment);
82123
return;
83124
}
84125
}
85126
}
86-
87-
$commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
88-
89-
foreach ($this->commentModel->getAll($taskId, $commentSortingDirection) as $comment) {
90-
$text = $comment['comment'];
91-
92-
$result = $this->findCheckBox($text, $number, $foundCheckboxes);
93-
94-
if ($result['success']) {
95-
$this->togglechar($text, $result['offset']);
96-
$comment['comment'] = $text;
97-
$this->commentModel->update($comment);
98-
return;
99-
}
100-
}
101127
}
102128
}
103129
}

‎Helper/CoreMarkdown.php

+32-5
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected function inlineTaskLink(array $Excerpt)
6363
if (preg_match('!#(\d+)!i', $Excerpt['text'], $matches)) {
6464
$link = $this->buildTaskLink($matches[1]);
6565

66-
if (! empty($link)) {
66+
if (!empty($link)) {
6767
return array(
6868
'extent' => strlen($matches[0]),
6969
'element' => array(
@@ -89,11 +89,11 @@ protected function inlineTaskLink(array $Excerpt)
8989
*/
9090
protected function inlineUserLink(array $Excerpt)
9191
{
92-
if (! $this->isPublicLink && preg_match('/^@([^\s,!:?]+)/', $Excerpt['text'], $matches)) {
92+
if (!$this->isPublicLink && preg_match('/^@([^\s,!:?]+)/', $Excerpt['text'], $matches)) {
9393
$username = rtrim($matches[1], '.');
9494
$user = $this->container['userCacheDecorator']->getByUsername($username);
9595

96-
if (! empty($user)) {
96+
if (!empty($user)) {
9797
$url = $this->container['helper']->url->to('UserViewController', 'profile', array('user_id' => $user['id']));
9898
$name = $user['name'] ?: $user['username'];
9999

@@ -128,7 +128,7 @@ private function buildTaskLink($task_id)
128128
if ($this->isPublicLink) {
129129
$token = $this->container['memoryCache']->proxy($this->container['taskFinderModel'], 'getProjectToken', $task_id);
130130

131-
if (! empty($token)) {
131+
if (!empty($token)) {
132132
return $this->container['helper']->url->to(
133133
'TaskViewController',
134134
'readonly',
@@ -165,4 +165,31 @@ protected function inlineLink($Excerpt)
165165
}
166166
return $Inline;
167167
}
168-
}
168+
169+
function text($text)
170+
{
171+
$markup = parent::text($text);
172+
173+
$this->nummerizeCheckboxes($markup);
174+
175+
return $markup;
176+
}
177+
178+
private function nummerizeCheckboxes(&$markup)
179+
{
180+
$counter = new Counter;
181+
$count = 0;
182+
183+
$markup = preg_replace_callback("/activecheckbox\"/m", array($counter, 'count'), $markup, -1, $count, PREG_OFFSET_CAPTURE);
184+
}
185+
}
186+
187+
class Counter
188+
{
189+
private static $count = 0;
190+
191+
public function count($matches) {
192+
$this::$count++;
193+
return $matches[0][0] . " data-number=". $this::$count . " ";
194+
}
195+
};

‎vendor/erusev/parsedown-extra/ParsedownExtra.php

+18-7
Original file line numberDiff line numberDiff line change
@@ -628,19 +628,30 @@ protected function processTag($elementMarkup) # recursive
628628
$elementMarkup = mb_convert_encoding($elementMarkup, 'HTML-ENTITIES', 'UTF-8');
629629

630630
# http://stackoverflow.com/q/4879946/200145
631-
$DOMDocument->loadHTML($elementMarkup);
632-
633-
if (!$DOMDocument->validate()) {
634-
$errormessage = 'could not parse html<br>';
635-
$errors = libxml_get_errors();
631+
$success = $DOMDocument->loadHTML($elementMarkup);
632+
$DOMDocument->removeChild($DOMDocument->doctype);
633+
634+
$errors = libxml_get_errors();
635+
if ($errors)
636+
{
637+
$errormessage = "<h1>HTML-parser error:</h1><br>";
638+
$errormessage .= "The input was interpret as HTML - did you miss the <code>markdown=1</code> attribute?<br>";
636639
foreach ($errors as $error) {
637-
$errormessage .= $error->message;
640+
$errormessage .= "Line: " . $error->line . " - " . $error->message;
638641
$errormessage .= '<br>';
639642
}
640643
return $errormessage;
641644
}
642645

643-
$DOMDocument->removeChild($DOMDocument->doctype);
646+
if (!isset($DOMDocument->firstChild->firstChild->firstChild))
647+
{
648+
$errormessage = "<h1>General HTML-parser error:</h1><br>";
649+
$errormessage .= "The input was interpret as HTML - did you miss the <code>markdown=1</code> attribute?<br>";
650+
$errormessage .= "Input:<pre><code>" . htmlspecialchars($elementMarkup) . "</code></pre>";
651+
$errormessage .= "Output:<pre><code>" . htmlspecialchars($DOMDocument->saveHTML()) . "</code></pre>";
652+
return $errormessage;
653+
}
654+
644655
$DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild, $DOMDocument->firstChild);
645656

646657
$elementText = '';

‎vendor/erusev/parsedown/Parsedown.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1977,7 +1977,7 @@ static function instance($name = 'default')
19771977
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+';
19781978

19791979
protected $voidElements = array(
1980-
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
1980+
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source'
19811981
);
19821982

19831983
protected $textLevelElements = array(

‎vendor/leblanc-simon/parsedown-checkbox/ParsedownCheckbox.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ protected function checkboxUnchecked($text)
7474
$text = self::escape($text);
7575
}
7676

77-
return '<input type="checkbox" class="activecheckbox" number="'. ParsedownCheckbox::$count++ .'"/> ' . $this->format($text);
77+
return '<input type="checkbox" class="activecheckbox"> ' . $this->format($text);
7878
}
7979

8080
protected function checkboxChecked($text)
@@ -83,7 +83,7 @@ protected function checkboxChecked($text)
8383
$text = self::escape($text);
8484
}
8585

86-
return '<input type="checkbox" class="activecheckbox" number="'. ParsedownCheckbox::$count++ .'" checked/> ' . $this->format($text);
86+
return '<input type="checkbox" class="activecheckbox" checked/> ' . $this->format($text);
8787
}
8888

8989
/**

0 commit comments

Comments
 (0)