001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.control.user.item.server.command;
018
019import com.echothree.control.user.item.common.edit.ItemDescriptionEdit;
020import com.echothree.control.user.item.common.edit.ItemEditFactory;
021import com.echothree.control.user.item.common.form.EditItemDescriptionForm;
022import com.echothree.control.user.item.common.result.EditItemDescriptionResult;
023import com.echothree.control.user.item.common.result.ItemResultFactory;
024import com.echothree.control.user.item.common.spec.ItemDescriptionSpec;
025import com.echothree.model.control.core.common.EntityAttributeTypes;
026import com.echothree.model.control.core.common.MimeTypeUsageTypes;
027import com.echothree.model.control.core.server.control.MimeTypeControl;
028import com.echothree.model.control.item.server.control.ItemControl;
029import com.echothree.model.control.item.server.logic.ItemDescriptionLogic;
030import com.echothree.model.control.item.server.logic.ItemDescriptionLogic.ImageDimensions;
031import com.echothree.model.control.party.common.PartyTypes;
032import com.echothree.model.control.party.server.control.PartyControl;
033import com.echothree.model.control.security.common.SecurityRoleGroups;
034import com.echothree.model.control.security.common.SecurityRoles;
035import com.echothree.model.data.core.server.entity.MimeType;
036import com.echothree.model.data.item.server.entity.Item;
037import com.echothree.model.data.item.server.entity.ItemDescription;
038import com.echothree.model.data.item.server.entity.ItemDescriptionType;
039import com.echothree.model.data.item.server.entity.ItemImageType;
040import com.echothree.model.data.user.common.pk.UserVisitPK;
041import com.echothree.util.common.command.EditMode;
042import com.echothree.util.common.message.ExecutionErrors;
043import com.echothree.util.common.persistence.BasePK;
044import com.echothree.util.common.persistence.type.ByteArray;
045import com.echothree.util.common.validation.FieldDefinition;
046import com.echothree.util.common.validation.FieldType;
047import com.echothree.util.server.control.BaseAbstractEditCommand;
048import com.echothree.util.server.control.CommandSecurityDefinition;
049import com.echothree.util.server.control.PartyTypeDefinition;
050import com.echothree.util.server.control.SecurityRoleDefinition;
051import com.echothree.util.server.persistence.Session;
052import java.util.Arrays;
053import java.util.Collections;
054import java.util.List;
055import javax.enterprise.context.RequestScoped;
056
057@RequestScoped
058public class EditItemDescriptionCommand
059        extends BaseAbstractEditCommand<ItemDescriptionSpec, ItemDescriptionEdit, EditItemDescriptionResult, ItemDescription, ItemDescription> {
060    
061    private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION;
062    private final static List<FieldDefinition> SPEC_FIELD_DEFINITIONS;
063    private final static List<FieldDefinition> EDIT_FIELD_DEFINITIONS;
064    
065    static {
066        COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(Collections.unmodifiableList(Arrays.asList(
067                new PartyTypeDefinition(PartyTypes.UTILITY.name(), null),
068                new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), Collections.unmodifiableList(Arrays.asList(
069                        new SecurityRoleDefinition(SecurityRoleGroups.ItemDescription.name(), SecurityRoles.Edit.name())
070                        )))
071                )));
072
073        SPEC_FIELD_DEFINITIONS = Collections.unmodifiableList(Arrays.asList(
074            new FieldDefinition("ItemName", FieldType.ENTITY_NAME, true, null, null),
075            new FieldDefinition("ItemDescriptionTypeName", FieldType.ENTITY_NAME, true, null, null),
076            new FieldDefinition("LanguageIsoName", FieldType.ENTITY_NAME, true, null, null)
077        ));
078        
079        EDIT_FIELD_DEFINITIONS = Collections.unmodifiableList(Arrays.asList(
080            new FieldDefinition("MimeTypeName", FieldType.MIME_TYPE, false, null, null),
081            new FieldDefinition("ItemImageTypeName", FieldType.ENTITY_NAME, false, null, null),
082            new FieldDefinition("ClobDescription", FieldType.STRING, false, 1L, null),
083            new FieldDefinition("StringDescription", FieldType.STRING, false, 1L, 512L)
084        ));
085    }
086    
087    /** Creates a new instance of EditItemDescriptionCommand */
088    public EditItemDescriptionCommand() {
089        super(COMMAND_SECURITY_DEFINITION, SPEC_FIELD_DEFINITIONS, EDIT_FIELD_DEFINITIONS);
090    }
091
092    @Override
093    public EditItemDescriptionResult getResult() {
094        return ItemResultFactory.getEditItemDescriptionResult();
095    }
096
097    @Override
098    public ItemDescriptionEdit getEdit() {
099        return ItemEditFactory.getItemDescriptionEdit();
100    }
101
102     ItemDescriptionType itemDescriptionType;
103     Item item;
104
105    @Override
106    public ItemDescription getEntity(EditItemDescriptionResult result) {
107        var itemControl = Session.getModelController(ItemControl.class);
108        ItemDescription itemDescription = null;
109        var itemDescriptionTypeName = spec.getItemDescriptionTypeName();
110
111        itemDescriptionType = itemControl.getItemDescriptionTypeByName(itemDescriptionTypeName);
112
113        if(itemDescriptionType != null) {
114            var itemName = spec.getItemName();
115
116            item = itemControl.getItemByName(itemName);
117
118            if(item != null) {
119                var partyControl = Session.getModelController(PartyControl.class);
120                var languageIsoName = spec.getLanguageIsoName();
121                var language = partyControl.getLanguageByIsoName(languageIsoName);
122
123                if(language != null) {
124                    if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) {
125                        itemDescription = itemControl.getItemDescription(itemDescriptionType, item, language);
126                    } else { // EditMode.UPDATE
127                        itemDescription = itemControl.getItemDescriptionForUpdate(itemDescriptionType, item, language);
128                    }
129
130                    if(itemDescription == null) {
131                        addExecutionError(ExecutionErrors.UnknownItemDescription.name(), itemDescriptionTypeName, itemName, languageIsoName);
132                    }
133                } else {
134                    addExecutionError(ExecutionErrors.UnknownLanguageIsoName.name(), languageIsoName);
135                }
136            } else {
137                addExecutionError(ExecutionErrors.UnknownItemName.name(), itemName);
138            }
139        } else {
140            addExecutionError(ExecutionErrors.UnknownItemDescriptionTypeName.name(), itemDescriptionTypeName);
141        }
142
143        return itemDescription;
144    }
145
146    @Override
147    public ItemDescription getLockEntity(ItemDescription itemDescription) {
148        return itemDescription;
149    }
150
151    @Override
152    public void fillInResult(EditItemDescriptionResult result, ItemDescription itemDescription) {
153        var itemControl = Session.getModelController(ItemControl.class);
154
155        result.setItemDescription(itemControl.getItemDescriptionTransfer(getUserVisit(), itemDescription));
156    }
157
158    MimeType mimeType;
159
160    @Override
161    public void doLock(ItemDescriptionEdit edit, ItemDescription itemDescription) {
162        var itemControl = Session.getModelController(ItemControl.class);
163        
164        mimeType = itemDescription.getLastDetail().getMimeType();
165
166        edit.setMimeTypeName(mimeType == null? null: mimeType.getLastDetail().getMimeTypeName());
167
168        if(mimeType == null) {
169            var itemStringDescription = itemControl.getItemStringDescription(itemDescription);
170
171            if(itemStringDescription != null) {
172                edit.setStringDescription(itemStringDescription.getStringDescription());
173            }
174        } else {
175            var entityAttributeTypeName = mimeType.getLastDetail().getEntityAttributeType().getEntityAttributeTypeName();
176
177            // EntityAttributeTypes.BLOB.name() does not return anything in edit
178            if(entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) {
179                var itemClobDescription = itemControl.getItemClobDescription(itemDescription);
180
181                if(itemClobDescription != null) {
182                    edit.setClobDescription(itemClobDescription.getClobDescription());
183                }
184            }
185        }
186    }
187
188    ItemImageType itemImageType;
189    ImageDimensions imageDimensions;
190
191    @Override
192    public void canUpdate(ItemDescription itemDescription) {
193        var mimeTypeName = edit.getMimeTypeName();
194
195        if(mimeTypeName == null) {
196            if(itemDescriptionType.getLastDetail().getMimeTypeUsageType() == null) {
197                var stringDescription = edit.getStringDescription();
198
199                if(stringDescription == null) {
200                    addExecutionError(ExecutionErrors.MissingStringDescription.name());
201                }
202            } else {
203                // No mimeTypeName was supplied, but yet we required a MimeTypeUsageType
204                addExecutionError(ExecutionErrors.InvalidMimeType.name());
205            }
206        } else {
207            var mimeTypeControl = Session.getModelController(MimeTypeControl.class);
208
209            mimeType = mimeTypeControl.getMimeTypeByName(mimeTypeName);
210
211            if(mimeType != null) {
212                var mimeTypeUsageType = itemDescriptionType.getLastDetail().getMimeTypeUsageType();
213
214                if(mimeTypeUsageType != null) {
215                    var mimeTypeUsage = mimeTypeControl.getMimeTypeUsage(mimeType, mimeTypeUsageType);
216
217                    if(mimeTypeUsage != null) {
218                        var mimeTypeDetail = mimeType.getLastDetail();
219                        var entityAttributeType = mimeTypeDetail.getEntityAttributeType();
220                        var entityAttributeTypeName = entityAttributeType.getEntityAttributeTypeName();
221
222                        if(entityAttributeTypeName.equals(EntityAttributeTypes.BLOB.name())) {
223                            var blobDescription = edit.getBlobDescription();
224
225                            if(blobDescription == null) {
226                                addExecutionError(ExecutionErrors.MissingBlobDescription.name());
227                            } else {
228                                var mimeTypeUsageTypeName = mimeTypeUsageType.getMimeTypeUsageTypeName();
229
230                                if(mimeTypeUsageTypeName.equals(MimeTypeUsageTypes.IMAGE.name())) {
231                                    var itemImageTypeName = edit.getItemImageTypeName();
232
233                                    if(itemImageTypeName != null) {
234                                        var itemControl = Session.getModelController(ItemControl.class);
235
236                                        itemImageType = itemControl.getItemImageTypeByName(itemImageTypeName);
237
238                                        if(itemImageType != null) {
239                                            imageDimensions = ItemDescriptionLogic.getInstance().getImageDimensions(mimeTypeDetail.getMimeTypeName(), blobDescription);
240
241                                            if(imageDimensions == null) {
242                                                addExecutionError(ExecutionErrors.InvalidImage.name());
243                                            } else {
244                                                var itemImageDescriptionType = itemControl.getItemImageDescriptionType(itemDescriptionType);
245
246                                                if(itemImageDescriptionType != null) {
247                                                    var minimumHeight = itemImageDescriptionType.getMinimumHeight();
248                                                    var minimumWidth = itemImageDescriptionType.getMinimumWidth();
249                                                    var maximumHeight = itemImageDescriptionType.getMaximumHeight();
250                                                    var maximumWidth = itemImageDescriptionType.getMaximumWidth();
251                                                    var imageHeight = imageDimensions.getHeight();
252                                                    var imageWidth = imageDimensions.getWidth();
253
254                                                    if(minimumHeight != null && imageHeight < minimumHeight) {
255                                                        addExecutionError(ExecutionErrors.ImageHeightLessThanMinimum.name(), imageHeight.toString(), minimumHeight.toString());
256                                                    }
257
258                                                    if(minimumWidth != null && imageWidth < minimumWidth) {
259                                                        addExecutionError(ExecutionErrors.ImageWidthLessThanMinimum.name(), imageWidth.toString(), minimumWidth.toString());
260                                                    }
261
262                                                    if(maximumHeight != null && imageHeight > maximumHeight) {
263                                                        addExecutionError(ExecutionErrors.ImageHeightGreaterThanMaximum.name(), imageHeight.toString(), maximumHeight.toString());
264                                                    }
265
266                                                    if(maximumWidth != null && imageWidth > maximumWidth) {
267                                                        addExecutionError(ExecutionErrors.ImageWidthGreaterThanMaximum.name(), imageWidth.toString(), maximumWidth.toString());
268                                                    }
269                                                }
270                                            }
271                                        } else {
272                                          addExecutionError(ExecutionErrors.UnknownItemImageTypeName.name(), itemImageTypeName);
273                                        }
274                                    } else {
275                                          addExecutionError(ExecutionErrors.MissingRequiredItemImageTypeName.name());
276                                    }
277                                }
278                            }
279                        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) {
280                            var clobDescription = edit.getClobDescription();
281
282                            if(clobDescription == null) {
283                                addExecutionError(ExecutionErrors.MissingClobDescription.name());
284                            }
285                        } else {
286                            addExecutionError(ExecutionErrors.UnknownEntityAttributeTypeName.name(),
287                                    entityAttributeTypeName);
288                        }
289                    } else {
290                        addExecutionError(ExecutionErrors.UnknownMimeTypeUsage.name());
291                    }
292                } else {
293                    // mimeTypeName was supplied, and there shouldn't be one
294                    addExecutionError(ExecutionErrors.InvalidMimeType.name());
295                }
296            } else {
297                addExecutionError(ExecutionErrors.UnknownMimeTypeName.name(), mimeTypeName);
298            }
299        }
300    }
301
302    @Override
303    public void doUpdate(ItemDescription itemDescription) {
304        var itemControl = Session.getModelController(ItemControl.class);
305        var partyPK = getPartyPK();
306
307        if(mimeType == null) {
308            var stringDescription = edit.getStringDescription();
309
310            updateItemDescription(itemControl, item, null, partyPK, itemDescription, null, null, stringDescription);
311        } else {
312            var entityAttributeType = mimeType.getLastDetail().getEntityAttributeType();
313            var entityAttributeTypeName = entityAttributeType.getEntityAttributeTypeName();
314
315            if(entityAttributeTypeName.equals(EntityAttributeTypes.BLOB.name())) {
316                var blobDescription = edit.getBlobDescription();
317
318                updateItemDescription(itemControl, item, mimeType, partyPK, itemDescription, blobDescription, null, null);
319            } else {
320                if(entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) {
321                    var clobDescription = edit.getClobDescription();
322
323                    updateItemDescription(itemControl, item, mimeType, partyPK, itemDescription, null, clobDescription, null);
324                }
325            }
326        }
327    }
328
329    protected void updateItemDescription(ItemControl itemControl, Item item, MimeType mimeType, BasePK updatedBy,
330            ItemDescription itemDescription, ByteArray blobDescription, String clobDescription, String stringDescription) {
331        var itemDescriptionDetailValue = itemControl.getItemDescriptionDetailValueForUpdate(itemDescription);
332
333        itemDescriptionDetailValue.setMimeTypePK(mimeType == null? null: mimeType.getPrimaryKey());
334        itemControl.updateItemDescriptionFromValue(itemDescriptionDetailValue, updatedBy);
335
336        var itemBlobDescription = itemControl.getItemBlobDescriptionForUpdate(itemDescription);
337
338        if(itemBlobDescription != null) {
339            if(blobDescription == null) {
340                itemControl.deleteItemBlobDescription(itemBlobDescription, updatedBy);
341                itemControl.deleteItemImageDescriptionByItemDescription(itemDescription, updatedBy);
342            } else {
343                var itemBlobDescriptionValue = itemControl.getItemBlobDescriptionValue(itemBlobDescription);
344                var itemImageDescriptionValue = itemControl.getItemImageDescriptionValueForUpdate(itemDescription);
345
346                itemBlobDescriptionValue.setBlobDescription(blobDescription);
347                itemControl.updateItemBlobDescriptionFromValue(itemBlobDescriptionValue, updatedBy);
348
349                if(imageDimensions != null) {
350                    itemImageDescriptionValue.setHeight(imageDimensions.getHeight());
351                    itemImageDescriptionValue.setWidth(imageDimensions.getWidth());
352                    itemImageDescriptionValue.setScaledFromParent(false);
353
354                    itemControl.updateItemImageDescriptionFromValue(itemImageDescriptionValue, updatedBy);
355                }
356
357                if(itemBlobDescriptionValue.hasBeenModified()) {
358                    ItemDescriptionLogic.getInstance().deleteItemImageDescriptionChildren(itemDescription, updatedBy);
359                }
360            }
361        } else if(blobDescription != null) {
362            itemControl.createItemBlobDescription(itemDescription, blobDescription, updatedBy);
363
364            if(imageDimensions != null) {
365                itemControl.createItemImageDescription(itemDescription, itemImageType, imageDimensions.getHeight(), imageDimensions.getWidth(), false,
366                        updatedBy);
367            }
368
369            ItemDescriptionLogic.getInstance().deleteItemImageDescriptionChildren(itemDescription, updatedBy);
370        }
371
372        var itemClobDescription = itemControl.getItemClobDescriptionForUpdate(itemDescription);
373
374        if(itemClobDescription != null) {
375            if(clobDescription == null) {
376                itemControl.deleteItemClobDescription(itemClobDescription, updatedBy);
377            } else {
378                var itemClobDescriptionValue = itemControl.getItemClobDescriptionValue(itemClobDescription);
379
380                itemClobDescriptionValue.setClobDescription(clobDescription);
381                itemControl.updateItemClobDescriptionFromValue(itemClobDescriptionValue, updatedBy);
382            }
383        } else if(clobDescription != null) {
384            itemControl.createItemClobDescription(itemDescription, clobDescription, updatedBy);
385        }
386
387        var itemStringDescription = itemControl.getItemStringDescriptionForUpdate(itemDescription);
388
389        if(itemStringDescription != null) {
390            if(stringDescription == null) {
391                itemControl.deleteItemStringDescription(itemStringDescription, updatedBy);
392            } else {
393                var itemStringDescriptionValue = itemControl.getItemStringDescriptionValue(itemStringDescription);
394
395                itemStringDescriptionValue.setStringDescription(stringDescription);
396                itemControl.updateItemStringDescriptionFromValue(itemStringDescriptionValue, updatedBy);
397            }
398        } else if(stringDescription != null) {
399            itemControl.createItemStringDescription(itemDescription, stringDescription, updatedBy);
400        }
401    }
402    
403}