diff --git a/application/Espo/Core/Controllers/Record.php b/application/Espo/Core/Controllers/Record.php
index 87b0cda083..fa677aab51 100644
--- a/application/Espo/Core/Controllers/Record.php
+++ b/application/Espo/Core/Controllers/Record.php
@@ -34,6 +34,8 @@ class Record extends Base
public static $defaultAction = 'list';
+ protected $defaultRecordServiceName = 'Record';
+
protected function getEntityManager()
{
return $this->getContainer()->get('entityManager');
@@ -48,7 +50,7 @@ class Record extends Base
if ($this->getServiceFactory()->checkExists($name)) {
$service = $this->getServiceFactory()->create($name);
} else {
- $service = $this->getServiceFactory()->create('Record');
+ $service = $this->getServiceFactory()->create($this->defaultRecordServiceName);
$service->setEntityName($name);
}
diff --git a/application/Espo/Core/Controllers/RecordTree.php b/application/Espo/Core/Controllers/RecordTree.php
new file mode 100644
index 0000000000..73a2e4875e
--- /dev/null
+++ b/application/Espo/Core/Controllers/RecordTree.php
@@ -0,0 +1,60 @@
+getAcl()->check($this->name, 'read')) {
+ throw new Forbidden();
+ }
+
+ $where = $request->get('where');
+ $parentId = $request->get('parentId');
+ $asc = $request->get('asc') === 'true';
+ $sortBy = $request->get('sortBy');
+
+ $collection = $this->getRecordService()->getTree($parentId, array(
+ 'where' => $where,
+ 'asc' => $asc,
+ 'sortBy' => $sortBy,
+ ));
+ return array(
+ 'list' => $collection->toArray(),
+ 'path' => $this->getRecordService()->getTreeItemPath($parentId)
+ );
+ }
+}
+
diff --git a/application/Espo/Core/Entities/TreeItem.php b/application/Espo/Core/Entities/TreeItem.php
new file mode 100644
index 0000000000..0f70bc7847
--- /dev/null
+++ b/application/Espo/Core/Entities/TreeItem.php
@@ -0,0 +1,43 @@
+get('childList');
+ if (is_null($childList)) {
+ $data['childList'] = null;
+ } else {
+ $arr = [];
+ foreach ($childList as $entity) {
+ $arr[] = $entity->toArray();
+ }
+ $data['childList'] = $arr;
+ }
+ return $data;
+ }
+}
+
diff --git a/application/Espo/Services/RecordTree.php b/application/Espo/Services/RecordTree.php
new file mode 100644
index 0000000000..9d4ee28972
--- /dev/null
+++ b/application/Espo/Services/RecordTree.php
@@ -0,0 +1,74 @@
+getSelectParams($params);
+ $selectParams['whereClause'][] = array(
+ 'parentId' => $parentId
+ );
+
+
+ $collection = $this->getRepository()->find($selectParams);
+ foreach ($collection as $entity) {
+ $childList = $this->getTree($entity->id, $params, $level + 1);
+ $entity->set('childList', $childList);
+
+ }
+ return $collection;
+ }
+
+ public function getTreeItemPath($parentId = null)
+ {
+ $arr = [];
+ while (1) {
+ if (empty($parentId)) {
+ break;
+ }
+ $parent = $this->getEntityManager()->getEntity($this->entityType, $parentId);
+ if ($parent) {
+ $parentId = $parent->get('parentId');
+ array_unshift($arr, $parent->id);
+ } else {
+ $parentId = null;
+ }
+ }
+ return $arr;
+ }
+}
+
diff --git a/custom/Espo/Custom/Controllers/ArticleCategory.php b/custom/Espo/Custom/Controllers/ArticleCategory.php
new file mode 100644
index 0000000000..4b3ebfd916
--- /dev/null
+++ b/custom/Espo/Custom/Controllers/ArticleCategory.php
@@ -0,0 +1,7 @@
+
+
<% if (layout.right) { %>
{{{<%= layout.right.name %>}}}
-
+
<% } %>
<% _.each(layout.rows, function (row, key) { %>
@@ -10,7 +10,7 @@
<%
var tag = 'tag' in defs ? defs.tag : false;
if (tag) {
- print( '<' + tag);
+ print( '<' + tag);
if ('id' in defs) {
print(' id="'+defs.id+'"');
}
@@ -18,7 +18,7 @@
print(' class="'+defs.class+'"');
};
print('>');
- }
+ }
%>{{{<%= defs.name %>}}}<%
if (tag) {
print( '' + tag + '>');
diff --git a/frontend/client/src/controllers/record-tree.js b/frontend/client/src/controllers/record-tree.js
new file mode 100644
index 0000000000..624e25b691
--- /dev/null
+++ b/frontend/client/src/controllers/record-tree.js
@@ -0,0 +1,42 @@
+/************************************************************************
+ * This file is part of EspoCRM.
+ *
+ * EspoCRM - Open Source CRM application.
+ * Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
+ * Website: http://www.espocrm.com
+ *
+ * EspoCRM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * EspoCRM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with EspoCRM. If not, see http://www.gnu.org/licenses/.
+ ************************************************************************/
+Espo.define('Controllers.RecordTree', 'Controllers.Record', function (Dep) {
+
+ return Dep.extend({
+
+ defaultAction: 'listTree',
+
+ beforeListTree: function () {
+ this.handleCheckAccess('read');
+ },
+
+ listTree: function (options) {
+ this.getCollection(function (collection) {
+ this.main(this.getViewName('listTree'), {
+ scope: this.name,
+ collection: collection
+ });
+ });
+ }
+
+ });
+
+});
diff --git a/frontend/client/src/views/list-tree.js b/frontend/client/src/views/list-tree.js
new file mode 100644
index 0000000000..4e5296d6d5
--- /dev/null
+++ b/frontend/client/src/views/list-tree.js
@@ -0,0 +1,36 @@
+/************************************************************************
+ * This file is part of EspoCRM.
+ *
+ * EspoCRM - Open Source CRM application.
+ * Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
+ * Website: http://www.espocrm.com
+ *
+ * EspoCRM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * EspoCRM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with EspoCRM. If not, see http://www.gnu.org/licenses/.
+ ************************************************************************/
+
+Espo.define('Views.ListTree', 'Views.List', function (Dep) {
+
+ return Dep.extend({
+
+ searchPanel: false,
+
+ createButton: false,
+
+ getRecordViewName: function () {
+ return this.getMetadata().get('clientDefs.' + this.scope + '.recordViews.listTree') || 'Record.ListTree';
+ }
+
+ });
+});
+
diff --git a/frontend/client/src/views/list.js b/frontend/client/src/views/list.js
index 026dcbca1b..fb90f3bf79 100644
--- a/frontend/client/src/views/list.js
+++ b/frontend/client/src/views/list.js
@@ -113,10 +113,14 @@ Espo.define('Views.List', ['Views.Main', 'SearchManager'], function (Dep, Search
}
},
+ getRecordViewName: function () {
+ return this.getMetadata().get('clientDefs.' + this.scope + '.recordViews.list') || 'Record.List';
+ },
+
afterRender: function () {
this.notify('Loading...');
- var listViewName = this.getMetadata().get('clientDefs.' + this.scope + '.recordViews.list') || 'Record.List';
+ var listViewName = this.getRecordViewName();
this.listenToOnce(this.collection, 'sync', function () {
this.createView('list', listViewName, {
diff --git a/frontend/client/src/views/record/list-tree.js b/frontend/client/src/views/record/list-tree.js
new file mode 100644
index 0000000000..99c241dbf1
--- /dev/null
+++ b/frontend/client/src/views/record/list-tree.js
@@ -0,0 +1,125 @@
+/************************************************************************
+ * This file is part of EspoCRM.
+ *
+ * EspoCRM - Open Source CRM application.
+ * Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
+ * Website: http://www.espocrm.com
+ *
+ * EspoCRM is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * EspoCRM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with EspoCRM. If not, see http://www.gnu.org/licenses/.
+ ************************************************************************/
+
+Espo.define('Views.Record.ListTree', 'Views.Record.List', function (Dep) {
+
+ return Dep.extend({
+
+ template: 'record.list-tree',
+
+ checkboxes: false,
+
+ selectable: false,
+
+ rowActionsView: false,
+
+ _internalLayoutType: 'list-row-expanded',
+
+ presentationType: 'tree',
+
+ header: false,
+
+ listContainerEl: '.list > ul',
+
+ _loadListLayout: function (callback) {
+ var type = this.type + 'Expanded';
+ this._helper.layoutManager.get(this.collection.name, type, function (listLayout) {
+ callback(listLayout);
+ });
+ },
+
+ _convertLayout: function (listLayout, model) {
+ model = model || this.collection.model.prototype;
+
+ var layout = {
+ rows: [],
+ right: false,
+ };
+
+ for (var i in listLayout.rows) {
+ var row = listLayout.rows[i];
+ var layoutRow = [];
+ for (var j in row) {
+
+ var e = row[j];
+ var type = e.type || model.getFieldType(e.name) || 'base';
+
+ var item = {
+ name: e.name,
+ view: e.view || model.getFieldParam(e.name, 'view') || this.getFieldManager().getViewName(type),
+ options: {
+ defs: {
+ name: e.name,
+ params: e.params || {}
+ },
+ mode: 'list'
+ }
+ };
+ if (e.link) {
+ item.options.mode = 'listLink';
+ }
+ layoutRow.push(item);
+ }
+ layout.rows.push(layoutRow);
+ }
+
+ if ('right' in listLayout) {
+ if (listLayout.right != false) {
+ layout.right = {
+ name: listLayout.right.name || 'right',
+ view: listLayout.right.view,
+ options: {
+ defs: {
+ params: {
+ width: listLayout.right.width || '7%'
+ }
+ }
+ }
+ };
+ }
+ } else {
+ if (this.rowActionsView) {
+ layout.right = this.getRowActionsDefs();
+ }
+ }
+ return layout;
+ },
+
+ getRowSelector: function (id) {
+ return 'li[data-id="' + id + '"]';
+ },
+
+ getItemEl: function (model, item) {
+ return this.options.el + ' li[data-id="' + model.id + '"] span.cell-' + item.name;
+ },
+
+ prepareInterbalLayout: function (internalLayout, model) {
+ var rows = internalLayout.rows || [];
+ rows.forEach(function (row) {
+ row.forEach(function (col) {
+ col.el = this.getItemEl(model, col);
+ }, this);
+ }, this);
+ },
+
+ });
+});
+
diff --git a/frontend/client/src/views/record/list.js b/frontend/client/src/views/record/list.js
index cf552a5cc3..f0339016bf 100644
--- a/frontend/client/src/views/record/list.js
+++ b/frontend/client/src/views/record/list.js
@@ -740,9 +740,7 @@ Espo.define('Views.Record.List', 'View', function (Dep) {
this.checkedList = [];
this.rows = [];
-
if (this.collection.length > 0) {
-
var i = 0;
var c = !this.pagination ? 1 : 2;
var func = function () {
@@ -756,10 +754,10 @@ Espo.define('Views.Record.List', 'View', function (Dep) {
this.wait(true);
this.getInternalLayout(function (internalLayout) {
- var count = this.collection.models.length;
+ var modelList = this.collection.models;
+ var count = modelList.length;
var built = 0;
- for (var i in this.collection.models) {
- var model = this.collection.models[i];
+ modelList.forEach(function (model) {
this.buildRow(i, model, function () {
built++;
if (built == count) {
@@ -767,7 +765,7 @@ Espo.define('Views.Record.List', 'View', function (Dep) {
this.wait(false);
}
}.bind(this));
- }
+ }, this);
}.bind(this));
if (this.pagination) {