From ffde81c85bc19c6b6475891774cc6a0972a18317 Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Wed, 11 Jul 2018 10:27:17 -0400 Subject: [PATCH] WIP - Tools for migrate/import --- tools/schema | 70 +++++++ tools/tb-github-project.php | 251 +++++++++++++++++++++++ tools/tb-migrate.php | 382 ++++++++++++++++++++++++++++++++++++ 3 files changed, 703 insertions(+) create mode 100644 tools/schema create mode 100644 tools/tb-github-project.php create mode 100644 tools/tb-migrate.php diff --git a/tools/schema b/tools/schema new file mode 100644 index 0000000..b7d0dfb --- /dev/null +++ b/tools/schema @@ -0,0 +1,70 @@ +CREATE TABLE `useroption` ( id INTEGER PRIMARY KEY AUTOINCREMENT , `new_tasks_at_bottom` INTEGER, `show_animations` INTEGER, `show_assignee` INTEGER, `multiple_tasks_per_row` INTEGER, `language` TEXT); +CREATE TABLE `jwt` ( id INTEGER PRIMARY KEY AUTOINCREMENT , `secret` TEXT); +CREATE TABLE `board` ( id INTEGER PRIMARY KEY AUTOINCREMENT , `name` TEXT, `is_active` INTEGER); +CREATE TABLE `category` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`name` TEXT,`default_task_color` TEXT,`board_id` INTEGER , FOREIGN KEY(`board_id`) + REFERENCES `board`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX index_foreignkey_category_board ON `category` (board_id) ; +CREATE TABLE `issuetracker` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`url` TEXT,`regex` TEXT,`board_id` INTEGER , FOREIGN KEY(`board_id`) + REFERENCES `board`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX index_foreignkey_issuetracker_board ON `issuetracker` (board_id) ; +CREATE TABLE `column` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`name` TEXT,`position` INTEGER,`board_id` INTEGER , `task_limit` INTEGER, FOREIGN KEY(`board_id`) + REFERENCES `board`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX index_foreignkey_column_board ON `column` (board_id) ; +CREATE TABLE `board_user` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`user_id` INTEGER,`board_id` INTEGER , FOREIGN KEY(`board_id`) + REFERENCES `board`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY(`user_id`) + REFERENCES `user`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX index_foreignkey_board_user_user ON `board_user` (user_id) ; +CREATE INDEX index_foreignkey_board_user_board ON `board_user` (board_id) ; +CREATE UNIQUE INDEX UQ_board_useruser_id__board_id ON `board_user` (`user_id`,`board_id`); +CREATE TABLE `activity` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`user_id` INTEGER,`log_text` TEXT,`before` TEXT,`after` TEXT,`item_type` TEXT,`item_id` INTEGER,`timestamp` INTEGER , FOREIGN KEY(`user_id`) + REFERENCES `user`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL ); +CREATE INDEX index_foreignkey_activity_user ON `activity` (user_id) ; +CREATE INDEX index_foreignkey_activity_item ON `activity` (item_id) ; +CREATE TABLE `autoaction` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`trigger` INTEGER,`source_id` INTEGER,`type` INTEGER,`change_to` INTEGER,`board_id` INTEGER , FOREIGN KEY(`board_id`) + REFERENCES `board`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL ); +CREATE INDEX index_foreignkey_autoaction_board ON `autoaction` (board_id) ; +CREATE INDEX index_foreignkey_autoaction_source ON `autoaction` (source_id) ; +CREATE TABLE `task` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`title` TEXT,`description` TEXT,`color` TEXT,`due_date` TEXT,`points` INTEGER,`position` INTEGER,`column_id` INTEGER , FOREIGN KEY(`column_id`) + REFERENCES `column`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL ); +CREATE INDEX index_foreignkey_task_column ON `task` (column_id) ; +CREATE TABLE `comment` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`text` TEXT,`user_id` INTEGER,`task_id` INTEGER , `timestamp` TEXT, `is_edited` INTEGER, FOREIGN KEY(`user_id`) + REFERENCES `user`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL, FOREIGN KEY(`task_id`) + REFERENCES `task`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL ); +CREATE INDEX index_foreignkey_comment_task ON `comment` (task_id) ; +CREATE INDEX index_foreignkey_comment_user ON `comment` (user_id) ; +CREATE TABLE `task_user` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`user_id` INTEGER,`task_id` INTEGER , FOREIGN KEY(`task_id`) + REFERENCES `task`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY(`user_id`) + REFERENCES `user`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX index_foreignkey_task_user_user ON `task_user` (user_id) ; +CREATE INDEX index_foreignkey_task_user_task ON `task_user` (task_id) ; +CREATE UNIQUE INDEX UQ_task_useruser_id__task_id ON `task_user` (`user_id`,`task_id`); +CREATE TABLE `category_task` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`category_id` INTEGER,`task_id` INTEGER , FOREIGN KEY(`task_id`) + REFERENCES `task`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY(`category_id`) + REFERENCES `category`(`id`) + ON DELETE CASCADE ON UPDATE CASCADE ); +CREATE INDEX index_foreignkey_category_task_category ON `category_task` (category_id) ; +CREATE INDEX index_foreignkey_category_task_task ON `category_task` (task_id) ; +CREATE UNIQUE INDEX UQ_category_taskcategory_id__task_id ON `category_task` (`category_id`,`task_id`); +CREATE TABLE `collapsed` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`user_id` INTEGER,`column_id` INTEGER , FOREIGN KEY(`user_id`) + REFERENCES `user`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL, FOREIGN KEY(`column_id`) + REFERENCES `column`(`id`) + ON DELETE SET NULL ON UPDATE SET NULL ); +CREATE INDEX index_foreignkey_collapsed_column ON `collapsed` (column_id) ; +CREATE INDEX index_foreignkey_collapsed_user ON `collapsed` (user_id) ; +CREATE TABLE `user` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT ,`security_level` INTEGER,`username` TEXT,`password_hash` TEXT,`email` TEXT,`default_board_id` INTEGER,`user_option_id` INTEGER,`last_login` TEXT,`active_token` TEXT ); +CREATE INDEX index_foreignkey_user_user_option ON `user` (user_option_id) ; +CREATE INDEX index_foreignkey_user_default_board ON `user` (default_board_id) ; diff --git a/tools/tb-github-project.php b/tools/tb-github-project.php new file mode 100644 index 0000000..a8f9623 --- /dev/null +++ b/tools/tb-github-project.php @@ -0,0 +1,251 @@ +ch = curl_init(); + $this->api = "https://api.github.com/"; + } + + function __destruct() { + print "Closing 'taskboard.sqlite'..."; + $this->db->exec('PRAGMA foreign_keys = ON'); + $this->db->close(); + print " Done.\n"; + } + + function import() { + $this->openDb(); + $this->getInfo(); + $this->loadData(); + $this->importData(); + } + + private function openDb() { + print "Opening 'taskboard.sqlite'..."; + $this->isNew = !file_exists('taskboard.sqlite'); + + if ($this->isNew) { + print "\n File not found, creating..."; + } + + $this->db = new SQLite3('taskboard.sqlite'); + print " Done.\n"; + + if ($this->isNew) { + print "Creating tables..."; + $schema = file_get_contents('schema'); + $this->db->exec($schema); + print " Done.\n"; + } + + $this->db->exec('PRAGMA foreign_keys = OFF'); + } + + private function getInfo() { + $this->user = readline("Enter your GitHub user name: "); + $this->pass = readline("Enter your GitHub password: "); + $this->repo = readline("What repo do you want to export projects from? "); + } + + private function loadData() { + $options = array( + CURLOPT_URL => $this->api . "repos/" . $this->user . "/" . $this->repo . "/projects", + CURLOPT_USERAGENT => "Googlebot/2.1 (+http://www.google.com/bot.html)", + CURLOPT_HTTPAUTH => CURLAUTH_BASIC, + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_USERPWD => $this->user . ":" . $this->pass, + CURLOPT_HTTPHEADER => array( + 'Accept: application/vnd.github.inertia-preview+json' + ) + ); + curl_setopt_array($this->ch, $options); + + $data = json_decode(curl_exec($this->ch)); + $this->projects = []; + + foreach ($data as $project) { + $this->projects[$project->id] = + (object)array("id" => $project->id, "name" => $project->name); + } + + print("Found " . count($this->projects) . " project" . + (count($this->projects) === 1 ? "" : "s") . ".\n"); + + foreach ($this->projects as $project) { + print " Loading Project " . $project->name . "...\n"; + + curl_setopt( + $this->ch, + CURLOPT_URL, + $this->api . "projects/" . $project->id . "/columns" + ); + $data = json_decode(curl_exec($this->ch)); + + $columns = []; + $colPos = 1; + + foreach ($data as $column) { + print " Loading Column " . $column->name . "..."; + + $currentColumn = (object)array( + "id" => $column->id, "name" => $column->name, "position" => $colPos + ); + $colPos++; + + curl_setopt( + $this->ch, + CURLOPT_URL, + $this->api . "projects/columns/" . $column->id . "/cards" + ); + $cardData = json_decode(curl_exec($this->ch)); + + $cards = []; + $cardPos = 1; + + foreach ($cardData as $card) { + $cards[] = (object)array( + "id" => $card->id, + "title" => "Imported", + "color" => "#ffffe0", + "description" => $card->note, + "position" => $cardPos, + "column_id" => $column->id + ); + $cardPos++; + } + + $this->projects[$project->id]->columns[$column->id] = $currentColumn; + $this->projects[$project->id]->columns[$column->id]->cards = $cards; + + print " Loaded " . ($cardPos - 1) . " cards (tasks).\n"; + } + + print " Loaded Project " . $project->name . ".\n"; + } + } + + private function importData() { + foreach($this->projects as $project) { + print " Importing Project " . $project->name . "...\n"; + + $exists = $this->db->querySingle('SELECT id FROM board WHERE name = "' . + $project->name . '"'); + + if (count($exists) === 0) { + $stmt = $this->db->prepare('INSERT INTO board (name, is_active) ' . + 'VALUES (:name, :is_active)'); + + $stmt->bindValue(":name", $project->name); + $stmt->bindValue(":is_active", true); + + if ($stmt->execute()) { + print " Created board.\n"; + } + } + + if ($this->isNew) { + $stmt = $this->db->prepare('INSERT INTO user (username, security_level, ' . + 'password_hash) VALUES (:uname, :secLev, :hash)'); + $stmt->bindValue(':uname', 'admin'); + $stmt->bindValue(':secLev', 1); + $stmt->bindValue(':hash', password_hash('admin', PASSWORD_BCRYPT)); + + if ($stmt->execute()) { + print " Created default admin user.\n"; + } + + $adminId = $this->db->lastInsertRowID(); + $stmt = $this->db->prepare('INSERT INTO useroption (id, ' . + 'new_tasks_at_bottom, show_animations, show_assignee, ' . + 'multiple_tasks_per_row, language) VALUES (:id, :new, :anim, ' . + ':assign, :mult, :lang)'); + $stmt->bindValue(':id', $adminId); + $stmt->bindValue(':new', true); + $stmt->bindValue(':anim', true); + $stmt->bindValue(':assign', true); + $stmt->bindValue(':mult', false); + $stmt->bindValue(':lang', 'en'); + + $stmt->execute(); + } + + $boardId = $this->cleanupExistingTables($project->name); + + $users = $this->db->query('SELECT id FROM user WHERE security_level = 1'); + $stmt = $this->db->prepare('INSERT INTO board_user (user_id, board_id) '. + 'VALUES (:user_id, :board_id)'); + + while ($user = $users->fetchArray()) { + $stmt->bindValue(':user_id', $user['id']); + $stmt->bindValue(':board_id', $boardId); + + $stmt->execute(); + } + print " Added admin user(s) to board.\n"; + + $colCount = 0; + $taskCount = 0; + + foreach ($project->columns as $column) { + $stmt = $this->db->prepare('INSERT INTO column (name, position, board_id) ' . + 'VALUES (:name, :pos, :board_id)'); + + $stmt->bindValue(':name', $column->name); + $stmt->bindValue(':pos', $column->position); + $stmt->bindValue(':board_id', $boardId); + + $stmt->execute(); + $colCount++; + $colId = $this->db->lastInsertRowID(); + + $stmt = $this->db->prepare('INSERT INTO task (title, description, ' . + 'color, position, column_id) VALUES (:title, :description, :color,' . + ':pos, :col_id)'); + foreach($column->cards as $card) { + $stmt->bindValue(':title', $card->title); + $stmt->bindValue(':description', $card->description); + $stmt->bindValue(':color', $card->color); + $stmt->bindValue(':pos', $card->position); + $stmt->bindValue(':col_id', $colId); + + $stmt->execute(); + $taskCount++; + } + } + + print " Inserted " . $colCount . " columns, and " . $taskCount . " tasks.\n"; + } + + print " Done Importing.\n"; + } + + private function cleanupExistingTables($boardName) { + $boardId = $this->db->querySingle('SELECT id FROM board ' . + 'WHERE name = "' . $boardName . '"'); + $this->db->exec('DELETE FROM board_user WHERE board_id = ' . $boardId); + $columns = $this->db->query('SELECT id FROM column WHERE board_id = ' . $boardId); + + while ($col = $columns->fetchArray()) { + $this->db->exec('DELETE FROM task WHERE column_id = ' . $col['id']); + } + + $this->db->exec('DELETE FROM column WHERE board_id = ' . $boardId); + + return $boardId; + } + +} + +$tb = new TbGitHubImport(); +$tb->import(); + diff --git a/tools/tb-migrate.php b/tools/tb-migrate.php new file mode 100644 index 0000000..4b431c5 --- /dev/null +++ b/tools/tb-migrate.php @@ -0,0 +1,382 @@ +oldDb = new SQLite3('taskboard.db', SQLITE3_OPEN_READONLY); + print "\t\tDone.\n"; + } catch (Exception $e) { + print "\t\tError opening 'taskboard.db'\n"; + print " " . $e->getMessage() . "\n"; + die(); + } + + print "Opening 'taskboard.sqlite'..."; + try { + unlink('taskboard.sqlite'); // Always start fresh. + + $this->newDb = new SQLite3('taskboard.sqlite'); + print "\t\tDone.\n\n"; + } catch (Exception $e) { + print "\t\tError creating 'taskboard.sqlite'\n"; + print " " . $e->getMessage() . "\n"; + die(); + } + } + + function __destruct() { + print "\n"; + print "Closing 'taskboard.sqlite'..."; + $this->newDb->exec('PRAGMA foreign_keys = ON'); + $this->newDb->close(); + print "\t\tDone.\n"; + + print "Closing 'taskboard.db'..."; + $this->oldDb->close(); + print "\t\tDone.\n"; + } + + function migrate() { + $this->createTables(); + + $this->migrateActivity(); + // TODO: attachments table + $this->migrateAutoAction(); + $this->migrateBoard(); + $this->migrateBoardUser(); + $this->migrateCategory(); + $this->migrateCollapsed(); + $this->migrateComment(); + $this->migrateItem(); + $this->migrateLane(); + $this->migrateUser(); + } + + private function createTables() { + print " Creating new tables..."; + $schema = file_get_contents('schema'); + + $this->newDb->exec($schema); + $this->newDb->exec('PRAGMA foreign_keys = OFF'); + print "\t\t\tDone.\n\n"; + } + + private function getItemType($str) { + if (strpos($str, 'item') !== false) { + return 'task'; + } + + if (strpos($str, 'column') !== false) { + return 'column'; + } + + if (strpos($str, 'board') !== false) { + return 'board'; + } + + if (strpos($str, 'changed') !== false || strpos($str, 'logged') !== false || + strpos($str, 'added') !== false || strpos($str, 'removed') !== false) { + return 'user'; + } + + return null; + } + + private function getItemId($itemId, $newValue) { + if (isset($itemId)) { + return $itemId; + } + + $obj = json_decode($newValue); + + if ($obj !== null) { + return $obj->id; + } + + return null; + } + + private function getActionType($row) { + switch ($row['action_id']) { + case 0: + return 1; + break; + case 2: + return 4; + break; + case 3: + return 6; + break; + default: + return null; + } + } + + private function getActionChange($row) { + if (isset($row['color'])) { + return $row['color']; + } + + if (isset($row['category_id'])) { + return $row['category_id']; + } + + if (isset($row['assignee_id'])) { + return $row['assignee_id']; + } + + return null; + } + + private function migrateTable($stmtStr, $fn, $oldTable = null) { + $table = isset($oldTable) ? $oldTable : explode(' ', $stmtStr)[2]; + + print " Migrating table `$table`..."; + + $results = $this->oldDb->query("SELECT * from $table ORDER BY id"); + + if ($results->numColumns() === 0) { + print " no rows.\n"; + return; + } + + $stmt = $this->newDb->prepare($stmtStr); + $rowCount = 0; + $errCount = 0; + + while ($row = $results->fetchArray()) { + $fn($stmt, $row); + + if ($stmt->execute() === false) { + $errCount++; + continue; + } + + $rowCount++; + } + + $stmt->close(); + print strlen($table) < 10 ? "\t" : ""; + print "\t$rowCount rows migrated.\n"; + + if ($errCount > 0) { + print " $errCount rows failed.\n"; + } + } + + private function migrateActivity() { + $stmtStr = 'INSERT INTO activity (id, user_id, log_text, before, after, ' . + 'item_type, item_id, timestamp) VALUES (:id, :user_id, :log_text, ' . + ':before, :after, :item_type, :item_id, :timestamp)'; + $knownNames = []; + + $this->migrateTable($stmtStr, function(&$stmt, $row) use ($knownNames) { + $name = explode(' ', $row['comment'])[0]; + + if (!array_key_exists($name, $knownNames)) { + $usr = $this->oldDb->query('SELECT id FROM user ' . + 'WHERE username = "' . $name . '"'); + + $knownNames[$name] = $usr->fetchArray()['id']; + } + + $type = $this->getItemType($row['comment']); + + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':user_id', $knownNames[$name]); + $stmt->bindValue(':log_text', $row['comment']); + $stmt->bindValue(':before', $row['old_value']); + $stmt->bindValue(':after', $row['new_value']); + $stmt->bindValue(':item_type', $type); + $stmt->bindValue(':item_id', $type === 'user' + ? $knownNames[$name] + : $this->getItemId($row['item_id'], $row['new_value'])); + $stmt->bindValue(':timestamp', $row['timestamp']); + }); + } + + private function migrateAutoAction() { + $stmtStr = 'INSERT INTO autoaction (id, trigger, source_id, type, ' . + 'change_to, board_id) VALUES (:id, :trigger, :source_id, :type, ' . + ':change_to, :board_id)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':trigger', ((int)$row['trigger_id']) + 1); + $stmt->bindValue(':source_id', $row['secondary_id']); + $stmt->bindValue(':type', $this->getActionType($row)); + $stmt->bindValue(':change_to', $this->getActionChange($row)); + $stmt->bindValue(':board_id', $row['board_id']); + }); + } + + private function migrateBoard() { + $stmtStr = 'INSERT INTO board (id, name, is_active) ' . + 'VALUES (:id, :name, :is_active)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':name', $row['name']); + $stmt->bindValue(':is_active', $row['active']); + }); + } + + private function migrateBoardUser() { + $stmtStr = 'INSERT INTO board_user (id, user_id, board_id) ' . + 'VALUES (:id, :user_id, :board_id)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':user_id', $row['user_id']); + $stmt->bindValue(':board_id', $row['board_id']); + }); + } + + private function migrateCategory() { + $stmtStr = 'INSERT INTO category (id, name, default_task_color, board_id) ' . + 'VALUES (:id, :name, "#ffffe0", :board_id)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':name', $row['name']); + $stmt->bindValue(':board_id', $row['board_id']); + }); + } + + private function migrateCollapsed() { + $stmtStr = 'INSERT INTO collapsed (id, user_id, column_id) ' . + 'VALUES (:id, :user_id, :column_id)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':user_id', $row['user_id']); + $stmt->bindValue(':column_id', $row['lane_id']); + }); + } + + private function migrateComment() { + $stmtStr = 'INSERT INTO comment (id, text, user_id, task_id, timestamp, ' . + 'is_edited) VALUES (:id, :text, :user_id, :task_id, :timestamp, 0)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':text', $row['text']); + $stmt->bindValue(':user_id', $row['user_id']); + $stmt->bindValue(':task_id', $row['item_id']); + $stmt->bindValue(':timestamp', $row['timestamp']); + }); + } + + private function migrateItem() { + $stmtStr = 'INSERT INTO task (id, title, description, color, due_date, ' . + 'points, position, column_id) VALUES (:id, :title, :description, :color, ' . + ':due_date, :points, :position, :column_id)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':title', $row['title']); + $stmt->bindValue(':description', $row['description']); + $stmt->bindValue(':color', $row['color']); + $stmt->bindValue(':due_date', $row['due_date']); + $stmt->bindValue(':points', $row['points']); + $stmt->bindValue(':position', $row['position']); + $stmt->bindValue(':column_id', $row['lane_id']); + + $this->newDb->exec('INSERT INTO task_user (user_id, task_id) ' . + 'VALUES (' . $row['assignee'] . ', ' . $row['id'] . ')'); + $this->newDb->exec('INSERT INTO category_task (category_id, task_id) ' . + 'VALUES (' . $row['category'] . ', ' . $row['id'] . ')'); + }, 'item', function ($lastId, $row) { + }); + } + + private function migrateLane() { + $stmtStr = 'INSERT INTO column (id, name, position, board_id) ' . + 'VALUES (:id, :name, :position, :board_id)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':name', $row['name']); + $stmt->bindValue(':position', $row['position']); + $stmt->bindValue(':board_id', $row['board_id']); + }, 'lane'); + } + + private function migrateOption() { + $stmtStr = 'INSERT INTO useroption (id, new_tasks_at_bottom, ' . + 'show_animations, show_assignee, multiple_tasks_per_row, language) ' . + 'VALUES (:id, :atBottom, :anims, :assignee, 0, "en")'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':atBottom', isset($row['tasks_order']) + ? $row['tasks_order'] + : $row['new_task_position']); + $stmt->bindValue(':anims', isset($row['animate']) + ? $row['animate'] + : $row['show_animations']); + $stmt->bindValue(':assignee', isset($row['show_assignee']) + ? $row['show_assignee'] + : 1); + }, 'option'); + } + + private function migrateUser() { + $stmtStr = 'INSERT INTO user (id, username, security_level, password_hash, ' . + 'email, default_board_id, user_option_id, last_login) VALUES (:id, ' . + ':uname, :secLev, :hash, :email, :board, :option, :last)'; + + $this->migrateTable($stmtStr, function (&$stmt, $row) { + $stmt->bindValue(':id', $row['id']); + $stmt->bindValue(':uname', $row['username']); + $stmt->bindValue(':secLev', $row['is_admin'] === 1 ? 1 : 3); + $stmt->bindValue(':hash', $row['password']); + $stmt->bindValue(':email', $row['email']); + $stmt->bindValue(':board', $row['default_board']); + $stmt->bindValue(':option', $row['id']); + $stmt->bindValue(':last', $row['last_login']); + }); + + $users = $this->newDb->query('SELECT id FROM user ORDER BY id'); + + while ($row = $users->fetchArray()) { + $stmt = $this->newDb->prepare('INSERT INTO useroption (id, ' . + 'new_tasks_at_bottom, show_animations, show_assignee, ' . + 'multiple_tasks_per_row, language) VALUES (:id, :atBottom, :anims, ' . + ':assignee, 0, "en")'); + + $opts = $this->oldDb->query( + 'SELECT * FROM option WHERE id = ' . (int)$row['id'] + ); + $opts = $opts->fetchArray(); + + if ($opts === false) { + continue; + } + + $stmt->bindValue(':id', $opts['id']); + $stmt->bindValue(':atBottom', isset($opts['tasks_order']) + ? $opts['tasks_order'] + : $opts['new_task_position']); + $stmt->bindValue(':anims', isset($opts['animate']) + ? $opts['animate'] + : $opts['show_animations']); + $stmt->bindValue(':assignee', isset($opts['show_assignee']) + ? $opts['show_assignee'] + : 1); + + $stmt->execute(); + } + + $stmt->close(); + } + +} + +$tbm = new TbMigrate(); +$tbm->migrate(); +