|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to edit the metadata of the CycloneDX SBOM. |
|
8 """ |
|
9 |
|
10 from PyQt6.QtCore import pyqtSlot, QCoreApplication |
|
11 from PyQt6.QtWidgets import QDialog, QDialogButtonBox |
|
12 |
|
13 from cyclonedx.model.component import ComponentType |
|
14 import trove_classifiers |
|
15 |
|
16 from .Ui_CycloneDXMetaDataDialog import Ui_CycloneDXMetaDataDialog |
|
17 |
|
18 |
|
19 class CycloneDXMetaDataDialog(QDialog, Ui_CycloneDXMetaDataDialog): |
|
20 """ |
|
21 Class implementing a dialog to edit the metadata of the CycloneDX SBOM. |
|
22 """ |
|
23 ComponentTypeMapping = { |
|
24 ComponentType.APPLICATION: QCoreApplication.translate( |
|
25 "CycloneDXMetaDataDialog", "Application"), |
|
26 ComponentType.CONTAINER: QCoreApplication.translate( |
|
27 "CycloneDXMetaDataDialog", "Container"), |
|
28 ComponentType.DEVICE: QCoreApplication.translate( |
|
29 "CycloneDXMetaDataDialog", "Device"), |
|
30 ComponentType.FILE: QCoreApplication.translate( |
|
31 "CycloneDXMetaDataDialog", "File"), |
|
32 ComponentType.FIRMWARE: QCoreApplication.translate( |
|
33 "CycloneDXMetaDataDialog", "Firmware"), |
|
34 ComponentType.FRAMEWORK: QCoreApplication.translate( |
|
35 "CycloneDXMetaDataDialog", "Framework"), |
|
36 ComponentType.LIBRARY: QCoreApplication.translate( |
|
37 "CycloneDXMetaDataDialog", "Library"), |
|
38 ComponentType.OPERATING_SYSTEM: QCoreApplication.translate( |
|
39 "CycloneDXMetaDataDialog", "Operating System"), |
|
40 } |
|
41 |
|
42 def __init__(self, metadata=None, parent=None): |
|
43 """ |
|
44 Constructor |
|
45 |
|
46 @param metadata dictionary containing metadata to populate the dialog |
|
47 (defaults to None) |
|
48 @type dict (optional) |
|
49 @param parent reference to the parent widget (defaults to None) |
|
50 @type QWidget (optional) |
|
51 """ |
|
52 super().__init__(parent) |
|
53 self.setupUi(self) |
|
54 |
|
55 self.__populateComponentTypeSelector() |
|
56 self.__populateLicenseSelector() |
|
57 |
|
58 if metadata: |
|
59 # populate the dialog from given metadata dictionary |
|
60 self.nameEdit.setText(metadata["Name"]) |
|
61 self.versionEdit.setText(metadata["Version"]) |
|
62 self.descriptionEdit.setPlainText(metadata["Description"]) |
|
63 self.authorEdit.setText(metadata["AuthorName"]) |
|
64 self.emailEdit.setText(metadata["AuthorEmail"]) |
|
65 self.licenseComboBox.setCurrentText(metadata["License"]) |
|
66 self.manufacturerEdit.setText(metadata["Manufacturer"]) |
|
67 self.supplierEdit.setText(metadata["Supplier"]) |
|
68 index = self.typeComboBox.findData(metadata["Type"]) |
|
69 self.typeComboBox.setCurrentIndex(index) |
|
70 |
|
71 self.nameEdit.textChanged.connect(self.__updateOkButton) |
|
72 self.typeComboBox.currentTextChanged.connect(self.__updateOkButton) |
|
73 self.licenseComboBox.currentTextChanged.connect(self.__updateOkButton) |
|
74 |
|
75 self.__updateOkButton() |
|
76 |
|
77 def __populateComponentTypeSelector(self): |
|
78 """ |
|
79 Private method to populate the component type selector. |
|
80 """ |
|
81 self.typeComboBox.addItem("", "") |
|
82 for componentType, displayStr in sorted( |
|
83 CycloneDXMetaDataDialog.ComponentTypeMapping.items(), |
|
84 key=lambda x: x[1] |
|
85 ): |
|
86 self.typeComboBox.addItem(displayStr, componentType) |
|
87 |
|
88 def __populateLicenseSelector(self): |
|
89 """ |
|
90 Private method to populate the license selector with the list of trove |
|
91 license types. |
|
92 """ |
|
93 self.licenseComboBox.addItem("") |
|
94 self.licenseComboBox.addItems(sorted( |
|
95 classifier.split("::")[-1].strip() |
|
96 for classifier in trove_classifiers.classifiers |
|
97 if classifier.startswith("License ::") |
|
98 )) |
|
99 |
|
100 @pyqtSlot() |
|
101 def __updateOkButton(self): |
|
102 """ |
|
103 Private slot to update the enabled state of the OK button. |
|
104 """ |
|
105 self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled( |
|
106 bool(self.nameEdit.text()) and |
|
107 bool(self.typeComboBox.currentText()) and |
|
108 bool(self.licenseComboBox.currentText()) |
|
109 ) |
|
110 |
|
111 def getMetaData(self): |
|
112 """ |
|
113 Public method to get the entered data. |
|
114 |
|
115 @return dictionary containing the metadata. |
|
116 @rtype dict |
|
117 """ |
|
118 return { |
|
119 "Name": self.nameEdit.text(), |
|
120 "Type": self.typeComboBox.currentData(), |
|
121 "Version": self.versionEdit.text(), |
|
122 "Description": self.descriptionEdit.toPlainText(), |
|
123 "AuthorName": self.authorEdit.text(), |
|
124 "AuthorEmail": self.emailEdit.text(), |
|
125 "License": self.licenseComboBox.currentText(), |
|
126 "Manufacturer": self.manufacturerEdit.text(), |
|
127 "Supplier": self.supplierEdit.text(), |
|
128 } |