diff --git a/CHANGELOG.md b/CHANGELOG.md
index 196e26d63..28228927a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+* **Build 14x** (2014-08-xx)
+ - Components and variables can now be accessed in the page code section via `$this->foo` in addition to `$this['foo']`.
+ - AJAX handlers in the CMS can now invoke the page cycle without rendering the page using `$this->pageCycle()`.
+
+* **Build 139** (2014-08-18)
+ - List widget has been refactored to improve efficiency.
+ - Added new list column type `nameFrom` (take name from X attribute) as an alternative to `select`.
+
+* **Build 137** (2014-08-14)
+ - Lists now support Filters (see Backend > Lists docs).
+ - Numerous hard coded phrases converted to localized strings.
+
* **Build 132** (2014-08-03)
- New system logging pages: Event log, Request log and Access log.
diff --git a/modules/backend/assets/js/october.popup.js b/modules/backend/assets/js/october.popup.js
index a6749fd0d..46cb5a0bf 100644
--- a/modules/backend/assets/js/october.popup.js
+++ b/modules/backend/assets/js/october.popup.js
@@ -1,6 +1,6 @@
/*
* Ajax Popup plugin
- *
+ *
* Data attributes:
* - data-control="popup" - enables the ajax popup plugin
* - data-ajax="popup-content.htm" - ajax content to load
@@ -99,14 +99,18 @@
this.$el.request(this.options.handler, {
data: this.options.extraData,
success: function(data, textStatus, jqXHR) {
- self.setContent(data.result)
- $(window).trigger('ajaxUpdateComplete', [this, data, textStatus, jqXHR])
- self.triggerEvent('popupComplete')
+ this.success(data, textStatus, jqXHR).done(function(){
+ self.setContent(data.result)
+ $(window).trigger('ajaxUpdateComplete', [this, data, textStatus, jqXHR])
+ self.triggerEvent('popupComplete')
+ })
},
error: function(jqXHR, textStatus, errorThrown) {
- alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
- self.hide()
- self.triggerEvent('popupError')
+ this.error(jqXHR, textStatus, errorThrown).done(function(){
+ alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
+ self.hide()
+ self.triggerEvent('popupError')
+ })
}
})
@@ -168,7 +172,7 @@
this.$backdrop = null;
}
}
-
+
Popup.prototype.setLoading = function(val) {
if (!this.$backdrop)
return;
diff --git a/modules/backend/behaviors/FormController.php b/modules/backend/behaviors/FormController.php
index b2c8e5cc8..0d5a8a9fb 100644
--- a/modules/backend/behaviors/FormController.php
+++ b/modules/backend/behaviors/FormController.php
@@ -154,7 +154,7 @@ class FormController extends ControllerBehavior
$this->controller->formAfterSave($model);
$this->controller->formAfterCreate($model);
- Flash::success($this->getLang('create[flash-save]', 'backend::lang.form.create_success'));
+ Flash::success($this->getLang('create[flashSave]', 'backend::lang.form.create_success'));
if ($redirect = $this->makeRedirect('create', $model))
return $redirect;
@@ -207,7 +207,7 @@ class FormController extends ControllerBehavior
$this->controller->formAfterSave($model);
$this->controller->formAfterUpdate($model);
- Flash::success($this->getLang('update[flash-save]', 'backend::lang.form.update_success'));
+ Flash::success($this->getLang('update[flashSave]', 'backend::lang.form.update_success'));
if ($redirect = $this->makeRedirect('update', $model))
return $redirect;
@@ -228,7 +228,7 @@ class FormController extends ControllerBehavior
$this->controller->formAfterDelete($model);
- Flash::success($this->getLang('update[flash-delete]', 'backend::lang.form.delete_success'));
+ Flash::success($this->getLang('update[flashDelete]', 'backend::lang.form.delete_success'));
if ($redirect = $this->makeRedirect('delete', $model))
return $redirect;
diff --git a/modules/backend/classes/Controller.php b/modules/backend/classes/Controller.php
index bc64d5868..dee0402d9 100644
--- a/modules/backend/classes/Controller.php
+++ b/modules/backend/classes/Controller.php
@@ -247,7 +247,7 @@ class Controller extends Extendable
/**
* Invokes the current controller action without rendering a view,
- * used by AJAX handler who may rely on the logic inside the action.
+ * used by AJAX handler that may rely on the logic inside the action.
*/
public function pageAction()
{
diff --git a/modules/backend/classes/ListColumn.php b/modules/backend/classes/ListColumn.php
index ea9d14346..7cc7ff096 100644
--- a/modules/backend/classes/ListColumn.php
+++ b/modules/backend/classes/ListColumn.php
@@ -41,7 +41,14 @@ class ListColumn
public $sortable = true;
/**
- * @var string Custom SQL for selecting this record value.
+ * @var string Model attribute to use for the display value, this will
+ * override any $sqlSelect definition.
+ */
+ public $nameFrom;
+
+ /**
+ * @var string Custom SQL for selecting this record display value,
+ * the @ symbol is replaced with the table name.
*/
public $sqlSelect;
@@ -103,6 +110,7 @@ class ListColumn
if (isset($config['searchable'])) $this->searchable = $config['searchable'];
if (isset($config['sortable'])) $this->sortable = $config['sortable'];
if (isset($config['invisible'])) $this->invisible = $config['invisible'];
+ if (isset($config['nameFrom'])) $this->nameFrom = $config['nameFrom'];
if (isset($config['select'])) $this->sqlSelect = $config['select'];
if (isset($config['relation'])) $this->relation = $config['relation'];
if (isset($config['format'])) $this->format = $config['format'];
diff --git a/modules/backend/controllers/EditorPreferences.php b/modules/backend/controllers/EditorPreferences.php
index bc37ec44a..e73c695de 100644
--- a/modules/backend/controllers/EditorPreferences.php
+++ b/modules/backend/controllers/EditorPreferences.php
@@ -55,13 +55,13 @@ class EditorPreferences extends Controller
$this->vars['language'] = 'css';
$this->vars['margin'] = 0;
- $this->getClassExtension('Backend.Behaviors.FormController')->update();
+ $this->asExtension('FormController')->update();
$this->pageTitle = Lang::get('backend::lang.editor.menu_label');
}
public function index_onSave()
{
- return $this->getClassExtension('Backend.Behaviors.FormController')->update_onSave();
+ return $this->asExtension('FormController')->update_onSave();
}
public function formFindModelObject()
diff --git a/modules/backend/controllers/Users.php b/modules/backend/controllers/Users.php
index ac6b25f41..9c7cdd0c2 100644
--- a/modules/backend/controllers/Users.php
+++ b/modules/backend/controllers/Users.php
@@ -49,7 +49,7 @@ class Users extends Controller
if ($context != 'myaccount' && $recordId == $this->user->id)
return Redirect::to(Backend::url('backend/users/myaccount'));
- return $this->getClassExtension('Backend.Behaviors.FormController')->update($recordId, $context);
+ return $this->asExtension('FormController')->update($recordId, $context);
}
/**
@@ -68,7 +68,7 @@ class Users extends Controller
*/
public function myaccount_onSave()
{
- $result = $this->getClassExtension('Backend.Behaviors.FormController')->update_onSave($this->user->id, 'myaccount');
+ $result = $this->asExtension('FormController')->update_onSave($this->user->id, 'myaccount');
/*
* If the password or login name has been updated, reauthenticate the user
diff --git a/modules/backend/lang/en/lang.php b/modules/backend/lang/en/lang.php
index e6483cc11..9514aafb3 100644
--- a/modules/backend/lang/en/lang.php
+++ b/modules/backend/lang/en/lang.php
@@ -47,6 +47,7 @@ return [
'menu_label' => 'Dashboard',
'widget_label' => 'Widget',
'widget_width' => 'Width',
+ 'full_width' => 'full width',
'add_widget' => 'Add widget',
'widget_inspector_title' => 'Widget configuration',
'widget_inspector_description' => 'Configure the report widget',
@@ -119,15 +120,13 @@ return [
'setup_title' => 'List Setup',
'setup_help' => 'Use checkboxes to select columns you want to see in the list. You can change position of columns by dragging them up or down.',
'records_per_page' => 'Records per page',
- 'records_per_page_help' => 'Select the number of records per page to display. Please note that high number of records on a single page can reduce performance.',
- 'apply_changes' => 'Apply changes',
- 'cancel' => 'Cancel'
+ 'records_per_page_help' => 'Select the number of records per page to display. Please note that high number of records on a single page can reduce performance.'
],
'fileupload' => [
- 'attachment' => 'Attachment',
- 'help' => 'Add a title and description for this attachment.',
- 'title_label' => 'Title',
- 'description_label' => 'Description'
+ 'attachment' => 'Attachment',
+ 'help' => 'Add a title and description for this attachment.',
+ 'title_label' => 'Title',
+ 'description_label' => 'Description'
],
'form' => [
'create_title' => "New :name",
@@ -163,6 +162,7 @@ return [
'select' => 'Select',
'select_all' => 'all',
'select_none' => 'none',
+ 'select_placeholder' => 'please select',
'insert_row' => 'Insert Row',
'delete_row' => 'Delete Row'
],
diff --git a/modules/backend/lang/it/lang.php b/modules/backend/lang/it/lang.php
index b93286705..c339aae68 100644
--- a/modules/backend/lang/it/lang.php
+++ b/modules/backend/lang/it/lang.php
@@ -113,9 +113,7 @@ return [
'setup_title' => 'Configura elenco',
'setup_help' => 'Utilizza le checkbox per selezionare le colonne che vuoi visualizzare nell\'elenco. Puoi cambiare la posizione delle colonne trascinandole verso l\'alto o il basso.',
'records_per_page' => 'Record per pagina',
- 'records_per_page_help' => 'Seleziona il numero di record da visualizzare su ogni pagina. Ricorda che un numero elevato di record in una singola pagina può ridurre le prestazioni.',
- 'apply_changes' => 'Applica modifiche',
- 'cancel' => 'Annulla'
+ 'records_per_page_help' => 'Seleziona il numero di record da visualizzare su ogni pagina. Ricorda che un numero elevato di record in una singola pagina può ridurre le prestazioni.'
],
'form' => [
'create_title' => "Nuovo :name",
diff --git a/modules/backend/lang/nl/lang.php b/modules/backend/lang/nl/lang.php
index 09f016964..c65889365 100644
--- a/modules/backend/lang/nl/lang.php
+++ b/modules/backend/lang/nl/lang.php
@@ -14,7 +14,7 @@ return [
'access_denied' => [
'label' => "Toegang geweigerd",
'help' => "Je hebt niet de benodigde rechten om deze pagina te bekijken.",
- 'cms_link' => "Ga naar CMS backend",
+ 'cms_link' => "Ga naar CMS",
],
],
'partial' => [
@@ -25,18 +25,18 @@ return [
'login' => 'Inloggen',
'reset' => 'Wissen',
'restore' => 'Herstellen',
- 'login_placeholder' => 'gebruikersnaam',
- 'password_placeholder' => 'wachtwoord',
+ 'login_placeholder' => 'Gebruikersnaam',
+ 'password_placeholder' => 'Wachtwoord',
'forgot_password' => "Wachtwoord vergeten?",
- 'enter_email' => "Vul het e-mailadres in",
- 'enter_login' => "Vul de gebruikersnaam in",
- 'email_placeholder' => "e-mail",
+ 'enter_email' => "Vul jouw e-mailadres in",
+ 'enter_login' => "Vul jouw gebruikersnaam in",
+ 'email_placeholder' => "E-mailadres",
'enter_new_password' => "Vul een nieuw wachtwoord in",
- 'password_reset' => "Herstel Wachtwoord",
+ 'password_reset' => "Herstel wachtwoord",
'restore_success' => "Een e-mail met instructies om het wachtwoord te herstellen is verzonden naar jouw e-mailadres.",
'restore_error' => "Een gebruiker met de gebruikersnaam ':login' is niet gevonden",
- 'reset_success' => "Het wachtwoord is succesvol hersteld. Je kan nu inloggen",
- 'reset_error' => "Ongeldige wachtwoordherstelinformatie aangeboden. Probeer overnieuw!",
+ 'reset_success' => "Het wachtwoord is succesvol hersteld. Je kunt nu inloggen",
+ 'reset_error' => "Ongeldige herstelinformatie aangeboden. Probeer het opnieuw!",
'reset_fail' => "Het is niet mogelijk het wachtwoord te herstellen!",
'apply' => 'Toepassen',
'cancel' => 'Annuleren',
@@ -45,36 +45,59 @@ return [
],
'dashboard' => [
'menu_label' => 'Overzicht',
+ 'widget_label' => 'Widget',
+ 'widget_width' => 'Breedte',
+ 'full_width' => 'Volledige breedte',
+ 'add_widget' => 'Widget toevoegen',
+ 'widget_inspector_title' => 'Widget configuratie',
+ 'widget_inspector_description' => 'Configureer de rapportage widget',
+ 'widget_columns_label' => 'Breedte :columns',
+ 'widget_columns_description' => 'De widget breedte, een getal tussen 1 en 10.',
+ 'widget_columns_error' => 'Voer een getal tussen 1 en 10 in als widget breedte.',
+ 'columns' => '{1} kolom|[2,Inf] kolommen',
+ 'widget_new_row_label' => 'Forceer nieuwe rij',
+ 'widget_new_row_description' => 'Plaats de widget in een nieuwe rij.',
+ 'widget_title_label' => 'Widget titel',
+ 'widget_title_error' => 'Een widget title is verplicht.',
+ 'status' => [
+ 'widget_title_default' => 'Systeem status',
+ 'online' => 'online',
+ 'update_available' => '{0} updates beschikbaar!|{1} update beschikbaar!|[2,Inf] updates beschikbaar!',
+ ]
],
'user' => [
'name' => 'Beheerder',
'menu_label' => 'Beheerders',
- 'list_title' => 'Beheer Beheerders',
- 'new' => 'Nieuwe Beheerder',
+ 'menu_description' => 'Beheer beheerders, groepen en rechten.',
+ 'list_title' => 'Beheer beheerders',
+ 'new' => 'Nieuwe beheerder',
'login' => "Gebruikersnaam",
'first_name' => "Voornaam",
'last_name' => "Achternaam",
- 'full_name' => "Volledige Naam",
- 'email' => "E-mail",
+ 'full_name' => "Volledige naam",
+ 'email' => "E-mailadres",
'groups' => "Groepen",
- 'groups_comment' => "Voeg de gebruiker eventueel toe aan gebruikersgroepen.",
+ 'groups_comment' => "Selecteer de groepen waar deze gebruiker bij hoort.",
'avatar' => "Avatar",
'password' => "Wachtwoord",
- 'password_confirmation' => "Bevestig Wachtwoord",
- 'superuser' => "Speciale Gebruiker",
+ 'password_confirmation' => "Bevestig wachtwoord",
+ 'superuser' => "Supergebruiker",
'superuser_comment' => "Vink deze optie aan om de gebruiker volledige rechten tot het systeem te geven.",
'send_invite' => 'Stuur uitnodiging per e-mail',
'send_invite_comment' => 'Vink deze optie aan om de gebruiker een uitnodiging per e-mail te sturen',
'delete_confirm' => 'Weet je zeker dat je deze beheerder wilt verwijderen?',
- 'return' => 'Terug naar beheerdersoverzicht',
+ 'return' => 'Terug naar het beheerdersoverzicht',
+ 'allow' => 'Toestaat',
+ 'inherit' => 'Overerven',
+ 'deny' => 'Weigeren',
'group' => [
'name' => 'Groep',
'name_field' => 'Naam',
'menu_label' => 'Groepen',
- 'list_title' => 'Beheer Groepen',
- 'new' => 'Nieuwe Beheerdersgroep',
+ 'list_title' => 'Beheer groepen',
+ 'new' => 'Nieuwe beheerdersgroep',
'delete_confirm' => 'Weet je zeker dat je deze beheerdersgroep wilt verwijderen?',
- 'return' => 'Terug naar groepenoverzicht',
+ 'return' => 'Terug naar het groepenoverzicht',
],
'preferences' => [
'not_authenticated' => 'Er is geen geauthenticeerde gebruiker om gegevens voor te laden of op te slaan.'
@@ -85,14 +108,28 @@ return [
'search_prompt' => 'Zoeken...',
'no_records' => 'Er zijn geen resultaten gevonden.',
'missing_model' => 'Geen model opgegeven voor het gedrag (behavior) van de lijst gebruikt in :class.',
- 'missing_column' => 'Er zijn geen kolommen voor :columns opgegeven.',
- 'missing_columns' => 'Er zijn geen kolommen opgegeven voor de lijst in :class.',
+ 'missing_column' => 'Er zijn geen kolomdefinities voor :columns.',
+ 'missing_columns' => 'De gebruikte lijst in :class heeft geen kolommen gedefineerd.',
'missing_definition' => "Het gedrag (behavior) van de lijst bevat geen kolom voor ':field'.",
'behavior_not_ready' => 'Gedrag (behavior) van de lijst is niet geladen. Controleer of makeLists() in de controller is aangeroepen.',
- 'invalid_column_datetime' => "Column value ':column' is not a DateTime object, are you missing a \$dates reference in the Model?",
+ 'invalid_column_datetime' => "De waarde van kolom ':column' is geen DateTime object, mist er een \$dates referentie in het Model?",
+ 'pagination' => 'Getoonde resultaten: :from-:to van :total',
+ 'prev_page' => 'Vorige pagina',
+ 'next_page' => 'Volgende pagina',
+ 'loading' => 'Laden...',
+ 'setup_title' => 'Lijst instellingen',
+ 'setup_help' => 'Selecteer door middel van vinkjes de kolommen welke je in de lijst wilt zien. Je kunt de volgorde van kolommen veranderen door ze omhoog of omlaag te slepen.',
+ 'records_per_page' => 'Resultaten per pagina',
+ 'records_per_page_help' => 'Selecteer het aantal resultaten dat per pagina getoond moet worden. Let op: een hoog getal kan voor prestatieproblemen zorgen.'
+ ],
+ 'fileupload' => [
+ 'attachment' => 'Bijlage',
+ 'help' => 'Voeg een titel en omschrijving toe aan deze bijlage.',
+ 'title_label' => 'Titel',
+ 'description_label' => 'Omschrijving'
],
'form' => [
- 'create_title' => "Maak :name",
+ 'create_title' => "Nieuwe :name",
'update_title' => "Bewerk :name",
'preview_title' => "Bekijk :name",
'create_success' => ':name is succesvol aangemaakt',
@@ -113,6 +150,7 @@ return [
'undefined_tab' => 'Overig',
'field_off' => 'Uit',
'field_on' => 'Aan',
+ 'add' => 'Toevoegen',
'apply' => 'Toepassen',
'cancel' => 'Annuleren',
'close' => 'Sluiten',
@@ -120,14 +158,26 @@ return [
'or' => 'of',
'confirm_tab_close' => 'Weet je zeker dat je dit tabblad wilt sluiten? Niet opgeslagen wijzigingen gaan verloren.',
'behavior_not_ready' => 'Gedrag (behavior) van het formulier is niet geladen. Controleer of initForm() in de controller is aangeroepen.',
+ 'preview_no_files_message' => 'Bestanden zijn niet geüploadet',
+ 'select' => 'Selecteer',
+ 'select_all' => 'alles',
+ 'select_none' => 'niets',
+ 'select_placeholder' => 'selecteer',
+ 'insert_row' => 'Rij invoegen',
+ 'delete_row' => 'Rij verwijderen'
],
'relation' => [
'missing_definition' => "Het gedrag (behavior) van de relatie bevat geen kolom voor ':field'.",
'missing_model' => "Geen model opgegeven voor het gedrag (behavior) van relatie gebruikt in :class.",
'invalid_action_single' => "Deze actie kan niet worden uitgevoerd op een enkele (singular) relatie.",
'invalid_action_multi' => "Deze actie kan niet worden uitgevoerd op meerdere (multiple) relatie.",
+ 'help' => "Klik op een item om toe te voegen",
+ 'related_data' => "Gerelateerde :name data",
'add' => "Toevoegen",
- 'add_name' => ":name Toevoegen",
+ 'add_selected' => "Selectie toevoegen",
+ 'add_a_new' => "Nieuwe :name toevoegen",
+ 'cancel' => "Annuleer",
+ 'add_name' => ":name toevoegen",
'create' => "Maken",
'create_name' => "Maak :name",
'update' => "Update",
@@ -136,6 +186,7 @@ return [
'remove_name' => "Verwijder :name",
'delete' => "Wissen",
'delete_name' => "Wis :name",
+ 'delete_confirm' => "Weet je het zeker?",
],
'model' => [
'name' => "Model",
@@ -143,25 +194,54 @@ return [
'missing_id' => "Record ID van het model is niet opgegeven.",
'missing_relation' => "Model ':class' bevat geen definitie voor ':relation'.",
'invalid_class' => "Model :model gebruikt in :class is ongeldig. Het moet de \Model klasse erven (inherit).",
- 'mass_assignment_failed' => "Mass assignment failed for Model attribute ':attribute'.",
+ 'mass_assignment_failed' => "Massa toewijzing voor Model attribute ':attribute' mislukt.",
],
'warnings' => [
'tips' => 'Systeem configuratie tips',
- 'tips_description' => 'Er zijn fouten opgetreden in uw systeem. Los deze problemen op om uw systeem goed te configureren.',
+ 'tips_description' => 'Er zijn problemen gevonden waar je aandacht aan moet besteden om uw systeem goed te configureren.',
'permissions' => 'De map :name of de submapen zijn niet schrijfbaar voor PHP. Zet de bijhorende rechten voor de webserver in deze map.',
- 'extension' => 'De PHP extensie :name is niet geinstalleerd. Installeer deze versie en activeer de extensie.'
+ 'extension' => 'De PHP extensie :name is niet geïnstalleerd. Installeer deze bibliotheek en activeer de extensie.'
],
'editor' => [
'menu_label' => 'Bewerker instellingen',
- 'menu_description' => 'Beheer code bewerker instellingen.',
- 'font_size' => 'Lettertype grootte',
- 'tab_size' => 'Tab positie',
+ 'menu_description' => 'Beheer bewerker instellingen, zoals lettergrootte en kleurschema.',
+ 'font_size' => 'Lettergrootte',
+ 'tab_size' => 'Tab grootte',
'use_hard_tabs' => 'Inspringen met tabs',
'code_folding' => 'Code invouwing',
'word_wrap' => 'Tekstterugloop',
'highlight_active_line' => 'Markeer actieve lijnen',
'show_invisibles' => 'Toon verborgen karakters',
- 'show_gutter' => 'Toon regelnummers',
+ 'show_gutter' => 'Toon "goot"',
'theme' => 'Kleurschema',
],
-];
\ No newline at end of file
+ 'tooltips' => [
+ 'preview_website' => 'Voorvertoning website'
+ ],
+ 'mysettings' => [
+ 'menu_label' => 'Mijn instellingen',
+ 'menu_description' => 'Instellingen gerelateerd aan jouw beheeraccount',
+ ],
+ 'myaccount' => [
+ 'menu_label' => 'Mijn account',
+ 'menu_description' => 'Werk accountinstellingen zoals naam, e-mailadres en wachtwoord bij.',
+ 'menu_keywords' => 'security login'
+ ],
+ 'backend_preferences' => [
+ 'menu_label' => 'CMS voorkeuren',
+ 'menu_description' => 'Beheer taalvoorkeur en de weergave van het CMS.',
+ 'locale' => 'Taal',
+ 'locale_comment' => 'Selecteer jouw gewenste taal.',
+ ],
+ 'access_log' => [
+ 'hint' => 'Dit logboek toont een lijst met succesvolle inlogpogingen door beheerders. Registraties blijven :days dagen bewaard..',
+ 'menu_label' => 'Toegangslogboek',
+ 'menu_description' => 'Bekijk een lijst met succesvolle inlogpogingen van gebruikers.',
+ 'created_at' => 'Datum & tijd',
+ 'login' => 'Gebruikersnaam',
+ 'ip_address' => 'IP-adres',
+ 'first_name' => 'Voornaam',
+ 'last_name' => 'Achternaam',
+ 'email' => 'E-mailadres',
+ ],
+];
diff --git a/modules/backend/lang/ro/lang.php b/modules/backend/lang/ro/lang.php
new file mode 100644
index 000000000..4e8440600
--- /dev/null
+++ b/modules/backend/lang/ro/lang.php
@@ -0,0 +1,227 @@
+ [
+ 'invalid_type' => 'Tipul campului folosit este invalid - :type.',
+ 'options_method_not_exists' => 'Clasa model :model trebuie sa defineasca o metoda :method() returnand optiuni pentru campul ":field".',
+ ],
+ 'widget' => [
+ 'not_registered' => "Un nume de clasa de widget ':name' nu a fost inregistrat",
+ 'not_bound' => "Un widget cu numele de clasa ':name' nu a fost mapat la controller",
+ ],
+ 'page' => [
+ 'untitled' => "Fara titlu",
+ 'access_denied' => [
+ 'label' => "Acces restrictionat",
+ 'help' => "Nu aveti permisiuni pentru a vizualiza aceasta pagina.",
+ 'cms_link' => "Inapoi in panoul de administrare",
+ ],
+ ],
+ 'partial' => [
+ 'not_found' => "Partialul ':name' nu a fost gasit.",
+ ],
+ 'account' => [
+ 'sign_out' => 'Deconectare',
+ 'login' => 'Login',
+ 'reset' => 'Resetare',
+ 'restore' => 'Restaurare',
+ 'login_placeholder' => 'login',
+ 'password_placeholder' => 'password',
+ 'forgot_password' => "Ati uitat parola?",
+ 'enter_email' => "Introduceti email",
+ 'enter_login' => "Introduceti login",
+ 'email_placeholder' => "email",
+ 'enter_new_password' => "Introduceti o noua parola",
+ 'password_reset' => "Resetare parola",
+ 'restore_success' => "Un mesaj a fost trimis catre adresa de email cu instructiuni pentru resetarea parolei.",
+ 'restore_error' => "Utilizatorul ':login' nu a fost gasit in sistem.",
+ 'reset_success' => "Parola a fost resetata cu succes. Va puteti conecta.",
+ 'reset_error' => "Date invalide pentru resetarea parolei. Va rugam incercati din nou!",
+ 'reset_fail' => "Eroare la resetarea parolei!",
+ 'apply' => 'Aplicare',
+ 'cancel' => 'Anulare',
+ 'delete' => 'Stergere',
+ 'ok' => 'OK',
+ ],
+ 'dashboard' => [
+ 'menu_label' => 'Dashboard',
+ 'widget_label' => 'Widget',
+ 'widget_width' => 'Latime',
+ 'add_widget' => 'Adauga widget',
+ 'widget_inspector_title' => 'Configurare widget',
+ 'widget_inspector_description' => 'Configurare raport widget',
+ 'widget_columns_label' => 'Latime :columns',
+ 'widget_columns_description' => 'Latime widget, un numar intre 1 si 10.',
+ 'widget_columns_error' => 'Va rugam sa introduceti latimea widget-ului ca un numar intre 1 si 10.',
+ 'columns' => '{1} coloana|[2,Inf] coloane',
+ 'widget_new_row_label' => 'Forteaza rand nou',
+ 'widget_new_row_description' => 'Amplasare widget pe un rand nou.',
+ 'widget_title_label' => 'Titlu widget',
+ 'widget_title_error' => 'Titlul widget-ului este necesar.',
+ 'status' => [
+ 'widget_title_default' => 'Status sistem',
+ 'online' => 'online',
+ 'update_available' => '{0} actualizari disponibile!|{1} actualizare disponibila!|[2,Inf] actualizari disponibile!',
+ ]
+ ],
+ 'user' => [
+ 'name' => 'Administrator',
+ 'menu_label' => 'Administratori',
+ 'menu_description' => 'Gestionare administratori, grupuri si permisiuni.',
+ 'list_title' => 'Gestionare Administratori',
+ 'new' => 'Administrator nou',
+ 'login' => "Login",
+ 'first_name' => "Prenume",
+ 'last_name' => "Nume",
+ 'full_name' => "Nume complet",
+ 'email' => "Email",
+ 'groups' => "Grupuri",
+ 'groups_comment' => "Specificati grupurile aferente acestei persoane.",
+ 'avatar' => "Avatar",
+ 'password' => "Parola",
+ 'password_confirmation' => "Confirmare Parola",
+ 'superuser' => "Super Utilizator",
+ 'superuser_comment' => "Bifati aceasta bifa pentru a permite acestei persoane sa aiba acces deplin.",
+ 'send_invite' => 'Trimitere invitatie prin email',
+ 'send_invite_comment' => 'Folositi aceasta bifa pentru a trimite o invitatie prin email catre utilizator',
+ 'delete_confirm' => 'Sunteti sigur(a) ca vreti sa stergeti acest administrator?',
+ 'return' => 'Intoarcere la lista de administratori',
+ 'group' => [
+ 'name' => 'Grup',
+ 'name_field' => 'Nume',
+ 'menu_label' => 'Grupuri',
+ 'list_title' => 'Gestionare Grupuri',
+ 'new' => 'Grup Nou de Administratori',
+ 'delete_confirm' => 'Sunteti sigur(a) ca vreti sa stergeti acest grup de administratori?',
+ 'return' => 'Intoarcere la lista de grupuri',
+ ],
+ 'preferences' => [
+ 'not_authenticated' => 'Nu exista niciun utilizator autentificat pentru care sa se incarce sau salveze preferinte.'
+ ]
+ ],
+ 'list' => [
+ 'default_title' => 'Lista',
+ 'search_prompt' => 'Cautare...',
+ 'no_records' => 'Nu exista inregistari pentru aceasta fereastra.',
+ 'missing_model' => 'Lista folosita in clasa :class nu are un model definit.',
+ 'missing_column' => 'Nu exista denifitii pentru coloana :columns.',
+ 'missing_columns' => 'Lista folosita in clasa :class nu are coloane definite.',
+ 'missing_definition' => "Lista nu contine o coloana pentru campul ':field'.",
+ 'behavior_not_ready' => 'Setarile initiale ale listei nu au fost definite, verificati existenta functiei makeLists() in controller.',
+ 'invalid_column_datetime' => "Valoarea coloanei ':column' nu este un obiect de tip DateTime, verificati existenta unei referinte \$dates in Model?",
+ 'pagination' => 'Afisare inregistrari: :from-:to din :total',
+ 'setup_title' => 'Setare lista',
+ 'setup_help' => 'Folositi bife pentru a selecta coloanele pe care doriti sa le vedeti in lista. Puteti modifica pozitia coloanelor glisandu-le in sus sau in jos.',
+ 'records_per_page' => 'Inregistari pe pagina',
+ 'records_per_page_help' => 'Selectati numarul de inregistari care sa fie afisat pe pagina. Sa tineti cont de faptul ca un numar mare de inregistari pe o singura pagina poate sa reduca performanta.',
+ 'apply_changes' => 'Aplicare schimbari',
+ 'cancel' => 'Anulare'
+ ],
+ 'form' => [
+ 'create_title' => "Nou :name",
+ 'update_title' => "Editare :name",
+ 'preview_title' => "Previzualizare :name",
+ 'create_success' => ':name a fost creat cu succes',
+ 'update_success' => ':name a fost actualizat cu succes',
+ 'delete_success' => ':name a fost sters cu succes',
+ 'missing_id' => "ID-ul inregistrarii formularului nu a fost specificat.",
+ 'missing_model' => 'Formularul folosit in clasa :class nu are un model definit.',
+ 'missing_definition' => "Formularul nu contine un camp pentru campul ':field'.",
+ 'not_found' => 'Inregistrarea formularului cu ID-ul :id nu a putut fi gasita.',
+ 'create' => 'Creare',
+ 'create_and_close' => 'Creare si inchidere',
+ 'creating' => 'Se creeaza...',
+ 'save' => 'Salvare',
+ 'save_and_close' => 'Salvare si inchidere',
+ 'saving' => 'Se salveaza...',
+ 'delete' => 'Stergere',
+ 'deleting' => 'Se sterge...',
+ 'undefined_tab' => 'Altele',
+ 'field_off' => 'Dezactivat',
+ 'field_on' => 'Activat',
+ 'add' => 'Adaugare',
+ 'apply' => 'Aplicare',
+ 'cancel' => 'Anulare',
+ 'close' => 'Inchidere',
+ 'ok' => 'OK',
+ 'or' => 'sau',
+ 'confirm_tab_close' => 'Sunteti sigur(a) ca doriti sa inchideti acest tab? Modificarile nesalvate vor fi pierdute.',
+ 'behavior_not_ready' => 'Setarile initiale ale formularului nu au fost definite, verificati existenta functiei initForm() in controller.',
+ 'preview_no_files_message' => 'Fisierele nu au fost incarcate',
+ 'select' => 'Selectare',
+ 'select_all' => 'toate',
+ 'select_none' => 'niciunul',
+ ],
+ 'relation' => [
+ 'missing_definition' => "Relatia nu contine definitii pentru campul ':field'.",
+ 'missing_model' => "Relatia folosita in clasa :class nu are un model definit.",
+ 'invalid_action_single' => "Aceasta actiune nu poate fi realizata pentru o relatie singulara.",
+ 'invalid_action_multi' => "Aceasta actiune nu poate fi realizata pentru o relatie multipla.",
+ 'add' => "Adaugare",
+ 'add_name' => "Adaugare :name",
+ 'create' => "Creare",
+ 'create_name' => "Creare :name",
+ 'update' => "Actualizare",
+ 'update_name' => "Actualizare :name",
+ 'remove' => "Inlaturare",
+ 'remove_name' => "Inlaturare :name",
+ 'delete' => "Stergere",
+ 'delete_name' => "Stergere :name",
+ ],
+ 'model' => [
+ 'name' => "Model",
+ 'not_found' => "Modelul ':class' cu ID-ul :id nu a putut fi gasit",
+ 'missing_id' => "Nu exista niciun ID specificat pentru care sa se realizeze cautarea inregistrarii modelului.",
+ 'missing_relation' => "Modelul ':class' nu contine o definitie pentru relatia ':relation'.",
+ 'invalid_class' => "Modelul :model folosit in clasa :class nu este valid, trebuie sa mosteneasca clasa \Model.",
+ 'mass_assignment_failed' => "Atribuirea in masa a esuat pentru atributul modelului ':attribute'.",
+ ],
+ 'warnings' => [
+ 'tips' => 'Sfaturi pentru configurarea sistemului',
+ 'tips_description' => 'Exista anumite conditii care necesita atentie pentru a configura sistemul corect.',
+ 'permissions' => 'Directorul :name si subdirectoarele sale nu au permisiuni de scriere pentru PHP. Va rugam sa setati permisiunile corespunzatoare pentru acest director.',
+ 'extension' => 'Libraria PHP :name nu este instalata. Va rugam sa instalati aceasta librarie si apoi sa activati extensia.'
+ ],
+ 'editor' => [
+ 'menu_label' => 'Preferinte Editor Cod',
+ 'menu_description' => 'Personalizati preferintele editorului de cod, preferinte precum dimensiunea fontului si culorile folosite.',
+ 'font_size' => 'Dimensiune font',
+ 'tab_size' => 'Lungime tab',
+ 'use_hard_tabs' => 'Indentare folosind tab-uri',
+ 'code_folding' => 'Code folding',
+ 'word_wrap' => 'Word wrap',
+ 'highlight_active_line' => 'Evidentiere linie activa',
+ 'show_invisibles' => 'Arata caractere invizibile',
+ 'show_gutter' => 'Afiseaza panou',
+ 'theme' => 'Schema culori',
+ ],
+ 'tooltips' => [
+ 'preview_website' => 'Previzualizare site'
+ ],
+ 'mysettings' => [
+ 'menu_label' => 'Setarile mele',
+ 'menu_description' => 'Setarile in legatura cu contul de administrare',
+ ],
+ 'myaccount' => [
+ 'menu_label' => 'Contul meu',
+ 'menu_description' => 'Actualizati datele contului, precum nume, adresa de email si parola.',
+ 'menu_keywords' => 'securitate login'
+ ],
+ 'backend_preferences' => [
+ 'menu_label' => 'Preferinte administrare',
+ 'menu_description' => 'Gestionati preferinte limba si setari aspect panou de administrare.',
+ 'locale' => 'Limba',
+ 'locale_comment' => 'Selectati limba dorita.',
+ ],
+ 'access_log' => [
+ 'hint' => 'Acest jurnal afiseaza o lista de conectari reusite, realizate de catre administratori. Inregistrarile sunt pastrate pentru un numar total de :days zile.',
+ 'menu_label' => 'Jurnal acces',
+ 'menu_description' => 'Vizualizati o lista de conectari reusite, realizate de catre administratori.',
+ 'created_at' => 'Data & Ora',
+ 'login' => 'Login',
+ 'ip_address' => 'Adresa IP',
+ 'first_name' => 'Prenume',
+ 'last_name' => 'Nume',
+ 'email' => 'Email',
+ ],
+];
diff --git a/modules/backend/lang/ru/lang.php b/modules/backend/lang/ru/lang.php
index b5986ba3d..5af2ab8df 100644
--- a/modules/backend/lang/ru/lang.php
+++ b/modules/backend/lang/ru/lang.php
@@ -47,7 +47,23 @@ return [
'menu_label' => 'Панель управления',
'widget_label' => 'Виджет',
'widget_width' => 'Ширина',
+ 'full_width' => 'полная ширина',
'add_widget' => 'Добавить виджет',
+ 'widget_inspector_title' => 'Конфигурации виджета',
+ 'widget_inspector_description' => 'Настройка отображения виджета',
+ 'widget_columns_label' => 'Ширина :columns',
+ 'widget_columns_description' => 'Ширина виджета может варьироваться от 1 до 10.',
+ 'widget_columns_error' => 'Пожалуйста, выберите ширину виджета.',
+ 'columns' => '{1} колонка|[2,4] колонки|[5,Inf] колонок',
+ 'widget_new_row_label' => 'Новая строка',
+ 'widget_new_row_description' => 'Поставить виджет с новой строки.',
+ 'widget_title_label' => 'Заголовок',
+ 'widget_title_error' => 'Заголовок виджета обязателен.',
+ 'status' => [
+ 'widget_title_default' => 'Статус системы',
+ 'online' => 'Онлайн',
+ 'update_available' => '{0} нет новый обновлений!|{1} доступно новое обновление!|[2,Inf] доступны новые обновления!',
+ ]
],
'user' => [
'name' => 'Администратора',
@@ -71,6 +87,9 @@ return [
'send_invite_comment' => 'Используйте эту опцию, чтобы отправить приглашение пользователю по электронной почте',
'delete_confirm' => 'Вы действительно хотите удалить этого администратора?',
'return' => 'Вернуться к списку администраторов',
+ 'allow' => 'Разрешить',
+ 'inherit' => 'Наследовать',
+ 'deny' => 'Запретить',
'group' => [
'name' => 'Группы',
'name_field' => 'Название',
@@ -94,6 +113,20 @@ return [
'missing_definition' => "Поведение списка не содержит столбец для ':field'.",
'behavior_not_ready' => 'Поведение списка не было инициализировано, проверьте вызов makeLists() в вашем контроллере.',
'invalid_column_datetime' => "Значение столбца ':column' не является объектом DateTime. Отсутствует \$dates ссылка в модели?",
+ 'pagination' => 'Отображено записей: :from-:to из :total',
+ 'prev_page' => 'Предыдущая страница',
+ 'next_page' => 'Следующая страница',
+ 'loading' => 'Загрузка...',
+ 'setup_title' => 'Настройка списка',
+ 'setup_help' => 'Используйте флажки для выбора колонок, которые вы хотите видеть в списке. Вы можете изменить положение столбцов, перетаскивая их вверх или вниз.',
+ 'records_per_page' => 'Записей на странице',
+ 'records_per_page_help' => 'Выберите количество записей на странице для отображения. Обратите внимание, что большое количество записей на одной странице может привести к снижению производительности.'
+ ],
+ 'fileupload' => [
+ 'attachment' => 'Приложение',
+ 'help' => 'Добавьте заголовок и описание для этого вложения.',
+ 'title_label' => 'Название',
+ 'description_label' => 'Описание'
],
'form' => [
'create_title' => "Создание :name",
@@ -129,14 +162,22 @@ return [
'select' => 'Выбрать',
'select_all' => 'все',
'select_none' => 'ничего',
+ 'select_placeholder' => 'Пожалуйста, выберите',
+ 'insert_row' => 'Вставить строку',
+ 'delete_row' => 'Удалить строку'
],
'relation' => [
'missing_definition' => "Поведение отношения не содержит определения для ':field'.",
'missing_model' => "Для поведения отношения, используемого в :class не определена модель.",
'invalid_action_single' => "Это действие не может быть выполнено для особого отношения.",
'invalid_action_multi' => "Это действие не может быть выполнено для множественных отношений.",
+ 'help' => "Нажмите на элемент, который нужно добавить",
+ 'related_data' => "Связанные :name данные",
'add' => "Добавить",
- 'add_name' => "Добавление :name",
+ 'add_selected' => "Добавить выбранные",
+ 'add_a_new' => "Добавить новый :name",
+ 'cancel' => "Отмена",
+ 'add_name' => "Добавить :name",
'create' => "Создать",
'create_name' => "Создание :name",
'update' => "Update",
@@ -145,6 +186,7 @@ return [
'remove_name' => "Удаление :name",
'delete' => "Удалить",
'delete_name' => "Удаление :name",
+ 'delete_confirm' => "Вы уверены?",
],
'model' => [
'name' => "Модель",
diff --git a/modules/backend/models/BackendPreferences.php b/modules/backend/models/BackendPreferences.php
index b365af49a..c7845c61f 100644
--- a/modules/backend/models/BackendPreferences.php
+++ b/modules/backend/models/BackendPreferences.php
@@ -38,6 +38,7 @@ class BackendPreferences extends Model
'de' => [Lang::get('system::lang.locale.de'), 'flag-de'],
'fr' => [Lang::get('system::lang.locale.fr'), 'flag-fr'],
'it' => [Lang::get('system::lang.locale.it'), 'flag-it'],
+ 'ro' => [Lang::get('system::lang.locale.ro'), 'flag-ro'],
];
// Sort the locales alphabetically
diff --git a/modules/backend/widgets/Form.php b/modules/backend/widgets/Form.php
index d8e2e1304..e3d973130 100644
--- a/modules/backend/widgets/Form.php
+++ b/modules/backend/widgets/Form.php
@@ -45,7 +45,7 @@ class Form extends WidgetBase
/**
* @var array Collection of all fields used in this form.
*/
- protected $allFields = [];
+ protected $fields = [];
/**
* @var array Collection of all form widgets used in this form.
@@ -179,10 +179,10 @@ class Form extends WidgetBase
public function renderField($field, $options = [])
{
if (is_string($field)) {
- if (!isset($this->allFields[$field]))
+ if (!isset($this->fields[$field]))
throw new ApplicationException(Lang::get('backend::lang.form.missing_definition', compact('field')));
- $field = $this->allFields[$field];
+ $field = $this->fields[$field];
}
if (!isset($options['useContainer'])) $options['useContainer'] = true;
@@ -241,7 +241,7 @@ class Form extends WidgetBase
$this->model->fill($data);
$this->data = (object) array_merge((array) $this->data, (array) $data);
- foreach ($this->allFields as $field)
+ foreach ($this->fields as $field)
$field->value = $this->getFieldValue($field);
return $data;
@@ -274,10 +274,10 @@ class Form extends WidgetBase
if (($updateFields = post('fields')) && is_array($updateFields)) {
foreach ($updateFields as $field) {
- if (!isset($this->allFields[$field]))
+ if (!isset($this->fields[$field]))
continue;
- $fieldObject = $this->allFields[$field];
+ $fieldObject = $this->fields[$field];
$result['#' . $fieldObject->getId('group')] = $this->makePartial('field', ['field' => $fieldObject]);
}
}
@@ -357,7 +357,7 @@ class Form extends WidgetBase
/*
* Bind all form widgets to controller
*/
- foreach ($this->allFields as $field) {
+ foreach ($this->fields as $field) {
if ($field->type != 'widget')
continue;
@@ -413,7 +413,7 @@ class Form extends WidgetBase
continue;
}
- $this->allFields[$name] = $fieldObj;
+ $this->fields[$name] = $fieldObj;
switch (strtolower($addToArea)) {
case 'primary':
@@ -589,7 +589,7 @@ class Form extends WidgetBase
*/
public function getFields()
{
- return $this->allFields;
+ return $this->fields;
}
/**
@@ -599,7 +599,7 @@ class Form extends WidgetBase
*/
public function getField($field)
{
- return $this->allFields[$field];
+ return $this->fields[$field];
}
/**
@@ -621,10 +621,10 @@ class Form extends WidgetBase
public function getFieldValue($field)
{
if (is_string($field)) {
- if (!isset($this->allFields[$field]))
+ if (!isset($this->fields[$field]))
throw new ApplicationException(Lang::get('backend::lang.form.missing_definition', compact('field')));
- $field = $this->allFields[$field];
+ $field = $this->fields[$field];
}
$columnName = $field->columnName;
@@ -694,7 +694,7 @@ class Form extends WidgetBase
* Boolean fields (checkbox, switch) won't be present value FALSE
* Number fields should be converted to integers
*/
- foreach ($this->allFields as $field) {
+ foreach ($this->fields as $field) {
if (!in_array($field->type, ['switch', 'checkbox', 'number']))
continue;
diff --git a/modules/backend/widgets/Lists.php b/modules/backend/widgets/Lists.php
index a0cce838b..c49b08648 100644
--- a/modules/backend/widgets/Lists.php
+++ b/modules/backend/widgets/Lists.php
@@ -46,7 +46,7 @@ class Lists extends WidgetBase
protected $visibleColumns;
/**
- * @var array All available columns.
+ * @var array Collection of all list columns used in this list.
*/
protected $columns;
@@ -265,9 +265,10 @@ class Lists extends WidgetBase
protected function prepareModel()
{
$query = $this->model->newQuery();
- $selects = [$this->model->getTable().'.*'];
- $tables = ['base'=>$this->model->getTable()];
+ $primaryTable = $this->model->getTable();
+ $selects = [$primaryTable.'.*'];
$joins = [];
+ $withs = [];
/*
* Extensibility
@@ -276,70 +277,134 @@ class Lists extends WidgetBase
$this->fireEvent('list.extendQueryBefore', [$query]);
/*
- * Related custom selects, must come first
+ * Prepare searchable column names
*/
- foreach ($this->getVisibleListColumns() as $column) {
- if (!isset($column->relation) || !isset($column->sqlSelect))
- continue;
+ $primarySearchable = [];
+ $relationSearchable = [];
- if (!$this->model->hasRelation($column->relation))
- throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', ['class'=>get_class($this->model), 'relation'=>$column->relation]));
+ $columnsToSearch = [];
+ if (!empty($this->searchTerm) && ($searchableColumns = $this->getSearchableColumns())) {
+ foreach ($searchableColumns as $column) {
+ /*
+ * Related
+ */
+ if ($this->isColumnRelated($column)) {
+ $table = $this->model->makeRelation($column->relation)->getTable();
+ $columnName = isset($column->sqlSelect)
+ ? DbDongle::raw($this->parseTableName($column->sqlSelect, $table))
+ : $table . '.' . $column->nameFrom;
- $alias = Db::getQueryGrammar()->wrap($column->columnName);
- $table = $this->model->makeRelation($column->relation)->getTable();
- $relationType = $this->model->getRelationType($column->relation);
- $sqlSelect = $this->parseTableName($column->sqlSelect, $table);
+ $relationSearchable[$column->relation][] = $columnName;
+ }
+ /*
+ * Primary
+ */
+ else {
+ $columnName = isset($column->sqlSelect)
+ ? DbDongle::raw($this->parseTableName($column->sqlSelect, $primaryTable))
+ : $primaryTable . '.' . $column->columnName;
- if (in_array($relationType, ['hasMany', 'belongsToMany', 'morphToMany', 'morphedByMany', 'morphMany', 'attachMany', 'hasManyThrough']))
- $selects[] = DbDongle::raw("group_concat(" . $sqlSelect . " separator ', ') as ". $alias);
- else
- $selects[] = DbDongle::raw($sqlSelect . ' as '. $alias);
-
- $joins[] = $column->relation;
- $tables[$column->relation] = $table;
+ $primarySearchable[] = $columnName;
+ }
+ }
}
- if ($joins)
- $query->joinWith(array_unique($joins), false);
+ /*
+ * Prepare related eager loads (withs) and custom selects (joins)
+ */
+ foreach ($this->getVisibleListColumns() as $column) {
+
+ if (!$this->isColumnRelated($column) || (!isset($column->sqlSelect) && !isset($column->nameFrom)))
+ continue;
+
+ if (isset($column->nameFrom))
+ $withs[] = $column->relation;
+
+ $joins[] = $column->relation;
+ }
+
+ /*
+ * Include any relation constraints
+ */
+ if ($joins) {
+ foreach (array_unique($joins) as $join) {
+ /*
+ * Apply a supplied search term for relation columns and
+ * constrain the query only if there is something to search for
+ */
+ $columnsToSearch = array_get($relationSearchable, $join, []);
+
+ if (count($columnsToSearch) > 0) {
+ $query->whereHas($join, function($_query) use ($columnsToSearch) {
+ $_query->searchWhere($this->searchTerm, $columnsToSearch);
+ });
+ }
+ }
+ }
+
+ /*
+ * Add eager loads to the query
+ */
+ if ($withs) {
+ $query->with(array_unique($withs));
+ }
/*
* Custom select queries
*/
foreach ($this->getVisibleListColumns() as $column) {
- if (!isset($column->sqlSelect) || isset($column->relation))
+ if (!isset($column->sqlSelect))
continue;
$alias = Db::getQueryGrammar()->wrap($column->columnName);
- $sqlSelect = $this->parseTableName($column->sqlSelect, $tables['base']);
- $selects[] = DbDongle::raw($sqlSelect . ' as '. $alias);
+
+ /*
+ * Relation column
+ */
+ if (isset($column->relation)) {
+ $table = $this->model->makeRelation($column->relation)->getTable();
+ $relationType = $this->model->getRelationType($column->relation);
+ $sqlSelect = $this->parseTableName($column->sqlSelect, $table);
+
+ /*
+ * Manipulate a count query for the sub query
+ */
+ $relationObj = $this->model->{$column->relation}();
+ $countQuery = $relationObj->getRelationCountQuery($relationObj->getRelated()->newQuery(), $query);
+
+ $joinSql = $this->isColumnRelated($column, true)
+ ? DbDongle::raw("group_concat(" . $sqlSelect . " separator ', ')")
+ : DbDongle::raw($sqlSelect);
+
+ $joinSql = $countQuery->select($joinSql)->toSql();
+
+ $selects[] = Db::raw("(".$joinSql.") as ".$alias);
+ }
+ /*
+ * Primary column
+ */
+ else {
+ $sqlSelect = $this->parseTableName($column->sqlSelect, $primaryTable);
+ $selects[] = DbDongle::raw($sqlSelect . ' as '. $alias);
+ }
}
/*
- * Handle a supplied search term
+ * Apply a supplied search term for primary columns
*/
- if (!empty($this->searchTerm) && ($searchableColumns = $this->getSearchableColumns())) {
- $query->orWhere(function($innerQuery) use ($searchableColumns, $tables) {
- $columnsToSearch = [];
- foreach ($searchableColumns as $column) {
-
- if (isset($column->sqlSelect)) {
- $table = (isset($column->relation)) ? $tables[$column->relation] : 'base';
- $columnName = DbDongle::raw($this->parseTableName($column->sqlSelect, $table));
- }
- else
- $columnName = $tables['base'] . '.' . $column->columnName;
-
- $columnsToSearch[] = $columnName;
- }
-
- $innerQuery->searchWhere($this->searchTerm, $columnsToSearch);
+ if (count($primarySearchable) > 0) {
+ $query->orWhere(function($innerQuery) use ($primarySearchable) {
+ $innerQuery->searchWhere($this->searchTerm, $primarySearchable);
});
}
/*
- * Handle sorting
+ * Apply sorting
*/
if ($sortColumn = $this->getSortColumn()) {
+ if (($column = array_get($this->columns, $sortColumn)) && $column->sqlSelect)
+ $sortColumn = $column->sqlSelect;
+
$query->orderBy($sortColumn, $this->sortDirection);
}
@@ -350,15 +415,17 @@ class Lists extends WidgetBase
$callback($query);
}
+ /*
+ * Add custom selects
+ */
+ $query->select($selects);
+
/*
* Extensibility
*/
Event::fire('backend.list.extendQuery', [$this, $query]);
$this->fireEvent('list.extendQuery', [$query]);
- // Grouping due to the joinWith() call
- $query->select($selects);
- $query->groupBy($this->model->getQualifiedKeyName());
return $query;
}
@@ -414,12 +481,31 @@ class Lists extends WidgetBase
return Html::attributes(['onclick' => $recordOnClick]);
}
+ /**
+ * Get all the registered columns for the instance.
+ * @return array
+ */
+ public function getColumns()
+ {
+ return $this->columns ?: $this->defineListColumns();
+ }
+
+ /**
+ * Get a specified column object
+ * @param string $column
+ * @return mixed
+ */
+ public function getColumn($column)
+ {
+ return $this->columns[$column];
+ }
+
/**
* Returns the list columns that are visible by list settings or default
*/
protected function getVisibleListColumns()
{
- $definitions = $this->getListColumns();
+ $definitions = $this->defineListColumns();
$columns = [];
/*
@@ -457,7 +543,7 @@ class Lists extends WidgetBase
/**
* Builds an array of list columns with keys as the column name and values as a ListColumn object.
*/
- protected function getListColumns()
+ protected function defineListColumns()
{
if (!isset($this->config->columns) || !is_array($this->config->columns) || !count($this->config->columns))
throw new ApplicationException(Lang::get('backend::lang.list.missing_columns', ['class'=>get_class($this->controller)]));
@@ -555,16 +641,33 @@ class Lists extends WidgetBase
*/
public function getColumnValue($record, $column)
{
+
+ $columnName = $column->columnName;
+
/*
- * If the column is a relation, it will be a custom select,
+ * Handle taking name from model attribute.
+ */
+ if ($column->nameFrom) {
+ if (!array_key_exists($columnName, $record->getRelations()))
+ $value = null;
+ elseif ($this->isColumnRelated($column, true))
+ $value = implode(', ', $record->{$columnName}->lists($column->nameFrom));
+ elseif ($this->isColumnRelated($column))
+ $value = $record->{$columnName}->{$column->nameFrom};
+ else
+ $value = $record->{$column->nameFrom};
+ }
+ /*
+ * Otherwise, if the column is a relation, it will be a custom select,
* so prevent the Model from attempting to load the relation
* if the value is NULL.
*/
- $columnName = $column->columnName;
- if ($record->hasRelation($columnName) && array_key_exists($columnName, $record->attributes))
- $value = $record->attributes[$columnName];
- else
- $value = $record->{$columnName};
+ else {
+ if ($record->hasRelation($columnName) && array_key_exists($columnName, $record->attributes))
+ $value = $record->attributes[$columnName];
+ else
+ $value = $record->{$columnName};
+ }
if (method_exists($this, 'eval'. studly_case($column->type) .'TypeValue'))
$value = $this->{'eval'. studly_case($column->type) .'TypeValue'}($record, $column, $value);
@@ -613,6 +716,9 @@ class Lists extends WidgetBase
{
return $this->controller->makePartial($column->path ?: $column->columnName, [
'listColumn' => $column,
+ 'listRecord' => $record,
+ 'listValue' => $value,
+ 'column' => $column,
'record' => $record,
'value' => $value
]);
@@ -738,7 +844,7 @@ class Lists extends WidgetBase
*/
protected function getSearchableColumns()
{
- $columns = $this->columns ?: $this->getListColumns();
+ $columns = $this->getColumns();
$searchable = [];
foreach ($columns as $column) {
@@ -848,7 +954,7 @@ class Lists extends WidgetBase
if ($this->sortableColumns !== null)
return $this->sortableColumns;
- $columns = $this->columns ?: $this->getListColumns();
+ $columns = $this->getColumns();
$sortable = [];
foreach ($columns as $column) {
@@ -912,12 +1018,12 @@ class Lists extends WidgetBase
/*
* Force all columns invisible
*/
- $allColumns = $this->getListColumns();
- foreach ($allColumns as $column) {
+ $columns = $this->defineListColumns();
+ foreach ($columns as $column) {
$column->invisible = true;
}
- return array_merge($allColumns, $this->getVisibleListColumns());
+ return array_merge($columns, $this->getVisibleListColumns());
}
//
@@ -962,4 +1068,38 @@ class Lists extends WidgetBase
return $this->onRefresh();
}
+ //
+ // Helpers
+ //
+
+ /**
+ * Check if column refers to a relation of the model
+ * @param ListColumn $column List column object
+ * @param boolean $multi If set, returns true only if the relation is a "multiple relation type"
+ * @return boolean
+ */
+ protected function isColumnRelated($column, $multi = false)
+ {
+ if (!isset($column->relation))
+ return false;
+
+ if (!$this->model->hasRelation($column->relation))
+ throw new ApplicationException(Lang::get('backend::lang.model.missing_relation', ['class'=>get_class($this->model), 'relation'=>$column->relation]));
+
+ if (!$multi)
+ return true;
+
+ $relationType = $this->model->getRelationType($column->relation);
+
+ return in_array($relationType, [
+ 'hasMany',
+ 'belongsToMany',
+ 'morphToMany',
+ 'morphedByMany',
+ 'morphMany',
+ 'attachMany',
+ 'hasManyThrough'
+ ]);
+ }
+
}
\ No newline at end of file
diff --git a/modules/backend/widgets/ReportContainer.php b/modules/backend/widgets/ReportContainer.php
index 623649777..161048c88 100644
--- a/modules/backend/widgets/ReportContainer.php
+++ b/modules/backend/widgets/ReportContainer.php
@@ -117,7 +117,7 @@ class ReportContainer extends WidgetBase
{
$sizes = [];
for ($i = 1; $i <= 10; $i++)
- $sizes[$i] = $i < 10 ? $i : $i.' (full width)';
+ $sizes[$i] = $i < 10 ? $i : $i.' (' . Lang::get('backend::lang.dashboard.full_width') . ')';
$this->vars['sizes'] = $sizes;
$this->vars['widgets'] = WidgetManager::instance()->listReportWidgets();
diff --git a/modules/backend/widgets/form/partials/_field_partial.htm b/modules/backend/widgets/form/partials/_field_partial.htm
index cfa8e9d87..960bd0c4b 100644
--- a/modules/backend/widgets/form/partials/_field_partial.htm
+++ b/modules/backend/widgets/form/partials/_field_partial.htm
@@ -1,2 +1,9 @@
-= $this->controller->makePartial($field->path ?: $field->columnName) ?>
\ No newline at end of file
+= $this->controller->makePartial($field->path ?: $field->columnName, [
+ 'formModel' => $formModel,
+ 'formField' => $field,
+ 'formValue' => $field->value,
+ 'model' => $formModel,
+ 'field' => $field,
+ 'value' => $field->value
+]) ?>
\ No newline at end of file
diff --git a/modules/backend/widgets/lists/partials/_setup_form.htm b/modules/backend/widgets/lists/partials/_setup_form.htm
index 0ae7b83eb..3d51f8d21 100644
--- a/modules/backend/widgets/lists/partials/_setup_form.htm
+++ b/modules/backend/widgets/lists/partials/_setup_form.htm
@@ -54,13 +54,13 @@
data-request="= $this->getEventHandler('onApplySetup') ?>"
data-dismiss="popup"
data-stripe-load-indicator>
- = e(trans('backend::lang.list.apply_changes')) ?>
+ = e(trans('backend::lang.form.apply')) ?>
= Form::close() ?>
\ No newline at end of file
diff --git a/modules/backend/widgets/reportcontainer/partials/_new_widget_popup.htm b/modules/backend/widgets/reportcontainer/partials/_new_widget_popup.htm
index 6121d674d..e27c7410b 100644
--- a/modules/backend/widgets/reportcontainer/partials/_new_widget_popup.htm
+++ b/modules/backend/widgets/reportcontainer/partials/_new_widget_popup.htm
@@ -10,7 +10,7 @@
-