diff --git a/QtClient/JoplinQtClient/schema.sql b/QtClient/JoplinQtClient/schema.sql
index 27a878bc6..aa660b99e 100755
--- a/QtClient/JoplinQtClient/schema.sql
+++ b/QtClient/JoplinQtClient/schema.sql
@@ -2,8 +2,7 @@ CREATE TABLE folders (
id TEXT PRIMARY KEY,
title TEXT,
created_time INT,
- updated_time INT,
- synced BOOLEAN DEFAULT 0
+ updated_time INT
);
CREATE TABLE notes (
@@ -24,23 +23,20 @@ CREATE TABLE notes (
todo_completed INT,
source_application TEXT,
application_data TEXT,
- `order` INT,
- synced BOOLEAN DEFAULT 0
+ `order` INT
);
CREATE TABLE tags (
id TEXT PRIMARY KEY,
title TEXT,
created_time INT,
- updated_time INT,
- synced BOOLEAN DEFAULT 0
+ updated_time INT
);
CREATE TABLE note_tags (
id INTEGER PRIMARY KEY,
note_id TEXT,
- tag_id TEXT,
- synced BOOLEAN DEFAULT 0
+ tag_id TEXT
);
CREATE TABLE resources (
@@ -49,15 +45,13 @@ CREATE TABLE resources (
mime TEXT,
filename TEXT,
created_time INT,
- updated_time INT,
- synced BOOLEAN DEFAULT 0
+ updated_time INT
);
CREATE TABLE note_resources (
id INTEGER PRIMARY KEY,
note_id TEXT,
- resource_id TEXT,
- synced BOOLEAN DEFAULT 0
+ resource_id TEXT
);
CREATE TABLE version (
diff --git a/debug_client/css/style.css b/debug_client/css/style.css
new file mode 100755
index 000000000..1b94a7d39
--- /dev/null
+++ b/debug_client/css/style.css
@@ -0,0 +1,75 @@
+/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
+html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
+body{margin:0}
+article,aside,footer,header,nav,section{display:block}
+h1{font-size:2em;margin:.67em 0}
+figcaption,figure,main{display:block}
+figure{margin:1em 40px}
+hr{box-sizing:content-box;height:0;overflow:visible}
+pre{font-family:monospace,monospace;font-size:1em}
+a{background-color:transparent;-webkit-text-decoration-skip:objects}
+a:active,a:hover{outline-width:0}
+abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
+b,strong{font-weight:inherit;font-weight:bolder}
+code,kbd,samp{font-family:monospace,monospace;font-size:1em}
+dfn{font-style:italic}
+mark{background-color:#ff0;color:#000}
+small{font-size:80%}
+sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
+sub{bottom:-.25em}
+sup{top:-.5em}
+audio,video{display:inline-block}
+audio:not([controls]){display:none;height:0}
+img{border-style:none}
+svg:not(:root){overflow:hidden}
+button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}
+button,input{overflow:visible}
+button,select{text-transform:none}
+button,html [type="button"],/* 1 */
+[type="reset"],[type="submit"]{-webkit-appearance:button}
+button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}
+button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}
+fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
+legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}
+progress{display:inline-block;vertical-align:baseline}
+textarea{overflow:auto}
+[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}
+[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}
+[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}
+[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
+::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
+details,/* 1 */
+menu{display:block}
+summary{display:list-item}
+canvas{display:inline-block}
+template{display:none}
+[hidden]{display:none}
+/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
+
+body {
+ padding: 1em;
+}
+
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+
+td {
+ border: 1px #ccc solid;
+ padding: .3em;
+}
+
+.form-group {
+ margin-bottom: 0.5em;
+}
+
+.form-group label {
+ width: 200px;
+ display: inline-block;
+}
+
+.form-group input {
+ width: 300px;
+ display: inline-block;
+}
diff --git a/debug_client/index.php b/debug_client/index.php
new file mode 100755
index 000000000..fe577f0e9
--- /dev/null
+++ b/debug_client/index.php
@@ -0,0 +1,190 @@
+ 'http://joplin.local',
+ 'clientId' => 'E3E3E3E3E3E3E3E3E3E3E3E3E3E3E3E3',
+ );
+ if (isset($config[$name])) return $config[$name];
+ throw new Exception('Unknown config: ' . $name);
+}
+
+function curlCmd($method, $url, $data) {
+ $cmd = array();
+ $cmd[] = 'curl';
+ if ($method != 'GET' && $method != 'POST') {
+ $cmd[] = '-X ' . $method;
+ }
+ if ($method != 'GET' && $method != 'DELETE') {
+ $cmd[] = "--data '" . http_build_query($data) . "'";
+ }
+ $cmd[] = "'" . $url . "'";
+
+ return implode(' ', $cmd);
+}
+
+function saveCurlCmd($cmd) {
+ $cmds = array();
+ if (isset($_SESSION['curlCommands'])) $cmds = $_SESSION['curlCommands'];
+ $cmds[] = $cmd;
+ while (count($cmds) > 100) {
+ array_splice($cmds, 0, 1);
+ }
+ $_SESSION['curlCommands'] = $cmds;
+}
+
+function execRequest($method, $path, $query = array(), $data = null) {
+ $url = config('baseUrl') . '/' . $path;
+ if (!empty($_SESSION['sessionId'])) {
+ $query['session'] = $_SESSION['sessionId'];
+ }
+ if (count($query)) $url .= '?' . http_build_query($query);
+
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ if ($data) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+ if ($method != 'GET' && $method != 'POST') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
+ }
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ $curlCmd = curlCmd($method, $url, $data);
+ saveCurlCmd($curlCmd);
+
+ $output = json_decode($response, true);
+ if ($output === null) {
+ $msg = 'Cannot decode JSON: ' . $response . "\n\n" . $curlCmd;
+ die($msg);
+ }
+
+ return $output;
+}
+
+function renderView($name, $parameters = array()) {
+ $path = dirname(__FILE__) . '/views/' . $name . '.php';
+ if (!file_exists($path)) throw new Exception('View not found: ' . $path);
+
+ extract($parameters);
+ ob_start();
+ include $path;
+ $content = ob_get_contents();
+ ob_end_clean();
+ return $content;
+}
+
+function differentProperties($old, $new, $oldPrefix = '') {
+ $output = array();
+ foreach ($old as $k1 => $v1) {
+ foreach ($new as $k2 => $v2) {
+ if ($k1 === $k2 && (string)$v1 !== (string)$v2) {
+ $output[$k1] = $v2;
+ }
+ }
+ }
+ return $output;
+}
+
+function removePrefix($array, $prefix) {
+ $output = array();
+ foreach ($array as $k => $v) {
+ if (strpos($k, $prefix) === 0) {
+ $k = substr($k, strlen($prefix));
+ }
+ $output[$k] = $v;
+ }
+ return $output;
+}
+
+function redirect($path) {
+ header('Location: ' . $path);
+ die();
+}
+
+initialize();
+
+$session = execRequest('POST', 'sessions', null, array(
+ 'email' => 'laurent@cozic.net',
+ 'password' => '12345678',
+ 'client_id' => config('clientId'),
+));
+
+$_SESSION['sessionId'] = $session['id'];
+
+$action = isset($_GET['action']) ? $_GET['action'] : 'folders';
+
+if (isset($_POST['create_folder'])) $action = 'create_folder';
+if (isset($_POST['delete_folder'])) $action = 'delete_folder';
+if (isset($_POST['update_folder'])) $action = 'update_folder';
+
+$pageParams = array(
+ 'title' => ucfirst($action),
+ 'contentHtml' => '',
+);
+
+switch ($action) {
+
+ case 'folders':
+
+ $folders = execRequest('GET', 'folders');
+ $pageParams['contentHtml'] = renderView('folders', array('folders' => $folders));
+ break;
+
+ case 'folder':
+
+ $folder = execRequest('GET', 'folders/' . $_GET['folder_id']);
+ $pageParams['contentHtml'] = renderView('folder', array('folder' => $folder));
+ break;
+
+ case 'notes':
+
+ $notes = execRequest('GET', 'folders/' . $_GET['folder_id'] . '/notes');
+ $pageParams['contentHtml'] = renderView('notes', array('notes' => $notes));
+ break;
+
+ case 'create_folder':
+
+ $data = array('title' => $_POST['folder_title']);
+ $folder = execRequest('POST', 'folders', null, $data);
+ redirect('/');
+ break;
+
+ case 'delete_folder':
+
+ $folder = execRequest('DELETE', 'folders/' . $_POST['folder_id']);
+ redirect('/');
+ break;
+
+ case 'update_folder':
+
+ $oldFolder = json_decode($_POST['original_folder'], true);
+ $newFolder = removePrefix($_POST, 'folder_');
+ $diff = differentProperties($oldFolder, $newFolder);
+ if (count($diff)) {
+ execRequest('PATCH', 'folders/' . $_POST['folder_id'], null, $diff);
+ }
+ redirect('/');
+ break;
+
+}
+
+echo renderView('page', $pageParams);
+
+echo '
';
+$curlCommands = isset($_SESSION['curlCommands']) ? $_SESSION['curlCommands'] : array();
+for ($i = count($curlCommands) - 1; $i >= 0; $i--) {
+ $cmd = $curlCommands[$i];
+ echo $cmd . "\n";
+}
+echo ' ';
\ No newline at end of file
diff --git a/debug_client/views/folder.php b/debug_client/views/folder.php
new file mode 100755
index 000000000..973f6e003
--- /dev/null
+++ b/debug_client/views/folder.php
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/debug_client/views/folders.php b/debug_client/views/folders.php
new file mode 100755
index 000000000..796fe1362
--- /dev/null
+++ b/debug_client/views/folders.php
@@ -0,0 +1,29 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/debug_client/views/notes.php b/debug_client/views/notes.php
new file mode 100755
index 000000000..157859458
--- /dev/null
+++ b/debug_client/views/notes.php
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/debug_client/views/page.php b/debug_client/views/page.php
new file mode 100755
index 000000000..d83ba6817
--- /dev/null
+++ b/debug_client/views/page.php
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ Home
+
+
+
+
\ No newline at end of file