From 2694628d6904f8d8de031b032b14471be545f276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20B=C4=82LTEANU?= Date: Fri, 25 Jul 2025 00:50:18 +0300 Subject: [PATCH] WIP --- app/controllers/issues_controller.rb | 23 +++++++ .../controllers/task_list_controller.js | 62 +++++++++++++++++++ app/views/issues/show.html.erb | 5 +- .../wiki_formatting/common_mark/formatter.rb | 1 + .../common_mark/markdown_filter.rb | 4 +- .../common_mark/sanitization_filter.rb | 2 +- 6 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 app/javascript/controllers/task_list_controller.js diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index a9c3f6183..b30f01a72 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -198,6 +198,29 @@ class IssuesController < ApplicationController def update return unless update_issue_from_params + if params[:sourcepos].present? + source_pos = params[:sourcepos] # e.g., "10:1-10:15" + is_checked = params[:checked] + + line_number = source_pos.split(':').first.to_i + + description_lines = @issue.description.lines + target_line = description_lines[line_number - 1] + + if target_line + if is_checked + # Change '[ ]' to '[x]' + target_line.sub!(/\[\s\]/, '[x]') + else + # Change '[x]' to '[ ]' + target_line.sub!(/\[x\]/i, '[ ]') + end + end + + @issue.description = description_lines.join + end + + # if params[:source] attachments = params[:attachments] || params.dig(:issue, :uploads) if @issue.attachments_addable? @issue.save_attachments(attachments) diff --git a/app/javascript/controllers/task_list_controller.js b/app/javascript/controllers/task_list_controller.js new file mode 100644 index 000000000..e0ec0a9a5 --- /dev/null +++ b/app/javascript/controllers/task_list_controller.js @@ -0,0 +1,62 @@ +import {Controller} from '@hotwired/stimulus' + +export default class extends Controller { + // Define the 'updateUrl' value that we passed from the HTML + static values = { + id: String, + }; + + connect() { + // Find all checkboxes within this controller's scope and enable them. + this.element.querySelectorAll('input[type="checkbox"].task-list-item-checkbox').forEach(checkbox => { + checkbox.disabled = false; + + checkbox.addEventListener('change', (event) => { + const isChecked = event.target.checked; + const sourcePosition = checkbox.parentElement.dataset.sourcepos; + + if (!sourcePosition) { + console.error('Task item is missing data-sourcepos attribute.'); + // Optionally revert the checkbox and show an error + event.target.checked = !isChecked; + return; + } + + this.updateTaskListItem(this.idValue, sourcePosition, isChecked); + }); + }); + } + + async updateTaskListItem(issueId, sourcePosition, isChecked) { + + // You need to get the CSRF token for Rails + const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content; + + try { + const response = await fetch(`/issues/${issueId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'X-CSRF-Token': csrfToken, + }, + body: JSON.stringify({ + sourcepos: sourcePosition, + checked: isChecked, + }), + }); + + if (!response.ok) { + // Handle server error: revert the checkbox state and alert the user + throw new Error(); + } + + console.log('Task updated successfully!'); + + } catch (error) { + console.error('Error:', error); + // Revert the checkbox state + } + } +} + diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index 696b8f0ec..f7d12d510 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -98,7 +98,10 @@ end %>

<%=l(:field_description)%>

-
+
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
diff --git a/lib/redmine/wiki_formatting/common_mark/formatter.rb b/lib/redmine/wiki_formatting/common_mark/formatter.rb index 8b7a18394..4c0b7036e 100644 --- a/lib/redmine/wiki_formatting/common_mark/formatter.rb +++ b/lib/redmine/wiki_formatting/common_mark/formatter.rb @@ -47,6 +47,7 @@ module Redmine github_pre_lang: false, hardbreaks: Redmine::Configuration['common_mark_enable_hardbreaks'] == true, tasklist_classes: true, + sourcepos: true, }.freeze, commonmarker_plugins: { syntax_highlighter: nil diff --git a/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb b/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb index b52a20f1c..cf5c92818 100644 --- a/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb +++ b/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb @@ -33,10 +33,10 @@ module Redmine def call html = Commonmarker.to_html(@text, options: { - extension: extensions, + extension: extensions, render: render_options, parse: parse_options - }, plugins: plugins) + }, plugins: plugins) html.rstrip! html diff --git a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb index af72adc32..5be803f0e 100644 --- a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb +++ b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb @@ -112,7 +112,7 @@ module Redmine # allow `id` in li element for footnotes # allow `class` in li element for task list items - allowlist[:attributes]["li"] = %w(id class) + allowlist[:attributes]["li"] = %w(id class data-sourcepos) allowlist[:transformers].push lambda{|env| node = env[:node] return unless node.name == "li" -- 2.39.5 (Apple Git-154)