Esempio n. 1
0
def test_get(note):
    """Test retrieving a notification."""
    assert Notification.get('test-notification') == note

    note.delete()
    with pytest.raises(KeyError):
        Notification.get('test-notification')
Esempio n. 2
0
def test_list_filter_user_and_group(note, user):
    """Test that list filter with group works."""
    # No user/group filter
    assert list(Notification.list()) == [note]

    # No user/group set on notification
    assert list(Notification.list(user=user)) == [note]

    # Invalid user set on notification
    note.user = '******'
    note.group = 'test-group'
    note.save()
    assert list(Notification.list(user=user)) == []

    # Invalid group set on notification
    note.user = '******'
    note.group = 'invalid-group'
    note.save()
    assert list(Notification.list(user=user)) == []

    # Valid user and group set on notification
    note.user = '******'
    note.group = 'test-group'
    note.save()
    assert list(Notification.list(user=user)) == [note]
Esempio n. 3
0
def _show_schedule_error_notification(repository, is_error, exception=None):
    """Show or hide a notification related scheduled backup operation."""
    from plinth.notification import Notification
    id_ = 'backups-schedule-error-' + repository.uuid
    try:
        note = Notification.get(id_)
        error_count = note.data['error_count']
    except KeyError:
        error_count = 0

    message = ugettext_noop(
        'A scheduled backup failed. Past {error_count} attempts for backup '
        'did not succeed. The latest error is: {error_message}')
    data = {
        'app_name': 'translate:' + ugettext_noop('Backups'),
        'app_icon': 'fa-files-o',
        'error_count': error_count + 1 if is_error else 0,
        'error_message': str(exception)
    }
    title = ugettext_noop('Error During Backup')
    actions_ = [{
        'type': 'link',
        'class': 'primary',
        'text': ugettext_noop('Go to {app_name}'),
        'url': 'backups:index'
    }, {
        'type': 'dismiss'
    }]
    note = Notification.update_or_create(id=id_, app_id='backups',
                                         severity='error', title=title,
                                         message=message, actions=actions_,
                                         data=data, group='admin')
    note.dismiss(should_dismiss=not is_error)
Esempio n. 4
0
def _show_schedule_setup_notification():
    """Show a notification hinting to setup a remote backup schedule."""
    from plinth.notification import Notification
    message = gettext_noop(
        'Enable an automatic backup schedule for data safety. Prefer an '
        'encrypted remote backup location or an extra attached disk.')
    data = {
        'app_name': 'translate:' + gettext_noop('Backups'),
        'app_icon': 'fa-files-o'
    }
    title = gettext_noop('Enable a Backup Schedule')
    actions_ = [{
        'type': 'link',
        'class': 'primary',
        'text': gettext_noop('Go to {app_name}'),
        'url': 'backups:index'
    }, {
        'type': 'dismiss'
    }]
    Notification.update_or_create(id='backups-remote-schedule',
                                  app_id='backups',
                                  severity='info',
                                  title=title,
                                  message=message,
                                  actions=actions_,
                                  data=data,
                                  group='admin')
Esempio n. 5
0
    def _show_new_release_notification(self):
        """When upgraded to new release, show a notification."""
        from plinth.notification import Notification
        try:
            note = Notification.get('upgrades-new-release')
            if note.data['version'] == plinth.__version__:
                # User already has notification for update to this version. It
                # may be dismissed or not yet dismissed
                return

            # User currently has a notification for an older version, update.
            dismiss = False
        except KeyError:
            # Don't show notification for the first version user runs, create
            # but don't show it.
            dismiss = True

        data = {
            'version': plinth.__version__,
            'app_name': 'Update',
            'app_icon': 'fa-refresh'
        }
        title = ugettext_noop('FreedomBox Updated')
        note = Notification.update_or_create(
            id='upgrades-new-release',
            app_id='upgrades',
            severity='info',
            title=title,
            body_template='upgrades-new-release.html',
            data=data,
            group='admin')
        note.dismiss(should_dismiss=dismiss)
Esempio n. 6
0
def _warn_about_low_ram_space(request):
    """Warn about insufficient RAM space."""
    from plinth.notification import Notification

    memory_info = _get_memory_info()
    if memory_info['free_bytes'] < 1024**3:
        # Translators: This is the unit of computer storage Mebibyte similar to
        # Megabyte.
        memory_available_unit = gettext_noop('MiB')
        memory_available = memory_info['free_bytes'] / 1024**2
    else:
        # Translators: This is the unit of computer storage Gibibyte similar to
        # Gigabyte.
        memory_available_unit = gettext_noop('GiB')
        memory_available = memory_info['free_bytes'] / 1024**3

    show = False
    if memory_info['percent_used'] > 90:
        severity = 'error'
        advice_message = gettext_noop(
            'You should disable some apps to reduce memory usage.')
        show = True
    elif memory_info['percent_used'] > 75:
        severity = 'warning'
        advice_message = gettext_noop(
            'You should not install any new apps on this system.')
        show = True

    if not show:
        try:
            Notification.get('diagnostics-low-ram-space').delete()
        except KeyError:
            pass
        return

    message = gettext_noop(
        # xgettext:no-python-format
        'System is low on memory: {percent_used}% used, {memory_available} '
        '{memory_available_unit} free. {advice_message}')
    title = gettext_noop('Low Memory')
    data = {
        'app_icon': 'fa-heartbeat',
        'app_name': 'translate:' + gettext_noop('Diagnostics'),
        'percent_used': f'{memory_info["percent_used"]:.1f}',
        'memory_available': f'{memory_available:.1f}',
        'memory_available_unit': 'translate:' + memory_available_unit,
        'advice_message': 'translate:' + advice_message
    }
    actions = [{'type': 'dismiss'}]
    Notification.update_or_create(id='diagnostics-low-ram-space',
                                  app_id='diagnostics',
                                  severity=severity,
                                  title=title,
                                  message=message,
                                  actions=actions,
                                  data=data,
                                  group='admin')
Esempio n. 7
0
def test_list_filter_group(note, user):
    """Test that list filter with group works."""
    # Invalid group set on notification
    note.group = 'invalid-group'
    note.save()
    assert list(Notification.list(user=user)) == []

    # Valid group set on notification
    note.group = 'test-group'
    note.save()
    assert list(Notification.list(user=user)) == [note]
Esempio n. 8
0
def test_dismiss(note):
    """Test that setting dismissed value works."""
    note.dismiss()
    assert note.dismissed
    Notification.get('test-notification') == note  # Saved

    note.dismiss(should_dismiss=True)
    assert note.dismissed

    note.dismiss(should_dismiss=False)
    assert not note.dismissed
Esempio n. 9
0
def test_list_filter_user(note, user):
    """Test that list filter with user works."""
    # Invalid user set on notification
    note.user = '******'
    note.save()
    assert list(Notification.list(user=user)) == []

    # Valid user set on notification
    note.user = '******'
    note.save()
    assert list(Notification.list(user=user)) == [note]
Esempio n. 10
0
def warn_about_low_disk_space(request):
    """Warn about insufficient space on root partition."""
    from plinth.notification import Notification

    try:
        root_info = get_disk_info('/')
    except PlinthError as exception:
        logger.exception('Error getting information about root partition: %s',
                         exception)
        return

    show = False
    if root_info['percent_used'] > 90 or root_info['free_gib'] < 1:
        severity = 'error'
        show = True
    elif root_info['percent_used'] > 75 or root_info['free_gib'] < 2:
        severity = 'warning'
        show = True

    if not show:
        try:
            Notification.get('storage-low-disk-space').delete()
        except KeyError:
            pass
    else:
        message = ugettext_noop(
            # xgettext:no-python-format
            'Low space on system partition: {percent_used}% used, '
            '{free_space} free.')
        title = ugettext_noop('Low disk space')
        data = {
            'app_icon': 'fa-hdd-o',
            'app_name': ugettext_noop('Storage'),
            'percent_used': root_info['percent_used'],
            'free_space': format_bytes(root_info['free_bytes'])
        }
        actions = [{
            'type': 'link',
            'class': 'primary',
            'text': 'Go to {app_name}',
            'url': 'storage:index'
        }, {
            'type': 'dismiss'
        }]
        Notification.update_or_create(id='storage-low-disk-space',
                                      app_id='storage',
                                      severity=severity,
                                      title=title,
                                      message=message,
                                      actions=actions,
                                      data=data,
                                      group='admin')
Esempio n. 11
0
def check_dist_upgrade(_):
    """Check for upgrade to new stable release."""
    from plinth.notification import Notification
    if is_dist_upgrade_enabled():
        output = actions.superuser_run('upgrades', ['start-dist-upgrade'])
        result = json.loads(output)
        dist_upgrade_started = result['dist_upgrade_started']
        reason = result['reason']
        if 'found-previous' in reason:
            logger.info(
                'Found previous dist-upgrade. If it was interrupted, it will '
                'be restarted.')
        elif 'already-' in reason:
            logger.info('Skip dist upgrade: System is already up-to-date.')
        elif 'codename-not-found' in reason:
            logger.warning('Skip dist upgrade: Codename not found in release '
                           'file.')
        elif 'upgrades-not-enabled' in reason:
            logger.info('Skip dist upgrade: Automatic updates are not '
                        'enabled.')
        elif 'test-not-set' in reason:
            logger.info('Skip dist upgrade: --test is not set.')
        elif 'not-enough-free-space' in reason:
            logger.warning('Skip dist upgrade: Not enough free space in /.')
            title = gettext_noop('Could not start distribution update')
            message = gettext_noop(
                'There is not enough free space in the root partition to '
                'start the distribution update. Please ensure at least 5 GB '
                'is free. Distribution update will be retried after 24 hours,'
                ' if enabled.')
            Notification.update_or_create(
                id='upgrades-dist-upgrade-free-space', app_id='upgrades',
                severity='warning', title=title, message=message, actions=[{
                    'type': 'dismiss'
                }], group='admin')
        elif 'started-dist-upgrade' in reason:
            logger.info('Started dist upgrade.')
            title = gettext_noop('Distribution update started')
            message = gettext_noop(
                'Started update to next stable release. This may take a long '
                'time to complete.')
            Notification.update_or_create(id='upgrades-dist-upgrade-started',
                                          app_id='upgrades', severity='info',
                                          title=title, message=message,
                                          actions=[{
                                              'type': 'dismiss'
                                          }], group='admin')
        else:
            logger.warning('Unhandled result of start-dist-upgrade: %s, %s',
                           dist_upgrade_started, reason)
def test_display_context_body_template(note, user, load_cfg):
    """Test display context for a notification with body template."""
    note.body_template = 'invalid-template.html'
    note.save()

    context = Notification.get_display_context(user)
    assert context['notifications'][0]['body'] == {
        'content': b'Template invalid-template.html does not exist.'
    }

    note.body_template = 'test-notification.html'
    note.save()

    context = Notification.get_display_context(user)
    context_note = context['notifications'][0]
    assert context_note['body'].content == b'Test notification body\n'
Esempio n. 13
0
def common(request):
    """Add additional context values to RequestContext for use in templates.

    Any resources referenced in the return value are expected to have been
    initialized or configured externally beforehand.
    """
    # Allow a value in configuration file to be translated.  Allow
    # the brand name 'FreedomBox' itself to be translated.
    gettext_noop('FreedomBox')

    from plinth.notification import Notification
    notifications_context = Notification.get_display_context(request,
                                                             user=request.user)

    slash_indices = [match.start() for match in re.finditer('/', request.path)]
    active_menu_urls = [request.path[:index + 1] for index in slash_indices]
    return {
        'cfg': cfg,
        'submenu': menu.main_menu.active_item(request),
        'active_menu_urls': active_menu_urls,
        'box_name': _(cfg.box_name),
        'user_is_admin': is_user_admin(request, True),
        'notifications': notifications_context['notifications'],
        'notifications_max_severity': notifications_context['max_severity']
    }
Esempio n. 14
0
def report_failing_drive(id, is_failing):
    """Show or withdraw notification about failing drive."""
    notification_id = 'storage-disk-failure-' + base64.b32encode(
        id.encode()).decode()

    from plinth.notification import Notification
    title = ugettext_noop('Disk failure imminent')
    message = ugettext_noop(
        'Disk {id} is reporting that it is likely to fail in the near future. '
        'Copy any data while you still can and replace the drive.')
    data = {
        'app_icon': 'fa-hdd-o',
        'app_name': 'translate:' + ugettext_noop('Storage'),
        'id': id
    }
    note = Notification.update_or_create(id=notification_id,
                                         app_id='storage',
                                         severity='error',
                                         title=title,
                                         message=message,
                                         actions=[{
                                             'type': 'dismiss'
                                         }],
                                         data=data,
                                         group='admin')
    note.dismiss(should_dismiss=not is_failing)
Esempio n. 15
0
def fixture_note():
    """Fixture to return a valid notification object."""
    Notification.objects.all().delete()
    return Notification.update_or_create(id='test-notification',
                                         app_id='test-app',
                                         severity='info',
                                         title='Test Title',
                                         data={'test-key': 'test-value'})
Esempio n. 16
0
def test_display_context(gettext, note, user, rf):
    """Test display context for a notification."""
    request = rf.get('/plinth/help/about/')

    data = {
        'test-key1': 'test-value1',
        'test-key2': 'translate:test-value2',
        'test-key3': {
            'test-key4': 'translate:test-value4',
            'test-key5': 'test-value5'
        }
    }
    expected_data = {
        'test-key1': 'test-value1',
        'test-key2': 'translated test-value2',
        'test-key3': {
            'test-key4': 'translated test-value4',
            'test-key5': 'test-value5'
        }
    }
    actions = [{'type': 'link', 'text': 'Test text', 'url': 'Test url'}]
    expected_actions = [{
        'type': 'link',
        'text': 'translated Test text',
        'url': 'Test url'
    }]
    gettext.side_effect = lambda string: 'translated ' + string

    note.severity = 'error'
    note.title = 'Test Title {test-key1}'
    note.message = 'Test message {test-key1}'
    note.actions = actions
    note.user = '******'
    note.group = 'test-group'
    note.data = data
    note.save()

    context = Notification.get_display_context(request, user)
    assert len(context['notifications']) == 1
    assert context['max_severity'] == 'error'

    context_note = context['notifications'][0]
    assert context_note['id'] == 'test-notification'
    assert context_note['app_id'] == 'test-app'
    assert context_note['severity'] == 'error'
    assert context_note['title'] == 'translated Test Title test-value1'
    assert context_note['message'] == 'translated Test message test-value1'
    assert context_note['body'] is None
    assert context_note['actions'] == expected_actions
    assert context_note['data'] == expected_data
    assert note.data == data
    now = datetime.datetime.now()
    assert (now - context_note['created_time']).seconds < 60
    assert (now - context_note['last_update_time']).seconds < 60
    assert context_note['user'] == 'test-user'
    assert context_note['group'] == 'test-group'
    assert not context_note['dismissed']
Esempio n. 17
0
def test_update(note):
    """Test updating a existing notification."""
    note = Notification.get('test-notification')
    assert note.app_id == 'test-app'
    assert note.severity == 'info'
    assert note.title == 'Test Title'
    assert note.data == {'test-key': 'test-value'}

    Notification.update_or_create(id='test-notification',
                                  app_id='test-app2',
                                  severity='error',
                                  title='Test Title2',
                                  data={'test-key2': 'test-value2'})
    note = Notification.get('test-notification')
    assert note.app_id == 'test-app2'
    assert note.severity == 'error'
    assert note.title == 'Test Title2'
    assert note.data == {'test-key2': 'test-value2'}
Esempio n. 18
0
def test_display_context_body_template(note, user, load_cfg, rf):
    """Test display context for a notification with body template."""
    request = rf.get('/plinth/help/about/')

    note.body_template = 'invalid-template.html'
    note.save()

    context = Notification.get_display_context(request, user)
    assert context['notifications'][0]['body'] == {
        'content': b'Template invalid-template.html does not exist.'
    }

    note.body_template = 'test-notification.html'
    note.save()

    context = Notification.get_display_context(request, user)
    context_note = context['notifications'][0]
    assert context_note['body'].content == \
        b'Test notification body /plinth/help/about/\n'
Esempio n. 19
0
def on_schedule_save(repository):
    """Dismiss notification. Called when repository's schedule is updated."""
    if not repository.schedule.enabled:
        return

    from plinth.notification import Notification
    try:
        note = Notification.get('backups-remote-schedule')
        note.dismiss()
    except KeyError:
        pass
Esempio n. 20
0
def test_create(note):
    """Test creating a new notification works."""
    note.delete()
    note.update_or_create(id='test-notification',
                          app_id='test-app2',
                          severity='error',
                          title='Test Title2',
                          data={'test-key2': 'test-value2'})
    note = Notification.get('test-notification')
    assert note.app_id == 'test-app2'
    assert note.severity == 'error'
    assert note.title == 'Test Title2'
    assert note.data == {'test-key2': 'test-value2'}
Esempio n. 21
0
def test_list_filter_dismissed(note):
    """Test that list filter with dismissed works."""
    assert list(Notification.list(dismissed=True)) == []
    assert list(Notification.list(dismissed=False)) == [note]
    assert list(Notification.list(dismissed=None)) == [note]
    note.dismiss()
    assert list(Notification.list(dismissed=True)) == [note]
    assert list(Notification.list(dismissed=False)) == []
    assert list(Notification.list(dismissed=None)) == [note]
Esempio n. 22
0
def test_delete(note):
    """Test deleting a new notification works."""
    note.delete()
    with pytest.raises(KeyError):
        Notification.get('test-notification')
Esempio n. 23
0
def test_list_filter_key(note):
    """Test that list filter with key works."""
    assert list(Notification.list(key='invalid')) == []
    assert list(Notification.list(key='test-notification')) == [note]
    assert list(Notification.list()) == [note]
Esempio n. 24
0
def test_list_filter_app_id(note):
    """Test that list filter with app_id works."""
    assert list(Notification.list(app_id='invalid')) == []
    assert list(Notification.list(app_id='test-app')) == [note]
    assert list(Notification.list()) == [note]