protected function keyOp($group, $op = 'preview') { $errors = []; $keymap = []; $this->logSql = 1; $this->sqltraces = []; $ltm_translations = $this->manager->getTranslationsTableName(); if (!in_array($group, $this->manager->config(Manager::EXCLUDE_GROUPS_KEY)) && $this->manager->config('admin_enabled')) { $srckeys = explode("\n", trim(\Request::get('srckeys'))); $dstkeys = explode("\n", trim(\Request::get('dstkeys'))); array_walk($srckeys, function (&$val, $key) use(&$srckeys) { $val = trim($val); if ($val === '') { unset($srckeys[$key]); } }); array_walk($dstkeys, function (&$val, $key) use(&$dstkeys) { $val = trim($val); if ($val === '') { unset($dstkeys[$key]); } }); if (!$group) { $errors[] = trans($this->packagePrefix . 'messages.keyop-need-group'); } elseif (count($srckeys) !== count($dstkeys) && ($op === 'copy' || $op === 'move' || count($dstkeys))) { $errors[] = trans($this->packagePrefix . 'messages.keyop-count-mustmatch'); } elseif (!count($srckeys)) { $errors[] = trans($this->packagePrefix . 'messages.keyop-need-keys'); } else { if (!count($dstkeys)) { $dstkeys = array_fill(0, count($srckeys), null); } $keys = array_combine($srckeys, $dstkeys); $hadErrors = false; foreach ($keys as $src => $dst) { $keyerrors = []; if ($dst !== null) { if ((substr($src, 0, 1) === '*') !== (substr($dst, 0, 1) === '*')) { $keyerrors[] = trans($this->packagePrefix . 'messages.keyop-wildcard-mustmatch'); } if ((substr($src, -1, 1) === '*') !== (substr($dst, -1, 1) === '*')) { $keyerrors[] = trans($this->packagePrefix . 'messages.keyop-wildcard-mustmatch'); } if (substr($src, 0, 1) === '*' && substr($src, -1, 1) === '*') { $keyerrors[] = trans($this->packagePrefix . 'messages.keyop-wildcard-once'); } } if (!empty($keyerrors)) { $hadErrors = true; $keymap[$src] = ['errors' => $keyerrors, 'dst' => $dst]; continue; } list($srcgrp, $srckey) = self::keyGroup($group, $src); list($dstgrp, $dstkey) = $dst === null ? [null, null] : self::keyGroup($group, $dst); if (substr($src, 0, 1) === '*') { if ($dst === null) { $rows = $this->getConnection()->select($sql = <<<SQL SELECT DISTINCT `group`, `key`, locale, id, NULL dst, NULL dstgrp FROM {$ltm_translations} t1 WHERE `group` = ? AND `key` LIKE BINARY ? ORDER BY locale, `key` SQL , [$srcgrp, '%' . mb_substr($srckey, 1)]); } else { $rows = $this->getConnection()->select($sql = <<<SQL SELECT DISTINCT `group`, `key`, locale, id, CONCAT(SUBSTR(`key`, 1, CHAR_LENGTH(`key`)-?), ?) dst, ? dstgrp FROM {$ltm_translations} t1 WHERE `group` = ? AND `key` LIKE BINARY ? AND NOT exists(SELECT * FROM {$ltm_translations} t2 WHERE t2.value IS NOT NULL AND t2.`group` = ? AND t1.locale = t2.locale AND t2.`key` LIKE BINARY CONCAT(SUBSTR(t1.`key`, 1, CHAR_LENGTH(t1.`key`)-?), ?)) ORDER BY locale, `key` SQL , [mb_strlen($srckey) - 1, mb_substr($dstkey, 1), $dstgrp, $srcgrp, '%' . mb_substr($srckey, 1), $dstgrp, mb_strlen($srckey) - 1, mb_substr($dstkey, 1)]); } } elseif (substr($src, -1, 1) === '*') { if ($dst === null) { $rows = $this->getConnection()->select($sql = <<<SQL SELECT DISTINCT `group`, `key`, locale, id, NULL dst, NULL dstgrp FROM {$ltm_translations} t1 WHERE `group` = ? AND `key` LIKE BINARY ? ORDER BY locale, `key` SQL , [$srcgrp, mb_substr($srckey, 0, -1) . '%']); } else { $rows = $this->getConnection()->select($sql = <<<SQL SELECT DISTINCT `group`, `key`, locale, id, CONCAT(?, SUBSTR(`key`, ?+1, CHAR_LENGTH(`key`)-?)) dst, ? dstgrp FROM {$ltm_translations} t1 WHERE `group` = ? AND `key` LIKE BINARY ? AND NOT exists(SELECT * FROM {$ltm_translations} t2 WHERE t2.value IS NOT NULL AND t2.`group` = ? AND t1.locale = t2.locale AND t2.`key` LIKE BINARY CONCAT(?, SUBSTR(t1.`key`, ?+1, CHAR_LENGTH(t1.`key`)-?))) ORDER BY locale, `key` SQL , [mb_substr($dstkey, 0, -1), mb_strlen($srckey) - 1, mb_strlen($srckey) - 1, $dstgrp, $srcgrp, mb_substr($srckey, 0, -1) . '%', $dstgrp, mb_substr($dstkey, 0, -1), mb_strlen($srckey) - 1, mb_strlen($srckey) - 1]); } } else { if ($dst === null) { $rows = $this->getConnection()->select($sql = <<<SQL SELECT DISTINCT `group`, `key`, locale, id, NULL dst, NULL dstgrp FROM {$ltm_translations} t1 WHERE `group` = ? AND `key` LIKE BINARY ? ORDER BY locale, `key` SQL , [$srcgrp, $srckey]); } else { $rows = $this->getConnection()->select($sql = <<<SQL SELECT DISTINCT `group`, `key`, locale, id, ? dst, ? dstgrp FROM {$ltm_translations} t1 WHERE `group` = ? AND `key` LIKE BINARY ? AND NOT exists(SELECT * FROM {$ltm_translations} t2 WHERE t2.value IS NOT NULL AND t2.`group` = ? AND t1.locale = t2.locale AND t2.`key` LIKE BINARY ?) ORDER BY locale, `key` SQL , [$dstkey, $dstgrp, $srcgrp, $srckey, $dstgrp, $dstkey]); } } $keymap[$src] = ['dst' => $dst, 'rows' => $rows]; } if (!$hadErrors && ($op === 'copy' || $op === 'move' || $op === 'delete')) { foreach ($keys as $src => $dst) { $rows = $keymap[$src]['rows']; $rowids = array_reduce($rows, function ($carry, $row) { return $carry . ',' . $row->id; }, ''); $rowids = substr($rowids, 1); list($srcgrp, $srckey) = self::keyGroup($group, $src); if ($op === 'move') { foreach ($rows as $row) { list($dstgrp, $dstkey) = self::keyGroup($row->dstgrp, $row->dst); $to_delete = $this->getConnection()->select(<<<SQL SELECT GROUP_CONCAT(id SEPARATOR ',') ids FROM {$ltm_translations} tr WHERE `group` = ? AND `key` = ? AND locale = ? AND id NOT IN ({$rowids}) SQL , [$dstgrp, $dstkey, $row->locale]); if (!empty($to_delete)) { $to_delete = $to_delete[0]->ids; if ($to_delete) { //$this->getConnection()->update("UPDATE ltm_translations SET is_deleted = 1 WHERE id IN ($to_delete)"); // have to delete right away, we will be bringing another key here // TODO: copy value to new key's saved value $this->getConnection()->delete("DELETE FROM {$ltm_translations} WHERE id IN ({$to_delete})"); } } $this->getConnection()->update("UPDATE {$ltm_translations} SET `group` = ?, `key` = ?, status = 1 WHERE id = ?", [$dstgrp, $dstkey, $row->id]); } } elseif ($op === 'delete') { //$this->getConnection()->delete("DELETE FROM ltm_translations WHERE id IN ($rowids)"); $this->getConnection()->update("UPDATE {$ltm_translations} SET is_deleted = 1 WHERE is_deleted = 0 AND id IN ({$rowids})"); } elseif ($op === 'copy') { // TODO: split operation into update and insert so that conflicting keys get new values instead of being replaced foreach ($rows as $row) { list($dstgrp, $dstkey) = self::keyGroup($row->dstgrp, $row->dst); $to_delete = $this->getConnection()->select(<<<SQL SELECT GROUP_CONCAT(id SEPARATOR ',') ids FROM {$ltm_translations} tr WHERE `group` = ? AND `key` = ? AND locale = ? AND id NOT IN ({$rowids}) SQL , [$dstgrp, $dstkey, $row->locale]); if (!empty($to_delete)) { $to_delete = $to_delete[0]->ids; if ($to_delete) { //$this->getConnection()->update("UPDATE ltm_translations SET is_deleted = 1 WHERE id IN ($to_delete)"); $this->getConnection()->delete("DELETE FROM {$ltm_translations} WHERE id IN ({$to_delete})"); } } $this->getConnection()->insert($sql = <<<SQL INSERT INTO {$ltm_translations} SELECT NULL id, 1 status, locale, ? `group`, ? `key`, value, sysdate() created_at, sysdate() updated_at, source, saved_value, is_deleted, was_used FROM {$ltm_translations} t1 WHERE id = ? SQL , [$dstgrp, $dstkey, $row->id]); } } } } } } else { $errors[] = trans($this->packagePrefix . 'messages.keyops-not-authorized'); } $this->logSql = 0; return \View::make($this->packagePrefix . 'keyop')->with('controller', ManagerServiceProvider::CONTROLLER_PREFIX . get_class($this))->with('package', $this->package)->with('errors', $errors)->with('keymap', $keymap)->with('op', $op)->with('group', $group); }