'File exceeds server upload limit.', UPLOAD_ERR_FORM_SIZE => 'File exceeds form size limit.', UPLOAD_ERR_PARTIAL => 'File was only partially uploaded.', UPLOAD_ERR_NO_FILE => 'No file selected.', UPLOAD_ERR_NO_TMP_DIR => 'No temp directory available.', UPLOAD_ERR_CANT_WRITE => 'Cannot write file to disk.', ]; $error = $err_codes[$file['error'] ?? UPLOAD_ERR_NO_FILE] ?? 'Upload failed.'; } elseif ($file['size'] > 2 * 1024 * 1024) { $error = 'File too large (max 2 MB).'; } } if (!$error) { $content = file_get_contents($file['tmp_name']); $orig_name = strtolower($file['name']); $ext = pathinfo($orig_name, PATHINFO_EXTENSION); // Auto-detect by extension, then by content if ($ext === 'csv' || (str_contains($content, ',') && str_contains($content, "\n"))) { $parsed = parse_csv_import($content); $type = 'CSV'; } else { $parsed = parse_txt_import($content); $type = 'TXT/MD'; } if (empty($parsed)) { $error = 'No tasks found in file. Check the format and try again.'; } else { $res = apply_import_tasks($user['username'], $list_id, $parsed, $append); if ($res['error']) { $error = $res['error']; } else { $result = [ 'added' => $res['added'], 'type' => $type, 'list' => get_list($user['username'], $list_id), ]; } } } } /* ── Load user's lists for the selector ───────────────────────── */ $my_lists = get_lists_for_user($user['username']); $page_title = 'Import Tasks'; require_once 'includes/header.php'; ?>
= $result['added'] ?> task= $result['added'] !== 1 ? 's' : '' ?> imported from = h($result['type']) ?> into = h($result['list']['title'] ?? '') ?>.
Header row is auto-detected. Recognised columns:
status,priority,order,text,created,completed
active,high,1,Buy groceries,,,
completed,normal,,Pay rent,,2026-01-15
active,low,,Read book,,,
Status: active / completed
Priority: high / normal / low
Single-column files (text only) are also accepted.
One task per line. Markdown task-list syntax supported:
- [ ] Normal task
- [x] Completed task
- [HIGH] Urgent thing !high
- [LOW] Someday maybe !low
Plain text also works
* Another bullet style
Priority prefix: [HIGH] / [LOW]
Priority suffix: !high / !low
Lines starting with # are skipped.