partkeepr

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

commit 8b9439a5e5cba85329748a0bb201da5b56de905d
parent 17bcd332daab889b5af89e8b938d1413bbed5052
Author: Felicia Hummel <felicitus@felicitus.org>
Date:   Fri,  6 Jan 2017 18:52:48 +0100

Merge pull request #762 from partkeepr/PartKeepr-54

Implemented advanced part parameters
Diffstat:
Mapp/AppKernel.php | 3+++
Mapp/SymfonyRequirements.php | 92+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mapp/check.php | 14+++++++-------
Mapp/config/config_partkeepr.yml | 11+++++++++++
Mapp/config/parameters.php.dist | 6++++++
Mapp/config/routing.yml | 5+++++
Mcomposer.json | 3++-
Mcomposer.lock | 409+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/PartKeepr/BatchJobBundle/Action/ExecuteBatchJobAction.php | 8+++-----
Msrc/PartKeepr/BatchJobBundle/DependencyInjection/PartKeeprBatchJobExtension.php | 2--
Msrc/PartKeepr/BatchJobBundle/Entity/BatchJob.php | 13++++++++-----
Msrc/PartKeepr/BatchJobBundle/Entity/BatchJobQueryField.php | 16+++++++++++-----
Msrc/PartKeepr/BatchJobBundle/Entity/BatchJobUpdateField.php | 15++++++++++-----
Msrc/PartKeepr/BatchJobBundle/PartKeeprBatchJobBundle.php | 2+-
Msrc/PartKeepr/CoreBundle/Controller/DefaultController.php | 23+++++++++++++++++++++++
Msrc/PartKeepr/CoreBundle/Services/VersionService.php | 23++++++++++++++++-------
Msrc/PartKeepr/CoreBundle/Tests/SetupWebTestCase.php | 1+
Msrc/PartKeepr/CoreBundle/Tests/WebTestCase.php | 1+
Msrc/PartKeepr/DoctrineReflectionBundle/Annotation/ByReference.php | 2+-
Msrc/PartKeepr/DoctrineReflectionBundle/Filter/AdvancedSearchFilter.php | 17+++++------------
Msrc/PartKeepr/DoctrineReflectionBundle/Filter/AssociationPropertyInterface.php | 10++++++----
Msrc/PartKeepr/DoctrineReflectionBundle/Filter/AssociationPropertyTrait.php | 3+--
Msrc/PartKeepr/DoctrineReflectionBundle/Filter/Filter.php | 6++----
Msrc/PartKeepr/DoctrineReflectionBundle/Filter/SearchFilter.php | 3---
Msrc/PartKeepr/DoctrineReflectionBundle/Filter/Sorter.php | 1-
Msrc/PartKeepr/DoctrineReflectionBundle/Resources/views/model.js.twig | 2+-
Msrc/PartKeepr/DoctrineReflectionBundle/Services/ReflectionService.php | 70++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/PartKeepr/DoctrineReflectionBundle/Tests/AdvancedSearchFilterTest.php | 36++++++++++++------------------------
Msrc/PartKeepr/FootprintBundle/Entity/Footprint.php | 2+-
Msrc/PartKeepr/FootprintBundle/Entity/FootprintCategory.php | 4++--
Msrc/PartKeepr/FrontendBundle/Controller/IndexController.php | 5+++++
Asrc/PartKeepr/FrontendBundle/Resources/public/images/icons/octopart.png | 0
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/DataApplicator.js | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/SearchPanel.js | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/SearchWindow.js | 31+++++++++++++++++++++++++++++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartDistributorGrid.js | 28++++++++++++++++++++--------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartEditor.js | 31+++++++++++--------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartEditorWindow.js | 25+++++++++++++++++++++++--
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartManufacturerGrid.js | 22++++++----------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterGrid.js | 328++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterValueEditor.js | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Unit/UnitEditor.js | 2+-
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/AttachmentGrid.js | 9+++++----
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/CurrencyNumberField.js | 5++---
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/PartParameterComboBox.js | 37+++++++++++++++++++++++++------------
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitCombo.js | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitField.js | 412+++++++------------------------------------------------------------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitList.js | 38+++++++++++++++++++++++++++++++++++---
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/UnitComboBox.js | 3+++
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Data/store/CurrencyStore.js | 18++++++++++++++++++
Msrc/PartKeepr/FrontendBundle/Resources/public/js/ExtJS/Enhancements/Ext.form.field.ComboBox-associationSupport.js | 8+++++---
Msrc/PartKeepr/FrontendBundle/Resources/public/js/PartKeepr.js | 48+++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/PartKeepr/FrontendBundle/Resources/views/index.html.twig | 8+++++++-
Msrc/PartKeepr/ImageBundle/Tests/ImageControllerTest.php | 1-
Msrc/PartKeepr/ImportBundle/Configuration/BaseConfiguration.php | 26++++++++++++++------------
Msrc/PartKeepr/ImportBundle/Configuration/Configuration.php | 11+++++------
Msrc/PartKeepr/ImportBundle/Configuration/FieldConfiguration.php | 6++++--
Msrc/PartKeepr/ImportBundle/Configuration/ManyToOneConfiguration.php | 14+++++++-------
Msrc/PartKeepr/ImportBundle/Configuration/OneToManyConfiguration.php | 8+++-----
Msrc/PartKeepr/ImportBundle/Controller/ImportController.php | 11++++++-----
Msrc/PartKeepr/ImportBundle/Entity/ImportPreset.php | 4++--
Msrc/PartKeepr/ImportBundle/Service/ImporterService.php | 4++--
Msrc/PartKeepr/ManufacturerBundle/Entity/Manufacturer.php | 2+-
Asrc/PartKeepr/OctoPartBundle/Controller/DefaultController.php | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/OctoPartBundle/DependencyInjection/Configuration.php | 29+++++++++++++++++++++++++++++
Asrc/PartKeepr/OctoPartBundle/DependencyInjection/PartKeeprOctoPartExtension.php | 28++++++++++++++++++++++++++++
Asrc/PartKeepr/OctoPartBundle/PartKeeprOctoPartBundle.php | 9+++++++++
Asrc/PartKeepr/OctoPartBundle/Resources/config/services.xml | 12++++++++++++
Asrc/PartKeepr/OctoPartBundle/Services/OctoPartService.php | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/PartBundle/Action/PartPostAction.php | 4++--
Msrc/PartKeepr/PartBundle/Action/PartPutAction.php | 6+++---
Msrc/PartKeepr/PartBundle/Controller/PartController.php | 15+++++++++++++++
Msrc/PartKeepr/PartBundle/Entity/Part.php | 46+++++++++++++++++++++++++++++++++++++---------
Msrc/PartKeepr/PartBundle/Entity/PartCategory.php | 2+-
Msrc/PartKeepr/PartBundle/Entity/PartDistributor.php | 87++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/PartKeepr/PartBundle/Entity/PartMeasurementUnit.php | 2+-
Msrc/PartKeepr/PartBundle/Entity/PartParameter.php | 254++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/PartKeepr/PartBundle/Services/PartService.php | 7++++---
Msrc/PartKeepr/PartBundle/Tests/InternalPartNumberTest.php | 10+++++-----
Msrc/PartKeepr/SetupBundle/Services/ConfigSetupService.php | 80++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/PartKeepr/StorageLocationBundle/Entity/StorageLocationCategory.php | 12++++++++++--
Msrc/PartKeepr/SystemPreferenceBundle/Action/GetPreferencesAction.php | 2+-
Msrc/PartKeepr/SystemPreferenceBundle/Action/SetPreferenceAction.php | 2+-
Msrc/PartKeepr/SystemPreferenceBundle/DependencyInjection/Configuration.php | 4++--
Msrc/PartKeepr/SystemPreferenceBundle/DependencyInjection/PartKeeprSystemPreferenceExtension.php | 4++--
Msrc/PartKeepr/SystemPreferenceBundle/Entity/SystemPreference.php | 1+
Msrc/PartKeepr/SystemPreferenceBundle/Exceptions/SystemPreferenceNotFoundException.php | 1+
Msrc/PartKeepr/SystemPreferenceBundle/Service/SystemPreferenceService.php | 21++++++++++-----------
Msrc/PartKeepr/SystemPreferenceBundle/Tests/SystemPreferenceTest.php | 9+++++----
Msrc/PartKeepr/UnitBundle/Entity/Unit.php | 20+++++++++++++++-----
Msrc/PartKeepr/UnitBundle/Tests/Model/UnitTest.php | 2+-
Msrc/PartKeepr/UploadedFileBundle/Controller/TemporaryFileController.php | 9+++++----
92 files changed, 2332 insertions(+), 1078 deletions(-)

diff --git a/app/AppKernel.php b/app/AppKernel.php @@ -66,6 +66,7 @@ class AppKernel extends Kernel $bundles[] = new PartKeepr\SystemPreferenceBundle\PartKeeprSystemPreferenceBundle(); $bundles[] = new PartKeepr\ImportBundle\PartKeeprImportBundle(); $bundles[] = new PartKeepr\BatchJobBundle\PartKeeprBatchJobBundle(); + $bundles[] = new PartKeepr\OctoPartBundle\PartKeeprOctoPartBundle(); return array_merge($bundles, $this->getCustomBundles()); } @@ -73,6 +74,8 @@ class AppKernel extends Kernel /** * Loads the configuration for an environment. Also loads a custom configuration for non-text environments from * app/config_config_custom.yml if it exists. + * + * @param $loader LoaderInterface */ public function registerContainerConfiguration(LoaderInterface $loader) { diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php @@ -48,16 +48,17 @@ class Requirement * @param bool $fulfilled Whether the requirement is fulfilled * @param string $testMessage The message for testing the requirement * @param string $helpHtml The help text formatted in HTML for resolving the problem - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from + * HTML tags) * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement */ public function __construct($fulfilled, $testMessage, $helpHtml, $helpText = null, $optional = false) { - $this->fulfilled = (bool)$fulfilled; - $this->testMessage = (string)$testMessage; - $this->helpHtml = (string)$helpHtml; - $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string)$helpText; - $this->optional = (bool)$optional; + $this->fulfilled = (bool) $fulfilled; + $this->testMessage = (string) $testMessage; + $this->helpHtml = (string) $helpHtml; + $this->helpText = null === $helpText ? strip_tags($this->helpHtml) : (string) $helpText; + $this->optional = (bool) $optional; } /** @@ -122,15 +123,22 @@ class PhpIniRequirement extends Requirement * Constructor that initializes the requirement. * * @param string $cfgName The configuration name used for ini_get() - * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, - * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement - * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. - * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. - * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. - * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) - * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) - * @param bool $optional Whether this is only an optional recommendation not a mandatory requirement + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to + * true or false, or a callback function receiving the configuration value + * as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration + * option does not exist, i.e. ini_get() returns false. This is helpful for + * abandoned configs in later PHP versions or configs of an optional + * extension, like Suhosin. Example: You require a config to be true but + * PHP later removes this config and defaults it to true internally. + * @param string|null $testMessage The message for testing the requirement (when null and $evaluation is a + * boolean a default message is derived) + * @param string|null $helpHtml The help text formatted in HTML for resolving the problem (when null and + * $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. + * stripped from HTML tags) + * @param bool $optional Whether this is only an optional recommendation not a mandatory + * requirement */ public function __construct( $cfgName, @@ -208,7 +216,8 @@ class RequirementCollection implements IteratorAggregate * @param bool $fulfilled Whether the requirement is fulfilled * @param string $testMessage The message for testing the requirement * @param string $helpHtml The help text formatted in HTML for resolving the problem - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from + * HTML tags) */ public function addRequirement($fulfilled, $testMessage, $helpHtml, $helpText = null) { @@ -221,7 +230,8 @@ class RequirementCollection implements IteratorAggregate * @param bool $fulfilled Whether the recommendation is fulfilled * @param string $testMessage The message for testing the recommendation * @param string $helpHtml The help text formatted in HTML for resolving the problem - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from + * HTML tags) */ public function addRecommendation($fulfilled, $testMessage, $helpHtml, $helpText = null) { @@ -232,14 +242,20 @@ class RequirementCollection implements IteratorAggregate * Adds a mandatory requirement in form of a php.ini configuration. * * @param string $cfgName The configuration name used for ini_get() - * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, - * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement - * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. - * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. - * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. - * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) - * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to + * true or false, or a callback function receiving the configuration value + * as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration + * option does not exist, i.e. ini_get() returns false. This is helpful for + * abandoned configs in later PHP versions or configs of an optional + * extension, like Suhosin. Example: You require a config to be true but + * PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a + * boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and + * $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. + * stripped from HTML tags) */ public function addPhpIniRequirement( $cfgName, @@ -257,14 +273,20 @@ class RequirementCollection implements IteratorAggregate * Adds an optional recommendation in form of a php.ini configuration. * * @param string $cfgName The configuration name used for ini_get() - * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to true or false, - * or a callback function receiving the configuration value as parameter to determine the fulfillment of the requirement - * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration option does not exist, i.e. ini_get() returns false. - * This is helpful for abandoned configs in later PHP versions or configs of an optional extension, like Suhosin. - * Example: You require a config to be true but PHP later removes this config and defaults it to true internally. - * @param string $testMessage The message for testing the requirement (when null and $evaluation is a boolean a default message is derived) - * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and $evaluation is a boolean a default help is derived) - * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. stripped from HTML tags) + * @param bool|callback $evaluation Either a boolean indicating whether the configuration should evaluate to + * true or false, or a callback function receiving the configuration value + * as parameter to determine the fulfillment of the requirement + * @param bool $approveCfgAbsence If true the Requirement will be fulfilled even if the configuration + * option does not exist, i.e. ini_get() returns false. This is helpful for + * abandoned configs in later PHP versions or configs of an optional + * extension, like Suhosin. Example: You require a config to be true but + * PHP later removes this config and defaults it to true internally. + * @param string $testMessage The message for testing the requirement (when null and $evaluation is a + * boolean a default message is derived) + * @param string $helpHtml The help text formatted in HTML for resolving the problem (when null and + * $evaluation is a boolean a default help is derived) + * @param string|null $helpText The help text (when null, it will be inferred from $helpHtml, i.e. + * stripped from HTML tags) */ public function addPhpIniRecommendation( $cfgName, @@ -556,7 +578,7 @@ class SymfonyRequirements extends RequirementCollection ); } - $pcreVersion = defined('PCRE_VERSION') ? (float)PCRE_VERSION : null; + $pcreVersion = defined('PCRE_VERSION') ? (float) PCRE_VERSION : null; $this->addRequirement( null !== $pcreVersion, @@ -787,7 +809,7 @@ class SymfonyRequirements extends RequirementCollection case 'k': return $size * 1024; default: - return (int)$size; + return (int) $size; } } } diff --git a/app/check.php b/app/check.php @@ -100,17 +100,17 @@ function echo_style($style, $message) { // ANSI color codes $styles = [ - 'reset' => "\033[0m", - 'red' => "\033[31m", - 'green' => "\033[32m", - 'yellow' => "\033[33m", - 'error' => "\033[37;41m", + 'reset' => "\033[0m", + 'red' => "\033[31m", + 'green' => "\033[32m", + 'yellow' => "\033[33m", + 'error' => "\033[37;41m", 'success' => "\033[37;42m", - 'title' => "\033[34m", + 'title' => "\033[34m", ]; $supports = has_color_support(); - echo ($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); + echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : ''); } function echo_block($style, $title, $message) diff --git a/app/config/config_partkeepr.yml b/app/config/config_partkeepr.yml @@ -564,6 +564,17 @@ services: arguments: - { groups: [ "default" ] } + resource.part_parameter: + parent: "api.resource" + arguments: [ "PartKeepr\\PartBundle\\Entity\\PartParameter" ] + tags: [ { name: "api.resource" } ] + calls: + - method: "initNormalizationContext" + arguments: [ { groups: [ "default" ] } ] + - method: "initDenormalizationContext" + arguments: + - { groups: [ "default" ] } + resource.manufacturer: parent: "api.resource" arguments: [ "PartKeepr\\ManufacturerBundle\\Entity\\Manufacturer" ] diff --git a/app/config/parameters.php.dist b/app/config/parameters.php.dist @@ -243,3 +243,9 @@ $container->setParameter('partkeepr.filesystem.data_directory', '%kernel.root_di * Specifies if PartKeepr should check for non-running cronjobs */ $container->setParameter('partkeepr.cronjob.check', true); + +/** + * Specifies which API key PartKeepr should use to talk to OctoPart. You can get an API key by registering at + * https://octopart.com/api/home and then registering an application. + */ +$container->setParameter('partkeepr.octopart.apikey', ''); diff --git a/app/config/routing.yml b/app/config/routing.yml @@ -3,6 +3,11 @@ part_keepr_auth: type: annotation prefix: / +part_keepr_octopart: + resource: "@PartKeeprOctoPartBundle/Controller/" + type: annotation + prefix: / + part_keepr_core: resource: "@PartKeeprCoreBundle/Controller/" type: annotation diff --git a/composer.json b/composer.json @@ -72,7 +72,8 @@ "knplabs/knp-gaufrette-bundle": "^0.2.0", "phpseclib/phpseclib": "~1.0", "snc/redis-bundle": "^1.1", - "predis/predis": "^1.0" + "predis/predis": "^1.0", + "guzzlehttp/guzzle": "6.2.2" }, "require-dev": { "phpunit/phpunit": "5.2", diff --git a/composer.lock b/composer.lock @@ -4,8 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "aa5cf28286123a03dae894496c419002", - "content-hash": "aa2171afd724cfc8e1db99359a898e52", + "content-hash": "15d2b8b3ed791b4888ca0687288e6ca4", "packages": [ { "name": "atelierspierrot/famfamfam-silk-sprite", @@ -52,7 +51,7 @@ "icons", "sprite" ], - "time": "2013-10-12 21:09:52" + "time": "2013-10-12T21:09:52+00:00" }, { "name": "behat/transliterator", @@ -92,7 +91,7 @@ "slug", "transliterator" ], - "time": "2015-09-28 16:26:35" + "time": "2015-09-28T16:26:35+00:00" }, { "name": "brainbits/fugue-icons-bundle", @@ -135,7 +134,7 @@ "fugue", "icons" ], - "time": "2013-05-15 12:31:26" + "time": "2013-05-15T12:31:26+00:00" }, { "name": "composer/installers", @@ -230,7 +229,7 @@ "zend", "zikula" ], - "time": "2015-02-18 17:17:01" + "time": "2015-02-18T17:17:01+00:00" }, { "name": "doctrine/annotations", @@ -298,7 +297,7 @@ "docblock", "parser" ], - "time": "2015-08-31 12:32:49" + "time": "2015-08-31T12:32:49+00:00" }, { "name": "doctrine/cache", @@ -368,7 +367,7 @@ "cache", "caching" ], - "time": "2015-08-31 12:36:41" + "time": "2015-08-31T12:36:41+00:00" }, { "name": "doctrine/collections", @@ -434,7 +433,7 @@ "collections", "iterator" ], - "time": "2015-04-14 22:21:58" + "time": "2015-04-14T22:21:58+00:00" }, { "name": "doctrine/common", @@ -507,7 +506,7 @@ "persistence", "spl" ], - "time": "2015-08-31 13:00:22" + "time": "2015-08-31T13:00:22+00:00" }, { "name": "doctrine/data-fixtures", @@ -564,7 +563,7 @@ "keywords": [ "database" ], - "time": "2015-03-30 12:14:13" + "time": "2015-03-30T12:14:13+00:00" }, { "name": "doctrine/dbal", @@ -635,7 +634,7 @@ "persistence", "queryobject" ], - "time": "2015-09-16 16:29:33" + "time": "2015-09-16T16:29:33+00:00" }, { "name": "doctrine/doctrine-bundle", @@ -714,7 +713,7 @@ "orm", "persistence" ], - "time": "2016-01-10 17:21:44" + "time": "2016-01-10T17:21:44+00:00" }, { "name": "doctrine/doctrine-cache-bundle", @@ -798,7 +797,7 @@ "cache", "caching" ], - "time": "2014-11-28 09:43:36" + "time": "2014-11-28T09:43:36+00:00" }, { "name": "doctrine/doctrine-fixtures-bundle", @@ -855,7 +854,7 @@ "Fixture", "persistence" ], - "time": "2015-08-04 22:43:14" + "time": "2015-08-04T22:43:14+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", @@ -980,7 +979,7 @@ "singularize", "string" ], - "time": "2014-12-20 21:24:13" + "time": "2014-12-20T21:24:13+00:00" }, { "name": "doctrine/instantiator", @@ -1034,7 +1033,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "doctrine/lexer", @@ -1088,7 +1087,7 @@ "lexer", "parser" ], - "time": "2014-09-09 13:34:57" + "time": "2014-09-09T13:34:57+00:00" }, { "name": "doctrine/migrations", @@ -1233,7 +1232,7 @@ "database", "orm" ], - "time": "2016-01-05 21:34:58" + "time": "2016-01-05T21:34:58+00:00" }, { "name": "dunglas/api-bundle", @@ -1364,7 +1363,7 @@ "type", "validator" ], - "time": "2015-04-03 17:49:18" + "time": "2015-04-03T17:49:18+00:00" }, { "name": "escapestudios/wsse-authentication-bundle", @@ -1472,7 +1471,7 @@ "font", "icon" ], - "time": "2015-11-30 17:28:02" + "time": "2015-11-30T17:28:02+00:00" }, { "name": "fr3d/ldap-bundle", @@ -1616,7 +1615,7 @@ "keywords": [ "rest" ], - "time": "2015-12-29 16:02:50" + "time": "2015-12-29T16:02:50+00:00" }, { "name": "friendsofsymfony/user-bundle", @@ -1762,7 +1761,178 @@ "tree", "uploadable" ], - "time": "2015-09-28 16:39:27" + "time": "2015-09-28T16:39:27+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "6.2.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.3.1", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2016-10-08T15:01:37+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "time": "2016-06-24T23:00:38+00:00" }, { "name": "imagine/imagine", @@ -1819,7 +1989,7 @@ "image manipulation", "image processing" ], - "time": "2015-09-19 16:54:05" + "time": "2015-09-19T16:54:05+00:00" }, { "name": "incenteev/composer-parameter-handler", @@ -1870,7 +2040,7 @@ "keywords": [ "parameters management" ], - "time": "2015-11-10 17:04:01" + "time": "2015-11-10T17:04:01+00:00" }, { "name": "ircmaxell/password-compat", @@ -1912,7 +2082,7 @@ "hashing", "password" ], - "time": "2014-11-20 16:49:30" + "time": "2014-11-20T16:49:30+00:00" }, { "name": "jdorn/sql-formatter", @@ -1962,7 +2132,7 @@ "highlight", "sql" ], - "time": "2014-01-12 16:20:24" + "time": "2014-01-12T16:20:24+00:00" }, { "name": "jms/translation-bundle", @@ -2118,7 +2288,7 @@ "filesystem", "media" ], - "time": "2015-05-26 08:25:40" + "time": "2015-05-26T08:25:40+00:00" }, { "name": "knplabs/knp-gaufrette-bundle", @@ -2176,7 +2346,7 @@ "filesystem", "media" ], - "time": "2015-09-18 12:09:25" + "time": "2015-09-18T12:09:25+00:00" }, { "name": "kriswallsmith/assetic", @@ -2254,7 +2424,7 @@ "compression", "minification" ], - "time": "2015-08-31 19:07:16" + "time": "2015-08-31T19:07:16+00:00" }, { "name": "michelf/php-markdown", @@ -2305,7 +2475,7 @@ "keywords": [ "markdown" ], - "time": "2015-03-01 12:03:08" + "time": "2015-03-01T12:03:08+00:00" }, { "name": "monolog/monolog", @@ -2381,7 +2551,7 @@ "logging", "psr-3" ], - "time": "2015-08-31 09:17:37" + "time": "2015-08-31T09:17:37+00:00" }, { "name": "moontoast/math", @@ -2417,7 +2587,7 @@ "bcmath", "math" ], - "time": "2013-01-19 17:42:34" + "time": "2013-01-19T17:42:34+00:00" }, { "name": "nelmio/api-doc-bundle", @@ -2585,7 +2755,7 @@ "parser", "php" ], - "time": "2013-08-25 17:11:40" + "time": "2013-08-25T17:11:40+00:00" }, { "name": "ocramius/proxy-manager", @@ -2648,7 +2818,7 @@ "proxy pattern", "service proxies" ], - "time": "2015-08-09 04:28:19" + "time": "2015-08-09T04:28:19+00:00" }, { "name": "paragonie/random_compat", @@ -2696,7 +2866,7 @@ "pseudorandom", "random" ], - "time": "2015-12-01 02:52:15" + "time": "2015-12-01T02:52:15+00:00" }, { "name": "partkeepr/extjs6", @@ -2833,7 +3003,7 @@ "reflection", "static analysis" ], - "time": "2014-11-14 11:43:04" + "time": "2014-11-14T11:43:04+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -2882,7 +3052,7 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2015-02-03 12:10:50" + "time": "2015-02-03T12:10:50+00:00" }, { "name": "phpseclib/phpseclib", @@ -2980,7 +3150,7 @@ "x.509", "x509" ], - "time": "2016-01-18 17:07:12" + "time": "2016-01-18T17:07:12+00:00" }, { "name": "predis/predis", @@ -3030,7 +3200,57 @@ "predis", "redis" ], - "time": "2015-07-30 18:34:15" + "time": "2015-07-30T18:34:15+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", @@ -3068,7 +3288,7 @@ "psr", "psr-3" ], - "time": "2012-12-21 11:40:51" + "time": "2012-12-21T11:40:51+00:00" }, { "name": "ramsey/uuid", @@ -3138,7 +3358,7 @@ "identifier", "uuid" ], - "time": "2015-12-17 15:21:44" + "time": "2015-12-17T15:21:44+00:00" }, { "name": "reputation-vip/composer-assets-installer", @@ -3178,7 +3398,7 @@ "email": "ap@reputationvip.com" } ], - "time": "2015-03-09 10:29:42" + "time": "2015-03-09T10:29:42+00:00" }, { "name": "sensio/distribution-bundle", @@ -3238,7 +3458,7 @@ "configuration", "distribution" ], - "time": "2016-02-12 16:21:25" + "time": "2016-02-12T16:21:25+00:00" }, { "name": "sensio/framework-extra-bundle", @@ -3293,7 +3513,7 @@ "annotations", "controllers" ], - "time": "2015-12-18 17:39:27" + "time": "2015-12-18T17:39:27+00:00" }, { "name": "sensiolabs/security-checker", @@ -3337,7 +3557,7 @@ } ], "description": "A security checker for your composer.lock", - "time": "2015-08-11 12:11:25" + "time": "2015-08-11T12:11:25+00:00" }, { "name": "snc/redis-bundle", @@ -3397,7 +3617,7 @@ "redis", "symfony" ], - "time": "2016-01-21 18:29:37" + "time": "2016-01-21T18:29:37+00:00" }, { "name": "sonata-project/exporter", @@ -3460,7 +3680,7 @@ "export", "xls" ], - "time": "2015-11-25 09:33:45" + "time": "2015-11-25T09:33:45+00:00" }, { "name": "stof/doctrine-extensions-bundle", @@ -3574,7 +3794,7 @@ "mail", "mailer" ], - "time": "2015-06-06 14:19:39" + "time": "2015-06-06T14:19:39+00:00" }, { "name": "symfony/assetic-bundle", @@ -3644,7 +3864,7 @@ "compression", "minification" ], - "time": "2015-11-17 09:45:47" + "time": "2015-11-17T09:45:47+00:00" }, { "name": "symfony/monolog-bundle", @@ -3703,7 +3923,7 @@ "log", "logging" ], - "time": "2015-11-17 10:02:29" + "time": "2015-11-17T10:02:29+00:00" }, { "name": "symfony/polyfill-intl-icu", @@ -3758,7 +3978,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -3814,7 +4034,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/polyfill-php54", @@ -3872,7 +4092,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/polyfill-php55", @@ -3928,7 +4148,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/polyfill-php56", @@ -3984,7 +4204,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/polyfill-php70", @@ -4043,7 +4263,7 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/polyfill-util", @@ -4095,7 +4315,7 @@ "polyfill", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2015-11-04T20:28:58+00:00" }, { "name": "symfony/security-acl", @@ -4156,7 +4376,7 @@ ], "description": "Symfony Security Component - ACL (Access Control List)", "homepage": "https://symfony.com", - "time": "2015-12-28 09:39:09" + "time": "2015-12-28T09:39:09+00:00" }, { "name": "symfony/swiftmailer-bundle", @@ -4213,7 +4433,7 @@ ], "description": "Symfony SwiftmailerBundle", "homepage": "http://symfony.com", - "time": "2016-01-15 16:41:20" + "time": "2016-01-15T16:41:20+00:00" }, { "name": "symfony/symfony", @@ -4346,7 +4566,7 @@ "keywords": [ "framework" ], - "time": "2016-01-14 12:01:11" + "time": "2016-01-14T12:01:11+00:00" }, { "name": "twig/extensions", @@ -4398,7 +4618,7 @@ "i18n", "text" ], - "time": "2015-08-22 16:38:35" + "time": "2015-08-22T16:38:35+00:00" }, { "name": "twig/twig", @@ -4459,7 +4679,7 @@ "keywords": [ "templating" ], - "time": "2015-11-05 12:49:06" + "time": "2015-11-05T12:49:06+00:00" }, { "name": "willdurand/jsonp-callback-validator", @@ -4499,7 +4719,7 @@ } ], "description": "JSONP callback validator.", - "time": "2014-01-20 22:35:06" + "time": "2014-01-20T22:35:06+00:00" }, { "name": "willdurand/negotiation", @@ -4548,7 +4768,7 @@ "header", "negotiation" ], - "time": "2015-10-01 07:42:40" + "time": "2015-10-01T07:42:40+00:00" }, { "name": "zendframework/zend-code", @@ -4601,7 +4821,7 @@ "code", "zf2" ], - "time": "2015-07-21 22:40:59" + "time": "2015-07-21T22:40:59+00:00" }, { "name": "zendframework/zend-eventmanager", @@ -4646,7 +4866,7 @@ "eventmanager", "zf2" ], - "time": "2015-07-16 19:00:49" + "time": "2015-07-16T19:00:49+00:00" }, { "name": "zendframework/zend-hydrator", @@ -4702,7 +4922,7 @@ "hydrator", "zf2" ], - "time": "2015-09-17 14:06:43" + "time": "2015-09-17T14:06:43+00:00" }, { "name": "zendframework/zend-ldap", @@ -4754,7 +4974,7 @@ "ldap", "zf2" ], - "time": "2015-06-03 15:32:02" + "time": "2015-06-03T15:32:02+00:00" }, { "name": "zendframework/zend-stdlib", @@ -4812,7 +5032,7 @@ "stdlib", "zf2" ], - "time": "2015-09-25 04:06:33" + "time": "2015-09-25T04:06:33+00:00" } ], "packages-dev": [ @@ -4826,7 +5046,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/codeclimate/php-test-reporter/zipball/9ec2098c9e9839840176c46d9cdab3a57c26ca1c", + "url": "https://api.github.com/repos/codeclimate/php-test-reporter/zipball/02790d733f9eb43d0a5981962c41846092fb387f", "reference": "02790d733f9eb43d0a5981962c41846092fb387f", "shasum": "" }, @@ -4941,7 +5161,7 @@ "export", "import" ], - "time": "2015-04-21 14:06:20" + "time": "2015-04-21T14:06:20+00:00" }, { "name": "guzzle/guzzle", @@ -5036,7 +5256,8 @@ "rest", "web service" ], - "time": "2015-03-18 18:23:50" + "abandoned": "guzzlehttp/guzzle", + "time": "2015-03-18T18:23:50+00:00" }, { "name": "liip/functional-test-bundle", @@ -5107,7 +5328,7 @@ "keywords": [ "Symfony2" ], - "time": "2016-02-08 10:14:11" + "time": "2016-02-08T10:14:11+00:00" }, { "name": "myclabs/deep-copy", @@ -5149,7 +5370,7 @@ "object", "object graph" ], - "time": "2015-11-07 22:20:37" + "time": "2015-11-07T22:20:37+00:00" }, { "name": "phpspec/prophecy", @@ -5209,7 +5430,7 @@ "spy", "stub" ], - "time": "2015-08-13 10:07:40" + "time": "2015-08-13T10:07:40+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5272,7 +5493,7 @@ "testing", "xunit" ], - "time": "2016-02-18 07:31:12" + "time": "2016-02-18T07:31:12+00:00" }, { "name": "phpunit/php-file-iterator", @@ -5319,7 +5540,7 @@ "filesystem", "iterator" ], - "time": "2015-06-21 13:08:43" + "time": "2015-06-21T13:08:43+00:00" }, { "name": "phpunit/php-text-template", @@ -5360,7 +5581,7 @@ "keywords": [ "template" ], - "time": "2015-06-21 13:50:34" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", @@ -5401,7 +5622,7 @@ "keywords": [ "timer" ], - "time": "2015-06-21 08:01:12" + "time": "2015-06-21T08:01:12+00:00" }, { "name": "phpunit/php-token-stream", @@ -5450,7 +5671,7 @@ "keywords": [ "tokenizer" ], - "time": "2015-09-15 10:49:45" + "time": "2015-09-15T10:49:45+00:00" }, { "name": "phpunit/phpunit", @@ -5524,7 +5745,7 @@ "testing", "xunit" ], - "time": "2016-02-04 13:12:44" + "time": "2016-02-04T13:12:44+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -5580,7 +5801,7 @@ "mock", "xunit" ], - "time": "2015-12-08 08:47:06" + "time": "2015-12-08T08:47:06+00:00" }, { "name": "satooshi/php-coveralls", @@ -5648,7 +5869,7 @@ "github", "test" ], - "time": "2013-05-04 08:07:33" + "time": "2013-05-04T08:07:33+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -5693,7 +5914,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13 06:45:14" + "time": "2016-02-13T06:45:14+00:00" }, { "name": "sebastian/comparator", @@ -5757,7 +5978,7 @@ "compare", "equality" ], - "time": "2015-07-26 15:48:44" + "time": "2015-07-26T15:48:44+00:00" }, { "name": "sebastian/diff", @@ -5809,7 +6030,7 @@ "keywords": [ "diff" ], - "time": "2015-02-22 15:13:53" + "time": "2015-02-22T15:13:53+00:00" }, { "name": "sebastian/environment", @@ -5859,7 +6080,7 @@ "environment", "hhvm" ], - "time": "2015-08-03 06:14:51" + "time": "2015-08-03T06:14:51+00:00" }, { "name": "sebastian/exporter", @@ -5925,7 +6146,7 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2015-06-21T07:55:53+00:00" }, { "name": "sebastian/global-state", @@ -5976,7 +6197,7 @@ "keywords": [ "global state" ], - "time": "2014-10-06 09:23:50" + "time": "2014-10-06T09:23:50+00:00" }, { "name": "sebastian/recursion-context", @@ -6029,7 +6250,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-06-21 08:04:50" + "time": "2015-06-21T08:04:50+00:00" }, { "name": "sebastian/resource-operations", @@ -6071,7 +6292,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28 20:34:47" + "time": "2015-07-28T20:34:47+00:00" }, { "name": "sebastian/version", @@ -6114,7 +6335,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-02-04 12:56:52" + "time": "2016-02-04T12:56:52+00:00" }, { "name": "sensio/generator-bundle", @@ -6162,7 +6383,7 @@ } ], "description": "This bundle generates code for you", - "time": "2015-03-17 06:36:52" + "time": "2015-03-17T06:36:52+00:00" } ], "aliases": [], diff --git a/src/PartKeepr/BatchJobBundle/Action/ExecuteBatchJobAction.php b/src/PartKeepr/BatchJobBundle/Action/ExecuteBatchJobAction.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\BatchJobBundle\Action; use Doctrine\ORM\EntityManager; @@ -8,7 +9,6 @@ use Dunglas\ApiBundle\Api\IriConverter; use Dunglas\ApiBundle\Exception\RuntimeException; use Dunglas\ApiBundle\Model\DataProviderInterface; use PartKeepr\BatchJobBundle\Entity\BatchJob; -use PartKeepr\BatchJobBundle\Entity\BatchJobQueryField; use PartKeepr\CategoryBundle\Exception\RootNodeNotFoundException; use PartKeepr\DoctrineReflectionBundle\Filter\AdvancedSearchFilter; use PartKeepr\DoctrineReflectionBundle\Services\ReflectionService; @@ -59,7 +59,7 @@ class ExecuteBatchJobAction } /** - * Executes a batch action + * Executes a batch action. * * @param Request $request * @param string $id @@ -73,7 +73,7 @@ class ExecuteBatchJobAction list($resourceType) = $this->extractAttributes($request); /** - * @var $batchJob BatchJob + * @var BatchJob */ $batchJob = $this->getItem($this->dataProvider, $resourceType, $id); @@ -122,7 +122,6 @@ class ExecuteBatchJobAction } } - $updateFieldConfigs[] = $updateFieldConfig; } @@ -139,7 +138,6 @@ class ExecuteBatchJobAction foreach ($data as $item) { foreach ($updateFieldConfigs as $updateField) { - try { $value = $this->iriConverter->getItemFromIri($updateField->value); } catch (\Exception $e) { diff --git a/src/PartKeepr/BatchJobBundle/DependencyInjection/PartKeeprBatchJobExtension.php b/src/PartKeepr/BatchJobBundle/DependencyInjection/PartKeeprBatchJobExtension.php @@ -1,6 +1,5 @@ <?php - namespace PartKeepr\BatchJobBundle\DependencyInjection; use Symfony\Component\Config\FileLocator; @@ -10,7 +9,6 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; class PartKeeprBatchJobExtension extends Extension { - /** * {@inheritdoc} */ diff --git a/src/PartKeepr/BatchJobBundle/Entity/BatchJob.php b/src/PartKeepr/BatchJobBundle/Entity/BatchJob.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\BatchJobBundle\Entity; use Doctrine\Common\Collections\ArrayCollection; @@ -24,7 +25,7 @@ class BatchJob extends BaseEntity private $name; /** - * Holds the batch job query fields + * Holds the batch job query fields. * * @ORM\OneToMany(targetEntity="PartKeepr\BatchJobBundle\Entity\BatchJobQueryField",mappedBy="batchJob",cascade={"persist", "remove"}, orphanRemoval=true) * @Groups({"default"}) @@ -34,7 +35,7 @@ class BatchJob extends BaseEntity private $batchJobQueryFields; /** - * Batch Job Update Fields + * Batch Job Update Fields. * * @ORM\OneToMany(targetEntity="PartKeepr\BatchJobBundle\Entity\BatchJobUpdateField",mappedBy="batchJob",cascade={"persist", "remove"}, orphanRemoval=true) * @Groups({"default"}) @@ -44,10 +45,12 @@ class BatchJob extends BaseEntity private $batchJobUpdateFields; /** - * Holds the base entity to query against + * Holds the base entity to query against. + * * @ORM\Column() * * @Groups({"default"}) + * * @var string */ private $baseEntity; @@ -63,7 +66,7 @@ class BatchJob extends BaseEntity */ public function getBatchJobUpdateFields() { - return $this->batchJobUpdateFields; + return $this->batchJobUpdateFields->getValues(); } /** @@ -87,7 +90,7 @@ class BatchJob extends BaseEntity */ public function getBatchJobQueryFields() { - return $this->batchJobQueryFields; + return $this->batchJobQueryFields->getValues(); } /** diff --git a/src/PartKeepr/BatchJobBundle/Entity/BatchJobQueryField.php b/src/PartKeepr/BatchJobBundle/Entity/BatchJobQueryField.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\BatchJobBundle\Entity; use Doctrine\ORM\Mapping as ORM; @@ -22,46 +23,51 @@ class BatchJobQueryField extends BaseEntity private $batchJob = null; /** - * The field name to query + * The field name to query. * * @ORM\Column(length=255) * @Groups({"default"}) + * * @var string */ private $property; /** - * The operator to use + * The operator to use. * * @ORM\Column(length=64) * @Groups({"default"}) + * * @var string */ private $operator; /** - * The value. May be an array if the operator is IN + * The value. May be an array if the operator is IN. * * @ORM\Column(type="text") * @Groups({"default"}) + * * @var string */ private $value; /** - * The description + * The description. * * @ORM\Column(type="text") * @Groups({"default"}) + * * @var string */ private $description; /** - * Defines if the value is dynamic (=the user gets prompted upon running the batch job which value to use) + * Defines if the value is dynamic (=the user gets prompted upon running the batch job which value to use). * * @Groups({"default"}) * @ORM\Column(type="boolean") + * * @var bool */ private $dynamic; diff --git a/src/PartKeepr/BatchJobBundle/Entity/BatchJobUpdateField.php b/src/PartKeepr/BatchJobBundle/Entity/BatchJobUpdateField.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\BatchJobBundle\Entity; use Doctrine\ORM\Mapping as ORM; @@ -6,7 +7,7 @@ use PartKeepr\CoreBundle\Entity\BaseEntity; use Symfony\Component\Serializer\Annotation\Groups; /** - * Represents a batch job update field + * Represents a batch job update field. * * @ORM\Entity */ @@ -22,37 +23,41 @@ class BatchJobUpdateField extends BaseEntity private $batchJob = null; /** - * The field name to update + * The field name to update. * * @ORM\Column(length=255) * @Groups({"default"}) + * * @var string */ private $property; /** - * The value to set + * The value to set. * * @ORM\Column(type="text") * @Groups({"default"}) + * * @var string */ private $value; /** - * The description + * The description. * * @ORM\Column(type="text") * @Groups({"default"}) + * * @var string */ private $description; /** - * Defines if the value is dynamic (=the user gets prompted upon running the batch job which value to use) + * Defines if the value is dynamic (=the user gets prompted upon running the batch job which value to use). * * @Groups({"default"}) * @ORM\Column(type="boolean") + * * @var bool */ private $dynamic; diff --git a/src/PartKeepr/BatchJobBundle/PartKeeprBatchJobBundle.php b/src/PartKeepr/BatchJobBundle/PartKeeprBatchJobBundle.php @@ -1,9 +1,9 @@ <?php + namespace PartKeepr\BatchJobBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; class PartKeeprBatchJobBundle extends Bundle { - } diff --git a/src/PartKeepr/CoreBundle/Controller/DefaultController.php b/src/PartKeepr/CoreBundle/Controller/DefaultController.php @@ -5,6 +5,7 @@ namespace PartKeepr\CoreBundle\Controller; use FOS\RestBundle\Controller\Annotations\View; use FOS\RestBundle\Controller\FOSRestController; use Sensio\Bundle\FrameworkExtraBundle\Configuration as Routing; +use Symfony\Component\Intl\Intl; class DefaultController extends FOSRestController { @@ -49,4 +50,26 @@ class DefaultController extends FOSRestController 'disk_used' => $this->get('partkeepr_systemservice')->getUsedDiskSpace(), ]; } + + /** + * Returns the available currencies. + * + * @Routing\Route("/api/currencies", defaults={"method" = "get","_format" = "json"}) + * @View() + */ + public function getCurrenciesAction() + { + $currencyData = Intl::getCurrencyBundle()->getCurrencyNames(); + + $currencies = []; + foreach ($currencyData as $code => $name) { + $currencies[] = [ + "code" => $code, + "name" => $name, + "symbol" => Intl::getCurrencyBundle()->getCurrencySymbol($code), + ]; + } + + return $currencies; + } } diff --git a/src/PartKeepr/CoreBundle/Services/VersionService.php b/src/PartKeepr/CoreBundle/Services/VersionService.php @@ -44,24 +44,31 @@ class VersionService /** * Extracts the current commit from GIT. + * * @return string */ - public function extractGITCommit () { + public function extractGITCommit() + { $result = shell_exec("git rev-parse HEAD"); + return trim($result); } /** * Extracts the current short commit from GIT. + * * @return string */ - public function extractShortGITCommit () { + public function extractShortGITCommit() + { $result = shell_exec("git rev-parse --short HEAD"); + return trim($result); } /** - * Sets the version string + * Sets the version string. + * * @param $version string The version */ public function setVersion($version) @@ -70,7 +77,8 @@ class VersionService } /** - * Returns the current version string + * Returns the current version string. + * * @return string The version */ public function getVersion() @@ -83,9 +91,10 @@ class VersionService $this->versionURI = $versionURI; } - public function getCanonicalVersion () { + public function getCanonicalVersion() + { if ($this->getVersion() === '{V_GIT}') { - return 'GIT development version Commit '. $this->extractGITCommit() . " Short Commit " . $this->extractShortGITCommit(); + return 'GIT development version Commit '.$this->extractGITCommit()." Short Commit ".$this->extractShortGITCommit(); } else { return $this->getVersion(); } @@ -126,7 +135,7 @@ class VersionService } /** - * Returns the latest version information from partkeepr.org + * Returns the latest version information from partkeepr.org. * * @return array|bool */ diff --git a/src/PartKeepr/CoreBundle/Tests/SetupWebTestCase.php b/src/PartKeepr/CoreBundle/Tests/SetupWebTestCase.php @@ -5,6 +5,7 @@ * Date: 10/9/15 * Time: 7:43 PM. */ + namespace PartKeepr\CoreBundle\Tests; /** diff --git a/src/PartKeepr/CoreBundle/Tests/WebTestCase.php b/src/PartKeepr/CoreBundle/Tests/WebTestCase.php @@ -5,6 +5,7 @@ * Date: 10/9/15 * Time: 7:43 PM. */ + namespace PartKeepr\CoreBundle\Tests; /** diff --git a/src/PartKeepr/DoctrineReflectionBundle/Annotation/ByReference.php b/src/PartKeepr/DoctrineReflectionBundle/Annotation/ByReference.php @@ -5,7 +5,7 @@ namespace PartKeepr\DoctrineReflectionBundle\Annotation; use Doctrine\ORM\Mapping\Annotation; /** - * Tells the system to pass the association by reference and not by value + * Tells the system to pass the association by reference and not by value. * * @Annotation * @Target("PROPERTY") diff --git a/src/PartKeepr/DoctrineReflectionBundle/Filter/AdvancedSearchFilter.php b/src/PartKeepr/DoctrineReflectionBundle/Filter/AdvancedSearchFilter.php @@ -104,17 +104,16 @@ class AdvancedSearchFilter extends AbstractFilter { foreach ($filters as $filter) { /** - * @var Filter $filter + * @var Filter */ $queryBuilder->andWhere( $this->getFilterExpression($queryBuilder, $filter) ); - } foreach ($sorters as $sorter) { /** - * @var Sorter $sorter + * @var Sorter */ if ($sorter->getAssociation() !== null) { // Pull in associations @@ -215,7 +214,7 @@ class AdvancedSearchFilter extends AbstractFilter foreach ($filter->getSubFilters() as $subFilter) { /** - * @var $subFilter Filter + * @var Filter */ if ($subFilter->getAssociation() !== null) { $this->addJoins($queryBuilder, $subFilter); @@ -224,7 +223,6 @@ class AdvancedSearchFilter extends AbstractFilter $subFilterExpressions[] = $this->getFilterExpression($queryBuilder, $subFilter); } - if ($filter->getType() == Filter::TYPE_AND) { return call_user_func_array([$queryBuilder->expr(), "andX"], $subFilterExpressions); } else { @@ -239,7 +237,6 @@ class AdvancedSearchFilter extends AbstractFilter $alias = 'o.'.$filter->getProperty(); } - if (strtolower($filter->getOperator()) == Filter::OPERATOR_IN) { if (!is_array($filter->getValue())) { throw new \Exception('Value needs to be an array for the IN operator'); @@ -317,7 +314,6 @@ class AdvancedSearchFilter extends AbstractFilter $sorters = []; - if (is_array($sorterData)) { foreach ($sorterData as $sorter) { $sorters[] = $this->extractJSONSorters($sorter); @@ -326,7 +322,6 @@ class AdvancedSearchFilter extends AbstractFilter $sorters[] = $this->extractJSONSorters($sorterData); } - return ['filters' => $filters, 'sorters' => $sorters]; } @@ -356,8 +351,7 @@ class AdvancedSearchFilter extends AbstractFilter * * @return Filter */ - private - function extractJSONFilters( + private function extractJSONFilters( $data ) { $filter = new Filter(); @@ -418,8 +412,7 @@ class AdvancedSearchFilter extends AbstractFilter * * @return Sorter A Sorter object */ - private - function extractJSONSorters( + private function extractJSONSorters( $data ) { $sorter = new Sorter(); diff --git a/src/PartKeepr/DoctrineReflectionBundle/Filter/AssociationPropertyInterface.php b/src/PartKeepr/DoctrineReflectionBundle/Filter/AssociationPropertyInterface.php @@ -4,8 +4,11 @@ namespace PartKeepr\DoctrineReflectionBundle\Filter; interface AssociationPropertyInterface { - public function getProperty (); + public function getProperty(); + public function setProperty($property); - public function getAssociation (); + + public function getAssociation(); + public function setAssociation($association); -}- \ No newline at end of file +} diff --git a/src/PartKeepr/DoctrineReflectionBundle/Filter/AssociationPropertyTrait.php b/src/PartKeepr/DoctrineReflectionBundle/Filter/AssociationPropertyTrait.php @@ -44,4 +44,4 @@ trait AssociationPropertyTrait { $this->association = $association; } -}- \ No newline at end of file +} diff --git a/src/PartKeepr/DoctrineReflectionBundle/Filter/Filter.php b/src/PartKeepr/DoctrineReflectionBundle/Filter/Filter.php @@ -1,6 +1,6 @@ <?php -namespace PartKeepr\DoctrineReflectionBundle\Filter; +namespace PartKeepr\DoctrineReflectionBundle\Filter; class Filter implements AssociationPropertyInterface { @@ -35,7 +35,7 @@ class Filter implements AssociationPropertyInterface ]; /** - * The type + * The type. * * @var string */ @@ -58,7 +58,6 @@ class Filter implements AssociationPropertyInterface */ private $subFilters; - public function __construct($type = self::TYPE_AND) { $this->setType($type); @@ -143,5 +142,4 @@ class Filter implements AssociationPropertyInterface { return count($this->subFilters) > 0; } - } diff --git a/src/PartKeepr/DoctrineReflectionBundle/Filter/SearchFilter.php b/src/PartKeepr/DoctrineReflectionBundle/Filter/SearchFilter.php @@ -1,10 +1,7 @@ <?php - namespace PartKeepr\DoctrineReflectionBundle\Filter; - class SearchFilter { - } diff --git a/src/PartKeepr/DoctrineReflectionBundle/Filter/Sorter.php b/src/PartKeepr/DoctrineReflectionBundle/Filter/Sorter.php @@ -4,7 +4,6 @@ namespace PartKeepr\DoctrineReflectionBundle\Filter; class Sorter implements AssociationPropertyInterface { - use AssociationPropertyTrait; /** diff --git a/src/PartKeepr/DoctrineReflectionBundle/Resources/views/model.js.twig b/src/PartKeepr/DoctrineReflectionBundle/Resources/views/model.js.twig @@ -5,7 +5,7 @@ Ext.define('{{ className }}', { idProperty: "@id", fields: [ {% for field in fields %} - { name: '{{ field.name|raw }}'{% if field.type%}, type: '{{ field.type }}'{% endif %}{% if not field.persist %}, persist: false{% endif %}{% if field.validators %}, validators: {{ field.validators|raw }}{% endif %}}{% if not loop.last %},{% endif %} + { name: '{{ field.name|raw }}'{% if field.type%}, type: '{{ field.type }}'{% endif %}{% if field.nullable%}, allowNull: true{% endif %}{% if not field.persist %}, persist: false{% endif %}{% if field.validators %}, validators: {{ field.validators|raw }}{% endif %}}{% if not loop.last %},{% endif %} {% endfor %} {% if associations.MANY_TO_ONE|length > 0 %} diff --git a/src/PartKeepr/DoctrineReflectionBundle/Services/ReflectionService.php b/src/PartKeepr/DoctrineReflectionBundle/Services/ReflectionService.php @@ -76,10 +76,10 @@ class ReflectionService $associationMappings = $this->getDatabaseAssociationMappings($cm, $bTree); $renderParams = [ - 'fields' => $fieldMappings, + 'fields' => $fieldMappings, 'associations' => $associationMappings, - 'className' => $this->convertPHPToExtJSClassName($entity), - 'parentClass' => $parentClass, + 'className' => $this->convertPHPToExtJSClassName($entity), + 'parentClass' => $parentClass, ]; $targetService = $this->reader->getClassAnnotation( @@ -161,7 +161,7 @@ class ReflectionService foreach ($propertyAnnotations as $propertyAnnotation) { $filter = "Symfony\\Component\\Validator\\Constraints\\NotNull"; - if (substr(get_class($propertyAnnotation),0, strlen($filter)) === $filter) { + if (substr(get_class($propertyAnnotation), 0, strlen($filter)) === $filter) { $nullable = false; } } @@ -175,11 +175,11 @@ class ReflectionService $byReference = true; } $associationMappings[$associationType][] = [ - 'name' => $association['fieldName'], - 'nullable' => $nullable, - 'target' => $this->convertPHPToExtJSClassName($association['targetEntity']), + 'name' => $association['fieldName'], + 'nullable' => $nullable, + 'target' => $this->convertPHPToExtJSClassName($association['targetEntity']), 'byReference' => $byReference, - 'getter' => $getter, + 'getter' => $getter, 'getterField' => $getterField, ]; } @@ -255,7 +255,6 @@ class ReflectionService $fieldMappings = []; $fields = $cm->getFieldNames(); - foreach ($fields as $field) { $currentMapping = $cm->getFieldMapping($field); @@ -266,11 +265,16 @@ class ReflectionService $currentMapping['type'] = 'string'; } + if (!array_key_exists("nullable", $currentMapping)) { + $currentMapping["nullable"] = false; + } + $fieldMappings[] = [ - 'name' => $currentMapping['fieldName'], - 'type' => $this->getExtJSFieldMapping($currentMapping['type']), + 'name' => $currentMapping['fieldName'], + 'type' => $this->getExtJSFieldMapping($currentMapping['type']), + 'nullable' => $currentMapping['nullable'], 'validators' => json_encode($asserts), - 'persist' => $this->allowPersist($cm, $field) + 'persist' => $this->allowPersist($cm, $field), ]; } @@ -316,51 +320,53 @@ class ReflectionService return 'undefined'; } - public function getExtJSAssertMapping (Constraint $assert) { + public function getExtJSAssertMapping(Constraint $assert) + { switch (get_class($assert)) { case "Symfony\\Component\\Validator\\Constraints\\NotBlank": /** - * @var $assert NotBlank + * @var NotBlank */ - return [ "type" => "presence", "message" => $assert->message]; + return ["type" => "presence", "message" => $assert->message]; break; default: return false; } } - public function getExtJSAssertMappings (ClassMetadata $cm, $field) { + public function getExtJSAssertMappings(ClassMetadata $cm, $field) + { $asserts = []; $propertyAnnotations = $this->reader->getPropertyAnnotations($cm->getReflectionProperty($field)); - foreach ($propertyAnnotations as $propertyAnnotation) { - $filter = "Symfony\\Component\\Validator\\Constraints\\"; + foreach ($propertyAnnotations as $propertyAnnotation) { + $filter = "Symfony\\Component\\Validator\\Constraints\\"; - if (substr(get_class($propertyAnnotation),0, strlen($filter)) === $filter) { - $assertMapping = $this->getExtJSAssertMapping($propertyAnnotation); + if (substr(get_class($propertyAnnotation), 0, strlen($filter)) === $filter) { + $assertMapping = $this->getExtJSAssertMapping($propertyAnnotation); - if ($assertMapping !== false) { - $asserts[] = $assertMapping; - } + if ($assertMapping !== false) { + $asserts[] = $assertMapping; } } + } - return $asserts; + return $asserts; } - public function allowPersist (ClassMetadata $cm, $field) { - - $groupsAnnotation = $this->reader->getPropertyAnnotation( + public function allowPersist(ClassMetadata $cm, $field) + { + $groupsAnnotation = $this->reader->getPropertyAnnotation( $cm->getReflectionProperty($field), 'Symfony\Component\Serializer\Annotation\Groups' ); - if ($groupsAnnotation !== null) { - if (in_array("readonly", $groupsAnnotation->getGroups())) { - return false; - } - + if ($groupsAnnotation !== null) { + if (in_array("readonly", $groupsAnnotation->getGroups())) { + return false; + } } + return true; } diff --git a/src/PartKeepr/DoctrineReflectionBundle/Tests/AdvancedSearchFilterTest.php b/src/PartKeepr/DoctrineReflectionBundle/Tests/AdvancedSearchFilterTest.php @@ -27,7 +27,6 @@ class AdvancedSearchFilterTest extends WebTestCase )->getReferenceRepository(); } - public function testEqualFilter() { $client = static::makeClient(true); @@ -36,11 +35,10 @@ class AdvancedSearchFilterTest extends WebTestCase [ "property" => "storageLocation.name", "operator" => "=", - "value" => "test", + "value" => "test", ], ]; - $client->request( 'GET', "/api/parts?filter=".json_encode($filter), @@ -49,7 +47,6 @@ class AdvancedSearchFilterTest extends WebTestCase ['CONTENT_TYPE' => 'application/json'] ); - $data = json_decode($client->getResponse()->getContent(), true); $this->assertArrayHasKey("hydra:member", $data); @@ -73,11 +70,10 @@ class AdvancedSearchFilterTest extends WebTestCase [ "property" => "name", "operator" => "=", - "value" => "FOOBAR", + "value" => "FOOBAR", ], ]; - $client->request( 'GET', "/api/parts?filter=".json_encode($filter), @@ -86,7 +82,6 @@ class AdvancedSearchFilterTest extends WebTestCase ['CONTENT_TYPE' => 'application/json'] ); - $data = json_decode($client->getResponse()->getContent(), true); $this->assertArrayHasKey("hydra:member", $data); @@ -115,7 +110,7 @@ class AdvancedSearchFilterTest extends WebTestCase [ "property" => "storageLocation", "operator" => "=", - "value" => $iriConverter->getIriFromItem($this->fixtures->getReference("storagelocation.first")), + "value" => $iriConverter->getIriFromItem($this->fixtures->getReference("storagelocation.first")), ], ]; @@ -127,7 +122,6 @@ class AdvancedSearchFilterTest extends WebTestCase ['CONTENT_TYPE' => 'application/json'] ); - $data = json_decode($client->getResponse()->getContent(), true); $this->assertArrayHasKey("hydra:member", $data); @@ -147,7 +141,7 @@ class AdvancedSearchFilterTest extends WebTestCase [ "property" => "storageLocation", "operator" => "IN", - "value" => [ + "value" => [ $iriConverter->getIriFromItem($this->fixtures->getReference("storagelocation.first")), $iriConverter->getIriFromItem($this->fixtures->getReference("storagelocation.second")), ], @@ -162,7 +156,6 @@ class AdvancedSearchFilterTest extends WebTestCase ['CONTENT_TYPE' => 'application/json'] ); - $data = json_decode($client->getResponse()->getContent(), true); $this->assertArrayHasKey("hydra:member", $data); @@ -177,11 +170,10 @@ class AdvancedSearchFilterTest extends WebTestCase [ "property" => "storageLocation.name", "operator" => "LIKE", - "value" => "%test%", + "value" => "%test%", ], ]; - $client->request( 'GET', "/api/parts?filter=".json_encode($filter), @@ -190,7 +182,6 @@ class AdvancedSearchFilterTest extends WebTestCase ['CONTENT_TYPE' => 'application/json'] ); - $data = json_decode($client->getResponse()->getContent(), true); $this->assertArrayHasKey("hydra:member", $data); @@ -203,12 +194,11 @@ class AdvancedSearchFilterTest extends WebTestCase $order = [ [ - "property" => "storageLocation.name", + "property" => "storageLocation.name", "direction" => "ASC", ], ]; - $client->request( 'GET', "/api/parts?order=".json_encode($order), @@ -229,23 +219,22 @@ class AdvancedSearchFilterTest extends WebTestCase $filter = [ [ - "type" => "OR", + "type" => "OR", "subfilters" => [ [ "property" => "storageLocation.name", "operator" => "=", - "value" => "test", + "value" => "test", ], [ "property" => "storageLocation.name", "operator" => "=", - "value" => "test2", + "value" => "test2", ], ], ], ]; - $client->request( 'GET', "/api/parts?filter=".json_encode($filter), @@ -266,23 +255,22 @@ class AdvancedSearchFilterTest extends WebTestCase $filter = [ [ - "type" => "OR", + "type" => "OR", "subfilters" => [ [ "property" => "name", "operator" => "=", - "value" => "FOOBAR", + "value" => "FOOBAR", ], [ "property" => "name", "operator" => "=", - "value" => "FOOBAR2", + "value" => "FOOBAR2", ], ], ], ]; - $client->request( 'GET', "/api/parts?filter=".json_encode($filter), diff --git a/src/PartKeepr/FootprintBundle/Entity/Footprint.php b/src/PartKeepr/FootprintBundle/Entity/Footprint.php @@ -199,7 +199,7 @@ class Footprint extends BaseEntity */ public function getAttachments() { - return $this->attachments; + return $this->attachments->getValues(); } /** diff --git a/src/PartKeepr/FootprintBundle/Entity/FootprintCategory.php b/src/PartKeepr/FootprintBundle/Entity/FootprintCategory.php @@ -75,7 +75,7 @@ class FootprintCategory extends AbstractCategory implements CategoryPathInterfac */ public function getFootprints() { - return $this->footprints; + return $this->footprints->getValues(); } /** @@ -85,7 +85,7 @@ class FootprintCategory extends AbstractCategory implements CategoryPathInterfac */ public function getChildren() { - return $this->children; + return $this->children->getValues(); } /** diff --git a/src/PartKeepr/FrontendBundle/Controller/IndexController.php b/src/PartKeepr/FrontendBundle/Controller/IndexController.php @@ -40,6 +40,11 @@ class IndexController extends Controller $aParameters['maxUploadSize'] = $this->getParameterWithDefault('partkeepr.upload.limit', false); } + if ($this->getParameterWithDefault('partkeepr.octopart.apikey', "") !== "") { + $aParameters['isOctoPartAvailable'] = true; + } else { + $aParameters['isOctoPartAvailable'] = false; + } // @todo Hardcoded for now due to GD, see #445 $aParameters['availableImageFormats'] = ['JPG', 'GIF', 'PNG']; diff --git a/src/PartKeepr/FrontendBundle/Resources/public/images/icons/octopart.png b/src/PartKeepr/FrontendBundle/Resources/public/images/icons/octopart.png Binary files differ. diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/DataApplicator.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/DataApplicator.js @@ -0,0 +1,306 @@ +Ext.define("PartKeepr.Components.OctoPart.DataApplicator", { + extend: "Ext.Base", + mixins: ['Ext.mixin.Observable'], + + constructor: function (config) + { + this.mixins.observable.constructor.call(this, config); + }, + setPart: function (part) + { + this.part = part; + }, + loadData: function (id) + { + Ext.Ajax.request({ + url: PartKeepr.getBasePath() + '/api/octopart/get/' + id, + success: this.onPartLoaded, + scope: this + }); + }, + onPartLoaded: function (response) + { + var data = Ext.decode(response.responseText); + + this.data = data; + + this.applyData(); + }, + checkRequirements: function () + { + var i, unit, file, image, distributor; + + var manufacturer = PartKeepr.getApplication().getManufacturerStore().findRecord("name", + this.data.manufacturer.name); + if (manufacturer === null) { + this.displayWaitWindow(i18n("Creating Manufacturer…"), this.data.manufacturer.name); + manufacturer = Ext.create("PartKeepr.ManufacturerBundle.Entity.Manufacturer"); + manufacturer.set("name", this.data.manufacturer.name); + manufacturer.save({ + success: function () + { + PartKeepr.getApplication().getManufacturerStore().load({ + callback: this.applyData, + scope: this + }); + }, + scope: this + + }); + return false; + } + + for (i in this.data.specs) { + if (this.data.specs[i].metadata.unit !== null) { + unit = PartKeepr.getApplication().getUnitStore().findRecord("symbol", + this.data.specs[i].metadata.unit.symbol, 0, false, true, true); + if (unit === null) { + this.displayWaitWindow(i18n("Creating Unit…"), this.data.specs[i].metadata.unit.name); + unit = Ext.create("PartKeepr.UnitBundle.Entity.Unit"); + unit.set("name", this.data.specs[i].metadata.unit.name); + unit.set("symbol", this.data.specs[i].metadata.unit.symbol); + unit.save({ + success: function () + { + PartKeepr.getApplication().getUnitStore().load({ + callback: this.applyData, + scope: this + }); + }, + scope: this + + }); + return false; + } + } + } + + for (i in this.data.offers) { + distributor = PartKeepr.getApplication().getDistributorStore().findRecord("name", + this.data.offers[i].seller.name, 0, false, true, true); + + if (distributor === null) { + this.displayWaitWindow(i18n("Creating Distributor…"), this.data.offers[i].seller.name); + distributor = Ext.create("PartKeepr.DistributorBundle.Entity.Distributor"); + distributor.set("name", this.data.offers[i].seller.name); + distributor.set("website", this.data.offers[i].seller.homepage_url); + distributor.save({ + success: function () + { + PartKeepr.getApplication().getDistributorStore().load({ + callback: this.applyData, + scope: this + }); + }, + scope: this + + }); + return false; + } + } + + if (this.data.datasheets.length > 0) { + file = this.data.datasheets.shift(); + this.displayWaitWindow(i18n("Uploading datasheet…"), file.url); + PartKeepr.getApplication().uploadFileFromURL(file.url, i18n("Datasheet"), this.onFileUploaded, this); + return false; + } + + if (this.data.cad_models.length > 0) { + file = this.data.cad_models.shift(); + this.displayWaitWindow(i18n("Uploading CAD Model…"), file.url); + PartKeepr.getApplication().uploadFileFromURL(file.url, i18n("CAD Model"), this.onFileUploaded, this); + return false; + } + + if (this.data.compliance_documents.length > 0) { + file = this.data.compliance_documents.shift(); + this.displayWaitWindow(i18n("Uploading Compliance Document…"), file.url); + PartKeepr.getApplication().uploadFileFromURL(file.url, i18n("Compliance Document"), this.onFileUploaded, this); + return false; + } + + if (this.data.reference_designs.length > 0) { + file = this.data.reference_designs.shift(); + this.displayWaitWindow(i18n("Uploading Reference Designs…"), file.url); + PartKeepr.getApplication().uploadFileFromURL(file.url, i18n("Reference Design"), this.onFileUploaded, this); + return false; + } + + if (this.data.imagesets.length > 0) { + file = this.data.imagesets.shift(); + image = null; + + if (file.swatch_image !== null) { + image = file.swatch_image; + } + + if (file.small_image !== null) { + image = file.small_image; + } + + if (file.medium_image !== null) { + image = file.medium_image; + } + + if (file.large_image !== null) { + image = file.large_image; + } + + if (image !== null) { + this.displayWaitWindow(i18n("Uploading Image…"), image.url); + PartKeepr.getApplication().uploadFileFromURL(image.url, i18n("Image"), this.onFileUploaded, this); + } + + return false; + } + + + return true; + }, + onFileUploaded: function (options, success, response) { + + if (success) { + var result = Ext.decode(response.responseText); + + var uploadedFile = Ext.create("PartKeepr.UploadedFileBundle.Entity.TempUploadedFile", result.response); + + this.part.attachments().add(uploadedFile); + } + + this.applyData(); + }, + displayWaitWindow: function (text, value) + { + this.waitMessage = Ext.MessageBox.show({ + msg: text + "<br/>"+ value, + progressText: value, + width: 300, + wait: { + interval: 100 + }, + }); + }, + applyData: function () + { + var spec, i, unit, value, siPrefix, distributor, j, partDistributor, currency; + + if (this.waitMessage instanceof Ext.window.MessageBox) { + this.waitMessage.hide(); + } + + if (!this.checkRequirements()) { + return; + } + + this.part.set("name", this.data.mpn); + this.part.set("description", this.data.short_description); + + var manufacturer = PartKeepr.getApplication().getManufacturerStore().findRecord("name", + this.data.manufacturer.name); + var partManufacturer; + + if (manufacturer === null) { + // @todo put out error message + } + + partManufacturer = Ext.create("PartKeepr.PartBundle.Entity.PartManufacturer"); + partManufacturer.setManufacturer(manufacturer); + partManufacturer.set("partNumber", this.data.mpn); + + this.part.manufacturers().add(partManufacturer); + + for (i=0;i<this.data.offers.length;i++) { + distributor = PartKeepr.getApplication().getDistributorStore().findRecord("name", + this.data.offers[i].seller.name, 0, false, true, true); + if (distributor === null) { + // @todo put out error message + continue; + } + + for (currency in this.data.offers[i].prices) { + for (j=0;j<this.data.offers[i].prices[currency].length;j++) { + partDistributor = Ext.create("PartKeepr.PartBundle.Entity.PartDistributor"); + partDistributor.setDistributor(distributor); + partDistributor.set("sku", this.data.offers[i].sku); + partDistributor.set("packagingUnit", this.data.offers[i].prices[currency][j][0]); + partDistributor.set("currency", currency); + partDistributor.set("price", this.data.offers[i].prices[currency][j][1]); + this.part.distributors().add(partDistributor); + } + } + } + for (i in this.data.specs) { + spec = Ext.create("PartKeepr.PartBundle.Entity.PartParameter"); + spec.set("name", this.data.specs[i].metadata.name); + + if (this.data.specs[i].metadata.unit !== null) { + unit = PartKeepr.getApplication().getUnitStore().findRecord("symbol", + this.data.specs[i].metadata.unit.symbol, 0, false, true, true); + + spec.setUnit(unit); + } + + switch (this.data.specs[i].metadata.datatype) { + case "string": + spec.set("valueType", "string"); + spec.set("stringValue", this.data.specs[i].value[0]); + break; + case "decimal": + case "integer": + spec.set("valueType", "numeric"); + + if (this.data.specs[i].min_value !== null) { + value = parseFloat(this.data.specs[i].min_value); + siPrefix = this.findSiPrefixForValueAndUnit(value, unit); + spec.set("minValue", this.applySiPrefix(value, siPrefix)); + spec.setMinSiPrefix(siPrefix); + } + + if (this.data.specs[i].max_value !== null) { + value = parseFloat(this.data.specs[i].max_value); + siPrefix = this.findSiPrefixForValueAndUnit(value, unit); + spec.set("maxValue", this.applySiPrefix(value, siPrefix)); + spec.setMaxSiPrefix(siPrefix); + } + + if (this.data.specs[i].value.length === 1) { + value = parseFloat(this.data.specs[i].value[0]); + siPrefix = this.findSiPrefixForValueAndUnit(value, unit); + spec.set("value", this.applySiPrefix(value, siPrefix)); + spec.setSiPrefix(siPrefix); + } + + break; + } + + this.part.parameters().add(spec); + + } + + this.fireEvent("refreshData"); + }, + applySiPrefix: function (value, siPrefix) { + return Ext.util.Format.round(value / Math.pow(siPrefix.get("base"), siPrefix.get("exponent")), 3); + }, + findSiPrefixForValueAndUnit: function (value, unit) { + var i = 0, prefixedValue, siPrefix; + + if (unit === null) { + return PartKeepr.getApplication().getSiPrefixStore().findRecord("exponent", 0, 0, false, false, true); + } + + siPrefix = PartKeepr.getApplication().getSiPrefixStore().findRecord("exponent", 0, 0, false, false, true); + + for (i=0;i<unit.prefixes().getCount();i++) { + siPrefix = unit.prefixes().getAt(i); + prefixedValue = Math.abs(this.applySiPrefix(value, siPrefix)); + + if (prefixedValue < 1000 && prefixedValue > 0.9) { + break; + } + } + + return siPrefix; + } +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/SearchPanel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/SearchPanel.js @@ -0,0 +1,140 @@ +Ext.define("PartKeepr.Components.OctoPart.SearchPanel", { + extend: "Ext.panel.Panel", + layout: 'border', + grid: null, + store: null, + searchBar: null, + xtype: 'octopartSearchPanel', + + initComponent: function () + { + this.store = Ext.create("Ext.data.Store", { + fields: [ + {name: 'title', type: 'string'}, + {name: 'url', type: 'string'}, + {name: 'mpn', type: 'string'} + ], + proxy: { + type: 'ajax', + startParam: '', + limitParam: '', + url: "", + reader: { + type: 'json', + totalProperty: 'hits', + rootProperty: 'results' + } + }, + autoLoad: false + }); + + this.grid = Ext.create({ + xtype: 'grid', + region: 'center', + columns: [ + { + text: i18n("Manufacturer"), + dataIndex: 'manufacturer', + flex: 1 + }, + { + text: i18n("Title"), + dataIndex: 'title', + flex: 2 + }, { + text: i18n("MPN"), + dataIndex: 'mpn', + flex: 1 + }, { + text: i18n("Details…"), + dataIndex: 'url', + renderer: function (v) + { + return '<span class="web-icon fugue-icon globe-small"/></span><a href="' + v + '" target="_blank">' + i18n( + "Details…") + "</a>"; + } + } + ], + store: this.store + }); + + this.addButton = Ext.create("Ext.button.Button", { + iconCls: 'fugue-icon blueprint--plus', + text: i18n("Add Data"), + disabled: true, + itemId: 'add', + handler: this.onAddClick, + scope: this + }); + + this.grid.addDocked(Ext.create("Ext.toolbar.Paging", { + store: this.store, + enableOverflow: true, + dock: 'bottom', + displayInfo: false, + grid: this.grid, + items: this.addButton + })); + + this.searchBar = Ext.create("Ext.form.field.Text", { + region: 'north', + height: 30, + emptyText: i18n("Enter Search Terms"), + listeners: { + specialkey: function (field, e) + { + if (e.getKey() == e.ENTER) { + this.startSearch(field.getValue()); + } + + }, + scope: this + } + }); + + this.items = [this.grid, this.searchBar]; + + this.grid.on("itemdblclick", this.onItemDblClick, this); + this.grid.on('selectionchange', + this.onSelectChange, + this); + + this.callParent(arguments); + + }, + onAddClick: function () + { + var record = this.grid.getSelection()[0]; + this.applyData(record); + }, + onSelectChange: function (selModel, selections) + { + this.addButton.setDisabled(selections.length === 0); + }, + setPart: function (part) + { + this.part = part; + }, + onItemDblClick: function (grid, record) + { + this.applyData(record); + }, + applyData: function (record) + { + var j = Ext.create("PartKeepr.Components.OctoPart.DataApplicator"); + j.setPart(this.part); + j.loadData(record.get("uid")); + j.on("refreshData", function () + { + this.fireEvent("refreshData"); + }, this); + }, + startSearch: function (query) + { + this.store.getProxy().setUrl( + PartKeepr.getBasePath() + '/api/octopart/query/?q=' + encodeURIComponent(query) + ); + this.store.load(); + this.searchBar.setValue(query); + } +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/SearchWindow.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/OctoPart/SearchWindow.js @@ -0,0 +1,31 @@ +Ext.define("PartKeepr.Components.OctoPart.SearchWindow", { + extend: "Ext.window.Window", + title: i18n("OctoPart Search"), + iconCls: "partkeepr-icon octopart", + width: 600, + height: 300, + layout: 'fit', + modal: true, + items: [ + { + xtype: "octopartSearchPanel", + itemId: 'octopartSearchPanel' + } + ], + initComponent: function () + { + this.callParent(arguments); + this.down("#octopartSearchPanel").on("refreshData", function () + { + this.fireEvent("refreshData"); + }, this); + }, + startSearch: function (query) + { + this.down("#octopartSearchPanel").startSearch(query); + }, + setPart: function (part) + { + this.down("#octopartSearchPanel").setPart(part); + } +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartDistributorGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartDistributorGrid.js @@ -2,6 +2,10 @@ Ext.define('PartKeepr.PartDistributorGrid', { extend: 'PartKeepr.BaseGrid', alias: 'widget.PartDistributorGrid', border: false, + selModel: { + selType: 'rowmodel', + mode: 'MULTI' + }, initComponent: function () { this.store = Ext.create("Ext.data.Store", { @@ -16,7 +20,7 @@ Ext.define('PartKeepr.PartDistributorGrid', { }); this.editing = Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit: 1 + clicksToEdit: 2 }); this.plugins = [this.editing]; @@ -84,21 +88,32 @@ Ext.define('PartKeepr.PartDistributorGrid', { header: i18n("Price per Item"), dataIndex: 'price', flex: 1, - renderer: function (val) + renderer: function (val,m,rec) { - return PartKeepr.getApplication().formatCurrency(val); + return PartKeepr.getApplication().formatCurrency(val, rec.get("currency")); }, editor: { xtype: 'CurrencyField', allowBlank: false } }, { + header: i18n("Currency"), + dataIndex: 'currency', + editor: { + xtype: 'combobox', + displayField: 'code', + valueField: 'code', + store: PartKeepr.getApplication().getCurrencyStore(), + forceSelection: true, + queryMode: 'local' + } + }, { header: i18n("Package Price"), flex: 1, dataIndex: 'packagePrice', renderer: function (val, p, rec) { - return PartKeepr.getApplication().formatCurrency(rec.get("price") * rec.get("packagingUnit")); + return PartKeepr.getApplication().formatCurrency(rec.get("price") * rec.get("packagingUnit"), rec.get("currency")); } }, { header: i18n("SKU"), @@ -148,10 +163,7 @@ Ext.define('PartKeepr.PartDistributorGrid', { }, onDeleteClick: function () { - var selection = this.getView().getSelectionModel().getSelection()[0]; - if (selection) { - this.store.remove(selection); - } + this.store.remove(this.getView().getSelectionModel().getSelection()); }, onSelectChange: function (selModel, selections) { diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartEditor.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartEditor.js @@ -227,6 +227,13 @@ Ext.define('PartKeepr.PartEditor', { }); // Creates the attachment grid + this.partParameterGrid = Ext.create("PartKeepr.PartParameterGrid", { + title: i18n("Part Parameters"), + iconCls: 'fugue-icon table', + layout: 'fit' + }); + + // Creates the attachment grid this.partAttachmentGrid = Ext.create("PartKeepr.PartAttachmentGrid", { title: i18n("Attachments"), iconCls: 'web-icon attach', @@ -302,6 +309,7 @@ Ext.define('PartKeepr.PartEditor', { }, this.partDistributorGrid, this.partManufacturerGrid, + this.partParameterGrid, this.partAttachmentGrid ] }; @@ -350,34 +358,16 @@ Ext.define('PartKeepr.PartEditor', { removeRecords = []; /** - * Iterate through all records and check if a valid parameter - * ID is assigned. If not, the record is removed as it is assumed - * that the record is invalid and being removed. - */ - - for (j = 0; j < this.record.parameters().getCount(); j++) { - if (this.record.parameters().getAt(j).get("unit_id") === 0) { - removeRecords.push(this.record.parameters().getAt(j)); - } - } - - if (removeRecords.length > 0) { - this.record.parameters().remove(removeRecords); - } - - removeRecords = []; - - /** * Iterate through all records and check if a valid manufacturer * ID is assigned. If not, the record is removed as it is assumed * that the record is invalid and being removed. */ - for (j = 0; j < this.record.manufacturers().getCount(); j++) { + /*for (j = 0; j < this.record.manufacturers().getCount(); j++) { if (this.record.manufacturers().getAt(j).getManufacturer() === null) { removeRecords.push(this.record.manufacturers().getAt(j)); } - } + }*/ if (removeRecords.length > 0) { this.record.manufacturers().remove(removeRecords); @@ -485,6 +475,7 @@ Ext.define('PartKeepr.PartEditor', { this.partDistributorGrid.bindStore(this.record.distributors()); this.partManufacturerGrid.bindStore(this.record.manufacturers()); this.partAttachmentGrid.bindStore(this.record.attachments()); + this.partParameterGrid.bindStore(this.record.parameters()); }, setTitle: function (title) { diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartEditorWindow.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartEditorWindow.js @@ -50,7 +50,7 @@ Ext.define('PartKeepr.PartEditorWindow', { * We need a delay, since if others are listening for "editorClose", the dialog plus the record could be destroyed * before any following listeners have a chance to receive the record, resulting in strange problems. */ - this.editor.on("editorClose", function (context) + this.editor.on("editorClose", function () { this.close(); }, this, {delay: 200}); @@ -61,6 +61,12 @@ Ext.define('PartKeepr.PartEditorWindow', { }, this); this.editor.on("itemSaved", this.onItemSaved, this); + this.octoPartButton = Ext.create("Ext.button.Button", { + text: i18n("OctoPart…"), + iconCls: 'partkeepr-icon octopart', + handler: Ext.bind(this.onOctoPartClick, this) + }); + this.saveButton = Ext.create("Ext.button.Button", { text: this.saveText, iconCls: 'fugue-icon disk', @@ -79,7 +85,7 @@ Ext.define('PartKeepr.PartEditorWindow', { dock: 'bottom', ui: 'footer', pack: 'start', - items: [this.saveButton, this.cancelButton] + items: [this.saveButton, this.cancelButton, this.octoPartButton] }); this.dockedItems = [this.bottomToolbar]; @@ -128,6 +134,21 @@ Ext.define('PartKeepr.PartEditorWindow', { this.copyPartDataCheckbox.disable(); } }, + onOctoPartClick: function () { + if (PartKeepr.isOctoPartAvailable()) { + this.octoPartQueryWindow = Ext.create("PartKeepr.Components.OctoPart.SearchWindow"); + this.octoPartQueryWindow.show(); + this.octoPartQueryWindow.setPart(this.editor.record); + this.octoPartQueryWindow.startSearch(this.editor.nameField.getValue()); + this.octoPartQueryWindow.on("refreshData", this.onRefreshData, this); + } else { + Ext.MessageBox.alert(i18n("OctoPart is not configured"), i18n("Your administrator needs to configure the API key for OctoPart in the parameters.php file - see parameters.php.dist for instructions")); + } + }, + onRefreshData: function () { + this.editor.getForm().loadRecord(this.editor.record); + this.octoPartQueryWindow.destroy(); + }, /** * Called when the save button was clicked */ diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartManufacturerGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartManufacturerGrid.js @@ -2,6 +2,10 @@ Ext.define('PartKeepr.PartManufacturerGrid', { extend: 'PartKeepr.BaseGrid', alias: 'widget.PartManufacturerGrid', border: false, + selModel: { + selType: 'rowmodel', + mode: 'MULTI' + }, initComponent: function () { this.store = Ext.create("Ext.data.Store", { @@ -16,7 +20,7 @@ Ext.define('PartKeepr.PartManufacturerGrid', { }); this.editing = Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit: 1 + clicksToEdit: 2 }); this.plugins = [this.editing]; @@ -77,17 +81,6 @@ Ext.define('PartKeepr.PartManufacturerGrid', { this.callParent(); this.getSelectionModel().on('selectionchange', this.onSelectChange, this); - this.on("edit", this.onEdit, this); - }, - onEdit: function (editor, data) - { - var id = data.record.get("manufacturer_id"); - - var rec = PartKeepr.getApplication().getManufacturerStore().findRecord("id", id); - - if (rec) { - data.record.set("manufacturer_name", rec.get("name")); - } }, onAddClick: function () { @@ -101,10 +94,7 @@ Ext.define('PartKeepr.PartManufacturerGrid', { }, onDeleteClick: function () { - var selection = this.getView().getSelectionModel().getSelection()[0]; - if (selection) { - this.store.remove(selection); - } + this.store.remove(this.getView().getSelectionModel().getSelection()); }, onSelectChange: function (selModel, selections) { diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterGrid.js @@ -1,151 +1,191 @@ Ext.define('PartKeepr.PartParameterGrid', { - extend: 'PartKeepr.BaseGrid', - alias: 'widget.PartParameterGrid', - border: false, - initComponent: function () { - this.store = Ext.create("Ext.data.Store", { - model: 'PartKeepr.PartBundle.Entity.PartParameter', - proxy: { - type: 'memory', - reader: { - type: 'json' - } - } - }); - - this.editing = Ext.create('Ext.grid.plugin.CellEditing', { - clicksToEdit: 1, - listeners: { - scope: this, - beforeedit: this.onBeforeEdit, - edit: this.onAfterEdit + extend: 'PartKeepr.BaseGrid', + alias: 'widget.PartParameterGrid', + border: false, + selModel: { + selType: 'rowmodel', + mode: 'MULTI' + }, + initComponent: function () + { + this.store = Ext.create("Ext.data.Store", { + model: 'PartKeepr.PartBundle.Entity.PartParameter', + proxy: { + type: 'memory', + reader: { + type: 'json' + } } }); - - this.plugins = [ this.editing ]; - - this.deleteButton = Ext.create("Ext.button.Button", { - text: i18n('Delete'), - disabled: true, - itemId: 'delete', - scope: this, - iconCls: 'fugue-icon table--minus', - handler: this.onDeleteClick - }); - - this.dockedItems = [{ - xtype: 'toolbar', - items: [{ - text: i18n('Add'), - scope: this, - iconCls: 'fugue-icon table--plus', - handler: this.onAddClick - }, this.deleteButton] - }]; - - this.columns = [ - { - header: i18n("Parameter"), - dataIndex: 'name', - flex: 0.2, - editor: { - xtype:'PartParameterComboBox', - allowBlank:false, - lazyRender: true, - listClass: 'x-combo-list-small', - selectOnTab: true - } - }, - { - header: i18n("Value"), - flex: 0.2, - dataIndex: "prefixedValue", - renderer: function (val,p,rec) { - if (!Ext.isObject(val)) { return ""; } - - var unitStore = PartKeepr.getApplication().getUnitStore(); - var foundRec = unitStore.findRecord("id", rec.get("unit_id"), 0, false, false, true); - - if (foundRec) { - return val.value + " "+val.symbol + foundRec.get("symbol"); - } else { - return val.value + " "+val.symbol; - } - - }, - editor: { - xtype: 'SiUnitField', - decimalPrecision: 20 - } - }, - { - header: i18n("Unit"), - flex: 0.2, - dataIndex: 'unit_id', - renderer: function (val,p,rec) { - var unitStore = PartKeepr.getApplication().getUnitStore(); - var foundRec = unitStore.findRecord("id", val, 0, false, false, true); - - if (foundRec) { - return foundRec.get("name"); - } else { - return ""; - } - }, - editor: { - xtype:'UnitComboBox', - allowBlank:true - } - }, - { - header: i18n("Description"), - dataIndex: 'description', - flex: 0.3, - editor: { - xtype:'textfield', - allowBlank:true - } - } - ]; - - this.callParent(); - - this.getSelectionModel().on('selectionchange', this.onSelectChange, this); - }, - onAddClick: function () { - this.editing.cancelEdit(); - - var rec = new PartKeepr.PartBundle.Entity.PartParameter({ - - }); - - this.store.insert(0, rec); - - this.editing.startEditByPosition({ row: 0, column: 0}); - }, - onDeleteClick: function () { - var selection = this.getView().getSelectionModel().getSelection()[0]; - if (selection) { - this.store.remove(selection); - } - }, - onSelectChange: function(selModel, selections){ - this.deleteButton.setDisabled(selections.length === 0); + + this.deleteButton = Ext.create("Ext.button.Button", { + text: i18n('Delete'), + disabled: true, + itemId: 'delete', + scope: this, + iconCls: 'fugue-icon table--minus', + handler: this.onDeleteClick + }); + + this.dockedItems = [ + { + xtype: 'toolbar', + items: [ + { + text: i18n('Add'), + scope: this, + iconCls: 'fugue-icon table--plus', + handler: this.onAddClick + }, this.deleteButton + ] + } + ]; + + this.columns = [ + { + header: i18n("Parameter"), + dataIndex: 'name', + flex: 0.2, + }, + { + header: i18n("Min Value"), + dataIndex: 'minValue', + flex: 0.2, + renderer: function (v, m, rec) + { + var siPrefix = "", unit = ""; + + if (v === null) { + return ""; + } + if (rec.get("valueType") === "string") { + return ""; + } + + if (rec.getUnit() instanceof PartKeepr.UnitBundle.Entity.Unit) { + unit = rec.getUnit().get("symbol"); + } + + if (rec.getMinSiPrefix() instanceof PartKeepr.SiPrefixBundle.Entity.SiPrefix) { + siPrefix = rec.getMinSiPrefix().get("symbol"); + } + + return v + siPrefix + unit; + } + }, { + header: i18n("Nominal Value"), + dataIndex: 'value', + flex: 0.2, + renderer: function (v, m, rec) + { + var siPrefix = "", unit = ""; + + if (rec.get("valueType") === "string") { + return rec.get("stringValue"); + } + if (v === null) { + return ""; + } + + if (rec.getUnit() instanceof PartKeepr.UnitBundle.Entity.Unit) { + unit = rec.getUnit().get("symbol"); + } + + if (rec.getSiPrefix() instanceof PartKeepr.SiPrefixBundle.Entity.SiPrefix) { + siPrefix = rec.getSiPrefix().get("symbol"); + } + + return v + siPrefix + unit; + } + }, { + header: i18n("Max Value"), + dataIndex: 'maxValue', + flex: 0.2, + renderer: function (v, m, rec) + { + var siPrefix = "", unit = ""; + + if (v === null) { + return ""; + } + if (rec.get("valueType") === "string") { + return ""; + } + + if (rec.getUnit() instanceof PartKeepr.UnitBundle.Entity.Unit) { + unit = rec.getUnit().get("symbol"); + } + + if (rec.getMaxSiPrefix() instanceof PartKeepr.SiPrefixBundle.Entity.SiPrefix) { + siPrefix = rec.getMaxSiPrefix().get("symbol"); + } + + return v + siPrefix + unit; + } + }, + { + header: i18n("Unit"), + flex: 0.2, + renderer: function (v, m, rec) + { + if (rec.getUnit() instanceof PartKeepr.UnitBundle.Entity.Unit) { + return rec.getUnit().get("name"); + } else { + return ""; + } + } + }, + { + header: i18n("Description"), + dataIndex: 'description', + flex: 0.3, + } + ]; + + this.callParent(); + + this.getSelectionModel().on('selectionchange', this.onSelectChange, this); + this.on("itemdblclick", this.onItemDblClick, this); + }, + onItemDblClick: function (grid, record) + { + this.editRecord(record); }, - onBeforeEdit: function (editor, e, o) { - var header = this.headerCt.getHeaderAtIndex(e.colIdx); - var edit = this.editing.getEditor(editor.record, header); - - if (e.field == "prefixedValue") { - var unit = PartKeepr.getApplication().getUnitStore().getById(e.record.get("unit_id")); - if (unit) { - edit.field.setStore(unit.prefixes()); - } - } + onAddClick: function () + { + var rec = Ext.create("PartKeepr.PartBundle.Entity.PartParameter"); + + this.store.insert(0, rec); + + this.editRecord(rec); }, - onAfterEdit: function (editor, e) { - var f = e.record.get("prefixedValue"); - e.record.set("siprefix_id", f.siprefix_id); - e.record.set("value", f.value); + editRecord: function (rec) + { + var k = Ext.create("PartKeepr.PartParameterValueEditor"); + + var j = Ext.create("Ext.window.Window", { + items: k, + modal: true, + title: i18n("Edit Parameter"), + layout: 'fit', + width: 600, + height: 300 + }); + + k.loadRecord(rec); + k.on("save", function () + { + j.destroy(); + }); + + j.show(); + }, + onDeleteClick: function () + { + this.store.remove(this.getView().getSelectionModel().getSelection()); + }, + onSelectChange: function (selModel, selections) + { + this.deleteButton.setDisabled(selections.length === 0); } }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterValueEditor.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterValueEditor.js @@ -0,0 +1,152 @@ +Ext.define("PartKeepr.PartParameterValueEditor", { + extend: "Ext.form.Panel", + + layout: { + type: 'vbox', + pack: 'start', + align: 'stretch' + + }, + items: [ + { + fieldLabel: i18n("Parameter Name"), + name: 'name', + xtype: 'PartParameterComboBox' + }, + { + fieldLabel: i18n("Description"), + name: 'description', + xtype: 'textarea' + }, + { + xtype: 'UnitComboBox', + fieldLabel: i18n("Unit"), + itemId: "unit", + returnObject: true, + name: 'unit' + }, + { + fieldLabel: i18n("Value Type"), + xtype: 'radiogroup', + name: 'valueType', + itemId: 'valueType', + items: [ + { + boxLabel: i18n("Numeric"), + inputValue: "numeric", + checked: true + }, { + boxLabel: i18n("Text"), + inputValue: "string" + } + ] + }, + { + xtype: 'container', + layout: 'card', + itemId: 'typeFields', + items: [ + { + xtype: 'fieldcontainer', + itemId: "numeric", + layout: 'vbox', + items: [ + { + fieldLabel: i18n("Min Value"), + name: 'minValue', + siFieldName: 'minSiPrefix', + siUnitItemId: 'minSiPrefix', + xtype: 'SiUnitField', + width: 200 + }, + { + xtype: 'SiUnitField', + itemId: "singleValue", + fieldLabel: i18n("Nominal Value"), + siUnitItemId: 'siPrefix', + name: 'value', + siFieldName: 'siPrefix', + width: 200 + }, + { + fieldLabel: i18n("Max Value"), + name: 'maxValue', + siFieldName: 'maxSiPrefix', + siUnitItemId: 'maxSiPrefix', + xtype: 'SiUnitField', + width: 200 + } + ] + }, + { + fieldLabel: i18n("Value"), + itemId: 'text', + name: 'stringValue', + xtype: 'textfield' + } + ] + }, + ], + + bbar: [ + { + xtype: 'button', + itemId: "save", + text: i18n("Save") + } + ], + initComponent: function () + { + this.callParent(arguments); + + this.unitFilter = Ext.create("PartKeepr.util.Filter", { + property: "@id", + operator: "in", + value: [] + }); + this.down("#valueType").on("change", this.onTypeChange, this); + this.down("#save").on("click", this.onSave, this); + this.down("#unit").on("change", this.onUnitChange, this); + }, + onUnitChange: function (combo, newValue) { + var prefixes,j, unitFilter = []; + + this.down("#siPrefix").getStore().removeFilter(this.unitFilter); + this.down("#minSiPrefix").getStore().removeFilter(this.unitFilter); + this.down("#maxSiPrefix").getStore().removeFilter(this.unitFilter); + + if (newValue instanceof PartKeepr.UnitBundle.Entity.Unit) { + prefixes = newValue.prefixes().getData(); + + for (j=0;j<prefixes.getCount();j++) { + unitFilter.push(prefixes.getAt(j).get("@id")); + } + } + + this.unitFilter.setValue(unitFilter); + this.down("#siPrefix").getStore().addFilter(this.unitFilter); + this.down("#minSiPrefix").getStore().addFilter(this.unitFilter); + this.down("#maxSiPrefix").getStore().addFilter(this.unitFilter); + }, + loadRecord: function (record) { + this.callParent(arguments); + + this.down("#valueType").setValue({ valueType: record.get("valueType")}); + }, + onSave: function () { + this.updateRecord(); + this.fireEvent("save"); + }, + onTypeChange: function (field, newValue) + { + switch (newValue.valueType) { + case "string": + this.down("#typeFields").setActiveItem(this.down("#text")); + break; + case "numeric": + this.down("#typeFields").setActiveItem(this.down("#numeric")); + break; + + } + } +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Unit/UnitEditor.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Unit/UnitEditor.js @@ -15,7 +15,7 @@ Ext.define('PartKeepr.UnitEditor', { columns: [ { text: i18n("Prefix"), dataIndex: "prefix", width: 60 }, { text: i18n("Symbol"), dataIndex: "symbol", width: 60 }, - { text: i18n("Power"), dataIndex: "exponent", flex: 1, renderer: function (value) { return "10<sup>"+value+"</sup>"; } } + { text: i18n("Power"), dataIndex: "exponent", flex: 1, renderer: function (value,m,rec) { return rec.get("base")+"<sup>"+value+"</sup>"; } } ] }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/AttachmentGrid.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/AttachmentGrid.js @@ -3,6 +3,10 @@ Ext.define('PartKeepr.AttachmentGrid', { alias: 'widget.AttachmentGrid', border: false, model: null, + selModel: { + selType: 'rowmodel', + mode: 'MULTI' + }, initComponent: function () { this.store = Ext.create("Ext.data.Store", { @@ -146,10 +150,7 @@ Ext.define('PartKeepr.AttachmentGrid', { }, onDeleteClick: function () { - var selection = this.getView().getSelectionModel().getSelection()[0]; - if (selection) { - this.store.remove(selection); - } + this.store.remove(this.getView().getSelectionModel().getSelection()); }, onSelectChange: function (selModel, selections) { diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/CurrencyNumberField.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/CurrencyNumberField.js @@ -7,7 +7,7 @@ Ext.define("PartKeepr.CurrencyField", { initComponent: function () { this.decimalPrecision = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.numdecimals", 2); - this.currencySign = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.symbol", "€"); + this.currencySign = ""; this.currencyAtEnd = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.currencySymbolAtEnd", true); if (PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.thousandsSeparator", true) === true) { @@ -21,4 +21,4 @@ Ext.define("PartKeepr.CurrencyField", { this.callParent(); } -});- \ No newline at end of file +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/PartParameterComboBox.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/PartParameterComboBox.js @@ -1,6 +1,6 @@ Ext.define("PartKeepr.PartParameterComboBox",{ extend:"Ext.form.field.ComboBox", - alias: 'widget.PartParameterComboBox', + xtype: 'PartParameterComboBox', displayField: 'name', valueField: 'name', autoSelect: false, @@ -9,34 +9,47 @@ Ext.define("PartKeepr.PartParameterComboBox",{ triggerAction: 'all', forceSelection: false, editable: true, + triggers: { + reload: { + cls: "x-form-reload-trigger", + weight: -1, + handler: function () + { + this.store.load(); + }, + scope: 'this' + } + }, initComponent: function () { - //this.store = PartKeepr.getApplication().getPartUnitStore(); - + this.store = Ext.create("Ext.data.Store", { fields: [{ name: 'name' }], + autoLoad: false, proxy: { type: 'ajax', - url: PartKeepr.getBasePath() + "/Part/getPartParameterNames", + url: PartKeepr.getBasePath() + "/api/parts/getPartParameterNames", reader: { - type: 'json', - root: 'response.data' + type: 'json' } } }); - - this.store.load(); - - /* Workaround to remember the value when loading */ + + /* Workaround to remember the value when loading */ this.store.on("beforeload", function () { this._oldValue = this.getValue(); }, this); - + /* Set the old value when load is complete */ this.store.on("load", function () { this.setValue(this._oldValue); }, this); - + this.callParent(); + this.store.load(); + }, + setValue: function (val) { + this._oldValue = val; + this.callParent(arguments); } }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitCombo.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitCombo.js @@ -0,0 +1,63 @@ +Ext.define("PartKeepr.Components.Widgets.SiUnitCombo", { + extend: "Ext.form.field.ComboBox", + xtype: 'SiUnitCombo', + + forceSelection: true, + singleSelect: true, + editable: true, + typeAhead: true, + + queryMode: 'local', + displayField: 'symbol', + valueField: 'symbol', + + defaultListConfig: { + loadingHeight: 70, + minWidth: 150, + maxHeight: 300, + shadow: 'sides' + }, + + createPicker: function () + { + var me = this, + picker, + pickerCfg = Ext.apply({ + xtype: 'siunitlist', + id: me.pickerId, + pickerField: me, + selectionModel: me.pickerSelectionModel, + floating: true, + hidden: true, + store: me.getPickerStore(), + displayField: me.displayField, + preserveScrollOnRefresh: true, + pageSize: me.pageSize, + tpl: me.tpl + }, me.listConfig, me.defaultListConfig); + + picker = me.picker = Ext.widget(pickerCfg); + if (me.pageSize) { + picker.pagingToolbar.on('beforechange', me.onPageChange, me); + } + + // We limit the height of the picker to fit in the space above + // or below this field unless the picker has its own ideas about that. + if (!picker.initialConfig.maxHeight) { + picker.on({ + beforeshow: me.onBeforePickerShow, + scope: me + }); + } + picker.getSelectionModel().on({ + beforeselect: me.onBeforeSelect, + beforedeselect: me.onBeforeDeselect, + focuschange: me.onFocusChange, + scope: me + }); + + picker.getNavigationModel().navigateOnSpace = false; + + return picker; + } +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitField.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitField.js @@ -1,390 +1,49 @@ /** * This class represents a field which can handle a number (value) bound to a specific SI prefix. - * + * * Internally, we use an object as value. Example: - * + * * { * value: 10 // The base value, in our case 10 * symbol: "n" // The symbol for display * power: -9 // The power * siprefix_id: 5 // The ID of the siprefix record * } - * + * */ -Ext.define("PartKeepr.SiUnitField",{ - extend:"Ext.form.field.Picker", +Ext.define("PartKeepr.SiUnitField", { + extend: "Ext.form.FieldContainer", alias: 'widget.SiUnitField', - - siPrefix: null, - - /** - * @cfg {RegExp} stripCharsRe @hide - */ - /** - * @cfg {RegExp} maskRe @hide - */ - /** - * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true) - */ - allowDecimals : true, - - /** - * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.') - */ - decimalSeparator : '.', - - /** - * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2) - */ - decimalPrecision : 2, - - /** - * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY). Will be used by - * the field's validation logic. - */ - minValue: Number.NEGATIVE_INFINITY, - - /** - * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE). Will be used by - * the field's validation logic. - */ - maxValue: Number.MAX_VALUE, - - /** - * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to 'The minimum - * value for this field is {minValue}') - */ - minText : 'The minimum value for this field is {0}', - - /** - * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to 'The maximum - * value for this field is {maxValue}') - */ - maxText : 'The maximum value for this field is {0}', - - /** - * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen - * if a valid character like '.' or '-' is left in the field with no number (defaults to '{value} is not a valid number') - */ - nanText : '{0} is not a valid number', - - /** - * @cfg {String} negativeText Error text to display if the value is negative and {@link #minValue} is set to - * <tt>0</tt>. This is used instead of the {@link #minText} in that circumstance only. - */ - negativeText : 'The value cannot be negative', - - /** - * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789'). - */ - baseChars : '0123456789', - - /** - * @cfg {Boolean} autoStripChars True to automatically strip not allowed characters from the field. Defaults to <tt>false</tt> - */ - autoStripChars: false, - - initComponent: function() { - var me = this, - allowed; - - me.callParent(); - - me.setMinValue(me.minValue); - me.setMaxValue(me.maxValue); - - // Build regexes for masking and stripping based on the configured options - if (me.disableKeyFilter !== true) { - allowed = me.baseChars + ''; - - var store = PartKeepr.getApplication().getSiPrefixStore(); - - for (var i=0;i<store.count();i++) { - allowed += store.getAt(i).get("symbol"); - } - - /** - * Fix because the µ-symbol on your keyboard is not greek "Mu" as defined by the Si standard. We wish that - * the user still can enter "µ", which automatically gets converted to "Mu". - */ - allowed += "µ"; - - if (me.allowDecimals) { - allowed += me.decimalSeparator; + layout: { + type: 'hbox' + }, + initComponent: function () + { + this.items = [ + { + xtype: 'numberfield', + hideTrigger: true, + emptyText: i18n("Value"), + decimalPrecision: 20, + name: this.name, + flex: 1 + }, { + xtype: 'SiUnitCombo', + itemId: this.siUnitItemId, + returnObject: true, + caseSensitive: true, + name: this.siFieldName, + width: 40 } - if (me.minValue < 0) { - allowed += '-'; - } - allowed = Ext.String.escapeRegex(allowed); - me.maskRe = new RegExp('[' + allowed + ']'); - if (me.autoStripChars) { - me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi'); - } - } - }, - onTriggerClick: function () { - this.expand(); - - var node = this.picker.getNode(this.siPrefix); - - if (node) { - this.picker.highlightItem(node); - this.picker.listEl.scrollChildIntoView(node, false); - } - }, - getStore: function () { - if (this.store) { - return this.store; - } - - return PartKeepr.getApplication().getSiPrefixStore(); - }, - setStore: function (store) { - if (this.picker) { - this.picker.bindStore(store); - } else { - this.store = store; - } - }, - createPicker: function() { - var siprefixtpl = new Ext.XTemplate( - '<tpl for=".">', - '<div class="thumb-wrap">', - '{symbol} {prefix}', - '</div>', - '</tpl>'); - - var tmp = Ext.create('PartKeepr.SiUnitList', { - store: this.getStore(), - singleSelect: true, - ownerCt: this.ownerCt, - renderTo: document.body, - //width: 200, - //height:200, - floating: true, - maxHeight: 300, - shadow: 'sides', - focusOnToFront: false, - hidden: true, - focusOnShow: true, - displayField: 'symbol', - isteners: { - scope: this, - itemclick: this.onSelect - } - }); - - this.mon(tmp, { - itemclick: this.onSelect, - scope: this - }); - return tmp; - }, - onSelect: function (t, rec) { - var val = this.getValue(); - - val.symbol = rec.get("symbol"); - val.power = rec.get("power"); - val.siprefix_id = rec.get("id"); - - //this.siUnit = rec; - this.setValue(val); - this.collapse(); - }, - /** - * Runs all of Number's validations and returns an array of any errors. Note that this first - * runs Text's validations, so the returned array is an amalgamation of all field errors. - * The additional validations run test that the value is a number, and that it is within the - * configured min and max values. - * @param {Mixed} value The value to get errors for (defaults to the current field value) - * @return {Array} All validation errors for this field - */ - getErrors: function(value) { - var me = this, - errors = me.callParent(arguments), - format = Ext.String.format, - num, retVal; - - retVal = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue()); - - value = retVal.value; - - if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid - return errors; - } - - value = String(value).replace(me.decimalSeparator, '.'); - - if(isNaN(value)){ - errors.push(format(me.nanText, value)); - } - - num = me.parseValue(value); - - if (me.minValue === 0 && num < 0) { - errors.push(this.negativeText); - } - else if (num < me.minValue) { - errors.push(format(me.minText, me.minValue)); - } - - if (num > me.maxValue) { - errors.push(format(me.maxText, me.maxValue)); - } - - - return errors; - }, - rawToValue: function(rawValue) { - var processValue; - - if (Ext.isObject(rawValue)) { - processValue = rawValue.value; - } else { - processValue = rawValue; - } - - return this.fixPrecision(this.parseValue(processValue)) || processValue || null; - }, - - valueToRaw: function(value) { - var me = this, - decimalSeparator = me.decimalSeparator; - value = me.parseValue(value); - value = me.fixPrecision(value); - value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.')); - value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator); - - if (Ext.isObject(this.siPrefix) && this.siPrefix.get("symbol") !== "") { - return value + " "+this.siPrefix.get("symbol"); - } else { - return value; - } - - }, - - onChange: function() { - var me = this, - value = me.getValue(), - valueIsNull = value === null; - - me.callParent(arguments); - }, - getValue: function () { - var v = this.callParent(arguments); - - if (this.siPrefix) { - return { - value: v, - symbol: this.siPrefix.get("symbol"), - power: this.siPrefix.get("power"), - siprefix_id: this.siPrefix.get("id") - }; - } else { - return { - value: v, - symbol: "", - power: 1, - siprefix_id: null - }; - } - }, - /** - * Replaces any existing {@link #minValue} with the new value. - * @param {Number} value The minimum value - */ - setMinValue : function(value) { - this.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY); - }, - - /** - * Replaces any existing {@link #maxValue} with the new value. - * @param {Number} value The maximum value - */ - setMaxValue: function(value) { - this.maxValue = Ext.Number.from(value, Number.MAX_VALUE); - }, - - // private - parseValue : function(value) { - value = parseFloat(String(value).replace(this.decimalSeparator, '.')); - return isNaN(value) ? null : value; - }, - - /** - * @private - * - */ - fixPrecision : function(value) { - var me = this, - nan = isNaN(value), - precision = me.decimalPrecision; - - if (nan || !value) { - return nan ? '' : value; - } else if (!me.allowDecimals || precision <= 0) { - precision = 0; - } - - return parseFloat(Ext.Number.toFixed(parseFloat(value), precision)); - }, - - beforeBlur : function() { - var me = this, - v = me.parseValue(me.getRawValue()); - - if (!Ext.isEmpty(v)) { - me.setValue(v); - } - }, - findSiPrefix: function (value) { - var store = PartKeepr.getApplication().getSiPrefixStore(); - var symbol; - - for (var i=0;i<store.count();i++) { - - symbol = store.getAt(i).get("symbol"); - - if (symbol !== "") { - if (strpos(value, symbol) !== false) { - return store.getAt(i); - } - } else { - emptyPrefix = store.getAt(i); - } - - } - - if (emptyPrefix) { - return emptyPrefix; - } else { - return null; - } - }, - setValue: function (v) { - if (Ext.isObject(v)) { - this.siPrefix = PartKeepr.getApplication().getSiPrefixStore().getById(v.siprefix_id); - - return this.callParent([v.value]); - } else { - return v; - } - }, - processRawValue: function (value) { - var prefix; - - value = PartKeepr.getApplication().convertMicroToMu(value); - - var siPrefix = this.findSiPrefix(value); - - this.siPrefix = siPrefix; - - if (siPrefix !== null) { - value = str_replace(siPrefix.get("symbol"), "", value); - return { value: value, symbol: siPrefix.get("symbol"), power: siPrefix.get("power"), siprefix_id: siPrefix.get("id") }; - } else { - return { value: value, symbol: "", power: 0, siprefix_id: null }; - } + ]; + + this.callParent(arguments); + this.down("#"+this.siUnitItemId).setStore(Ext.create("Ext.data.Store", + { + model: 'PartKeepr.SiPrefixBundle.Entity.SiPrefix', + pageSize: 99999999, + autoLoad: true + })); } -});- \ No newline at end of file +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitList.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/SiUnitList.js @@ -3,5 +3,38 @@ Ext.define('PartKeepr.SiUnitList', { alias: 'widget.siunitlist', getInnerTpl: function(displayField) { return '<span style="display: inline-block; width: 15px;">{' + displayField + '}</span><span style="display: inline-block; width: 40px;">{prefix}</span>(10<sup>{exponent}</span>)'; - } -});- \ No newline at end of file + }, + initComponent: function () { + this.callParent(arguments); + + this.getSelectionModel().on("select", this.onItemSelect, this); + + }, + onItemSelect: function(selection, record) { + if (record !== null) { + this.pickerField.collapse(); + } + console.log(record); + return; + // The selection change events won't fire when clicking on the selected element. Detect it here. + var me = this, + pickerField = me.pickerField, + valueField = pickerField.valueField, + selected = me.getSelectionModel().getSelection(); + + console.log(valueField); + console.log(me.getSelectionModel()); + if (!pickerField.multiSelect && selected.length) { + selected = selected[0]; + // Not all pickerField's have a collapse API, i.e. Ext.ux.form.MultiSelect. + console.log(selected); + console.log(record.get(valueField)); + console.log(selected.get(valueField)); + + if (selected && pickerField.isEqual(record.get(valueField), selected.get(valueField)) && pickerField.collapse) { + console.log("FOO123"); + pickerField.collapse(); + } + } + }, +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/UnitComboBox.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Widgets/UnitComboBox.js @@ -1,6 +1,9 @@ Ext.define("PartKeepr.UnitComboBox",{ extend:"PartKeepr.ReloadableComboBox", alias: 'widget.UnitComboBox', + forceSelection: true, + allowBlank: true, + emptyText: i18n("Unit"), initComponent: function () { this.store = PartKeepr.getApplication().getUnitStore(); this.callParent(); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Data/store/CurrencyStore.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Data/store/CurrencyStore.js @@ -0,0 +1,18 @@ +Ext.define("PartKeepr.Data.Store.CurrencyStore", { + extend: "Ext.data.Store", + fields: ["code", "name", "symbol"], + + constructor: function () + { + this.callParent(arguments); + + this.setProxy({ + url: PartKeepr.getBasePath() + "/api/currencies", + type: "ajax", + reader: { + type: 'json', + rootProperty: '' + } + }); + } +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/ExtJS/Enhancements/Ext.form.field.ComboBox-associationSupport.js b/src/PartKeepr/FrontendBundle/Resources/public/js/ExtJS/Enhancements/Ext.form.field.ComboBox-associationSupport.js @@ -8,10 +8,13 @@ Ext.define('Ext.form.field.ComboBox', { returnObject: false }, getValue: function () { - if (this.getReturnObject() == true) { + if (this.getReturnObject() === true) { + if (this.callParent(arguments) === "" && this.allowBlank === true) { + return null; + } return this.getSelection(); } else { return this.callParent(arguments); } } -});- \ No newline at end of file +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/PartKeepr.js b/src/PartKeepr/FrontendBundle/Resources/public/js/PartKeepr.js @@ -24,6 +24,7 @@ Ext.application({ // Set static data of the server PartKeepr.setMaxUploadSize(window.parameters.maxUploadSize); PartKeepr.setAvailableImageFormats(window.parameters.availableImageFormats); + PartKeepr.setOctoPartAvailable(window.parameters.isOctoPartAvailable); var authenticationProvider = Ext.create(window.parameters.authentication_provider); PartKeepr.Auth.AuthenticationProvider.setAuthenticationProvider(authenticationProvider); @@ -268,6 +269,10 @@ Ext.application({ autoLoad: true }); + this.currencyStore = Ext.create("PartKeepr.Data.Store.CurrencyStore", { + autoLoad: true + }); + this.distributorStore = Ext.create("Ext.data.Store", { model: 'PartKeepr.DistributorBundle.Entity.Distributor', @@ -473,6 +478,10 @@ Ext.application({ { return this.distributorStore; }, + getCurrencyStore: function () + { + return this.currencyStore; + }, getDefaultPartUnit: function () { return this.partUnitStore.findRecord("default", true); @@ -485,6 +494,23 @@ Ext.application({ { return this.siPrefixStore; }, + uploadFileFromURL: function (url, description, callback, scope) + { + var uploadURL = PartKeepr.getBasePath() + "/api/temp_uploaded_files/upload"; + + var options = { + url: uploadURL, + method: 'POST', + params: { + description: description, + url: url + }, + callback: callback, + scope: scope + }; + + Ext.Ajax.request(options); + }, /** * Converts the Character "micro" (µ, available on german keyboards via AltGr+m) to the Character "Mu" (μ). * @@ -625,12 +651,22 @@ Ext.application({ { return this.username; }, - formatCurrency: function (value) + formatCurrency: function (value, code) { var format = Ext.util.Format; format.currencyPrecision = PartKeepr.getApplication().getUserPreference( "partkeepr.formatting.currency.numdecimals", 2); + format.currencySign = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.symbol", "€"); + + if (code !== null) { + var currency = this.getCurrencyStore().findRecord("code", code, 0, false, false, true); + + if (currency !== null) { + format.currencySign = currency.get("symbol"); + } + } + format.currencyAtEnd = PartKeepr.getApplication().getUserPreference( "partkeepr.formatting.currency.currencySymbolAtEnd", true); @@ -697,6 +733,16 @@ PartKeepr.getMaxUploadSize = function () return PartKeepr.maxUploadSize; }; +PartKeepr.setOctoPartAvailable = function (octoPartAvailable) +{ + PartKeepr.octoPartAvailable = octoPartAvailable; +}; + +PartKeepr.isOctoPartAvailable = function () +{ + return PartKeepr.octoPartAvailable; +}; + PartKeepr.bytesToSize = function (bytes) { var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; diff --git a/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig b/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig @@ -79,7 +79,8 @@ {% javascripts output='js/compiled/main2.js' '@PartKeeprFrontendBundle/Resources/public/js/Util/i18n.js' - '@PartKeeprFrontendBundle/Resources/public/js/Data/ReflectionFieldTreeModel.js' + '@PartKeeprFrontendBundle/Resources/public/js/Data/store/CurrencyStore.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' @@ -219,11 +220,13 @@ '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/PartUnitComboBox.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/StorageLocationComboBox.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/ResistorCalculator.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/Widgets/SiUnitCombo.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/ContextMenu/CharPickerMenu.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Editor/Editor.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Distributor/DistributorEditor.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Part/Editor/PartEditor.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Manufacturer/ManufacturerEditor.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/Part/Editor/PartParameterValueEditor.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/PartMeasurementUnit/PartMeasurementUnitEditor.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Unit/UnitEditor.js' '@PartKeeprFrontendBundle/Resources/public/js/Components/Footprint/FootprintEditor.js' @@ -289,6 +292,9 @@ '@PartKeeprFrontendBundle/Resources/public/js/Ext.ux.Wizard.Header.js' '@PartKeeprFrontendBundle/Resources/public/js/Ext.ux.Wizard.js' '@PartKeeprFrontendBundle/Resources/public/js/Ext.ux.Wizard.CardLayout.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/OctoPart/SearchPanel.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/OctoPart/SearchWindow.js' + '@PartKeeprFrontendBundle/Resources/public/js/Components/OctoPart/DataApplicator.js' '@PartKeeprFrontendBundle/Resources/public/js/php.default.min.js' %} <script type="text/javascript" src="{{ asset_url }}"></script> {% endjavascripts %} diff --git a/src/PartKeepr/ImageBundle/Tests/ImageControllerTest.php b/src/PartKeepr/ImageBundle/Tests/ImageControllerTest.php @@ -62,7 +62,6 @@ class ImageControllerTest extends WebTestCase $uri ); - $this->assertEquals(404, $client->getResponse()->getStatusCode()); } } diff --git a/src/PartKeepr/ImportBundle/Configuration/BaseConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/BaseConfiguration.php @@ -1,9 +1,7 @@ <?php - namespace PartKeepr\ImportBundle\Configuration; - use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\ORM\EntityManager; use Dunglas\ApiBundle\Api\IriConverter; @@ -24,9 +22,9 @@ class BaseConfiguration protected $iriConverter; - static $logs = []; + public static $logs = []; - static $persistEntities = []; + public static $persistEntities = []; public function __construct(ClassMetadata $classMetadata, $baseEntity, ReflectionService $reflectionService, EntityManager $em, AdvancedSearchFilter $advancedSearchFilter, IriConverter $iriConverter) { @@ -38,28 +36,32 @@ class BaseConfiguration $this->iriConverter = $iriConverter; } - public function import ($row) { - + public function import($row) + { } - public function persist ($entity) { + public function persist($entity) + { self::$persistEntities[] = $entity; } - public function getPersistEntities () { + public function getPersistEntities() + { return self::$persistEntities; } - - public function log ($message) { + public function log($message) + { self::$logs[] = $message; } - public function getLog () { + public function getLog() + { return self::$logs; } - public function clearLog () { + public function clearLog() + { self::$logs = []; } } diff --git a/src/PartKeepr/ImportBundle/Configuration/Configuration.php b/src/PartKeepr/ImportBundle/Configuration/Configuration.php @@ -1,10 +1,8 @@ <?php + namespace PartKeepr\ImportBundle\Configuration; -use Symfony\Component\Finder\Tests\Iterator\Iterator; use Symfony\Component\PropertyAccess\PropertyAccess; -use Symfony\Component\PropertyAccess\Tests\Fixtures\TraversableArrayObject; - class Configuration extends BaseConfiguration { @@ -79,8 +77,9 @@ class Configuration extends BaseConfiguration return true; } - public function import ($row) { - $obj = new $this->baseEntity; + public function import($row) + { + $obj = new $this->baseEntity(); $this->persist($obj); $accessor = PropertyAccess::createPropertyAccessor(); @@ -105,7 +104,7 @@ class Configuration extends BaseConfiguration $name = $oneToManyAssociation->getAssociationName(); $data = $oneToManyAssociation->import($row); if ($data !== null) { - $accessor->setValue($obj, $name, array($data)); + $accessor->setValue($obj, $name, [$data]); } } diff --git a/src/PartKeepr/ImportBundle/Configuration/FieldConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/FieldConfiguration.php @@ -1,6 +1,6 @@ <?php -namespace PartKeepr\ImportBundle\Configuration; +namespace PartKeepr\ImportBundle\Configuration; class FieldConfiguration extends BaseConfiguration { @@ -75,14 +75,16 @@ class FieldConfiguration extends BaseConfiguration switch ($this->fieldConfiguration) { case self::FIELDCONFIGURATION_FIXEDVALUE: $this->log(sprintf("Would set field %s to fixed value %s", $this->fieldName, $this->fixedValue)); + return $this->fixedValue; break; case self::FIELDCONFIGURATION_COPYFROM: $this->log(sprintf("Would set field %s to value %s (import column %s)", $this->fieldName, $row[$this->copyFromField], $this->copyFromField)); + return $row[$this->copyFromField]; break; default: - return null; + return; } } } diff --git a/src/PartKeepr/ImportBundle/Configuration/ManyToOneConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/ManyToOneConfiguration.php @@ -1,6 +1,6 @@ <?php -namespace PartKeepr\ImportBundle\Configuration; +namespace PartKeepr\ImportBundle\Configuration; class ManyToOneConfiguration extends Configuration { @@ -127,6 +127,7 @@ class ManyToOneConfiguration extends Configuration case self::IMPORTBEHAVIOUR_ALWAYSSETTO: $targetEntity = $this->iriConverter->getItemFromIri($this->setToEntity); $this->log(sprintf("Would set %s to %s#%s", $this->associationName, $this->baseEntity, $targetEntity->getId())); + return $targetEntity; break; case self::IMPORTBEHAVIOUR_MATCHDATA: @@ -159,22 +160,24 @@ class ManyToOneConfiguration extends Configuration } $this->log(sprintf("Would set %s to %s#%s", $this->associationName, $this->baseEntity, $result->getId())); + return $result; } catch (\Exception $e) { - } switch ($this->notFoundBehaviour) { case self::NOTFOUNDBEHAVIOUR_STOPIMPORT: - $this->log(sprintf("Would stop import as the match %s for association %s was not found", implode(",",$descriptions), $this->getAssociationName())); + $this->log(sprintf("Would stop import as the match %s for association %s was not found", implode(",", $descriptions), $this->getAssociationName())); break; case self::NOTFOUNDBEHAVIOUR_SETTOENTITY: $targetEntity = $this->iriConverter->getItemFromIri($this->notFoundSetToEntity); - $this->log(sprintf("Would set the association %s to %s, since the match %s for association %s was not found", $this->getAssociationName(), $this->notFoundSetToEntity, implode(",",$descriptions))); + $this->log(sprintf("Would set the association %s to %s, since the match %s for association %s was not found", $this->getAssociationName(), $this->notFoundSetToEntity, implode(",", $descriptions))); + return $targetEntity; break; case self::NOTFOUNDBEHAVIOUR_CREATEENTITY: $this->log(sprintf("Would create a new entity of type %s", $this->baseEntity)); + return parent::import($row); break; @@ -182,8 +185,6 @@ class ManyToOneConfiguration extends Configuration break; } - - return null; } /** @@ -201,5 +202,4 @@ class ManyToOneConfiguration extends Configuration { $this->associationName = $associationName; } - } diff --git a/src/PartKeepr/ImportBundle/Configuration/OneToManyConfiguration.php b/src/PartKeepr/ImportBundle/Configuration/OneToManyConfiguration.php @@ -1,6 +1,6 @@ <?php -namespace PartKeepr\ImportBundle\Configuration; +namespace PartKeepr\ImportBundle\Configuration; class OneToManyConfiguration extends Configuration { @@ -35,15 +35,14 @@ class OneToManyConfiguration extends Configuration { switch ($this->importBehaviour) { case self::IMPORTBEHAVIOUR_IGNORE: - return null; + return; break; case self::IMPORTBEHAVIOUR_CREATENEW: $this->log(sprintf("Would create a new entity of type %s for relation %s", $this->baseEntity, $this->getAssociationName())); + return parent::import($row); break; } - - return null; } /** @@ -61,5 +60,4 @@ class OneToManyConfiguration extends Configuration { $this->associationName = $associationName; } - } diff --git a/src/PartKeepr/ImportBundle/Controller/ImportController.php b/src/PartKeepr/ImportBundle/Controller/ImportController.php @@ -1,8 +1,7 @@ <?php -namespace PartKeepr\ImportBundle\Controller; +namespace PartKeepr\ImportBundle\Controller; -use Dunglas\ApiBundle\JsonLd\Response; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\JsonResponse; @@ -13,11 +12,11 @@ class ImportController extends Controller { protected function detectFileFormat() { - } /** * @Route("/getSource/") + * * @return JsonResponse */ public function getSourceAction(Request $request) @@ -30,6 +29,7 @@ class ImportController extends Controller /** * @Route("/getPreview/") * @Method({"POST"}) + * * @return JsonResponse */ public function getPreviewAction(Request $request) @@ -39,7 +39,6 @@ class ImportController extends Controller $configuration = json_decode($request->get("configuration")); $baseEntity = $request->get("baseEntity"); - $data = $this->extractCSVData($tempFileIri, false); $importService = $this->get("importer_service"); $importService->setBaseEntity($baseEntity); @@ -53,9 +52,11 @@ class ImportController extends Controller /** * @Route("/executeImport/") * @Method({"POST"}) + * * @return JsonResponse */ - public function importAction (Request $request) { + public function importAction(Request $request) + { $tempFileIri = $request->get("file"); $configuration = json_decode($request->get("configuration")); diff --git a/src/PartKeepr/ImportBundle/Entity/ImportPreset.php b/src/PartKeepr/ImportBundle/Entity/ImportPreset.php @@ -15,7 +15,7 @@ use Symfony\Component\Serializer\Annotation\Groups; class ImportPreset extends BaseEntity { /** - * Holds the base entity + * Holds the base entity. * * @ORM\Column(length=255) * @Groups({"default"}) @@ -35,7 +35,7 @@ class ImportPreset extends BaseEntity private $name; /** - * Defines the preset configuration + * Defines the preset configuration. * * @ORM\Column(type="text") * @Groups({"default"}) diff --git a/src/PartKeepr/ImportBundle/Service/ImporterService.php b/src/PartKeepr/ImportBundle/Service/ImporterService.php @@ -1,6 +1,6 @@ <?php -namespace PartKeepr\ImportBundle\Service; +namespace PartKeepr\ImportBundle\Service; use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\ORM\EntityManager; @@ -72,7 +72,7 @@ class ImporterService $entity = $configuration->import($row); $entities[] = $entity; $logs[] = implode("<br/>", - [ "data" => implode(",",$row), '<p style="text-indent: 50px;">', "log" => " ".implode("<br/> ", $configuration->getLog() ), '</p>']); + ["data" => implode(",", $row), '<p style="text-indent: 50px;">', "log" => " ".implode("<br/> ", $configuration->getLog()), '</p>']); $configuration->clearLog(); } diff --git a/src/PartKeepr/ManufacturerBundle/Entity/Manufacturer.php b/src/PartKeepr/ManufacturerBundle/Entity/Manufacturer.php @@ -252,7 +252,7 @@ class Manufacturer extends BaseEntity */ public function getIcLogos() { - return $this->icLogos; + return $this->icLogos->getValues(); } /** diff --git a/src/PartKeepr/OctoPartBundle/Controller/DefaultController.php b/src/PartKeepr/OctoPartBundle/Controller/DefaultController.php @@ -0,0 +1,68 @@ +<?php + +namespace PartKeepr\OctoPartBundle\Controller; + +use FOS\RestBundle\Controller\Annotations\View; +use FOS\RestBundle\Controller\FOSRestController; +use Sensio\Bundle\FrameworkExtraBundle\Configuration as Routing; +use Symfony\Component\HttpFoundation\Request; + +class DefaultController extends FOSRestController +{ + /** + * @Routing\Route("/api/octopart/get/{id}", defaults={"method" = "GET","_format" = "json"}) + * @Routing\Method({"GET"}) + * + * @param $id string the Part UID + * + * @View() + * + * @return \stdClass + */ + public function indexAction($id) + { + $data = $this->get("partkeepr.octopart_service")->getPartByUID($id); + + return $data; + } + + /** + * @Routing\Route("/api/octopart/query/", defaults={"method" = "GET","_format" = "json"}) + * @Routing\Method({"GET"}) + * + * @param Request $request + * + * @View() + * + * @return array + */ + public function getPartsByQueryAction(Request $request) + { + $start = 0; + + $responseData = []; + + $query = $request->query->get("q"); + + if ($request->query->has("page")) { + $start = $request->query->get("page"); + } + + $data = $this->get("partkeepr.octopart_service")->getPartyByQuery($query, $start); + + $responseData["hits"] = $data["hits"]; + $responseData["results"] = []; + + foreach ($data["results"] as $result) { + $responseItem = []; + $responseItem["mpn"] = $result["item"]["mpn"]; + $responseItem["title"] = $result["snippet"]; + $responseItem["manufacturer"] = $result["item"]["manufacturer"]["name"]; + $responseItem["url"] = $result["item"]["octopart_url"]; + $responseItem["uid"] = $result["item"]["uid"]; + $responseData["results"][] = $responseItem; + } + + return $responseData; + } +} diff --git a/src/PartKeepr/OctoPartBundle/DependencyInjection/Configuration.php b/src/PartKeepr/OctoPartBundle/DependencyInjection/Configuration.php @@ -0,0 +1,29 @@ +<?php + +namespace PartKeepr\OctoPartBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This is the class that validates and merges configuration from your app/config files. + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} + */ +class Configuration implements ConfigurationInterface +{ + /** + * {@inheritdoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $treeBuilder->root('part_keepr_octopart'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/src/PartKeepr/OctoPartBundle/DependencyInjection/PartKeeprOctoPartExtension.php b/src/PartKeepr/OctoPartBundle/DependencyInjection/PartKeeprOctoPartExtension.php @@ -0,0 +1,28 @@ +<?php + +namespace PartKeepr\OctoPartBundle\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +/** + * This is the class that loads and manages your bundle configuration. + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} + */ +class PartKeeprOctoPartExtension extends Extension +{ + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $this->processConfiguration($configuration, $configs); + + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } +} diff --git a/src/PartKeepr/OctoPartBundle/PartKeeprOctoPartBundle.php b/src/PartKeepr/OctoPartBundle/PartKeeprOctoPartBundle.php @@ -0,0 +1,9 @@ +<?php + +namespace PartKeepr\OctoPartBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class PartKeeprOctoPartBundle extends Bundle +{ +} diff --git a/src/PartKeepr/OctoPartBundle/Resources/config/services.xml b/src/PartKeepr/OctoPartBundle/Resources/config/services.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" ?> + +<container xmlns="http://symfony.com/schema/dic/services" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + <services> + <service id="partkeepr.octopart_service" class="PartKeepr\OctoPartBundle\Services\OctoPartService"> + <argument type="string">%partkeepr.octopart.apikey%</argument> + </service> + </services> +</container> diff --git a/src/PartKeepr/OctoPartBundle/Services/OctoPartService.php b/src/PartKeepr/OctoPartBundle/Services/OctoPartService.php @@ -0,0 +1,55 @@ +<?php + +namespace PartKeepr\OctoPartBundle\Services; + +use Guzzle\Http\Client; + +class OctoPartService +{ + const OCTOPART_ENDPOINT = "http://octopart.com/api/v3/"; + + private $apiKey; + + public function __construct($apiKey) + { + $this->apiKey = $apiKey; + } + + public function getPartByUID($uid) + { + $client = new Client(); + $request = $client->createRequest('GET', self::OCTOPART_ENDPOINT."parts/".$uid); + + $request->getQuery()->add("apikey", $this->apiKey); + $request->getQuery()->add("include", [ + "short_description", + "datasheets", + "compliance_documents", + "descriptions", + "imagesets", + "specs", + "reference_designs", + "cad_models", + ]); + + $request->send(); + + return json_decode($request->getResponse()->getBody(), true); + } + + public function getPartyByQuery($query, $start = 0) + { + $client = new Client(); + $request = $client->createRequest('GET', self::OCTOPART_ENDPOINT."parts/search"); + + $request->getQuery()->add("apikey", $this->apiKey); + $request->getQuery()->add("q", $query); + $request->getQuery()->add("start", ($start - 1) * 20); + $request->getQuery()->add("include", ["short_description"]); + $request->getQuery()->add("limit", 20); + + $request->send(); + + return json_decode($request->getResponse()->getBody(), true); + } +} diff --git a/src/PartKeepr/PartBundle/Action/PartPostAction.php b/src/PartKeepr/PartBundle/Action/PartPostAction.php @@ -52,12 +52,12 @@ class PartPostAction } /** - * @var $resourceType ResourceInterface + * @var ResourceInterface */ list($resourceType, $format) = $this->extractAttributes($request); /** - * @var $part Part + * @var Part */ $part = $this->serializer->deserialize( $request->getContent(), diff --git a/src/PartKeepr/PartBundle/Action/PartPutAction.php b/src/PartKeepr/PartBundle/Action/PartPutAction.php @@ -29,7 +29,7 @@ class PartPutAction */ private $serializer; - /** + /** * @var PartService */ private $partService; @@ -60,7 +60,7 @@ class PartPutAction public function __invoke(Request $request, $id) { /** - * @var $resourceType ResourceInterface + * @var ResourceInterface */ list($resourceType, $format) = $this->extractAttributes($request); @@ -82,7 +82,7 @@ class PartPutAction $context['object_to_populate'] = $data; /** - * @var $part Part + * @var Part */ $part = $this->serializer->deserialize( $requestData, diff --git a/src/PartKeepr/PartBundle/Controller/PartController.php b/src/PartKeepr/PartBundle/Controller/PartController.php @@ -62,4 +62,19 @@ class PartController extends FOSRestController $this->get('doctrine.orm.entity_manager')->flush(); } + + /** + * @Routing\Route("/api/parts/getPartParameterNames", defaults={"method" = "get","_format" = "json"}) + * @View() + * + * @return array An array with name and description properties + */ + public function getParameterNamesAction() + { + $dql = "SELECT p.name, p.description FROM PartKeepr\PartBundle\Entity\PartParameter p GROUP BY p.name, p.description"; + + $query = $this->get("doctrine.orm.default_entity_manager")->createQuery($dql); + + return $query->getArrayResult(); + } } diff --git a/src/PartKeepr/PartBundle/Entity/Part.php b/src/PartKeepr/PartBundle/Entity/Part.php @@ -179,6 +179,7 @@ class Part extends BaseEntity * * @ORM\OneToMany(targetEntity="PartKeepr\PartBundle\Entity\PartParameter", * mappedBy="part",cascade={"persist", "remove"}, orphanRemoval=true) + * @Groups({"default"}) * * @var ArrayCollection */ @@ -486,7 +487,7 @@ class Part extends BaseEntity */ public function getDistributors() { - return $this->distributors; + return $this->distributors->getValues(); } /** @@ -496,7 +497,7 @@ class Part extends BaseEntity */ public function getAttachments() { - return $this->attachments; + return $this->attachments->getValues(); } /** @@ -506,17 +507,17 @@ class Part extends BaseEntity */ public function getManufacturers() { - return $this->manufacturers; + return $this->manufacturers->getValues(); } /** * Returns the parameters assigned to this part. * - * @return array An array of PartParameter objects + * @return ArrayCollection An array of PartParameter objects */ public function getParameters() { - return $this->parameters; + return $this->parameters->getValues(); } /** @@ -638,7 +639,7 @@ class Part extends BaseEntity */ public function getStockLevels() { - return $this->stockLevels; + return $this->stockLevels->getValues(); } /** @@ -712,7 +713,7 @@ class Part extends BaseEntity * * For a list of exceptions, see * - * @see PartKeepr\Part.Part::onPrePersist() + * @see Part::onPrePersist() * * @ORM\PreUpdate */ @@ -764,6 +765,30 @@ class Part extends BaseEntity } /** + * Adds a Part Parameter. + * + * @param PartParameter $partParameter A parameter to add + */ + public function addParameter($partParameter) + { + if ($partParameter instanceof PartParameter) { + $partParameter->setPart($this); + } + $this->parameters->add($partParameter); + } + + /** + * Removes a Part Parameter. + * + * @param PartParameter $partParameter An parameter to remove + */ + public function removeParameter($partParameter) + { + $partParameter->setPart(null); + $this->parameters->removeElement($partParameter); + } + + /** * Adds a Part Attachment. * * @param PartAttachment $partAttachment An attachment to add @@ -783,7 +808,10 @@ class Part extends BaseEntity */ public function removeAttachment($partAttachment) { - $partAttachment->setPart(null); + if ($partAttachment instanceof PartAttachment) { + $partAttachment->setPart(null); + } + $this->attachments->removeElement($partAttachment); } @@ -838,7 +866,7 @@ class Part extends BaseEntity */ public function getProjectParts() { - return $this->projectParts; + return $this->projectParts->getValues(); } /** diff --git a/src/PartKeepr/PartBundle/Entity/PartCategory.php b/src/PartKeepr/PartBundle/Entity/PartCategory.php @@ -71,7 +71,7 @@ class PartCategory extends AbstractCategory implements CategoryPathInterface */ public function getChildren() { - return $this->children; + return $this->children->getValues(); } /** diff --git a/src/PartKeepr/PartBundle/Entity/PartDistributor.php b/src/PartKeepr/PartBundle/Entity/PartDistributor.php @@ -7,6 +7,7 @@ use PartKeepr\CoreBundle\Entity\BaseEntity; use PartKeepr\DistributorBundle\Entity\Distributor; use PartKeepr\PartBundle\Exceptions\PackagingUnitOutOfRangeException; use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; /** * This class represents the link between a part and a distributor. @@ -61,6 +62,16 @@ class PartDistributor extends BaseEntity private $price; /** + * Specifies the currency of the part. + * + * @ORM\Column(type="string", length=3,nullable=true) + * @Groups({"default"}) + * + * @Assert\Currency + */ + private $currency; + + /** * The distributor's SKU (stock keeping unit) for the part. Used with barcodes. * * @ORM\Column(type="string",nullable=true) @@ -79,6 +90,32 @@ class PartDistributor extends BaseEntity } /** + * @return mixed + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @param mixed $currency + */ + public function setCurrency($currency) + { + $this->currency = $currency; + } + + /** + * Returns the packaging unit. + * + * @return int The packaging unit + */ + public function getPackagingUnit() + { + return $this->packagingUnit; + } + + /** * Sets the packaging unit for a specific distributor. * * For example, some distributors only sell resistors in packs of 100, so you can't order just one. We use the @@ -102,13 +139,13 @@ class PartDistributor extends BaseEntity } /** - * Returns the packaging unit. + * Returns the part. * - * @return int The packaging unit + * @return Part The part */ - public function getPackagingUnit() + public function getPart() { - return $this->packagingUnit; + return $this->part; } /** @@ -122,13 +159,13 @@ class PartDistributor extends BaseEntity } /** - * Returns the part. + * Returns the distributor. * - * @return Part The part + * @return Distributor The distributor */ - public function getPart() + public function getDistributor() { - return $this->part; + return $this->distributor; } /** @@ -142,13 +179,13 @@ class PartDistributor extends BaseEntity } /** - * Returns the distributor. + * Returns the order number. * - * @return Distributor The distributor + * @return string The order number */ - public function getDistributor() + public function getOrderNumber() { - return $this->distributor; + return $this->orderNumber; } /** @@ -162,13 +199,11 @@ class PartDistributor extends BaseEntity } /** - * Returns the order number. - * - * @return string The order number + * Returns the price. */ - public function getOrderNumber() + public function getPrice() { - return $this->orderNumber; + return $this->price; } /** @@ -182,11 +217,13 @@ class PartDistributor extends BaseEntity } /** - * Returns the price. + * Returns the SKU (stock keeping unit). + * + * @return string The SKU */ - public function getPrice() + public function getSku() { - return $this->price; + return $this->sku; } /** @@ -198,14 +235,4 @@ class PartDistributor extends BaseEntity { $this->sku = $sku; } - - /** - * Returns the SKU (stock keeping unit). - * - * @return string The SKU - */ - public function getSku() - { - return $this->sku; - } } diff --git a/src/PartKeepr/PartBundle/Entity/PartMeasurementUnit.php b/src/PartKeepr/PartBundle/Entity/PartMeasurementUnit.php @@ -147,6 +147,6 @@ class PartMeasurementUnit extends BaseEntity */ public function getParts() { - return $this->parts; + return $this->parts->getValues(); } } diff --git a/src/PartKeepr/PartBundle/Entity/PartParameter.php b/src/PartKeepr/PartBundle/Entity/PartParameter.php @@ -3,8 +3,10 @@ namespace PartKeepr\PartBundle\Entity; use Doctrine\ORM\Mapping as ORM; +use PartKeepr\CoreBundle\Entity\BaseEntity; use PartKeepr\SiPrefixBundle\Entity\SiPrefix; use PartKeepr\UnitBundle\Entity\Unit; +use Symfony\Component\Serializer\Annotation\Groups; /** * This object represents a parameter. Each parameter can have an unit (defined by the class "Unit") associated with @@ -12,15 +14,13 @@ use PartKeepr\UnitBundle\Entity\Unit; * * @ORM\Entity @ORM\HasLifecycleCallbacks */ -class PartParameter +class PartParameter extends BaseEntity { - /** - * @ORM\Id @ORM\Column(type="integer") - * @ORM\GeneratedValue(strategy="AUTO") - * - * @var int - */ - private $id; + const VALUE_TYPE_STRING = 'string'; + + const VALUE_TYPE_NUMERIC = 'numeric'; + + const VALUE_TYPES = [self::VALUE_TYPE_STRING, self::VALUE_TYPE_NUMERIC]; /** * @ORM\ManyToOne(targetEntity="PartKeepr\PartBundle\Entity\Part", inversedBy="parameters") @@ -34,6 +34,7 @@ class PartParameter * The name of the parameter (e.g. Resistance, Voltage). * * @ORM\Column(type="string") + * @Groups({"default"}) * * @var string */ @@ -43,6 +44,7 @@ class PartParameter * A description for this parameter. * * @ORM\Column(type="string") + * @Groups({"default"}) * * @var string */ @@ -52,6 +54,7 @@ class PartParameter * The unit for this type. May be null. * * @ORM\ManyToOne(targetEntity="PartKeepr\UnitBundle\Entity\Unit") + * @Groups({"default"}) * * @var \PartKeepr\UnitBundle\Entity\Unit */ @@ -63,38 +66,188 @@ class PartParameter * Example: If you have 10µ, the value field will contain "10", the prefix object is linked to the SiPrefix * representing "µ" and the rawValue field will contain 0.000001 * - * @ORM\Column(type="float") + * @ORM\Column(type="float",nullable=true) + * @Groups({"default"}) * * @var float */ private $value; /** + * The maximum value of the parameter. + * + * @ORM\Column(type="float",name="maximumValue",nullable=true) + * @Groups({"default"}) + * + * @var float + */ + private $maxValue; + + /** + * The minimum value of the parameter. + * + * @ORM\Column(type="float",name="minimumValue",nullable=true) + * @Groups({"default"}) + * + * @var float + */ + private $minValue; + + /** + * The string value if the parameter is a string. + * + * @ORM\Column(type="string") + * @Groups({"default"}) + * + * @var string + */ + private $stringValue; + + /** + * The type of the value. + * + * @ORM\Column(type="string") + * @Groups({"default"}) + * + * @var string + */ + private $valueType; + + /** * The SiPrefix of the unit. * * @ORM\ManyToOne(targetEntity="PartKeepr\SiPrefixBundle\Entity\SiPrefix") + * @Groups({"default"}) * - * @var object + * @var SiPrefix */ private $siPrefix; /** - * The raw value of the unit. + * The SiPrefix of the min value. * - * @ORM\Column(type="float") + * @ORM\ManyToOne(targetEntity="PartKeepr\SiPrefixBundle\Entity\SiPrefix") + * @Groups({"default"}) * - * @var float + * @var SiPrefix */ - private $rawValue; + private $minSiPrefix; /** - * Sets the name for this parameter. + * The SiPrefix of the min value. * - * @param string $name The name + * @ORM\ManyToOne(targetEntity="PartKeepr\SiPrefixBundle\Entity\SiPrefix") + * @Groups({"default"}) + * + * @var SiPrefix */ - public function setName($name) + private $maxSiPrefix; + + public function __construct() { - $this->name = $name; + $this->setValueType(self::VALUE_TYPE_STRING); + } + + /** + * @return SiPrefix + */ + public function getMinSiPrefix() + { + return $this->minSiPrefix; + } + + /** + * @param SiPrefix $minSiPrefix + */ + public function setMinSiPrefix($minSiPrefix) + { + $this->minSiPrefix = $minSiPrefix; + } + + /** + * @return SiPrefix + */ + public function getMaxSiPrefix() + { + return $this->maxSiPrefix; + } + + /** + * @param SiPrefix $maxSiPrefix + */ + public function setMaxSiPrefix($maxSiPrefix) + { + $this->maxSiPrefix = $maxSiPrefix; + } + + /** + * @return float + */ + public function getMaxValue() + { + return $this->maxValue; + } + + /** + * @param float $maxValue + */ + public function setMaxValue($maxValue) + { + $this->maxValue = $maxValue; + } + + /** + * @return float + */ + public function getMinValue() + { + return $this->minValue; + } + + /** + * @param float $minValue + */ + public function setMinValue($minValue) + { + $this->minValue = $minValue; + } + + /** + * @return string + */ + public function getStringValue() + { + return $this->stringValue; + } + + /** + * @param string $stringValue + */ + public function setStringValue($stringValue) + { + $this->stringValue = $stringValue; + } + + /** + * @return string + */ + public function getValueType() + { + return $this->valueType; + } + + /** + * @param string $valueType + * + * @throws \Exception + */ + public function setValueType($valueType) + { + if (!in_array($valueType, self::VALUE_TYPES)) { + throw new \Exception("Invalid value type given"); + } + + $this->valueType = $valueType; } /** @@ -108,13 +261,13 @@ class PartParameter } /** - * Sets the description for this parameter. + * Sets the name for this parameter. * - * @param string $description The description + * @param string $name The name */ - public function setDescription($description) + public function setName($name) { - $this->description = $description; + $this->name = $name; } /** @@ -128,13 +281,13 @@ class PartParameter } /** - * Sets the unit. + * Sets the description for this parameter. * - * @param \PartKeepr\UnitBundle\Entity\Unit $unit The unit to set + * @param string $description The description */ - public function setUnit(Unit $unit = null) + public function setDescription($description) { - $this->unit = $unit; + $this->description = $description; } /** @@ -148,13 +301,13 @@ class PartParameter } /** - * Sets the part. + * Sets the unit. * - * @param Part $part The part to set + * @param \PartKeepr\UnitBundle\Entity\Unit $unit The unit to set */ - public function setPart(Part $part) + public function setUnit(Unit $unit = null) { - $this->part = $part; + $this->unit = $unit; } /** @@ -168,15 +321,13 @@ class PartParameter } /** - * Sets the value. + * Sets the part. * - * @param float $value The value to set + * @param Part $part The part to set */ - public function setValue($value) + public function setPart(Part $part = null) { - $this->value = $value; - - $this->recalculateRawValue(); + $this->part = $part; } /** @@ -190,15 +341,13 @@ class PartParameter } /** - * Sets the si prefix for this parameter. + * Sets the value. * - * @param \PartKeepr\SiPrefixBundle\Entity\SiPrefix $prefix The prefix to set, or null + * @param float $value The value to set */ - public function setSiPrefix(SiPrefix $prefix = null) + public function setValue($value) { - $this->siPrefix = $prefix; - - $this->recalculateRawValue(); + $this->value = $value; } /** @@ -212,25 +361,12 @@ class PartParameter } /** - * Returns the ID for this object. - * - * @param none + * Sets the si prefix for this parameter. * - * @return int The ID for this object + * @param \PartKeepr\SiPrefixBundle\Entity\SiPrefix $prefix The prefix to set, or null */ - public function getId() - { - return $this->id; - } - - private function recalculateRawValue() + public function setSiPrefix(SiPrefix $prefix = null) { - if (is_object($this->getSiPrefix())) { - $power = $this->getSiPrefix()->getExponent(); - } else { - $power = 0; - } - - $this->rawValue = $this->getValue() * pow(10, $power); + $this->siPrefix = $prefix; } } diff --git a/src/PartKeepr/PartBundle/Services/PartService.php b/src/PartKeepr/PartBundle/Services/PartService.php @@ -15,7 +15,7 @@ class PartService private $partLimit; /** - * Whether to check if the internal part number is unique or not + * Whether to check if the internal part number is unique or not. * * @var bool */ @@ -50,14 +50,15 @@ class PartService } /** - * Checks if the given internal part number is unique + * Checks if the given internal part number is unique. * * @param string $internalPartNumber The internal part number to checkl * @param Part|null $part An optional part to exclude within the check * * @return bool */ - public function isInternalPartNumberUnique ($internalPartNumber, Part $part = null) { + public function isInternalPartNumberUnique($internalPartNumber, Part $part = null) + { if (!$this->checkInternalPartNumberUniqueness) { return true; } diff --git a/src/PartKeepr/PartBundle/Tests/InternalPartNumberTest.php b/src/PartKeepr/PartBundle/Tests/InternalPartNumberTest.php @@ -32,15 +32,15 @@ class InternalPartNumberTest extends WebTestCase $client = static::makeClient(true); /** - * @var $iriConverter IriConverter + * @var IriConverter */ $iriConverter = $this->getContainer()->get('api.iri_converter'); $part = [ - "name" => "foobar", - "storageLocation" => $iriConverter->getIriFromItem($this->fixtures->getReference("storagelocation.first")), - "category" => $iriConverter->getIriFromItem($this->fixtures->getReference("partcategory.first")), - "partUnit" => $iriConverter->getIriFromItem($this->fixtures->getReference("partunit.default")), + "name" => "foobar", + "storageLocation" => $iriConverter->getIriFromItem($this->fixtures->getReference("storagelocation.first")), + "category" => $iriConverter->getIriFromItem($this->fixtures->getReference("partcategory.first")), + "partUnit" => $iriConverter->getIriFromItem($this->fixtures->getReference("partunit.default")), "internalPartNumber" => "foo123", ]; diff --git a/src/PartKeepr/SetupBundle/Services/ConfigSetupService.php b/src/PartKeepr/SetupBundle/Services/ConfigSetupService.php @@ -32,19 +32,19 @@ class ConfigSetupService { // Parameter defaults to ensure they exist $parameters = [ - 'database_driver' => null, - 'database_host' => null, - 'database_port' => null, - 'database_name' => null, + 'database_driver' => null, + 'database_host' => null, + 'database_port' => null, + 'database_name' => null, 'database_password' => null, - 'mailer_transport' => null, - 'mailer_host' => null, - 'mailer_port' => null, + 'mailer_transport' => null, + 'mailer_host' => null, + 'mailer_port' => null, 'mailer_encryption' => null, - 'mailer_user' => null, - 'mailer_password' => null, - 'mailer_auth_mode' => null, + 'mailer_user' => null, + 'mailer_password' => null, + 'mailer_auth_mode' => null, 'authentication_provider' => 'PartKeepr.Auth.HTTPBasicAuthenticationProvider', @@ -52,37 +52,37 @@ class ConfigSetupService 'secret' => $this->generateSecret(), - 'fr3d_ldap.driver.host' => '127.0.0.1', - 'fr3d_ldap.driver.port' => 389, - 'fr3d_ldap.driver.username' => null, - 'fr3d_ldap.driver.password' => null, - 'fr3d_ldap.driver.bindRequiresDn' => false, - 'fr3d_ldap.driver.baseDn' => '', - 'fr3d_ldap.driver.accountFilterFormat' => null, - 'fr3d_ldap.driver.optReferrals' => null, - 'fr3d_ldap.driver.useSsl' => null, - 'fr3d_ldap.driver.useStartTls' => null, - 'fr3d_ldap.driver.accountCanonicalForm' => null, - 'fr3d_ldap.driver.accountDomainName' => null, + 'fr3d_ldap.driver.host' => '127.0.0.1', + 'fr3d_ldap.driver.port' => 389, + 'fr3d_ldap.driver.username' => null, + 'fr3d_ldap.driver.password' => null, + 'fr3d_ldap.driver.bindRequiresDn' => false, + 'fr3d_ldap.driver.baseDn' => '', + 'fr3d_ldap.driver.accountFilterFormat' => null, + 'fr3d_ldap.driver.optReferrals' => null, + 'fr3d_ldap.driver.useSsl' => null, + 'fr3d_ldap.driver.useStartTls' => null, + 'fr3d_ldap.driver.accountCanonicalForm' => null, + 'fr3d_ldap.driver.accountDomainName' => null, 'fr3d_ldap.driver.accountDomainNameShort' => null, - 'fr3d_ldap.user.enabled' => false, - 'fr3d_ldap.user.baseDn' => 'dc=example,dc=com', - 'fr3d_ldap.user.filter' => null, - 'fr3d_ldap.user.attribute.username' => 'samaccountname', - 'fr3d_ldap.user.attribute.email' => 'email', - - 'partkeepr.filesystem.data_directory' => '%kernel.root_dir%/../data/', - 'partkeepr.cronjob.check' => true, - 'partkeepr.filesystem.quota' => false, - 'partkeepr.auth.max_users' => 'unlimited', - 'partkeepr.category.path_separator' => ' ➤ ', - 'partkeepr.maintenance' => false, - 'partkeepr.maintenance.title' => '', - 'partkeepr.maintenance.message' => '', - 'cache.dunglas' => false, - 'cache.doctrine' => 'array', - 'partkeepr.parts.limit' => false, - 'partkeepr.users.limit' => false, + 'fr3d_ldap.user.enabled' => false, + 'fr3d_ldap.user.baseDn' => 'dc=example,dc=com', + 'fr3d_ldap.user.filter' => null, + 'fr3d_ldap.user.attribute.username' => 'samaccountname', + 'fr3d_ldap.user.attribute.email' => 'email', + + 'partkeepr.filesystem.data_directory' => '%kernel.root_dir%/../data/', + 'partkeepr.cronjob.check' => true, + 'partkeepr.filesystem.quota' => false, + 'partkeepr.auth.max_users' => 'unlimited', + 'partkeepr.category.path_separator' => ' ➤ ', + 'partkeepr.maintenance' => false, + 'partkeepr.maintenance.title' => '', + 'partkeepr.maintenance.message' => '', + 'cache.dunglas' => false, + 'cache.doctrine' => 'array', + 'partkeepr.parts.limit' => false, + 'partkeepr.users.limit' => false, 'partkeepr.parts.internalpartnumberunique' => false, ]; diff --git a/src/PartKeepr/StorageLocationBundle/Entity/StorageLocationCategory.php b/src/PartKeepr/StorageLocationBundle/Entity/StorageLocationCategory.php @@ -30,6 +30,8 @@ class StorageLocationCategory extends AbstractCategory implements CategoryPathIn * @ORM\OneToMany(targetEntity="StorageLocationCategory", mappedBy="parent") * @ORM\OrderBy({"lft" = "ASC"}) * @Groups({"tree"}) + * + * @var ArrayCollection */ protected $children; @@ -46,6 +48,12 @@ class StorageLocationCategory extends AbstractCategory implements CategoryPathIn */ protected $categoryPath; + public function __construct() + { + parent::__construct(); + $this->storageLocations = new ArrayCollection(); + } + /** * Sets the parent category. * @@ -75,7 +83,7 @@ class StorageLocationCategory extends AbstractCategory implements CategoryPathIn */ public function getStorageLocations() { - return $this->storageLocations; + return $this->storageLocations->getValues(); } /** @@ -85,7 +93,7 @@ class StorageLocationCategory extends AbstractCategory implements CategoryPathIn */ public function getChildren() { - return $this->children; + return $this->children->getValues(); } /** diff --git a/src/PartKeepr/SystemPreferenceBundle/Action/GetPreferencesAction.php b/src/PartKeepr/SystemPreferenceBundle/Action/GetPreferencesAction.php @@ -50,7 +50,7 @@ class GetPreferencesAction $preferences = $this->systemPreferenceService->getPreferences(); /** - * @var ResourceInterface $resourceType + * @var ResourceInterface */ list($resourceType) = $this->extractAttributes($request); diff --git a/src/PartKeepr/SystemPreferenceBundle/Action/SetPreferenceAction.php b/src/PartKeepr/SystemPreferenceBundle/Action/SetPreferenceAction.php @@ -58,7 +58,7 @@ class SetPreferenceAction } /** - * @var ResourceInterface $resourceType + * @var ResourceInterface */ list($resourceType) = $this->extractAttributes($request); diff --git a/src/PartKeepr/SystemPreferenceBundle/DependencyInjection/Configuration.php b/src/PartKeepr/SystemPreferenceBundle/DependencyInjection/Configuration.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\SystemPreferenceBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; @@ -20,4 +21,4 @@ class Configuration implements ConfigurationInterface return $treeBuilder; } -}- \ No newline at end of file +} diff --git a/src/PartKeepr/SystemPreferenceBundle/DependencyInjection/PartKeeprSystemPreferenceExtension.php b/src/PartKeepr/SystemPreferenceBundle/DependencyInjection/PartKeeprSystemPreferenceExtension.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\SystemPreferenceBundle\DependencyInjection; use Symfony\Component\Config\FileLocator; @@ -20,4 +21,4 @@ class PartKeeprSystemPreferenceExtension extends Extension $loader->load('services.xml'); $loader->load('actions.xml'); } -}- \ No newline at end of file +} diff --git a/src/PartKeepr/SystemPreferenceBundle/Entity/SystemPreference.php b/src/PartKeepr/SystemPreferenceBundle/Entity/SystemPreference.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\SystemPreferenceBundle\Entity; use Doctrine\ORM\Mapping as ORM; diff --git a/src/PartKeepr/SystemPreferenceBundle/Exceptions/SystemPreferenceNotFoundException.php b/src/PartKeepr/SystemPreferenceBundle/Exceptions/SystemPreferenceNotFoundException.php @@ -1,4 +1,5 @@ <?php + namespace PartKeepr\SystemPreferenceBundle\Exceptions; use PartKeepr\CoreBundle\Exceptions\TranslatableException; diff --git a/src/PartKeepr/SystemPreferenceBundle/Service/SystemPreferenceService.php b/src/PartKeepr/SystemPreferenceBundle/Service/SystemPreferenceService.php @@ -1,6 +1,6 @@ <?php -namespace PartKeepr\SystemPreferenceBundle\Service; +namespace PartKeepr\SystemPreferenceBundle\Service; use Doctrine\ORM\EntityManager; use Doctrine\ORM\NoResultException; @@ -51,9 +51,9 @@ class SystemPreferenceService } /** - * Returns a specific preference value + * Returns a specific preference value. * - * @param string $key The preference key to retrieve + * @param string $key The preference key to retrieve * * @throws \PartKeepr\AuthBundle\Exceptions\UserPreferenceNotFoundException Thrown if the preference key was not found * @@ -67,9 +67,9 @@ class SystemPreferenceService } /** - * Returns a specific preference object + * Returns a specific preference object. * - * @param string $key The preference key to retrieve + * @param string $key The preference key to retrieve * * @throws SystemPreferenceNotFoundException Thrown if the preference key was not found * @@ -92,7 +92,7 @@ class SystemPreferenceService } /** - * Returns all system preferences + * Returns all system preferences. * * @return SystemPreference[] An array of SystemPreference objects */ @@ -101,13 +101,14 @@ class SystemPreferenceService $dql = "SELECT sp FROM PartKeepr\SystemPreferenceBundle\Entity\SystemPreference sp"; $query = $this->entityManager->createQuery($dql); + return $query->getResult(); } /** - * Removes a specific system preference + * Removes a specific system preference. * - * @param string $key The key to delete + * @param string $key The key to delete */ public function deletePreference($key) { @@ -118,5 +119,4 @@ class SystemPreferenceService $query->execute(); } - -}- \ No newline at end of file +} diff --git a/src/PartKeepr/SystemPreferenceBundle/Tests/SystemPreferenceTest.php b/src/PartKeepr/SystemPreferenceBundle/Tests/SystemPreferenceTest.php @@ -12,7 +12,8 @@ class SystemPreferenceTest extends WebTestCase $this->loadFixtures([]); } - public function testSystemPreferenceService() { + public function testSystemPreferenceService() + { $this->getContainer()->get("partkeepr.system_preference_service")->setSystemPreference("foo", "bar"); $this->assertEquals("bar", $this->getContainer()->get("partkeepr.system_preference_service")->getSystemPreferenceValue("foo")); @@ -33,9 +34,9 @@ class SystemPreferenceTest extends WebTestCase $client = static::makeClient(true); $parameters = [ - "preferenceKey" => "foobar", - "@type" => "SystemPreference", - "preferenceValue" => "test" + "preferenceKey" => "foobar", + "@type" => "SystemPreference", + "preferenceValue" => "test", ]; // First test: Ensure invalid auth key is returned diff --git a/src/PartKeepr/UnitBundle/Entity/Unit.php b/src/PartKeepr/UnitBundle/Entity/Unit.php @@ -114,16 +114,26 @@ class Unit extends BaseEntity */ public function getPrefixes() { - return $this->prefixes; + return $this->prefixes->getValues(); } /** - * Sets the SiPrefixes. + * Adds a prefix. * - * @param $array + * @param SiPrefix */ - public function setPrefixes($array) + public function addPrefix($prefix) { - $this->prefixes = $array; + $this->prefixes->add($prefix); + } + + /** + * Adds a prefix. + * + * @param SiPrefix + */ + public function removePrefix($prefix) + { + $this->prefixes->removeElement($prefix); } } diff --git a/src/PartKeepr/UnitBundle/Tests/Model/UnitTest.php b/src/PartKeepr/UnitBundle/Tests/Model/UnitTest.php @@ -29,7 +29,7 @@ class UnitTest extends \PHPUnit_Framework_TestCase $unit = $this->getUnit(); $newSiPrefix = new SiPrefix(); - $unit->setPrefixes([$newSiPrefix]); + $unit->addPrefix($newSiPrefix); $this->assertEquals([$newSiPrefix], $unit->getPrefixes()); } diff --git a/src/PartKeepr/UploadedFileBundle/Controller/TemporaryFileController.php b/src/PartKeepr/UploadedFileBundle/Controller/TemporaryFileController.php @@ -39,7 +39,7 @@ class TemporaryFileController extends FileController if ($request->files->has('userfile') && $request->files->get('userfile') != null) { /** - * @var $file UploadedFile + * @var UploadedFile */ $file = $request->files->get('userfile'); if (!$file->isValid()) { @@ -56,13 +56,10 @@ class TemporaryFileController extends FileController throw new \Exception($error); } - if ($this->container->hasParameter("partkeepr.upload.limit") && $this->container->getParameter("partkeepr.upload.limit") !== false && $file->getSize() > $this->container->getParameter("partkeepr.upload.limit")) { - throw new \Exception($this->get('translator')->trans('The uploaded file is too large.')); - } /* @@ -76,6 +73,10 @@ class TemporaryFileController extends FileController throw new \Exception($this->get('translator')->trans('No valid file given')); } + if ($request->request->has("description")) { + $uploadedFile->setDescription($request->request->get("description")); + } + $this->getDoctrine()->getManager()->persist($uploadedFile); $this->getDoctrine()->getManager()->flush();