Pull in Vikunja Tasks

Hey all, just a quick one I made to pull in Vikunja tasks:


local baseUrl = "https://vikunja.your.domain"

function getVikunjaTasks()
  local token = "Your-Vikunja-API-Token"
  local apiUrl = (baseUrl .. "api/v1/tasks/all?filter=done=false")

  local response = http.request(
    apiUrl, {
    method = "GET",
    headers = {
      Authorization = "Bearer " .. token,
      Accept = "application/json",
    }
  })
  if response.ok then 
    return response.body
  else 
    return "Not OK response"
  end
end

templates.vikunjaRecurringTasks = function(task)
  local function convert_date(date)
    return date:sub(1,10)
  end
    
  return string.format("* [Link](" .. baseUrl .. "tasks/%s) [due: %s] %s", task.id, convert_date(task.due_date), task.title ) .. "\n"
end

Result

${template.each(
  query[[
    from getVikunjaTasks() 
    where due_date <= date.nextmonth()
    order by due_date
  ]], templates.vikunjaRecurringTasks
)}

The result of templates.vikunjaRecurringTasks looks like this:

Based on your code, i have refactorized and i distribute It my silverbullet libraries.

compatible with 1.0.0

Nice! I'll be sure to include your libraries in my setup -- Lots of useful things! Thanks!

@albert if you want to improve something, I'm very open to contributions(bug fixes, integrations of new features) ...

Hey guys, just created an account to say this is awesome. I'm also a vikunja user and new to silverbullet yet it seems magical :slight_smile:

I've updated mine slightly:

local baseUrl = "https://vikunja.domain.tld"

-- Get the tasks
function getVikunjaTasks()
  local token = "YourToken"
  local apiUrl = (baseUrl .. "/api/v1/tasks?filter=done=false")

  local response = http.request(
    apiUrl, {
    method = "GET",
    headers = {
      Authorization = "Bearer " .. token,
      Accept = "application/json",
    }
  })
  if response.ok then 
    return response.body
  else 
    return "Not OK response"
  end
end

-- Mark a Vikunja task as done
function completeVikunjaTask(taskId)
  local token = "YourToken"
  local apiUrl = baseUrl .. "/api/v1/tasks/" .. taskId

  local response = http.request(apiUrl, {
    method = "POST",
    headers = {
      Authorization = "Bearer " .. token,
      ["Content-Type"] = "application/json",
    },
    body = '{"done": true}'
  })

  if response.ok then
    editor.flashNotification("Task marked as done!")
    editor.reloadPage()
  else
    editor.flashNotification("Failed to complete task", "error")
  end
end

-- Module-level click delegation: handles Done buttons on any page
js.window.addEventListener("click", function(e)
  local taskId = e.target and e.target.getAttribute and e.target.getAttribute("data-vikunja-id")
  if taskId then
    completeVikunjaTask(tonumber(taskId))
  end
end)

-- Returns an inline HTML button; clicks are caught by the module-level js.window listener
function vikunjaButton(taskId)
  return widget.new {
    html = string.format('<button data-vikunja-id="%d">Done</button>', taskId)
  }
end

-- Template used by getTasks.md — embeds an inline ${vikunjaButton(id)} per row
templates.vikunjaRecurringTasks = function(task)
  local due = (task.due_date or ""):sub(1, 10)
  return string.format(
    "* ${vikunjaButton(%d)} [%s](%s/tasks/%s) **[📅 %s]** %s\n",
    task.id, task.identifier, baseUrl, task.id, due, task.title)
end

-- Render all open tasks as an HTML widget with a Done button per row
function vikunjaTasksWithButtons()
  local tasks = getVikunjaTasks()
  if type(tasks) ~= "table" then return tasks end

  table.sort(tasks, function(a, b)
    return (a.due_date or "") < (b.due_date or "")
  end)

  local rows = {}
  for _, task in ipairs(tasks) do
    local due = (task.due_date or ""):sub(1, 10)
    table.insert(rows, string.format(
      '<div style="display:flex;gap:6px;align-items:baseline;padding:2px 0">' ..
      '<button data-vikunja-id="%d">Done</button>' ..
      ' <a href="%s/tasks/%d">%s</a> <strong>[  %s]</strong> %s</div>',
      task.id, baseUrl, task.id, task.identifier, due, task.title
    ))
  end

  return widget.new {
    html = '<div>' .. table.concat(rows, "\n") .. '</div>'
  }
end

This shows a "Done" button so you don't have to go to Vikunja to mark something as done.

This is nice! I just saw the link. Feel free to add the changes I just posted :slight_smile: