FreeCAD

Форк
0
/
addonmanager_devmode_license_selector.py 
275 строк · 11.5 Кб
1
# SPDX-License-Identifier: LGPL-2.1-or-later
2
# ***************************************************************************
3
# *                                                                         *
4
# *   Copyright (c) 2022 FreeCAD Project Association                        *
5
# *                                                                         *
6
# *   This file is part of FreeCAD.                                         *
7
# *                                                                         *
8
# *   FreeCAD is free software: you can redistribute it and/or modify it    *
9
# *   under the terms of the GNU Lesser General Public License as           *
10
# *   published by the Free Software Foundation, either version 2.1 of the  *
11
# *   License, or (at your option) any later version.                       *
12
# *                                                                         *
13
# *   FreeCAD is distributed in the hope that it will be useful, but        *
14
# *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
15
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
16
# *   Lesser General Public License for more details.                       *
17
# *                                                                         *
18
# *   You should have received a copy of the GNU Lesser General Public      *
19
# *   License along with FreeCAD. If not, see                               *
20
# *   <https://www.gnu.org/licenses/>.                                      *
21
# *                                                                         *
22
# ***************************************************************************
23

24
""" Contains a class to manage selection of a license for an Addon. """
25

26
import os
27
from datetime import date
28
from typing import Optional, Tuple
29

30
import FreeCAD
31
import FreeCADGui
32

33
from PySide.QtWidgets import QFileDialog, QDialog
34
from PySide.QtGui import QDesktopServices
35
from PySide.QtCore import QUrl, QFile, QIODevice
36

37
try:
38
    from PySide.QtGui import (
39
        QRegularExpressionValidator,
40
    )
41
    from PySide.QtCore import QRegularExpression
42

43
    RegexWrapper = QRegularExpression
44
    RegexValidatorWrapper = QRegularExpressionValidator
45
except ImportError:
46
    QRegularExpressionValidator = None
47
    QRegularExpression = None
48
    from PySide.QtGui import (
49
        QRegExpValidator,
50
    )
51
    from PySide.QtCore import QRegExp
52

53
    RegexWrapper = QRegExp
54
    RegexValidatorWrapper = QRegExpValidator
55

56
translate = FreeCAD.Qt.translate
57

58

59
class LicenseSelector:
60
    """Choose from a selection of licenses, or provide your own. Includes the capability to create
61
    the license file itself for a variety of popular open-source licenses, as well as providing
62
    links to opensource.org's page about the various licenses (which often link to other resources).
63
    """
64

65
    licenses = {
66
        "Apache-2.0": (
67
            "Apache License, Version 2.0",
68
            "https://opensource.org/licenses/Apache-2.0",
69
        ),
70
        "BSD-2-Clause": (
71
            "The 2-Clause BSD License",
72
            "https://opensource.org/licenses/BSD-2-Clause",
73
        ),
74
        "BSD-3-Clause": (
75
            "The 3-Clause BSD License",
76
            "https://opensource.org/licenses/BSD-3-Clause",
77
        ),
78
        "CC0-1.0": (
79
            "No Rights Reserved/Public Domain",
80
            "https://creativecommons.org/choose/zero/",
81
        ),
82
        "GPL-2.0-or-later": (
83
            "GNU General Public License version 2",
84
            "https://opensource.org/licenses/GPL-2.0",
85
        ),
86
        "GPL-3.0-or-later": (
87
            "GNU General Public License version 3",
88
            "https://opensource.org/licenses/GPL-3.0",
89
        ),
90
        "LGPL-2.1-or-later": (
91
            "GNU Lesser General Public License version 2.1",
92
            "https://opensource.org/licenses/LGPL-2.1",
93
        ),
94
        "LGPL-3.0-or-later": (
95
            "GNU Lesser General Public License version 3",
96
            "https://opensource.org/licenses/LGPL-3.0",
97
        ),
98
        "MIT": (
99
            "The MIT License",
100
            "https://opensource.org/licenses/MIT",
101
        ),
102
        "MPL-2.0": (
103
            "Mozilla Public License 2.0",
104
            "https://opensource.org/licenses/MPL-2.0",
105
        ),
106
    }
107

108
    def __init__(self, path_to_addon):
109
        self.other_label = translate(
110
            "AddonsInstaller",
111
            "Other...",
112
            "For providing a license other than one listed",
113
        )
114
        self.path_to_addon = path_to_addon
115
        self.dialog = FreeCADGui.PySideUic.loadUi(
116
            os.path.join(os.path.dirname(__file__), "developer_mode_license.ui")
117
        )
118
        self.pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
119
        for short_code, details in LicenseSelector.licenses.items():
120
            self.dialog.comboBox.addItem(f"{short_code}: {details[0]}", userData=short_code)
121
        self.dialog.comboBox.addItem(self.other_label)
122
        self.dialog.otherLineEdit.hide()
123
        self.dialog.otherLabel.hide()
124

125
        # Connections:
126
        self.dialog.comboBox.currentIndexChanged.connect(self._selection_changed)
127
        self.dialog.aboutButton.clicked.connect(self._about_clicked)
128
        self.dialog.browseButton.clicked.connect(self._browse_clicked)
129
        self.dialog.createButton.clicked.connect(self._create_clicked)
130

131
        # Set up the first selection to whatever the user chose last time
132
        short_code = self.pref.GetString("devModeLastSelectedLicense", "LGPL-2.1-or-later")
133
        self.set_license(short_code)
134

135
    def exec(self, short_code: str = None, license_path: str = "") -> Optional[Tuple[str, str]]:
136
        """The main method for executing this dialog, as a modal that returns a tuple of the
137
        license's "short code" and optionally the path to the license file. Returns a tuple
138
        of None,None if the user cancels the operation."""
139

140
        if short_code:
141
            self.set_license(short_code)
142
        self.dialog.pathLineEdit.setText(license_path)
143
        result = self.dialog.exec()
144
        if result == QDialog.Accepted:
145
            new_short_code = self.dialog.comboBox.currentData()
146
            new_license_path = self.dialog.pathLineEdit.text()
147
            if not new_short_code:
148
                new_short_code = self.dialog.otherLineEdit.text()
149
            self.pref.SetString("devModeLastSelectedLicense", new_short_code)
150
            return new_short_code, new_license_path
151
        return None
152

153
    def set_license(self, short_code):
154
        """Set the currently-selected license."""
155
        index = self.dialog.comboBox.findData(short_code)
156
        if index != -1:
157
            self.dialog.comboBox.setCurrentIndex(index)
158
        else:
159
            self.dialog.comboBox.setCurrentText(self.other_label)
160
            self.dialog.otherLineEdit.setText(short_code)
161

162
    def _selection_changed(self, _: int):
163
        """Callback: when the license selection changes, the UI is updated here."""
164
        if self.dialog.comboBox.currentText() == self.other_label:
165
            self.dialog.otherLineEdit.clear()
166
            self.dialog.otherLineEdit.show()
167
            self.dialog.otherLabel.show()
168
            self.dialog.aboutButton.setDisabled(True)
169
        else:
170
            self.dialog.otherLineEdit.hide()
171
            self.dialog.otherLabel.hide()
172
            self.dialog.aboutButton.setDisabled(False)
173

174
    def _current_short_code(self) -> str:
175
        """Gets the currently-selected license short code"""
176
        short_code = self.dialog.comboBox.currentData()
177
        if not short_code:
178
            short_code = self.dialog.otherLineEdit.text()
179
        return short_code
180

181
    def _about_clicked(self):
182
        """Callback: when the About button is clicked, try to launch a system-default web browser
183
        and display the OSI page about the currently-selected license."""
184
        short_code = self.dialog.comboBox.currentData()
185
        if short_code in LicenseSelector.licenses:
186
            url = LicenseSelector.licenses[short_code][1]
187
            QDesktopServices.openUrl(QUrl(url))
188
        else:
189
            FreeCAD.Console.PrintWarning(
190
                f"Internal Error: unrecognized license short code {short_code}\n"
191
            )
192

193
    def _browse_clicked(self):
194
        """Callback: browse for an existing license file."""
195
        start_dir = os.path.join(
196
            self.path_to_addon,
197
            self.dialog.pathLineEdit.text().replace("/", os.path.sep),
198
        )
199
        license_path, _ = QFileDialog.getOpenFileName(
200
            parent=self.dialog,
201
            caption=translate(
202
                "AddonsInstaller",
203
                "Select the corresponding license file in your Addon",
204
            ),
205
            dir=start_dir,
206
        )
207
        if license_path:
208
            self._set_path(self.path_to_addon, license_path)
209

210
    def _set_path(self, start_dir: str, license_path: str):
211
        """Sets the value displayed in the path widget to the relative path from
212
        start_dir to license_path"""
213
        license_path = license_path.replace("/", os.path.sep)
214
        base_dir = start_dir.replace("/", os.path.sep)
215
        if base_dir[-1] != os.path.sep:
216
            base_dir += os.path.sep
217
        if not license_path.startswith(base_dir):
218
            FreeCAD.Console.PrintError("Selected file not in Addon\n")
219
            # Eventually offer to copy it?
220
            return
221
        relative_path = license_path[len(base_dir) :]
222
        relative_path = relative_path.replace(os.path.sep, "/")
223
        self.dialog.pathLineEdit.setText(relative_path)
224

225
    def _create_clicked(self):
226
        """Asks the users for the path to save the new license file to, then copies our internal
227
        copy of the license text to that file."""
228
        start_dir = os.path.join(
229
            self.path_to_addon,
230
            self.dialog.pathLineEdit.text().replace("/", os.path.sep),
231
        )
232
        license_path, _ = QFileDialog.getSaveFileName(
233
            parent=self.dialog,
234
            caption=translate(
235
                "AddonsInstaller",
236
                "Location for new license file",
237
            ),
238
            dir=os.path.join(start_dir, "LICENSE"),
239
        )
240
        if license_path:
241
            self._set_path(start_dir, license_path)
242
            short_code = self._current_short_code()
243
            qf = QFile(f":/licenses/{short_code}.txt")
244
            if qf.exists():
245
                qf.open(QIODevice.ReadOnly)
246
                byte_data = qf.readAll()
247
                qf.close()
248

249
                string_data = str(byte_data, encoding="utf-8")
250

251
                if "<%%YEAR%%>" in string_data or "<%%COPYRIGHT HOLDER%%>" in string_data:
252
                    info_dlg = FreeCADGui.PySideUic.loadUi(
253
                        os.path.join(
254
                            os.path.dirname(__file__),
255
                            "developer_mode_copyright_info.ui",
256
                        )
257
                    )
258
                    info_dlg.yearLineEdit.setValidator(
259
                        RegexValidatorWrapper(RegexWrapper("^[12]\\d{3}$"))
260
                    )
261
                    info_dlg.yearLineEdit.setText(str(date.today().year))
262
                    result = info_dlg.exec()
263
                    if result != QDialog.Accepted:
264
                        return  # Don't create the file, just bail out
265

266
                    holder = info_dlg.copyrightHolderLineEdit.text()
267
                    year = info_dlg.yearLineEdit.text()
268

269
                    string_data = string_data.replace("<%%YEAR%%>", year)
270
                    string_data = string_data.replace("<%%COPYRIGHT HOLDER%%>", holder)
271

272
                with open(license_path, "w", encoding="utf-8") as f:
273
                    f.write(string_data)
274
            else:
275
                FreeCAD.Console.PrintError(f"Cannot create license file of type {short_code}\n")
276

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.