partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit 17bcd332daab889b5af89e8b938d1413bbed5052
parent 06595d45564fa4f8973a6095a65142be8bb3e3da
Author: Felicia Hummel <felicia@partkeepr.com>
Date:   Tue, 27 Dec 2016 17:28:48 +0100

Initial commit of m:n relation export

Diffstat:
Msrc/PartKeepr/ExportBundle/EventListener/AbstractResponderViewListener.php | 5+++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/ModelTreeMaker/ModelTreeMaker.js | 16+++++++++-------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Project/ProjectPartGrid.js | 2+-
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/EntityQueryPanel.js | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Data/HydraModel.js | 18+++++++++++++++++-
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Data/ReflectionFieldTreeModel.js | 8++++++++
Msrc/PartKeepr/FrontendBundle/Resources/views/index.html.twig | 1+
Msrc/PartKeepr/ProjectBundle/Entity/ProjectPart.php | 1+
8 files changed, 138 insertions(+), 56 deletions(-)

diff --git a/src/PartKeepr/ExportBundle/EventListener/AbstractResponderViewListener.php b/src/PartKeepr/ExportBundle/EventListener/AbstractResponderViewListener.php @@ -102,7 +102,12 @@ abstract class AbstractResponderViewListener $finalData[$key][$mapping] = $finalData[$key][$mapping]->format(\DateTime::W3C); } } + + if ($finalData[$key][$mapping] === null) { + $finalData[$key][$mapping] = "null"; + } } catch (\Exception $e) { + $finalData[$key][$mapping] = "null"; } } } diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/ModelTreeMaker/ModelTreeMaker.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/ModelTreeMaker/ModelTreeMaker.js @@ -59,14 +59,16 @@ Ext.define("PartKeepr.ModelTreeMaker.ModelTreeMaker", { if (fields[i]["$reference"] === undefined) { // Field is a scalar field if (this.ignoreFields.indexOf(fields[i].name) === -1 && !this.customFieldIgnorer(fields[i])) { - newNode = node.appendChild({ + + newNode = node.appendChild(Ext.create("PartKeepr.Data.ReflectionFieldTreeModel", { text: fields[i].name, leaf: true, data: { name: prefix + fields[i].name, type: "field" - } - }); + }, + entityIndex: "" + })); if (callback) { newNode.set(callback(fields[i], newNode)); @@ -83,14 +85,14 @@ Ext.define("PartKeepr.ModelTreeMaker.ModelTreeMaker", { } if (!associationAlreadyProcessed) { - childNode = node.appendChild({ + childNode = node.appendChild(Ext.create("PartKeepr.Data.ReflectionFieldTreeModel", { text: fields[i].name, data: { name: prefix + fields[i].name, type: "manytoone" }, leaf: false - }); + })); if (callback) { childNode.set(callback(fields[i], childNode)); @@ -114,7 +116,7 @@ Ext.define("PartKeepr.ModelTreeMaker.ModelTreeMaker", { } if (!associationAlreadyProcessed) { - childNode = node.appendChild({ + childNode = node.appendChild(Ext.create("PartKeepr.Data.ReflectionFieldTreeModel",{ text: associations[i].name, data: { name: prefix + associations[i].name, @@ -122,7 +124,7 @@ Ext.define("PartKeepr.ModelTreeMaker.ModelTreeMaker", { reference: associations[i].cls }, leaf: false - }); + })); if (callback) { childNode.set(callback(associations[i].cls, childNode)); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Project/ProjectPartGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Project/ProjectPartGrid.js @@ -93,7 +93,7 @@ Ext.define('PartKeepr.ProjectPartGrid', { this.bbar = [ Ext.create("PartKeepr.Exporter.GridExporterButton", { itemId: 'export', - genericExporter: false, + genericExporter: true, tooltip: i18n("Export"), iconCls: "fugue-icon application-export", disabled: this.store.isLoading() diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/EntityQueryPanel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/EntityQueryPanel.js @@ -13,7 +13,15 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { region: 'east', width: 265, itemId: 'fieldTree', + plugins: { + ptype: 'cellediting', + clicksToEdit: 1, + pluginId: "cellediting", + }, split: true, + viewConfig: { + markDirty: false + }, store: { folderSort: true, sorters: [ @@ -23,6 +31,33 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { } ] }, + columns: [ + { + xtype: 'treecolumn', //this is so we know which column will show the tree + text: i18n("Field"), + dataIndex: 'text', + flex: 3, + }, { + text: i18n("Index"), + flex: 1, + align: 'center', + dataIndex: 'entityIndex', + format: '0', + editor: { + xtype: 'numberfield', + minValue: 0, + maxValue: 99 + }, + renderer: function (val, md, record) { + if (record.get("data") !== null && + record.get("data").type !== "onetomany") { + return ""; + } else { + return record.get("entityIndex"); + } + } + } + ], useArrows: true } ], @@ -50,7 +85,11 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { this.down("#fieldTree").on("itemdblclick", this.onTreeDblClick, this); rootNode.set("text", this.model.getName()); - this.treeMaker(rootNode, this.model, ""); + var treeMaker = Ext.create("PartKeepr.ModelTreeMaker.ModelTreeMaker"); + treeMaker.addIgnoreField("@id"); + treeMaker.make(rootNode, this.model, "", Ext.bind(this.appendFieldData, this), ["entityIndex"]); + rootNode.expand(); + rootNode.expand(); this.store = Ext.create("Ext.data.Store", { @@ -75,10 +114,27 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { } ] }); + + this.down("#fieldTree").getPlugin("cellediting").on("beforeedit", this.onBeforeEdit, this); this.down("#grid").addDocked(this.bottomToolbar); this.down("#grid").reconfigure(this.store, this.columns); }, + onBeforeEdit: function (editor, context) + { + if (context.record.get("data") !== null && + context.record.get("data").type !== "onetomany") { + + return false; + } + }, + /** + * @param {Ext.data.field.Field} The model + */ + appendFieldData: function (field, node) + { + node.set("entityIndex", 0); + }, /** * Returns the parameters for the query string. * @return {Object} An object containing all parameters @@ -130,8 +186,15 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { */ addColumn: function (record) { - var columns; - if (this.hasColumn(record) || record.get("data") === undefined) { + var columns, fieldPath; + + fieldPath = this.getFieldPath(record).join("."); + + if (this.hasColumn(fieldPath) || record.get("data").name === undefined) { + return; + } + + if (record.get("data").type !== "field") { return; } @@ -140,8 +203,8 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { this.syncColumns(); this.columns.push({ - dataIndex: record.get("data"), - text: record.get("data"), + dataIndex: fieldPath, + text: fieldPath, renderer: function (value, metadata, record, rowIndex, colIndex) { return record.get(this.getColumns()[colIndex].dataIndex); @@ -151,6 +214,22 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { this.down("#grid").reconfigure(this.store, this.columns); }, + getFieldPath: function (record) + { + var fieldPath = []; + + if (record.parentNode !== null && !record.parentNode.isRoot()) { + fieldPath = this.getFieldPath(record.parentNode); + } + + if (typeof(record.get("data")) === "object" && record.get("data").type === "onetomany") { + fieldPath.push(record.get("text") + "[" + record.get("entityIndex") + "]"); + } else { + fieldPath.push(record.get("text")); + } + + return fieldPath; + }, /** * Removes a specific column to the grid. Must be a record and has the "data" property defined. * @@ -158,16 +237,18 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { */ removeColumn: function (record) { - var i; + var i, fieldPath; + + fieldPath = this.getFieldPath(record).join("."); - if (!this.hasColumn(record) || record.get("data") === undefined) { + if (!this.hasColumn(fieldPath) || record.get("data").name === undefined) { return; } this.syncColumns(); for (i = 0; i < this.columns.length; i++) { - if (this.columns[i].dataIndex === record.get("data")) { + if (this.columns[i].dataIndex === fieldPath) { Ext.Array.removeAt(this.columns, i); } } @@ -205,12 +286,12 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { * @param {Ext.data.Model} The record to process * @return {Boolean} true if the column exist, false otherwise */ - hasColumn: function (record) + hasColumn: function (name) { var i, columns = this.down('#grid').getColumns(); for (i = 0; i < columns.length; i++) { - if (columns[i].dataIndex === record.get("data")) { + if (columns[i].dataIndex === name) { return true; } } @@ -225,45 +306,13 @@ Ext.define("PartKeepr.Widgets.EntityQueryPanel", { */ onTreeDblClick: function (tree, record) { - if (this.hasColumn(record)) { + var fieldPath = this.getFieldPath(record).join("."); + + if (this.hasColumn(fieldPath)) { this.removeColumn(record); } else { this.addColumn(record); } - }, - /** - * Builds the field tree recursively. Handles infinite recursions (e.g. in trees). - * - * @param {Ext.data.NodeInterface} The current node - * @param {Ext.data.Model} The model - * @param {String} The prefix. Omit if first called - */ - treeMaker: function (node, model, prefix) - { - var fields = model.getFields(); - this.visitedModels.push(model.getName()); - for (var i = 0; i < fields.length; i++) { - - if (fields[i]["$reference"] === undefined) { - node.appendChild({ - text: fields[i].name, - leaf: true, - data: prefix + fields[i].name - }); - } else { - for (var j = 0; j < this.visitedModels.length; j++) { - if (this.visitedModels[j] === fields[i].reference.cls.getName()) { - return; - } - } - - var childNode = node.appendChild({ - text: fields[i].name, - leaf: false - }); - - this.treeMaker(childNode, fields[i].reference.cls, prefix + fields[i].name + "."); - } - } } -}); +}) +; diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Data/HydraModel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Data/HydraModel.js @@ -15,7 +15,7 @@ Ext.define("PartKeepr.data.HydraModel", { }, get: function (fieldName) { - var ret, role, item; + var ret, role, item, openingBracket, closingBracket, subEntity, index, subEntityStore; ret = this.callParent(arguments); @@ -35,6 +35,22 @@ Ext.define("PartKeepr.data.HydraModel", { parts.shift(); return item.get(parts.join(".")); } + } else { + openingBracket = parts[0].indexOf("["); + + if (openingBracket !== -1) { + subEntity = parts[0].substring(0, openingBracket); + closingBracket = parts[0].indexOf("]", openingBracket); + index = parts[0].substring(openingBracket+1, closingBracket); + + subEntityStore = this[this.associations[subEntity].name](); + item = subEntityStore.getAt(index); + + if (item !== null) { + parts.shift(); + return item.get(parts.join(".")); + } + } } } return ret; diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Data/ReflectionFieldTreeModel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Data/ReflectionFieldTreeModel.js @@ -0,0 +1,8 @@ +Ext.define("PartKeepr.Data.ReflectionFieldTreeModel", { + extend: "Ext.data.TreeModel", + + fields: [{ + name: "entityIndex", + type: 'int' + }] +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig b/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig @@ -79,6 +79,7 @@ {% javascripts output='js/compiled/main2.js' '@PartKeeprFrontendBundle/Resources/public/js/Util/i18n.js' + '@PartKeeprFrontendBundle/Resources/public/js/Data/ReflectionFieldTreeModel.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/EntityQueryPanel.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/EntityPicker.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/PresetComboBox.js' diff --git a/src/PartKeepr/ProjectBundle/Entity/ProjectPart.php b/src/PartKeepr/ProjectBundle/Entity/ProjectPart.php @@ -45,6 +45,7 @@ class ProjectPart extends BaseEntity * Specifies the project which belongs to this project part. * * @ORM\ManyToOne(targetEntity="PartKeepr\ProjectBundle\Entity\Project", inversedBy="parts") + * @Groups({"default"}) * @Assert\NotNull() * * @var Project