diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f95b139
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/vendor
+/bin
+composer.phar
+composer.lock
+.DS_Store
+.idea
\ No newline at end of file
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
new file mode 100644
index 0000000..d3b8f8f
--- /dev/null
+++ b/.scrutinizer.yml
@@ -0,0 +1,19 @@
+checks:
+ php:
+ code_rating: true
+ duplication: true
+
+filter:
+ paths:
+ - src/*
+ excluded_paths:
+ - tests/*
+
+build:
+ tests:
+ override:
+ -
+ command: phpunit --coverage-clover=coverage
+ coverage:
+ file: coverage
+ format: php-clover
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0394476
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: php
+
+php:
+ - 5.5
+ - 5.6
+ - hhvm
+
+before_script:
+ - travis_retry composer self-update
+ - travis_retry composer install --prefer-source --no-interaction
+
+script: phpunit --coverage-text
+
+notifications:
+ email:
+ - "adrian@anavallasuiza.com"
+ - "carlos@anavallasuiza.com"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c13f8ad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,84 @@
+# Anavel translation [](https://travis-ci.org/anavel/translation)
+
+Manage laravel translation files from your admin panel. This package depends on [Anavel foundation](https://github.com/anavel/foundation)
+
+### Features
+
+* Easily manage app and vendor translation files.
+* Automatically reorders translations (alphabetically)
+* Easily create new language lines
+* Arrays supported
+
+## Installation
+
+
+## Configuration
+
+Publish translation config file with `php artisan vendor:publish`
+
+Include the files you want to manage within the `files` array, like this:
+
+```
+ /*
+ |--------------------------------------------------------------------------
+ | Files to translate
+ |--------------------------------------------------------------------------
+ |
+ */
+ 'files' => [
+ 'user' => [
+ 'aFileName',
+ 'anotherFileName'
+ ],
+ 'vendor' => [
+ 'vendorname' => 'vendorFileName'
+ ]
+ ],
+```
+
+`user` is an array of filenames (without extension) located in Laravel's default folder (resources/lang/LOCALE_NAME).
+`vendor` is an associative array of filenames (without extension), keyed by vendorname, located in Laravel's default folder (resources/lang/vendor/VENDORNAME/LOCALE_NAME).
+
+This package will read and then write those files, so your app must have write permissions to those folders. You must specify a disc driver for Laravel to use:
+
+config/anavel-translation.php:
+
+```
+ /*
+ |--------------------------------------------------------------------------
+ | File Disc Driver
+ |--------------------------------------------------------------------------
+ |
+ | Disc driver pointing to resources/lang folder
+ |
+ */
+ 'filedriver' => 'YOUR_DRIVER_NAME',
+```
+
+config/filesystem.php:
+
+```
+ 'disks' => [
+ [SOME OTHER FILE DRIVERS ],
+ 'YOUR_DRIVER_NAME' => [
+ 'driver' => 'local',
+ 'root' => base_path('resources/lang'),
+ ],
+ ]
+```
+
+## Versioning
+
+If you use a versioning system (such as git) you should add the language folder to your gitignore. Otherwise, you might
+get conflicts if different users update the translations.
+
+## How it works
+
+ Translations reads the files that you specify in the config and displays their content in a form, tabbed by locale.
+ The translation key becomes the input label and the translation itself becomes the input value.
+ Locales are taken from the Anavel foundation config.
+
+ To make the translation process easier, Translation shows the same language entries in all locales, even if a key is missing in a given locale.
+ In that case, the displayed text will be taken from the fallback locale (as Laravel does).
+
+ When saved, translations are written back to the files. If a file doesn't exist in a locale, a new one is created.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..1f0e69c
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,37 @@
+{
+ "name": "anavel/translation",
+ "description": "Manage laravel translation files from your admin panel",
+ "authors": [
+ {
+ "name": "Adrian P. Blunier",
+ "email": "adrian@anavallasuiza.com"
+ },
+ {
+ "name": "Carlos Morales",
+ "email": "carlos@anavallasuiza.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.5.9",
+ "illuminate/support": "5.1.*",
+ "illuminate/translation": "5.1.*",
+ "anavel/foundation": "dev-master"
+ },
+ "require-dev": {
+ "orchestra/testbench": "~3.1",
+ "phpunit/phpunit": "~4.0",
+ "whatthejeff/nyancat-phpunit-resultprinter": "~1.2",
+ "mockery/mockery": "^0.9.4"
+ },
+ "autoload": {
+ "psr-4": {
+ "Anavel\\Translation\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Anavel\\Translation\\Tests\\": "tests"
+ }
+ },
+ "minimum-stability": "stable"
+}
diff --git a/config/anavel-translation.php b/config/anavel-translation.php
new file mode 100644
index 0000000..ff13a69
--- /dev/null
+++ b/config/anavel-translation.php
@@ -0,0 +1,44 @@
+ 'Translation',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Displayed icon
+ |--------------------------------------------------------------------------
+ |
+ */
+ 'icon' => 'fa-globe',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Files to translate
+ |--------------------------------------------------------------------------
+ |
+ */
+ 'files' => [
+ 'user' => [
+
+ ],
+ 'vendor' => [
+
+ ]
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | File Disc Driver
+ |--------------------------------------------------------------------------
+ |
+ | Disc driver pointing to resources/lang folder
+ |
+ */
+ 'filedriver' => 'lang',
+];
\ No newline at end of file
diff --git a/lang/en/messages.php b/lang/en/messages.php
new file mode 100644
index 0000000..16ef7df
--- /dev/null
+++ b/lang/en/messages.php
@@ -0,0 +1,29 @@
+ 'Create',
+ 'edit_title' => 'Edit',
+ 'show_title' => 'Show',
+ 'search_input' => 'Search',
+ 'create_button' => 'Create',
+ 'empty_list' => 'Empty table',
+ 'actions_table_header' => 'Actions',
+ 'show_button' => 'Show',
+ 'edit_button' => 'Edit',
+ 'delete_button' => 'Delete',
+ 'back_button' => 'Back',
+ 'cancel_button' => 'Cancel',
+ 'save_button' => 'Save',
+ 'confirm_button' => 'Confirm',
+ 'alert_translations_saved_title' => 'Everything OK!',
+ 'alert_translations_saved_text' => 'The new item was saved successfully',
+ 'alert_translations_destroy_title' => 'Everything OK!',
+ 'alert_translations_destroy_text' => 'The item was deleted successfully',
+ 'alert_empty_translations_title' => 'Nothing to update!',
+ 'alert_empty_translations_text' => 'No translations sent',
+ 'new_line' => 'New translation line',
+ 'new_line_key_label' => 'Key',
+ 'new_line_key_placeholder' => 'You can create arrays using dots: mainkey.key',
+ 'new_line_value_label' => 'Translation',
+ 'new_button' => 'Add new',
+];
diff --git a/lang/es/messages.php b/lang/es/messages.php
new file mode 100644
index 0000000..b5a5208
--- /dev/null
+++ b/lang/es/messages.php
@@ -0,0 +1,29 @@
+ 'Crear',
+ 'edit_title' => 'Editar',
+ 'show_title' => 'Ver',
+ 'search_input' => 'Buscar',
+ 'create_button' => 'Crear',
+ 'empty_list' => 'Tabla vacía',
+ 'actions_table_header' => 'Acciones',
+ 'show_button' => 'Ver',
+ 'edit_button' => 'Editar',
+ 'delete_button' => 'Borrar',
+ 'back_button' => 'Volver',
+ 'cancel_button' => 'Cancelar',
+ 'save_button' => 'Guardar',
+ 'confirm_button' => 'Confirmar',
+ 'alert_translations_saved_title' => '¡Todo bien!',
+ 'alert_translations_saved_text' => 'El elemento ha sido guardado correctamente',
+ 'alert_translations_destroy_title' => '¡Todo bien!',
+ 'alert_translations_destroy_text' => 'El elemento ha sido borrado correctamente',
+ 'alert_empty_translations_title' => '¡Nada que actualizar!',
+ 'alert_empty_translations_text' => 'No se han recibido traducciones',
+ 'new_line' => 'Nueva línea de traducción',
+ 'new_line_key_label' => 'Clave',
+ 'new_line_key_placeholder' => 'Puedes crear arrays usando puntos: claveprincipal.clave',
+ 'new_line_value_label' => 'Traducción',
+ 'new_button' => 'Añadir nueva',
+];
diff --git a/lang/gl/messages.php b/lang/gl/messages.php
new file mode 100644
index 0000000..470455a
--- /dev/null
+++ b/lang/gl/messages.php
@@ -0,0 +1,29 @@
+ 'Crear',
+ 'edit_title' => 'Editar',
+ 'show_title' => 'Ver',
+ 'search_input' => 'Buscar',
+ 'create_button' => 'Crear',
+ 'empty_list' => 'Táboa vacía',
+ 'actions_table_header' => 'Accións',
+ 'show_button' => 'Ver',
+ 'edit_button' => 'Editar',
+ 'delete_button' => 'Borrar',
+ 'back_button' => 'Volver',
+ 'cancel_button' => 'Cancelar',
+ 'save_button' => 'Gardar',
+ 'confirm_button' => 'Confirmar',
+ 'alert_translations_saved_title' => '¡Todo ben!',
+ 'alert_translations_saved_text' => 'O elemento foi gardado correctamente',
+ 'alert_translations_destroy_title' => 'Todo ben!',
+ 'alert_translations_destroy_text' => 'O elemento foi borrado correctamente',
+ 'alert_empty_translations_title' => '¡Nada que actualizar!',
+ 'alert_empty_translations_text' => 'Non se recibiron traducións',
+ 'new_line' => 'Nova liña de tradución',
+ 'new_line_key_label' => 'Clave',
+ 'new_line_key_placeholder' => 'Podes crear arrays usando puntos: claveprincipal.clave',
+ 'new_line_value_label' => 'Tradución',
+ 'new_button' => 'Engadir nova',
+];
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..f2a2d27
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ ./tests/
+
+
+
+
+
+ ./src/
+
+
+
\ No newline at end of file
diff --git a/src/Http/Controllers/FileController.php b/src/Http/Controllers/FileController.php
new file mode 100644
index 0000000..2a76cc4
--- /dev/null
+++ b/src/Http/Controllers/FileController.php
@@ -0,0 +1,226 @@
+lang = config('anavel.translation_languages');
+ $this->config = config('anavel-translation.files');
+ $this->fallback = config('app.fallback_locale');
+ }
+
+ public function edit($param, $param2 = null)
+ {
+ $editLangsMissingKeys = [];
+
+ $editLangs = $this->getLangsFromFile($param, $param2);
+
+ // Add back keys from fallback_locale to other langs that don't have them. This way all langs have the same keys
+ // and it is easier for the user to translate and keep track of them.
+ foreach ($editLangs as $langKey => $lang) {
+ if (! empty($editLangs[$this->fallback])) {
+ if ($langKey === $this->fallback) {
+ $this->ksortTree($editLangs[$langKey]);
+ continue;
+ }
+ $missingKeys = $this->arrayDiffKeyRecursive($editLangs[$this->fallback], $editLangs[$langKey]);
+
+ if (! empty($missingKeys)) {
+ foreach (array_keys($missingKeys) as $missingKey) {
+ $editLangs[$langKey][$missingKey] = $editLangs[$this->fallback][$missingKey];
+ }
+ $editLangsMissingKeys[$langKey] = array_keys($missingKeys);
+ }
+ }
+ $this->ksortTree($editLangs[$langKey]);
+ }
+
+
+ return View::make('anavel-translation::pages.edit', compact('editLangs', 'editLangsMissingKeys'));
+ }
+
+ public function update(Request $request, $param, $param2 = null)
+ {
+ if (! $request->has('translations')) {
+ session()->flash('anavel-alert', [
+ 'type' => 'error',
+ 'icon' => 'fa-error',
+ 'title' => trans('anavel-translation::messages.alert_empty_translations_title'),
+ 'text' => trans('anavel-translation::messages.alert_empty_translations_text')
+ ]);
+
+ return redirect()->back()->withInput();
+ }
+
+ $translations = $request->input('translations');
+
+ $disk = $this->getDisk();
+
+ foreach ($this->lang as $lang) {
+ if (! empty($translations[$lang])) {
+ $translation = $this->arrayFilterRecursive($translations[$lang]);
+ $fileRoute = empty($param2) ? $lang . '/' . $param . '.php' : 'vendor/' . $param . '/' . $lang . '/' . $param2 . '.php';
+ $string = "put($fileRoute, $string);
+ }
+ }
+
+
+ session()->flash('anavel-alert', [
+ 'type' => 'success',
+ 'icon' => 'fa-check',
+ 'title' => trans('anavel-translation::messages.alert_translations_saved_title'),
+ 'text' => trans('anavel-translation::messages.alert_translations_saved_text')
+ ]);
+
+ return redirect(null, 200)->route('anavel-translation.file.edit', [$param, $param2]);
+ }
+
+ public function create(Request $request, $param, $param2 = null)
+ {
+ if (! $request->has('translations-new')) {
+ session()->flash('anavel-alert', [
+ 'type' => 'error',
+ 'icon' => 'fa-error',
+ 'title' => trans('anavel-translation::messages.alert_empty_new_translations_title'),
+ 'text' => trans('anavel-translation::messages.alert_empty_new_translations_text')
+ ]);
+
+ return redirect()->back()->withInput();
+ }
+
+ $newLine = $request->input('translations-new');
+
+
+ $langs = $this->getLangsFromFile($param, $param2);
+
+ $disk = $this->getDisk();
+
+ //We'll only add this line to the main lang
+ $translation = $langs[$this->fallback];
+
+ //http://stackoverflow.com/questions/1417019/how-to-recursively-create-a-multidimensional-array#1417033
+
+ $key = $newLine['key'];
+ $val = $newLine['value'];
+ $path = explode('.', $key);
+
+ $newTranslation = array();
+ $tmp = &$newTranslation;
+ foreach ($path as $segment) {
+ $tmp[$segment] = array();
+ $tmp = &$tmp[$segment];
+ }
+ $tmp = $val;
+
+ $translation[key($newTranslation)] = $newTranslation[key($newTranslation)];
+
+
+ $fileRoute = empty($param2) ? $this->fallback . '/' . $param . '.php' : 'vendor/' . $param . '/' . $this->fallback . '/' . $param2 . '.php';
+ $string = "put($fileRoute, $string);
+
+ session()->flash('anavel-alert', [
+ 'type' => 'success',
+ 'icon' => 'fa-check',
+ 'title' => trans('anavel-translation::messages.alert_translations_saved_title'),
+ 'text' => trans('anavel-translation::messages.alert_translations_saved_text')
+ ]);
+
+ return redirect(null, 200)->route('anavel-translation.file.edit', [$param, $param2]);
+ }
+
+ protected function getDisk()
+ {
+ $diskDriver = config('anavel-translation.filedriver');
+ if (empty($diskDriver)) {
+ throw new \Exception('filedriver should be set in config');
+ }
+
+ return Storage::disk($diskDriver);
+ }
+
+ protected function getLangsFromFile($param, $param2 = null)
+ {
+ $langs = [];
+ foreach ($this->lang as $lang) {
+ $file = empty($param2) ? $param : "$param::$param2";
+ $transResult = trans($file, [], null, $lang);
+ /*
+ trans returns the original string if it can't find the translation. Since we are not
+ looking for a specific line but a whole file, an array should be returned. If that's not the case, the file doesn't exist.
+ */
+ $langs[$lang] = is_array($transResult) ? $transResult : [];
+ }
+
+ return $langs;
+ }
+
+ protected function arrayDiffKeyRecursive(array $arr1, array $arr2)
+ {
+ $diff = array_diff_key($arr1, $arr2);
+ $intersect = array_intersect_key($arr1, $arr2);
+
+ foreach ($intersect as $k => $v) {
+ if (is_array($arr1[$k]) && is_array($arr2[$k])) {
+ $d = $this->arrayDiffKeyRecursive($arr1[$k], $arr2[$k]);
+
+ if ($d) {
+ $diff[$k] = $d;
+ }
+ }
+ }
+
+ return $diff;
+ }
+
+ protected function arrayFilterRecursive(array $array)
+ {
+ foreach ($array as &$value) {
+ if (is_array($value)) {
+ $value = $this->arrayFilterRecursive($value);
+ }
+ }
+
+ return array_filter($array);
+ }
+
+ /**
+ * @return bool
+ * @author Kevin van Zonneveld <kevin@vanzonneveld.net>
+ * @copyright 2008 Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD Licence
+ * @version SVN: Release: $Id: ksortTree.inc.php 223 2009-01-25 13:35:12Z kevin $
+ * @link http://kevin.vanzonneveld.net/
+ *
+ * @param array $array
+ */
+ protected function ksortTree(&$array)
+ {
+ if (! is_array($array)) {
+ return false;
+ }
+
+ ksort($array);
+ foreach ($array as $k => $v) {
+ $this->ksortTree($array[$k]);
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Http/Controllers/HomeController.php b/src/Http/Controllers/HomeController.php
new file mode 100644
index 0000000..8f8ccce
--- /dev/null
+++ b/src/Http/Controllers/HomeController.php
@@ -0,0 +1,33 @@
+ 'translation',
+ 'namespace' => 'Anavel\Translation\Http\Controllers'
+ ],
+ function () {
+ Route::get('/', [
+ 'as' => 'anavel-translation.home',
+ 'uses' => 'HomeController@index'
+ ]);
+
+ Route::get('{param}/{param2?}', [
+ 'as' => 'anavel-translation.file.edit',
+ 'uses' => 'FileController@edit'
+ ]);
+
+ Route::post('{param}/{param2?}', [
+ 'as' => 'anavel-translation.file.create',
+ 'uses' => 'FileController@create'
+ ]);
+
+ Route::put('{param}/{param2?}', [
+ 'as' => 'anavel-translation.file.update',
+ 'uses' => 'FileController@update'
+ ]);
+ }
+);
diff --git a/src/Providers/ViewComposersServiceProvider.php b/src/Providers/ViewComposersServiceProvider.php
new file mode 100644
index 0000000..ff8fdf6
--- /dev/null
+++ b/src/Providers/ViewComposersServiceProvider.php
@@ -0,0 +1,27 @@
+app->view->composer('anavel-translation::molecules.sidebar.default', 'Anavel\Translation\View\Composers\SidebarComposer');
+ }
+}
diff --git a/src/Services/ConfigurationReader.php b/src/Services/ConfigurationReader.php
new file mode 100644
index 0000000..90d68d4
--- /dev/null
+++ b/src/Services/ConfigurationReader.php
@@ -0,0 +1,35 @@
+config) || empty($this->config)) {
+ return null;
+ }
+
+ $params = func_get_args();
+
+ $lastParam = array_pop($params);
+
+ $nestedConfig = $this->config;
+
+ if (is_array($params) && count($params) > 0) {
+ foreach ($params as $configKey) {
+ if (! array_key_exists($configKey, $nestedConfig)) {
+ $nestedConfig = array();
+ return null;
+ }
+
+ $nestedConfig = $nestedConfig[$configKey];
+ }
+ }
+
+ if (array_key_exists($lastParam, $nestedConfig)) {
+ return $nestedConfig[$lastParam];
+ }
+
+ return null;
+ }
+}
diff --git a/src/TranslationModuleProvider.php b/src/TranslationModuleProvider.php
new file mode 100644
index 0000000..689d0fc
--- /dev/null
+++ b/src/TranslationModuleProvider.php
@@ -0,0 +1,90 @@
+loadViewsFrom(__DIR__.'/../views', 'anavel-translation');
+
+ $this->loadTranslationsFrom(__DIR__.'/../lang', 'anavel-translation');
+
+// $this->publishes([
+// __DIR__.'/../public/js' => public_path('vendor/anavel-translation/js'),
+// ], 'assets');
+
+ $this->publishes([
+ __DIR__.'/../config/anavel-translation.php' => config_path('anavel-translation.php'),
+ ], 'config');
+ }
+
+ /**
+ * Register the service provider.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ $this->mergeConfigFrom(__DIR__.'/../config/anavel-translation.php', 'anavel-translation');
+
+ $this->app->register('Anavel\Translation\Providers\ViewComposersServiceProvider');
+
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return [];
+ }
+
+ public function name()
+ {
+ return config('anavel-translation.name');
+ }
+
+ public function routes()
+ {
+ return __DIR__.'/Http/routes.php';
+ }
+
+ public function mainRoute()
+ {
+ return route('anavel-translation.home');
+ }
+
+ public function hasSidebar()
+ {
+ return true;
+ }
+
+ public function sidebarMenu()
+ {
+ return 'anavel-translation::molecules.sidebar.default';
+ }
+
+ public function isActive()
+ {
+ $uri = Request::route()->uri();
+
+ if (strpos($uri, 'translation') !== false) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/View/Composers/SidebarComposer.php b/src/View/Composers/SidebarComposer.php
new file mode 100644
index 0000000..246e644
--- /dev/null
+++ b/src/View/Composers/SidebarComposer.php
@@ -0,0 +1,50 @@
+ route('anavel-translation.file.edit', [$userFile]),
+ 'name' => $userFile,
+ 'isActive' => URL::current() === route('anavel-translation.file.edit', [$userFile])
+ ];
+ }
+ }
+ if (array_key_exists('vendor', $files)) {
+ foreach ($files['vendor'] as $vendorKey => $vendorFile) {
+ $items[] = [
+ 'route' => route('anavel-translation.file.edit', [$vendorKey, $vendorFile]),
+ 'name' => "$vendorKey : $vendorFile",
+ 'isActive' => URL::current() === route('anavel-translation.file.edit', [$vendorKey, $vendorFile])
+ ];
+ }
+ }
+
+ $view->with([
+ 'items' => $items
+ ]);
+ }
+}
diff --git a/tests/Config/ConfigurationReaderTest.php b/tests/Config/ConfigurationReaderTest.php
new file mode 100644
index 0000000..7412b54
--- /dev/null
+++ b/tests/Config/ConfigurationReaderTest.php
@@ -0,0 +1,64 @@
+sut = new ConfigTraitClass();
+
+ $this->config = require __DIR__ . '/config.php';
+ }
+
+ public function test_returns_null_if_config_not_set()
+ {
+ $value = $this->sut->getConfigValue('something');
+
+ $this->assertNull($value);
+ }
+
+ public function test_returns_null_if_value_not_set()
+ {
+ $this->sut->config = [];
+
+ $value = $this->sut->getConfigValue('something');
+
+ $this->assertNull($value);
+ }
+
+ public function test_call_with_one_param()
+ {
+ $this->sut->config = $this->config;
+
+ $value = $this->sut->getConfigValue('single');
+
+ $this->assertEquals('one', $value);
+ }
+
+ public function test_call_with_two_params()
+ {
+ $this->sut->config = $this->config;
+
+ $value = $this->sut->getConfigValue('nested', 'second-level');
+
+ $this->assertEquals('two', $value);
+ }
+
+}
+
+class ConfigTraitClass
+{
+ public $config;
+
+ use ConfigurationReader;
+}
\ No newline at end of file
diff --git a/tests/Config/config.php b/tests/Config/config.php
new file mode 100644
index 0000000..6357fa5
--- /dev/null
+++ b/tests/Config/config.php
@@ -0,0 +1,8 @@
+ 'one',
+
+ 'nested' => [
+ 'second-level' => 'two'
+ ]
+];
\ No newline at end of file
diff --git a/tests/Controllers/FileControllerTest.php b/tests/Controllers/FileControllerTest.php
new file mode 100644
index 0000000..676396d
--- /dev/null
+++ b/tests/Controllers/FileControllerTest.php
@@ -0,0 +1,352 @@
+ ['en', 'es']]);
+ config(['anavel-translation.filedriver' => 'diskdriver']);
+ config(['app.fallback_locale' => 'en']);
+
+
+ $this->sut = new FileController();
+
+ $this->config = [
+ 'user' => [
+ 'one'
+ ],
+
+ 'vendor' => [
+ 'vendorname' => 'vendorfile'
+ ]
+ ];
+ }
+
+ public function test_edit_returns_array_when_file_empty()
+ {
+ config(['anavel-translation.files' => $this->config]);
+
+ $result = $this->sut->edit('test');
+
+ $this->assertObjectHasAttribute('data', $result);
+ $this->assertTrue($result->offsetExists('editLangs'));
+ $viewData = $result->offsetGet('editLangs');
+ $this->assertArrayHasKey('en', $viewData);
+ $this->assertArrayHasKey('es', $viewData);
+
+ $this->assertInternalType('array', $viewData['en']);
+ $this->assertInternalType('array', $viewData['es']);
+ }
+
+ public function test_edit_returns_array_with_files_contents()
+ {
+ \App::instance('translator', $transMock = $this->mock('Illuminate\Filesystem\Filesystem\FileLoader'));
+ config(['anavel-translation.files' => $this->config]);
+
+ $transMock->shouldReceive('trans')->andReturn(['yeah' => 'yeah']);
+
+ $result = $this->sut->edit('test');
+
+ $this->assertObjectHasAttribute('data', $result);
+ $this->assertTrue($result->offsetExists('editLangs'));
+ $viewData = $result->offsetGet('editLangs');
+ $this->assertArrayHasKey('en', $viewData);
+ $this->assertArrayHasKey('es', $viewData);
+
+ $this->assertInternalType('array', $viewData['en']);
+ $this->assertInternalType('array', $viewData['es']);
+
+ $this->assertArrayHasKey('yeah', $viewData['en']);
+ }
+
+ public function test_edit_adds_missing_keys_from_fallback_locale()
+ {
+ \App::instance('translator', $transMock = $this->mock('Illuminate\Filesystem\Filesystem\FileLoader'));
+ config(['anavel-translation.files' => $this->config]);
+
+ $transMock->shouldReceive('trans')->with('test', [], null, 'en')->andReturn([
+ 'yeah' => 'yeah',
+ 'ohyeah' => 'yeah',
+ ]);
+ $transMock->shouldReceive('trans')->with('test', [], null, 'es')->andReturn([
+ 'yeah' => 'yeah'
+ ]);
+
+ $result = $this->sut->edit('test');
+
+ $this->assertObjectHasAttribute('data', $result);
+ $this->assertTrue($result->offsetExists('editLangs'));
+ $viewData = $result->offsetGet('editLangs');
+ $this->assertArrayHasKey('en', $viewData);
+ $this->assertArrayHasKey('es', $viewData);
+
+ $this->assertInternalType('array', $viewData['en']);
+ $this->assertInternalType('array', $viewData['es']);
+
+ $this->assertArrayHasKey('yeah', $viewData['en']);
+ $this->assertArrayHasKey('ohyeah', $viewData['es']);
+
+ $this->assertTrue($result->offsetExists('editLangsMissingKeys'));
+ $viewData = $result->offsetGet('editLangsMissingKeys');
+ $this->assertArrayHasKey('es', $viewData);
+ $this->assertInternalType('array', $viewData['es']);
+ $this->assertContains('ohyeah', $viewData['es']);
+ }
+
+ public function test_edit_calls_vendor_file_when_param2_not_empty()
+ {
+ \App::instance('translator', $transMock = $this->mock('Illuminate\Filesystem\Filesystem\FileLoader'));
+ config(['anavel-translation.files' => $this->config]);
+
+ $transMock->shouldReceive('trans')->with('vendor::test', [], null,
+ \Mockery::any())->andReturn(['yeah' => 'yeah']);
+
+ $result = $this->sut->edit('vendor', 'test');
+
+ $this->assertObjectHasAttribute('data', $result);
+ $this->assertTrue($result->offsetExists('editLangs'));
+ $viewData = $result->offsetGet('editLangs');
+ $this->assertArrayHasKey('en', $viewData);
+ $this->assertArrayHasKey('es', $viewData);
+
+ $this->assertInternalType('array', $viewData['en']);
+ $this->assertInternalType('array', $viewData['es']);
+
+ $this->assertArrayHasKey('yeah', $viewData['en']);
+ }
+
+ public function test_update_bails_if_empty_array()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations')->times(1)->andReturn(false);
+
+ $result = $this->sut->update($requestMock, 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('error', $alert['type']);
+ }
+
+ public function test_update_saves_array_to_file()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations')->times(1)->andReturn($returnArray = [
+ 'en' => [
+ 'key' => 'value'
+ ],
+ 'es' => [
+ 'key' => 'value'
+ ]
+ ]);
+
+ Storage::shouldReceive('disk')->times(1)->with('diskdriver')->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('en/test.php', \Mockery::any())->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('es/test.php', \Mockery::any())->andReturn(\Mockery::self());
+
+ $result = $this->sut->update($requestMock, 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('success', $alert['type']);
+ }
+
+ public function test_update_saves_array_to_vendor_file()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations')->times(1)->andReturn($returnArray = [
+ 'en' => [
+ 'key' => 'value'
+ ],
+ 'es' => [
+ 'key' => 'value'
+ ]
+ ]);
+
+ Storage::shouldReceive('disk')->times(1)->with('diskdriver')->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('vendor/vendorname/en/test.php',
+ \Mockery::any())->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('vendor/vendorname/es/test.php',
+ \Mockery::any())->andReturn(\Mockery::self());
+
+ $result = $this->sut->update($requestMock, 'vendorname', 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('success', $alert['type']);
+ }
+
+ public function test_update_filters_empty_keys()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations')->times(1)->andReturn($returnArray = [
+ 'en' => [
+ 'key' => 'value',
+ 'otherkey' => ''
+ ],
+ 'es' => [
+ 'key' => 'value'
+ ]
+ ]);
+
+ Storage::shouldReceive('disk')->times(1)->with('diskdriver')->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('vendor/vendorname/en/test.php', ' \'value\',
+);')->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('vendor/vendorname/es/test.php',
+ \Mockery::any())->andReturn(\Mockery::self());
+
+ $result = $this->sut->update($requestMock, 'vendorname', 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('success', $alert['type']);
+ }
+
+ public function test_update_throws_exception_if_disk_not_set()
+ {
+ $this->setExpectedException('Exception', 'filedriver should be set in config');
+
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ config(['anavel-translation.filedriver' => null]);
+
+
+ $requestMock->shouldReceive('has')->with('translations')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations')->times(1)->andReturn($returnArray = [
+ 'en' => [
+ 'key' => 'value'
+ ],
+ 'es' => [
+ 'key' => 'value'
+ ]
+ ]);
+
+ $result = $this->sut->update($requestMock, 'test');
+ }
+
+
+ public function test_create_bails_when_input_empty()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations-new')->times(1)->andReturn(false);
+
+ $result = $this->sut->create($requestMock, 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('error', $alert['type']);
+ }
+
+ public function test_create_throws_exception_if_disk_not_set()
+ {
+ $this->setExpectedException('Exception', 'filedriver should be set in config');
+
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ config(['anavel-translation.filedriver' => null]);
+
+
+ $requestMock->shouldReceive('has')->with('translations-new')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations-new')->times(1)->andReturn($returnArray = [
+ 'key' => 'somekey',
+ 'value' => 'somevalue'
+ ]);
+
+ $result = $this->sut->create($requestMock, 'test');
+ }
+
+ public function test_create_adds_simple_line_to_fallback_locale()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations-new')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations-new')->times(1)->andReturn($returnArray = [
+ 'key' => 'somekey',
+ 'value' => 'somevalue'
+ ]);
+
+ Storage::shouldReceive('disk')->times(1)->with('diskdriver')->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('en/test.php', \Mockery::any())->andReturn(\Mockery::self());
+
+ $result = $this->sut->create($requestMock, 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('success', $alert['type']);
+ }
+
+ public function test_create_adds_array_line_to_fallback_locale()
+ {
+ $requestMock = $this->mock('Illuminate\Http\Request');
+
+ $requestMock->shouldReceive('has')->with('translations-new')->times(1)->andReturn(true);
+ $requestMock->shouldReceive('input')->with('translations-new')->times(1)->andReturn($returnArray = [
+ 'key' => 'some.arrayKey',
+ 'value' => 'somevalue'
+ ]);
+
+ $finalArray = [
+ 'some' =>
+ [
+ 'arrayKey' => 'somevalue',
+ ],
+ ];
+
+ $finalString = "times(1)->with('diskdriver')->andReturn(\Mockery::self());
+ Storage::shouldReceive('put')->times(1)->with('en/test.php', $finalString)->andReturn(\Mockery::self());
+
+ $result = $this->sut->create($requestMock, 'test');
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $result);
+ $this->assertTrue($result->getSession()->has('anavel-alert'));
+
+ $alert = $result->getSession()->get('anavel-alert');
+
+ $this->assertEquals('success', $alert['type']);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/Controllers/HomeControllerTest.php b/tests/Controllers/HomeControllerTest.php
new file mode 100644
index 0000000..ddc0067
--- /dev/null
+++ b/tests/Controllers/HomeControllerTest.php
@@ -0,0 +1,68 @@
+config = [
+ 'user' => [
+ 'one'
+ ],
+
+ 'vendor' => [
+ 'vendorname' => 'vendorfile'
+ ]
+ ];
+
+ $this->sut = new HomeController();
+ }
+
+ public function test_is_instance_of_controller()
+ {
+ $this->assertInstanceOf('Anavel\Foundation\Http\Controllers\Controller', $this->sut);
+ }
+
+ public function test_edit_throws_exception_if_no_files_configured()
+ {
+ config(['anavel-translation.files' => null]);
+ $this->setExpectedException('Exception', 'No files configured');
+
+ $this->sut->index('test');
+ }
+
+ public function test_edit_throws_exception_if_files_is_not_array()
+ {
+ $this->setExpectedException('Exception', 'Files should be an array');
+ config(['anavel-translation.files' => 'Chompy']);
+
+ $this->sut->index('test');
+ }
+
+ public function test_edit_throws_exception_if_user_nor_vendor_are_found()
+ {
+ $this->setExpectedException('Exception', '"user" or "vendor" files should be set');
+ config(['anavel-translation.files' => ['test']]);
+
+ $this->sut->index('test');
+ }
+
+ public function test_redirects_if_files_configured()
+ {
+ config(['anavel-translation.files' => $this->config]);
+
+ $response = $this->sut->index();
+
+ $this->assertInstanceOf('Illuminate\Http\RedirectResponse', $response);
+ }
+}
\ No newline at end of file
diff --git a/tests/TestBase.php b/tests/TestBase.php
new file mode 100644
index 0000000..83c7c5a
--- /dev/null
+++ b/tests/TestBase.php
@@ -0,0 +1,46 @@
+set('anavel.translation_languages', ['gl', 'en', 'es']);
+ }
+
+ protected function getPackageProviders($app)
+ {
+ return ['Anavel\Translation\TranslationModuleProvider'];
+ }
+
+
+ public function mock($className)
+ {
+ return Mockery::mock($className);
+ }
+
+ public function tearDown()
+ {
+ Mockery::close();
+ }
+}
diff --git a/views/atoms/form/translation-group.blade.php b/views/atoms/form/translation-group.blade.php
new file mode 100644
index 0000000..21ebc5c
--- /dev/null
+++ b/views/atoms/form/translation-group.blade.php
@@ -0,0 +1,23 @@
+
\ No newline at end of file
diff --git a/views/molecules/sidebar/default.blade.php b/views/molecules/sidebar/default.blade.php
new file mode 100644
index 0000000..5469799
--- /dev/null
+++ b/views/molecules/sidebar/default.blade.php
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/views/pages/edit.blade.php b/views/pages/edit.blade.php
new file mode 100644
index 0000000..fd5c1d0
--- /dev/null
+++ b/views/pages/edit.blade.php
@@ -0,0 +1,103 @@
+@extends('anavel::layouts.master')
+
+@section('content-header')
+