• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Wirecloud / wirecloud / 9759450081

02 Jul 2024 10:37AM UTC coverage: 88.257% (+0.05%) from 88.21%
9759450081

Pull #545

github

web-flow
Merge f9702d03a into 2a2576628
Pull Request #545: Add ability to define different widget layouts for different screen sizes

8199 of 9815 branches covered (83.54%)

Branch coverage included in aggregate %.

426 of 469 new or added lines in 16 files covered. (90.83%)

31 existing lines in 8 files now uncovered.

21675 of 24034 relevant lines covered (90.18%)

31.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.71
src/wirecloud/platform/workspace/utils.py
1
# -*- coding: utf-8 -*-
2

3
# Copyright (c) 2008-2017 CoNWeT Lab., Universidad Politécnica de Madrid
4
# Copyright (c) 2019-2020 Future Internet Consulting and Development Solutions S.L.
5

6
# This file is part of Wirecloud.
7

8
# Wirecloud is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU Affero General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
12

13
# Wirecloud is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU Affero General Public License for more details.
17

18
# You should have received a copy of the GNU Affero General Public License
19
# along with Wirecloud.  If not, see <http://www.gnu.org/licenses/>.
20

21
import base64
13✔
22
from io import BytesIO
13✔
23
from copy import deepcopy
13✔
24
from Crypto.Cipher import AES
13✔
25
import json
13✔
26
import os
13✔
27
import re
13✔
28

29
from django.conf import settings
13✔
30
from django.core.cache import cache
13✔
31
from django.db.models import Q
13✔
32
from django.shortcuts import get_object_or_404
13✔
33
from django.utils.translation import ugettext as _
13✔
34
import markdown
13✔
35

36
from wirecloud.catalogue import utils as catalogue
13✔
37
from wirecloud.catalogue.models import CatalogueResource
13✔
38
from wirecloud.commons.models import Organization
13✔
39
from wirecloud.commons.utils.cache import CacheableData
13✔
40
from wirecloud.commons.utils.db import save_alternative
13✔
41
from wirecloud.commons.utils.downloader import download_http_content
13✔
42
from wirecloud.commons.utils.encoding import LazyEncoder
13✔
43
from wirecloud.commons.utils.html import clean_html
13✔
44
from wirecloud.commons.utils.template.parsers import TemplateParser
13✔
45
from wirecloud.commons.utils.urlify import URLify
13✔
46
from wirecloud.commons.utils.wgt import WgtFile
13✔
47
from wirecloud.platform.context.utils import get_context_values
13✔
48
from wirecloud.platform.iwidget.utils import parse_value_from_text
13✔
49
from wirecloud.platform.localcatalogue.utils import install_component
13✔
50
from wirecloud.platform.preferences.views import get_workspace_preference_values, get_tab_preference_values, update_workspace_preferences
13✔
51
from wirecloud.platform.models import IWidget, Tab, Workspace
13✔
52

53

54
def deleteTab(tab, user):
13✔
55
    # Delete iwidgets
56
    for iwidget in tab.iwidget_set.all():
13✔
57
        iwidget.delete()
1✔
58

59
    # Delete tab
60
    tab.delete()
13✔
61

62

63
def createTab(title, workspace, allow_renaming=False, name=None):
13✔
64

65
    if name is None or name.strip() == '':
13✔
66
        name = URLify(title)
13✔
67

68
    visible = False
13✔
69
    tabs = Tab.objects.filter(workspace=workspace, visible=True)
13✔
70
    if tabs.count() == 0:
13✔
71
        visible = True
13✔
72

73
    # It's always the last tab
74
    position = Tab.objects.filter(workspace=workspace).count()
13✔
75

76
    # Creating tab
77
    tab = Tab(name=name, title=title, visible=visible, position=position, workspace=workspace)
13✔
78
    if allow_renaming:
13✔
79
        save_alternative(Tab, 'name', tab)
13✔
80
    else:
81
        tab.save()
13✔
82

83
    return tab
13✔
84

85

86
def setVisibleTab(user, workspace_id, tab):
13✔
87
    visibleTabs = Tab.objects.filter(workspace__users__id=user.id, workspace__pk=workspace_id, visible=True).exclude(pk=tab.pk)
13✔
88
    for visibleTab in visibleTabs:
13✔
89
        visibleTab.visible = False
12✔
90
        visibleTab.save()
12✔
91
    tab.visible = True
13✔
92
    tab.save()
13✔
93

94

95
def encrypt_value(value):
13✔
96
    cipher = AES.new(settings.SECRET_KEY[:32].encode("utf-8"), mode=AES.MODE_ECB)
12✔
97
    json_value = json.dumps(value, ensure_ascii=False).encode('utf8')
12✔
98
    padded_value = json_value + (cipher.block_size - len(json_value) % cipher.block_size) * b' '
12✔
99
    return base64.b64encode(cipher.encrypt(padded_value)).decode('utf-8')
12✔
100

101

102
def decrypt_value(value):
13✔
103
    cipher = AES.new(settings.SECRET_KEY[:32].encode("utf-8"), mode=AES.MODE_ECB)
12✔
104
    try:
12✔
105
        value = cipher.decrypt(base64.b64decode(value))
12✔
106
        return json.loads(value.decode('utf8'))
12✔
107
    except Exception:
12✔
108
        return ''
12✔
109

110

111
def get_workspace_list(user):
13✔
112

113
    if not user.is_authenticated:
13✔
114
        return Workspace.objects.filter(public=True, searchable=True)
13✔
115

116
    # Now we can fetch all the workspaces for the user
117
    workspaces = Workspace.objects.filter(Q(public=True, searchable=True) | Q(users__id=user.id))
13✔
118

119
    return workspaces
13✔
120

121

122
def _process_variable(component_type, component_id, vardef, value, forced_values, values_by_varname, current_user, workspace_creator):
13✔
123
    varname = vardef['name']
13✔
124
    entry = {
13✔
125
        'type': vardef['type'],
126
        'secure': vardef['secure'],
127
    }
128
    if component_id in forced_values[component_type] and varname in forced_values[component_type][component_id]:
13✔
129
        fv_entry = forced_values[component_type][component_id][varname]
13✔
130

131
        entry['value'] = fv_entry['value']
13✔
132
        if vardef['secure']:
13!
133
            entry['value'] = encrypt_value(entry['value'])
×
134
        else:
135
            entry['value'] = parse_value_from_text(entry, entry['value'])
13✔
136

137
        entry['readonly'] = True
13✔
138
        entry['hidden'] = fv_entry.get('hidden', False)
13✔
139

140
    else:
141
        # Handle multiuser variables
142
        variable_user = current_user if vardef.get("multiuser", False) else workspace_creator
13✔
143
        if value is None or value["users"].get(str(variable_user.id), None) is None:
13✔
144
            value = parse_value_from_text(entry, vardef['default'])
13✔
145
        else:
146
            value = value["users"].get(str(variable_user.id), None)
13✔
147

148
        entry['value'] = value
13✔
149
        entry['readonly'] = False
13✔
150
        entry['hidden'] = False
13✔
151

152
    values_by_varname[component_type][component_id][varname] = entry
13✔
153

154

155
def _populate_variables_values_cache(workspace, user, key, forced_values=None):
13✔
156
    """ populates VariableValue cached values for that user """
157
    values_by_varname = {
13✔
158
        "ioperator": {},
159
        "iwidget": {},
160
    }
161
    if forced_values is None:
13✔
162
        context_values = get_context_values(workspace, user)
13✔
163
        preferences = get_workspace_preference_values(workspace)
13✔
164
        forced_values = process_forced_values(workspace, user, context_values, preferences)
13✔
165

166
    for iwidget in IWidget.objects.filter(tab__workspace=workspace):
13✔
167
        # forced_values uses string keys
168
        svariwidget = str(iwidget.id)
13✔
169
        values_by_varname["iwidget"][svariwidget] = {}
13✔
170

171
        if iwidget.widget is None:
13!
172
            continue
×
173

174
        iwidget_info = iwidget.widget.resource.get_processed_info()
13✔
175

176
        for vardef in iwidget_info.get('preferences', {}):
13✔
177
            value = iwidget.variables.get(vardef['name'], None)
13✔
178
            _process_variable("iwidget", svariwidget, vardef, value, forced_values, values_by_varname, user, workspace.creator)
13✔
179

180
        for vardef in iwidget_info.get('properties', {}):
13✔
181
            value = iwidget.variables.get(vardef['name'], None)
13✔
182
            _process_variable("iwidget", svariwidget, vardef, value, forced_values, values_by_varname, user, workspace.creator)
13✔
183

184
    for operator_id, operator in workspace.wiringStatus.get('operators', {}).items():
13✔
185

186
        values_by_varname["ioperator"][operator_id] = {}
13✔
187
        vendor, name, version = operator['name'].split('/')
13✔
188
        try:
13✔
189
            operator_info = CatalogueResource.objects.get(vendor=vendor, short_name=name, version=version).get_processed_info()
13✔
190
        except CatalogueResource.DoesNotExist:
1✔
191
            continue
1✔
192
        for vardef in operator_info.get('preferences', {}):
13✔
193
            value = operator.get("preferences", {}).get(vardef['name'], {}).get("value")
13✔
194
            _process_variable("ioperator", operator_id, vardef, value, forced_values, values_by_varname, user, workspace.creator)
13✔
195

196
        for vardef in operator_info.get('properties', {}):
13✔
197
            value = operator.get("properties", {}).get(vardef['name'], {}).get("value")
12✔
198
            _process_variable("ioperator", operator_id, vardef, value, forced_values, values_by_varname, user, workspace.creator)
12✔
199

200
    cache.set(key, values_by_varname)
13✔
201

202
    return values_by_varname
13✔
203

204

205
def _variable_values_cache_key(workspace, user):
13✔
206
    return '_variables_values_cache/%s/%s/%s' % (workspace.id, workspace.last_modified, user.id)
13✔
207

208

209
def _workspace_cache_key(workspace, user):
13✔
210
    return '_workspace_global_data/%s/%s/%s' % (workspace.id, workspace.last_modified, user.id)
13✔
211

212

213
class VariableValueCacheManager():
13✔
214

215
    workspace = None
13✔
216
    user = None
13✔
217
    values = None
13✔
218
    forced_values = None
13✔
219

220
    def __init__(self, workspace, user, forced_values=None):
13✔
221
        self.workspace = workspace
13✔
222
        self.user = user
13✔
223
        self.forced_values = forced_values
13✔
224

225
    def _process_entry(self, entry):
13✔
226

227
        if entry['secure'] is True:
13✔
228
            value = decrypt_value(entry['value'])
12✔
229
            return parse_value_from_text(entry, value)
12✔
230
        else:
231
            return entry['value']
13✔
232

233
    def get_variable_values(self):
13✔
234
        if self.values is None:
13✔
235
            key = _variable_values_cache_key(self.workspace, self.user)
13✔
236
            self.values = cache.get(key)
13✔
237
            if self.values is None:
13✔
238
                self.values = _populate_variables_values_cache(self.workspace, self.user, key, self.forced_values)
13✔
239

240
        return self.values
13✔
241

242
    def get_variable_value_from_varname(self, component_type, component_id, var_name):
13✔
243
        values = self.get_variable_values()
13✔
244
        entry = values[component_type][str(component_id)][var_name]
13✔
245
        return self._process_entry(entry)
13✔
246

247
    # Get variable data
248
    def get_variable_data(self, component_type, component_id, var_name):
13✔
249
        values = self.get_variable_values()
13✔
250
        entry = values[component_type][str(component_id)][var_name]
13✔
251

252
        # If secure and has value, censor it
253
        if entry['secure'] and entry["value"] != "":
13✔
254
            value = "********"
12✔
255
        else:
256
            value = entry["value"]
13✔
257

258
        return {
13✔
259
            'name': var_name,
260
            'secure': entry['secure'],
261
            'readonly': entry['readonly'],
262
            'hidden': entry['hidden'],
263
            'value': value,
264
        }
265

266

267
def get_workspace_data(workspace, user):
13✔
268

269
    longdescription = workspace.longdescription
13✔
270
    if longdescription != '':
13✔
271
        longdescription = clean_html(markdown.markdown(longdescription, output_format='xhtml5'))
13✔
272
    else:
273
        longdescription = workspace.description
13✔
274

275
    return {
13✔
276
        'id': str(workspace.id),
277
        'name': workspace.name,
278
        'title': workspace.title,
279
        'public': workspace.public,
280
        'shared': workspace.is_shared(),
281
        'requireauth': workspace.requireauth,
282
        'owner': workspace.creator.username,
283
        'removable': workspace.is_editable_by(user),
284
        'lastmodified': workspace.last_modified,
285
        'description': workspace.description,
286
        'longdescription': longdescription,
287
    }
288

289

290
class TemplateValueProcessor:
13✔
291

292
    _RE = re.compile(r'(%+)\(([a-zA-Z][\w-]*(?:\.[a-zA-Z\][\w-]*)*)\)')
13✔
293

294
    def __init__(self, context):
13✔
295
        self._context = context
13✔
296

297
    def __repl(self, matching):
13✔
298
        plen = len(matching.group(1))
13✔
299
        if (plen % 2) == 0:
13!
300
            return '%' * (plen / 2) + '(' + matching.group(1) + ')'
×
301

302
        var_path = matching.group(2).split('.')
13✔
303
        current_context = self._context
13✔
304

305
        while len(var_path) > 0:
13✔
306
            current_path = var_path.pop(0)
13✔
307

308
            if hasattr(current_context, current_path):
13!
309
                current_context = getattr(current_context, current_path)
×
310
            elif current_path in current_context:
13✔
311
                current_context = current_context[current_path]
13✔
312
            else:
313
                current_context = self._context
12✔
314
                break
12✔
315

316
        if current_context != self._context:
13✔
317
            return current_context
13✔
318
        else:
319
            return matching.group(0)
12✔
320

321
    def process(self, value):
13✔
322
        return self._RE.sub(self.__repl, value)
13✔
323

324

325
def normalize_forced_values(workspace):
13✔
326

327
    if 'extra_prefs' not in workspace.forcedValues:
13✔
328
        workspace.forcedValues['extra_prefs'] = []
13✔
329

330
    if 'ioperator' not in workspace.forcedValues:
13✔
331
        workspace.forcedValues['ioperator'] = {}
13✔
332

333
    if 'iwidget' not in workspace.forcedValues:
13✔
334
        workspace.forcedValues['iwidget'] = {}
13✔
335

336

337
def process_forced_values(workspace, user, concept_values, preferences):
13✔
338
    normalize_forced_values(workspace)
13✔
339
    forced_values = deepcopy(workspace.forcedValues)
13✔
340

341
    if len(forced_values['iwidget']) == 0 and len(forced_values['ioperator']) == 0:
13✔
342
        forced_values['empty_params'] = []
13✔
343
        return forced_values
13✔
344

345
    param_values = {}
13✔
346
    empty_params = []
13✔
347
    for param in forced_values['extra_prefs']:
13✔
348
        if param['name'] in preferences and (param['required'] is False or preferences[param['name']]['value'].strip() != ''):
13✔
349
            param_values[param['name']] = preferences[param['name']]['value']
13✔
350
        else:
351
            empty_params.append(param['name'])
13✔
352
            param_values[param['name']] = ''
13✔
353
    forced_values['empty_params'] = empty_params
13✔
354

355
    processor = TemplateValueProcessor({'user': user, 'context': concept_values, 'params': param_values})
13✔
356

357
    collection = forced_values['iwidget']
13✔
358
    for key in collection:
13✔
359
        values = collection[key]
13✔
360
        for var_name in values:
13✔
361
            collection[key][var_name]['value'] = processor.process(values[var_name]['value'])
13✔
362

363
    collection = forced_values['ioperator']
13✔
364
    for key in collection:
13✔
365
        values = collection[key]
13✔
366
        for var_name in values:
13✔
367
            collection[key][var_name]['value'] = processor.process(values[var_name]['value'])
13✔
368

369
    return forced_values
13✔
370

371

372
def _get_global_workspace_data(workspaceDAO, user):
13✔
373
    data_ret = get_workspace_data(workspaceDAO, user)
13✔
374

375
    # Workspace preferences
376
    preferences = get_workspace_preference_values(workspaceDAO)
13✔
377
    data_ret['preferences'] = preferences
13✔
378

379
    data_ret['users'] = []
13✔
380
    data_ret['groups'] = []
13✔
381

382
    if workspaceDAO.creator == user:
13✔
383
        for u in workspaceDAO.users.all():
13✔
384
            try:
13✔
385
                is_organization = u.organization is not None
13✔
386
            except Organization.DoesNotExist:
13✔
387
                is_organization = False
13✔
388

389
            data_ret['users'].append({
13✔
390
                "fullname": u.get_full_name(),
391
                "username": u.username,
392
                "organization": is_organization,
393
                "accesslevel": "owner" if workspaceDAO.creator == u else "read",
394
            })
395

396
        for g in workspaceDAO.groups.all():
13✔
397
            try:
12✔
398
                is_organization = g.organization is not None
12✔
399
            except Organization.DoesNotExist:
12✔
400
                is_organization = False
12✔
401

402
            if is_organization is False:
12✔
403
                data_ret['groups'].append({
12✔
404
                    "name": g.name,
405
                    "accesslevel": "read",
406
                })
407

408
    # Process forced variable values
409
    concept_values = get_context_values(workspaceDAO, user)
13✔
410
    forced_values = process_forced_values(workspaceDAO, user, concept_values, preferences)
13✔
411
    data_ret['empty_params'] = forced_values['empty_params']
13✔
412
    data_ret['extra_prefs'] = forced_values['extra_prefs']
13✔
413
    if len(forced_values['empty_params']) > 0:
13✔
414
        return json.dumps(data_ret, cls=LazyEncoder)
13✔
415

416
    cache_manager = VariableValueCacheManager(workspaceDAO, user, forced_values)
13✔
417

418
    # Tabs processing
419
    # Check if the workspace's tabs have order
420
    tabs = Tab.objects.filter(workspace=workspaceDAO).order_by('position')
13✔
421
    if tabs.count() == 0:
13✔
422
        tabs = [createTab(_('Tab'), workspaceDAO)]
1✔
423

424
    data_ret['tabs'] = [get_tab_data(tab, workspace=workspaceDAO, cache_manager=cache_manager, user=user) for tab in tabs]
13✔
425
    data_ret['wiring'] = deepcopy(workspaceDAO.wiringStatus)
13✔
426
    for operator_id, operator in data_ret['wiring'].get('operators', {}).items():
13✔
427
        try:
13✔
428
            (vendor, name, version) = operator['name'].split('/')
13✔
429
        except ValueError:
×
430
            continue
×
431

432
        try:
13✔
433
            resource = CatalogueResource.objects.get(vendor=vendor, short_name=name, version=version)
13✔
434
            operator_info = resource.get_processed_info(process_variables=True)
13✔
435
            # Check if the resource is available, if not, variables should not be retrieved
436
            if not resource.is_available_for(workspaceDAO.creator):
13✔
437
                raise CatalogueResource.DoesNotExist
1✔
438
        except CatalogueResource.DoesNotExist:
1✔
439
            operator["preferences"] = {}
1✔
440
            operator["properties"] = {}
1✔
441
            continue
1✔
442

443
        operator_forced_values = forced_values['ioperator'].get(operator_id, {})
13✔
444
        # Build operator preference data
445
        for preference_name, preference in operator.get('preferences', {}).items():
13✔
446
            vardef = operator_info['variables']['preferences'].get(preference_name)
13✔
447
            value = preference.get('value', None)
13✔
448

449
            # Handle multiuser
450
            variable_user = user if vardef is not None and vardef["multiuser"] else workspaceDAO.creator
13✔
451

452
            if preference_name in operator_forced_values:
13✔
453
                preference['value'] = operator_forced_values[preference_name]['value']
13✔
454
            elif value is None or value["users"].get(str(variable_user.id), None) is None:
13✔
455
                # If not defined / not defined for the current user, take the default value
456
                preference['value'] = parse_value_from_text(vardef, vardef['default'])
1✔
457
            else:
458
                preference['value'] = value["users"].get(str(variable_user.id))
13✔
459

460
            # Secure censor
461
            if vardef is not None and vardef["secure"]:
13✔
462
                preference['value'] = "" if preference.get('value') is None or decrypt_value(preference.get('value')) == "" else "********"
12✔
463

464
        # Build operator property data
465
        for property_name, property in operator.get('properties', {}).items():
13✔
466
            vardef = operator_info['variables']['properties'].get(property_name)
12✔
467
            value = property.get('value', None)
12✔
468

469
            # Handle multiuser
470
            variable_user = user if vardef is not None and vardef["multiuser"] else workspaceDAO.creator
12✔
471

472
            if property_name in operator_forced_values:
12!
473
                property['value'] = operator_forced_values[property_name]['value']
×
474
            elif value is None or value["users"].get(str(variable_user.id), None) is None:
12!
475
                # If not defined / not defined for the current user, take the default value
476
                property['value'] = parse_value_from_text(vardef, vardef['default'])
×
477
            else:
478
                property['value'] = value["users"].get(str(variable_user.id))
12✔
479

480
            # Secure censor
481
            if vardef is not None and vardef["secure"]:
12✔
482
                property['value'] = "" if property.get('value') is None or decrypt_value(property.get('value')) == "" else "********"
12✔
483

484
    return json.dumps(data_ret, cls=LazyEncoder)
13✔
485

486

487
def get_global_workspace_data(workspace, user):
13✔
488
    key = _workspace_cache_key(workspace, user)
13✔
489
    data = cache.get(key)
13✔
490
    if data is None:
13✔
491
        data = CacheableData(_get_global_workspace_data(workspace, user), timestamp=workspace.last_modified)
13✔
492
        key = _workspace_cache_key(workspace, user)
13✔
493
        cache.set(key, data)
13✔
494

495
    return data
13✔
496

497

498
def get_tab_data(tab, workspace=None, cache_manager=None, user=None):
13✔
499

500
    if workspace is None:
13✔
501
        workspace = tab.workspace
13✔
502

503
    if cache_manager is None:
13✔
504
        cache_manager = VariableValueCacheManager(workspace, user)
13✔
505

506
    return {
13✔
507
        'id': str(tab.id),
508
        'name': tab.name,
509
        'title': tab.title,
510
        'visible': tab.visible,
511
        'preferences': get_tab_preference_values(tab),
512
        'iwidgets': [get_iwidget_data(widget, workspace, cache_manager, user) for widget in tab.iwidget_set.order_by('id')]
513
    }
514

515

516
def get_iwidget_data(iwidget, workspace, cache_manager=None, user=None):
13✔
517

518
    permissions = iwidget.permissions
13✔
519
    if workspace.creator != user and 'owner' in permissions:
13!
UNCOV
520
        del permissions['owner']
×
521

522
    data_ret = {
13✔
523
        'id': str(iwidget.id),
524
        'title': iwidget.name,
525
        'tab': iwidget.tab.id,
526
        'layout': iwidget.layout,
527
        'widget': iwidget.widget_uri,
528
        'layoutConfigurations': [],
529
        'readonly': iwidget.readOnly,
530
        'permissions': permissions,
531
        'preferences': {},
532
        'properties': {}
533
    }
534

535
    if not 'configurations' in iwidget.positions:
13!
UNCOV
536
        raise ValueError(_('Invalid iwidget format stored in the database. Please, update the iwidget positions field.'))
×
537

538
    for layoutConfiguration in iwidget.positions.get('configurations'):
13✔
539
        widget_position = layoutConfiguration.get('widget', {})
13✔
540
        dataLayout = {
13✔
541
            'top': widget_position.get('top', 0),
542
            'left': widget_position.get('left', 0),
543
            'anchor': widget_position.get('anchor', 'top-left'),
544
            'relx': True if iwidget.layout != 1 else widget_position.get('relx', True),
545
            'rely': True if iwidget.layout != 1 else widget_position.get('rely', False),
546
            'relheight': True if iwidget.layout != 1 else widget_position.get('relheight', False),
547
            'relwidth': True if iwidget.layout != 1 else widget_position.get('relwidth', True),
548
            'zIndex': widget_position.get('zIndex', 0),
549
            'width': widget_position.get('width', 0),
550
            'height': widget_position.get('height', 0),
551
            'fulldragboard': widget_position.get('fulldragboard', False),
552
            'minimized': widget_position.get('minimized', False),
553
            'titlevisible': widget_position.get('titlevisible', True),
554
            'id': layoutConfiguration.get('id', 0),
555
            'moreOrEqual': layoutConfiguration.get('moreOrEqual', 0),
556
            'lessOrEqual': layoutConfiguration.get('lessOrEqual', -1)
557
        }
558

559
        data_ret['layoutConfigurations'].append(dataLayout)
13✔
560

561
    if iwidget.widget is None or not iwidget.widget.resource.is_available_for(workspace.creator):
13✔
562
        # The widget used by this iwidget is missing
563
        return data_ret
13✔
564

565
    if cache_manager is None:
13✔
566
        cache_manager = VariableValueCacheManager(workspace, user)
13✔
567

568
    iwidget_info = iwidget.widget.resource.get_processed_info()
13✔
569
    data_ret['preferences'] = {preference['name']: cache_manager.get_variable_data("iwidget", iwidget.id, preference['name']) for preference in iwidget_info['preferences']}
13✔
570
    data_ret['properties'] = {property['name']: cache_manager.get_variable_data("iwidget", iwidget.id, property['name']) for property in iwidget_info['properties']}
13✔
571

572
    return data_ret
13✔
573

574

575
def create_workspace(owner, mashup, new_name=None, new_title=None, preferences={}, searchable=True, public=False, allow_renaming=False, dry_run=False):
13✔
576

577
    from wirecloud.platform.workspace.mashupTemplateParser import buildWorkspaceFromTemplate, check_mashup_dependencies
13✔
578

579
    if type(mashup) == str:
13✔
580
        values = mashup.split('/', 3)
13✔
581
        if len(values) != 3:
13✔
582
            raise ValueError(_('invalid mashup id'))
12✔
583

584
        (mashup_vendor, mashup_name, mashup_version) = values
13✔
585
        try:
13✔
586
            resource = CatalogueResource.objects.get(vendor=mashup_vendor, short_name=mashup_name, version=mashup_version)
13✔
587
            if not resource.is_available_for(owner) or resource.resource_type() != 'mashup':
13✔
588
                raise CatalogueResource.DoesNotExist
12✔
589
        except CatalogueResource.DoesNotExist:
12✔
590
            raise ValueError(_('Mashup not found: %(mashup)s') % {'mashup': mashup})
12✔
591

592
        base_dir = catalogue.wgt_deployer.get_base_dir(mashup_vendor, mashup_name, mashup_version)
13✔
593
        wgt_file = WgtFile(os.path.join(base_dir, resource.template_uri))
13✔
594
        template = TemplateParser(wgt_file.get_template())
13✔
595
    elif isinstance(mashup, Workspace):
13✔
596
        from wirecloud.platform.workspace.mashupTemplateGenerator import build_json_template_from_workspace
12✔
597
        options = {
12✔
598
            'vendor': 'api',
599
            'name': mashup.name,
600
            'version': '1.0',
601
            'title': mashup.title if mashup.title is not None and mashup.title.strip() != "" else mashup.name,
602
            'description': 'Temporal mashup for the workspace copy operation',
603
            'email': 'a@example.com',
604
        }
605

606
        template = TemplateParser(build_json_template_from_workspace(options, mashup, mashup.creator))
12✔
607
    else:
608
        wgt = mashup if isinstance(mashup, WgtFile) else WgtFile(mashup)
13✔
609
        template = TemplateParser(wgt.get_template())
13✔
610

611
        resource_info = template.get_resource_processed_info(process_urls=False)
13✔
612
        if resource_info["type"] != 'mashup':
13✔
613
            raise ValueError("WgtFile is not a mashup")
12✔
614

615
        for embedded_resource in resource_info['embedded']:
13✔
616
            if embedded_resource['src'].startswith('https://'):
13!
UNCOV
617
                resource_file = download_http_content(embedded_resource['src'])
×
618
            else:
619
                resource_file = BytesIO(wgt.read(embedded_resource['src']))
13✔
620

621
            extra_resource_contents = WgtFile(resource_file)
13✔
622
            install_component(extra_resource_contents, executor_user=owner, users=[owner])
13✔
623

624
    check_mashup_dependencies(template, owner)
13✔
625

626
    if dry_run:
13✔
627
        # TODO check name conflict
628
        return None
12✔
629

630
    workspace, _foo = buildWorkspaceFromTemplate(template, owner, allow_renaming=allow_renaming, new_name=new_name, new_title=new_title, searchable=searchable, public=public)
13✔
631

632
    if len(preferences) > 0:
13!
UNCOV
633
        update_workspace_preferences(workspace, preferences, invalidate_cache=False)
×
634

635
    return workspace
13✔
636

637

638
def delete_workspace(workspace=None, user=None, name=None):
13✔
639
    if workspace is None:
13✔
640
        workspace = get_object_or_404(Workspace, creator__username=user, name=name)
12✔
641

642
    # Remove the workspace
643
    iwidgets = IWidget.objects.filter(tab__workspace=workspace)
13✔
644
    for iwidget in iwidgets:
13✔
645
        iwidget.delete()
13✔
646
    workspace.delete()
13✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc