Added permissions for the new functions.

This commit is contained in:
Jan Böhmer 2019-09-13 17:13:58 +02:00
parent d2bae3a4f2
commit da14ee942d
14 changed files with 213 additions and 64 deletions

View file

@ -12,7 +12,7 @@ groups:
perms: # Here comes a list with all Permission names (they have a perm_[name] coloumn in DB) perms: # Here comes a list with all Permission names (they have a perm_[name] coloumn in DB)
# Part related permissions # Part related permissions
parts: # e.g. this maps to perms_parts in User/Group database parts: # e.g. this maps to perms_parts in User/Group database
group: "parts" group: "parts"
@ -29,10 +29,10 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
label: "perm.create" label: "perm.create"
bit: 4 bit: 4
alsoSet: ['read', 'edit'] alsoSet: ['read', 'edit']
move: #move:
label: "perm.part.move" # label: "perm.part.move"
bit: 6 # bit: 6
alsoSet: 'read' # alsoSet: 'read'
delete: delete:
label: "perm.delete" label: "perm.delete"
bit: 8 bit: 8
@ -80,15 +80,15 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
bit: 2 bit: 2
alsoSet: 'read' alsoSet: 'read'
parts_category:
<<: *PART_ATTRIBUTE
label: "perm.part.category"
parts_description: parts_description:
<<: *PART_ATTRIBUTE <<: *PART_ATTRIBUTE
label: "perm.part.description" label: "perm.part.description"
parts_instock: parts_minamount:
<<: *PART_ATTRIBUTE
label: "perm.part.instock"
parts_mininstock:
<<: *PART_ATTRIBUTE <<: *PART_ATTRIBUTE
label: "perm.part.mininstock" label: "perm.part.mininstock"
@ -100,24 +100,61 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
<<: *PART_ATTRIBUTE <<: *PART_ATTRIBUTE
label: "perm.part.comment" label: "perm.part.comment"
parts_storelocation:
<<: *PART_ATTRIBUTE
label: "perm.part.storelocation"
parts_manufacturer: parts_manufacturer:
<<: *PART_ATTRIBUTE <<: *PART_ATTRIBUTE
label: "perm.part.manufacturer" label: "perm.part.manufacturer"
parts_orderdetails: parts_mpn:
<<: *PART_ATTRIBUTE <<: *PART_ATTRIBUTE
label: "perm.part.mpn"
parts_status:
<<: *PART_ATTRIBUTE
label: "perm.part.status"
parts_tags:
<<: *PART_ATTRIBUTE
label: "perm.part.tags"
parts_unit:
<<: *PART_ATTRIBUTE
label: "perm.part.unit"
parts_mass:
<<: *PART_ATTRIBUTE
label: "perm.part.mass"
parts_orderdetails: &PART_MULTI_ATTRIBUTE
label: "perm.part.orderdetails" label: "perm.part.orderdetails"
group: "parts"
operations:
read:
label: "perm.read"
bit: 0
edit:
label: "perm.edit"
bit: 2
alsoSet: 'read'
create:
label: "perm.create"
bit: 4
alsoSet: ['read', 'edit']
delete:
label: "perm.delete"
bit: 6
alsoSet: ['read']
parts_prices: parts_prices:
<<: *PART_ATTRIBUTE <<: *PART_MULTI_ATTRIBUTE
label: "perm.part.prices" label: "perm.part.prices"
parts_lots:
<<: *PART_MULTI_ATTRIBUTE
label: "perm.part.lots"
parts_attachments: parts_attachments:
<<: *PART_ATTRIBUTE <<: *PART_MULTI_ATTRIBUTE
label: "perm.part.attachments" label: "perm.part.attachments"
parts_order: parts_order:
@ -150,7 +187,7 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
label: "perm.list_parts" label: "perm.list_parts"
bit: 10 bit: 10
show_users: show_users:
label: "perm.list_parts" label: "perm.show_users"
bit: 12 bit: 12
footprints: footprints:
@ -177,6 +214,14 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
<<: *PART_CONTAINING <<: *PART_CONTAINING
label: "perm.part.attachment_types" label: "perm.part.attachment_types"
currencies:
<<: *PART_CONTAINING
label: "perm.currencies"
measurement_units:
<<: *PART_CONTAINING
label: "perm.measurement_units"
tools: tools:
label: "perm.part.tools" label: "perm.part.tools"
operations: operations:

View file

@ -130,7 +130,7 @@ class Part extends AttachmentContainingDBElement
* @var Orderdetail[] * @var Orderdetail[]
* @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true) * @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
* @Assert\Valid() * @Assert\Valid()
* @ColumnSecurity(prefix="orderdetails", type="object") * @ColumnSecurity(prefix="orderdetails", type="collection")
*/ */
protected $orderdetails; protected $orderdetails;

View file

@ -129,6 +129,11 @@ class PermissionsEmbed
*/ */
protected $parts_name = 0; protected $parts_name = 0;
/** @var int
* @ORM\Column(type="smallint")
*/
protected $parts_category = 0;
/** /**
* @var int * @var int
* @ORM\Column(type="smallint") * @ORM\Column(type="smallint")
@ -139,13 +144,7 @@ class PermissionsEmbed
* @var int * @var int
* @ORM\Column(type="smallint") * @ORM\Column(type="smallint")
*/ */
protected $parts_instock = 0; protected $parts_minamount = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_mininstock = 0;
/** /**
* @var int * @var int
@ -157,7 +156,24 @@ class PermissionsEmbed
* @var int * @var int
* @ORM\Column(type="smallint") * @ORM\Column(type="smallint")
*/ */
protected $parts_storelocation = 0; protected $parts_lots = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_tags = 0;
/** @var int
* @ORM\Column(type="smallint")
*/
protected $parts_unit = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_mass = 0;
/** /**
* @var int * @var int
@ -165,6 +181,18 @@ class PermissionsEmbed
*/ */
protected $parts_manufacturer = 0; protected $parts_manufacturer = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_status = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_mpn = 0;
/** /**
* @var int * @var int
* @ORM\Column(type="smallint") * @ORM\Column(type="smallint")
@ -243,6 +271,17 @@ class PermissionsEmbed
*/ */
protected $attachment_types = 0; protected $attachment_types = 0;
/** @var int
* @ORM\Column(type="integer")
*/
protected $currencies = 0;
/**
* @var int
* @ORM\Column(type="integer")
*/
protected $measurement_units = 0;
/** /**
* @var int * @var int
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
@ -276,7 +315,7 @@ class PermissionsEmbed
public function getBitValue(string $permission_name, int $bit_n): int public function getBitValue(string $permission_name, int $bit_n): int
{ {
if(!$this->isValidPermissionName($permission_name)) { if(!$this->isValidPermissionName($permission_name)) {
throw new \InvalidArgumentException('No permission with the given name is existing!'); throw new \InvalidArgumentException(sprintf('No permission with the name "%s" is existing!', $permission_name));
} }
$perm_int = $this->$permission_name; $perm_int = $this->$permission_name;

View file

@ -166,6 +166,12 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
*/ */
protected $group; protected $group;
/**
* @var array
* @ORM\Column(type="json")
*/
protected $settings = [];
/** @var PermissionsEmbed /** @var PermissionsEmbed
* @ORM\Embedded(class="PermissionsEmbed", columnPrefix="perms_") * @ORM\Embedded(class="PermissionsEmbed", columnPrefix="perms_")
* @ValidPermission() * @ValidPermission()
@ -173,7 +179,8 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
protected $permissions; protected $permissions;
/** /**
* @ORM\Column(type="string", name="config_currency") * @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency", fetch="EAGER")
* @ORM\JoinColumn(name="currency_id", referencedColumnName="id")
*/ */
protected $currency = ""; protected $currency = "";

View file

@ -109,13 +109,13 @@ class PartBaseType extends AbstractType
'attr' => ['min' => 0, 'placeholder' => $this->trans->trans('part.editmininstock.placeholder')], 'attr' => ['min' => 0, 'placeholder' => $this->trans->trans('part.editmininstock.placeholder')],
'label' => $this->trans->trans('part.edit.mininstock'), 'label' => $this->trans->trans('part.edit.mininstock'),
'measurement_unit' => $part->getPartUnit(), 'measurement_unit' => $part->getPartUnit(),
'disabled' => !$this->security->isGranted('mininstock.edit', $part), 'disabled' => !$this->security->isGranted('minamount.edit', $part),
]) ])
->add('category', StructuralEntityType::class, [ ->add('category', StructuralEntityType::class, [
'class' => Category::class, 'class' => Category::class,
'label' => $this->trans->trans('part.edit.category'), 'label' => $this->trans->trans('part.edit.category'),
'disable_not_selectable' => true, 'disable_not_selectable' => true,
'disabled' => !$this->security->isGranted('move', $part), 'disabled' => !$this->security->isGranted('category.edit', $part),
]) ])
->add('footprint', StructuralEntityType::class, [ ->add('footprint', StructuralEntityType::class, [
'class' => Footprint::class, 'class' => Footprint::class,
@ -129,7 +129,7 @@ class PartBaseType extends AbstractType
'label' => $this->trans->trans('part.edit.tags'), 'label' => $this->trans->trans('part.edit.tags'),
'empty_data' => "", 'empty_data' => "",
'attr' => ['data-role' => 'tagsinput'], 'attr' => ['data-role' => 'tagsinput'],
'disabled' => !$this->security->isGranted('edit', $part) 'disabled' => !$this->security->isGranted('tags.edit', $part)
]); ]);
//Manufacturer section //Manufacturer section
@ -144,41 +144,45 @@ class PartBaseType extends AbstractType
'required' => false, 'required' => false,
'empty_data' => '', 'empty_data' => '',
'label' => $this->trans->trans('part.edit.manufacturer_url.label'), 'label' => $this->trans->trans('part.edit.manufacturer_url.label'),
'disabled' => !$this->security->isGranted('manufacturer.edit', $part), 'disabled' => !$this->security->isGranted('mpn.edit', $part),
]) ])
->add('manufacturer_product_number', TextType::class, [ ->add('manufacturer_product_number', TextType::class, [
'required' => false, 'required' => false,
'empty_data' => '', 'empty_data' => '',
'label' => $this->trans->trans('part.edit.mpn'), 'label' => $this->trans->trans('part.edit.mpn'),
'disabled' => !$this->security->isGranted('manufacturer.edit', $part)]) 'disabled' => !$this->security->isGranted('mpn.edit', $part)])
->add('manufacturing_status', ChoiceType::class, [ ->add('manufacturing_status', ChoiceType::class, [
'label' => $this->trans->trans('part.edit.manufacturing_status'), 'label' => $this->trans->trans('part.edit.manufacturing_status'),
'choices' => $status_choices, 'choices' => $status_choices,
'required' => false, 'required' => false,
'disabled' => !$this->security->isGranted('manufacturer.edit', $part) 'disabled' => !$this->security->isGranted('status.edit', $part)
]); ]);
//Advanced section //Advanced section
$builder->add('needsReview', CheckboxType::class, [ $builder->add('needsReview', CheckboxType::class, [
'label_attr' => ['class' => 'checkbox-custom'], 'label_attr' => ['class' => 'checkbox-custom'],
'required' => false, 'required' => false,
'label' => $this->trans->trans('part.edit.needs_review') 'label' => $this->trans->trans('part.edit.needs_review'),
'disabled' => !$this->security->isGranted('edit', $part)
]) ])
->add('favorite', CheckboxType::class, [ ->add('favorite', CheckboxType::class, [
'label_attr' => ['class' => 'checkbox-custom'], 'label_attr' => ['class' => 'checkbox-custom'],
'required' => false, 'required' => false,
'label' => $this->trans->trans('part.edit.is_favorite') 'label' => $this->trans->trans('part.edit.is_favorite'),
'disabled' => !$this->security->isGranted('change_favorite', $part)
]) ])
->add('mass', SIUnitType::class, [ ->add('mass', SIUnitType::class, [
'unit' => 'g', 'unit' => 'g',
'label' => $this->trans->trans('part.edit.mass'), 'label' => $this->trans->trans('part.edit.mass'),
'required' => false 'required' => false,
'disabled' => !$this->security->isGranted('mass.edit', $part)
]) ])
->add('partUnit', StructuralEntityType::class, [ ->add('partUnit', StructuralEntityType::class, [
'class' => MeasurementUnit::class, 'class' => MeasurementUnit::class,
'required' => false, 'required' => false,
'disable_not_selectable' => true, 'disable_not_selectable' => true,
'label' => $this->trans->trans('part.edit.partUnit') 'label' => $this->trans->trans('part.edit.partUnit'),
'disabled' => !$this->security->isGranted('unit.edit', $part)
]); ]);
@ -193,10 +197,12 @@ class PartBaseType extends AbstractType
//Part Lots section //Part Lots section
$builder->add('partLots', CollectionType::class, [ $builder->add('partLots', CollectionType::class, [
'entry_type' => PartLotType::class, 'entry_type' => PartLotType::class,
'allow_add' => true, 'allow_delete' => true, 'allow_add' => $this->security->isGranted('lots.create', $part),
'allow_delete' => $this->security->isGranted('lots.delete', $part),
'label' => false, 'label' => false,
'entry_options' => [ 'entry_options' => [
'measurement_unit' => $part->getPartUnit() 'measurement_unit' => $part->getPartUnit(),
'disabled' => !$this->security->isGranted('lots.edit', $part),
], ],
'by_reference' => false 'by_reference' => false
]); ]);
@ -204,16 +210,19 @@ class PartBaseType extends AbstractType
//Attachment section //Attachment section
$builder->add('attachments', CollectionType::class, [ $builder->add('attachments', CollectionType::class, [
'entry_type' => AttachmentFormType::class, 'entry_type' => AttachmentFormType::class,
'allow_add' => true, 'allow_delete' => true, 'allow_add' => $this->security->isGranted('attachments.create', $part),
'allow_delete' => $this->security->isGranted('attachments.delete', $part),
'label' => false, 'label' => false,
'entry_options' => [ 'entry_options' => [
'data_class' => PartAttachment::class 'data_class' => PartAttachment::class,
'disabled' => !$this->security->isGranted('attachments.edit', $part),
], ],
'by_reference' => false 'by_reference' => false
]); ]);
$builder->add('master_picture_attachment', EntityType::class, [ $builder->add('master_picture_attachment', EntityType::class, [
'required' => false, 'required' => false,
'disabled' => !$this->security->isGranted('attachments.edit', $part),
'label' => $this->trans->trans('part.edit.master_attachment'), 'label' => $this->trans->trans('part.edit.master_attachment'),
'class' => PartAttachment::class, 'class' => PartAttachment::class,
'attr' => ['class' => 'selectpicker'], 'attr' => ['class' => 'selectpicker'],
@ -238,12 +247,14 @@ class PartBaseType extends AbstractType
//Orderdetails section //Orderdetails section
$builder->add('orderdetails', CollectionType::class, [ $builder->add('orderdetails', CollectionType::class, [
'entry_type' => OrderdetailType::class, 'entry_type' => OrderdetailType::class,
'allow_add' => true, 'allow_delete' => true, 'allow_add' => $this->security->isGranted('orderdetails.create', $part),
'allow_delete' => $this->security->isGranted('orderdetails.delete', $part),
'label' => false, 'label' => false,
'by_reference' => false, 'by_reference' => false,
'prototype_data' => new Orderdetail(), 'prototype_data' => new Orderdetail(),
'entry_options' => [ 'entry_options' => [
'measurement_unit' => $part->getPartUnit() 'measurement_unit' => $part->getPartUnit(),
'disabled' => !$this->security->isGranted('attachments.edit', $part),
] ]
]); ]);

View file

@ -51,16 +51,19 @@ use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use function GuzzleHttp\Promise\queue; use function GuzzleHttp\Promise\queue;
class PartLotType extends AbstractType class PartLotType extends AbstractType
{ {
protected $trans; protected $trans;
protected $security;
public function __construct(TranslatorInterface $trans) public function __construct(TranslatorInterface $trans, Security $security)
{ {
$this->trans = $trans; $this->trans = $trans;
$this->security = $security;
} }
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)

View file

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20190913141126 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE `groups` ADD perms_parts_category SMALLINT NOT NULL, ADD perms_parts_minamount SMALLINT NOT NULL, ADD perms_parts_lots SMALLINT NOT NULL, ADD perms_parts_tags SMALLINT NOT NULL, ADD perms_parts_unit SMALLINT NOT NULL, ADD perms_parts_mass SMALLINT NOT NULL, ADD perms_parts_status SMALLINT NOT NULL, ADD perms_parts_mpn SMALLINT NOT NULL, ADD perms_currencies INT NOT NULL, ADD perms_measurement_units INT NOT NULL, DROP perms_parts_instock, DROP perms_parts_mininstock, DROP perms_parts_storelocation');
$this->addSql('ALTER TABLE users ADD currency_id INT DEFAULT NULL, ADD settings LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', ADD perms_parts_category SMALLINT NOT NULL, ADD perms_parts_minamount SMALLINT NOT NULL, ADD perms_parts_lots SMALLINT NOT NULL, ADD perms_parts_tags SMALLINT NOT NULL, ADD perms_parts_unit SMALLINT NOT NULL, ADD perms_parts_mass SMALLINT NOT NULL, ADD perms_parts_status SMALLINT NOT NULL, ADD perms_parts_mpn SMALLINT NOT NULL, ADD perms_currencies INT NOT NULL, ADD perms_measurement_units INT NOT NULL, DROP config_currency, DROP perms_parts_instock, DROP perms_parts_mininstock, DROP perms_parts_storelocation');
$this->addSql('ALTER TABLE users ADD CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id)');
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE `groups` ADD perms_parts_instock SMALLINT NOT NULL, ADD perms_parts_mininstock SMALLINT NOT NULL, ADD perms_parts_storelocation SMALLINT NOT NULL, DROP perms_parts_category, DROP perms_parts_minamount, DROP perms_parts_lots, DROP perms_parts_tags, DROP perms_parts_unit, DROP perms_parts_mass, DROP perms_parts_status, DROP perms_parts_mpn, DROP perms_currencies, DROP perms_measurement_units');
$this->addSql('ALTER TABLE `users` DROP FOREIGN KEY FK_1483A5E938248176');
$this->addSql('DROP INDEX IDX_1483A5E938248176 ON `users`');
$this->addSql('ALTER TABLE `users` ADD config_currency VARCHAR(255) NOT NULL COLLATE utf8_general_ci, ADD perms_parts_instock SMALLINT NOT NULL, ADD perms_parts_mininstock SMALLINT NOT NULL, ADD perms_parts_storelocation SMALLINT NOT NULL, DROP currency_id, DROP settings, DROP perms_parts_category, DROP perms_parts_minamount, DROP perms_parts_lots, DROP perms_parts_tags, DROP perms_parts_unit, DROP perms_parts_mass, DROP perms_parts_status, DROP perms_parts_mpn, DROP perms_currencies, DROP perms_measurement_units');
}
}

View file

@ -30,6 +30,7 @@
namespace App\Security\Annotations; namespace App\Security\Annotations;
use Doctrine\Common\Annotations\Annotation; use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Collections\ArrayCollection;
use \InvalidArgumentException; use \InvalidArgumentException;
/** /**
@ -65,7 +66,7 @@ class ColumnSecurity
/** /**
* @var string The name of the property. This is used to determine the default placeholder. * @var string The name of the property. This is used to determine the default placeholder.
* @Annotation\Enum({"integer", "string", "object", "boolean", "datetime"}) * @Annotation\Enum({"integer", "string", "object", "boolean", "datetime", "collection"})
*/ */
public $type = 'string'; public $type = 'string';
@ -97,6 +98,8 @@ class ColumnSecurity
return '???'; return '???';
case 'object': case 'object':
return null; return null;
case 'collection':
return new ArrayCollection();
case 'boolean': case 'boolean':
return false; return false;
case 'datetime': case 'datetime':

View file

@ -26,10 +26,10 @@ class PartVoter extends ExtendedVoter
if (false !== strpos($attribute, '.')) { if (false !== strpos($attribute, '.')) {
[$perm, $op] = explode('.', $attribute); [$perm, $op] = explode('.', $attribute);
return in_array($op, $this->resolver->listOperationsForPermission('parts_'.$perm), false); return $this->resolver->isValidOperation('parts_' . $perm, $op);
} }
return in_array($attribute, $this->resolver->listOperationsForPermission('parts'), false); return $this->resolver->isValidOperation('parts', $attribute);
} }
return false; return false;

View file

@ -86,11 +86,9 @@ class StructureVoter extends ExtendedVoter
case Supplier::class: case Supplier::class:
return 'suppliers'; return 'suppliers';
case Currency::class: case Currency::class:
//TODO: Implement own permission return 'currencies';
return 'suppliers';
case MeasurementUnit::class: case MeasurementUnit::class:
//TODO: Implement own permission return 'measurement_units';
return 'suppliers';
} }
//When the class is not supported by this class return null //When the class is not supported by this class return null
return null; return null;

View file

@ -1,5 +1,5 @@
{% set delete_btn %} {% set delete_btn %}
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_attachment_entry(this);"> <button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_attachment_entry(this);" {% if not is_granted('attachments.delete', part) %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
{% trans %}attachment.delete{% endtrans %} {% trans %}attachment.delete{% endtrans %}
</button> </button>
@ -7,7 +7,7 @@
{{ form_row(form.master_picture_attachment) }} {{ form_row(form.master_picture_attachment) }}
<table class="table table-striped table-sm" id="attachments_table" data-prototype="{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}"> <table class="table table-striped table-sm" id="attachments_table" data-prototype="{% if form.attachments.vars.prototype is defined %}{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody> <tbody>
{% for attachment in form.attachments %} {% for attachment in form.attachments %}
<tr> <tr>
@ -54,7 +54,7 @@
</tbody> </tbody>
</table> </table>
<button type="button" class="btn btn-success" onclick="create_attachment_entry(this)"> <button type="button" class="btn btn-success" onclick="create_attachment_entry(this)" {% if not is_granted('attachments.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i> <i class="fas fa-plus-square fa-fw"></i>
{% trans %}attachment.create{% endtrans %} {% trans %}attachment.create{% endtrans %}
</button> </button>

View file

@ -1,12 +1,13 @@
{% set delete_btn %} {% set delete_btn %}
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_lot_entry(this);"> <button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_lot_entry(this);"
{% if not is_granted('lots.delete', part) %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
{% trans %}part_lot.delete{% endtrans %} {% trans %}part_lot.delete{% endtrans %}
</button> </button>
{% endset %} {% endset %}
<table class="table table-striped table-sm" id="lots_table" data-prototype="{{ form_widget(form.partLots.vars.prototype)|e('html_attr') }}"> <table class="table table-striped table-sm" id="lots_table" data-prototype="{% if form.partLots.vars.prototype is defined %}{{ form_widget(form.partLots.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody> <tbody>
{% for lot in form.partLots %} {% for lot in form.partLots %}
<tr> <tr>
@ -21,7 +22,8 @@
</tbody> </tbody>
</table> </table>
<button type="button" class="btn btn-success" onclick="create_lot_entry(this)"> <button type="button" class="btn btn-success" onclick="create_lot_entry(this)"
{% if not is_granted('lots.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i> <i class="fas fa-plus-square fa-fw"></i>
{% trans %}part_lot.create{% endtrans %} {% trans %}part_lot.create{% endtrans %}
</button> </button>

View file

@ -1,14 +1,14 @@
{% form_theme form with ['Parts/edit/edit_form_styles.html.twig', "bootstrap_4_layout.html.twig"] %} {% form_theme form with ['Parts/edit/edit_form_styles.html.twig', "bootstrap_4_layout.html.twig"] %}
<table class="table table-striped table-sm" id="orderdetails_table" data-prototype="{{ form_widget(form.orderdetails.vars.prototype)|e('html_attr') }}"> <table class="table table-striped table-sm" id="orderdetails_table" data-prototype="{% if form.orderdetails.vars.prototype is defined %}{{ form_widget(form.orderdetails.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody> <tbody>
{% for detail in form.orderdetails %} {% for detail in form.orderdetails %}
{{ form_widget(detail) }} {{ form_widget(detail, {'disable_delete' : not is_granted('orderdetails.delete', part)}) }}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<button type="button" class="btn btn-success" onclick="create_orderdetail_entry(this)"> <button type="button" class="btn btn-success" onclick="create_orderdetail_entry(this)" {% if not is_granted('orderdetails.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i> <i class="fas fa-plus-square fa-fw"></i>
{% trans %}orderdetail.create{% endtrans %} {% trans %}orderdetail.create{% endtrans %}
</button> </button>

View file

@ -53,7 +53,7 @@
</button> </button>
</td> </td>
<td> <td>
<button type="button" class="btn btn-danger order_btn_delete" onclick="delete_orderdetail_entry(this);" title="{% trans %}orderdetail.delete{% endtrans %}"> <button type="button" class="btn btn-danger order_btn_delete" onclick="delete_orderdetail_entry(this);" title="{% trans %}orderdetail.delete{% endtrans %}" {% if disable_delete %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
{{ form_errors(form) }} {{ form_errors(form) }}