├── tests ├── __init__.py ├── test_app │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_utils.py │ │ ├── test_urls.py │ │ ├── test_dump.py │ │ ├── test_admin.py │ │ ├── test_load.py │ │ ├── test_forms.py │ │ ├── test_auth.py │ │ └── test_views.py │ ├── smuggler_fixtures │ │ ├── garbage │ │ │ ├── garbage.json │ │ │ └── invalid_page_dump.json │ │ ├── site_dump.json │ │ ├── page_dump.json │ │ ├── all_dump.json │ │ └── big_file.json │ ├── templates │ │ └── 404.html │ ├── models.py │ ├── admin.py │ └── urls.py └── test_settings.py ├── setup.cfg ├── etc ├── screenshot-0.png └── screenshot-1.png ├── test-requirements.txt ├── smuggler ├── locale │ ├── ca │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fo │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── nl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── pl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── uk │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── pt_BR │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── templates │ └── smuggler │ │ ├── change_list.html │ │ └── load_data_form.html ├── __init__.py ├── urls.py ├── settings.py ├── utils.py ├── forms.py └── views.py ├── .gitignore ├── .tx └── config ├── Makefile ├── manage.py ├── MANIFEST.in ├── tox.ini ├── AUTHORS ├── .travis.yml ├── setup.py ├── COPYING.LESSER └── README.rst /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_app/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /tests/test_app/smuggler_fixtures/garbage/garbage.json: -------------------------------------------------------------------------------- 1 | not really a json file at all! -------------------------------------------------------------------------------- /tests/test_app/templates/404.html: -------------------------------------------------------------------------------- 1 | This needs to exist for tests to pass on Django 1.4 -------------------------------------------------------------------------------- /etc/screenshot-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/etc/screenshot-0.png -------------------------------------------------------------------------------- /etc/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/etc/screenshot-1.png -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | Django 2 | coverage 3 | coveralls 4 | tox 5 | freezegun 6 | wheel 7 | flake8 8 | -------------------------------------------------------------------------------- /smuggler/locale/ca/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/ca/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/fo/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/fo/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/pl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/pl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/uk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/uk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /smuggler/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semente/django-smuggler/HEAD/smuggler/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /tests/test_app/smuggler_fixtures/site_dump.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "pk": 1, 3 | "model": "sites.site", 4 | "fields": { 5 | "domain": "example.com", 6 | "name": "test.com" 7 | } 8 | }] 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.bak 3 | *.swp 4 | *~ 5 | ._* 6 | .#* 7 | *# 8 | .tox/ 9 | build/ 10 | dist/ 11 | django_smuggler.egg-info/ 12 | README.html 13 | 14 | # coverage 15 | .coverage 16 | htmlcov 17 | 18 | -------------------------------------------------------------------------------- /tests/test_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Page(models.Model): 5 | title = models.CharField(max_length=255) 6 | path = models.SlugField(unique=True) 7 | body = models.TextField() 8 | -------------------------------------------------------------------------------- /tests/test_app/smuggler_fixtures/page_dump.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "pk": 1, 3 | "model": "test_app.page", 4 | "fields": { 5 | "title": "test", 6 | "path": "", 7 | "body": "test body" 8 | } 9 | }] 10 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [django-smuggler.master] 5 | file_filter = smuggler/locale//LC_MESSAGES/django.po 6 | source_file = smuggler/locale/en/LC_MESSAGES/django.po 7 | source_lang = en 8 | 9 | -------------------------------------------------------------------------------- /tests/test_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from tests.test_app.models import Page 4 | 5 | 6 | class PageAdmin(admin.ModelAdmin): 7 | change_list_template = 'smuggler/change_list.html' 8 | 9 | 10 | admin.site.register(Page, PageAdmin) 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: coverage tests lint 2 | 3 | tests: 4 | python manage.py test 5 | 6 | coverage: 7 | coverage run --source smuggler --branch manage.py test 8 | coverage report -m 9 | coverage html 10 | python -mwebbrowser htmlcov/index.html 11 | 12 | lint: 13 | flake8 smuggler tests 14 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.test_settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /tests/test_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls.static import static 3 | from django.contrib import admin 4 | from django.urls import include, path 5 | 6 | admin.autodiscover() 7 | 8 | urlpatterns = [ 9 | path('admin/', include('smuggler.urls')), 10 | path('admin/', admin.site.urls) 11 | ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 12 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include COPYING.LESSER 3 | include MANIFEST.in 4 | include README.rst 5 | recursive-include etc * 6 | recursive-include smuggler/locale *.po *.mo 7 | recursive-include smuggler/templates *.html 8 | 9 | include test-requirements.txt 10 | include .travis.yml 11 | include Makefile 12 | include tox.ini 13 | recursive-include tests *.html 14 | recursive-include tests *.json 15 | recursive-include tests *.py 16 | -------------------------------------------------------------------------------- /tests/test_app/smuggler_fixtures/all_dump.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 1, 4 | "model": "sites.site", 5 | "fields": { 6 | "domain": "example.com", 7 | "name": "test.com" 8 | } 9 | }, 10 | { 11 | "pk": 1, 12 | "model": "test_app.page", 13 | "fields": { 14 | "title": "test", 15 | "path": "", 16 | "body": "test body" 17 | } 18 | }] 19 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{36,37,38}-dj{22,30,31} 3 | 4 | [testenv] 5 | basepython = py36: python3.6 6 | py37: python3.7 7 | py38: python3.8 8 | commands = python -W module manage.py test 9 | deps = freezegun 10 | dj22: Django>=2.2,<3.0 11 | dj30: Django>=3.0,<3.1 12 | dj31: Django>=3.1,<3.2 13 | 14 | [testenv:flake8] 15 | commands = flake8 smuggler tests 16 | deps = flake8 17 | -------------------------------------------------------------------------------- /tests/test_app/smuggler_fixtures/garbage/invalid_page_dump.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 1, 4 | "model": "test_app.page", 5 | "fields": { 6 | "title": "test", 7 | "path": "", 8 | "body": "test body" 9 | } 10 | }, 11 | { 12 | "pk": 2, 13 | "model": "test_app.page", 14 | "fields": { 15 | "title": "test", 16 | "path": "", 17 | "body": "test body" 18 | } 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Smuggler was created in late 2009 by Guilherme Semente. Following is a 2 | (probably incomplete) list of its contributors: 3 | 4 | * Guilherme Semente 5 | * Lachie Cox 6 | * Jaap Roes 7 | * Peter Conerly 8 | * Kleiver Fonseca 9 | * Mario Orlandi 10 | 11 | Thanks to Interaction Consortium for 12 | sponsoring the first releases of the project. 13 | -------------------------------------------------------------------------------- /smuggler/templates/smuggler/change_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_list.html" %} 2 | {% load i18n %} 3 | 4 | {% block object-tools-items %} 5 | {% if user.is_superuser %} 6 |
  • 7 | 8 | {% trans "Dump data" %} 9 | 10 |
  • 11 |
  • 12 | 13 | {% trans "Load data" %} 14 | 15 |
  • 16 | {% endif %} 17 | {{ block.super }} 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | language: python 3 | python: 4 | - 3.6 5 | - 3.7 6 | - 3.8 7 | env: 8 | - DJANGO_VERSION='Django>=2.2,<3.0' 9 | - DJANGO_VERSION='Django>=3.0,<3.1' 10 | - DJANGO_VERSION='Django>=3.1,<3.2' 11 | install: 12 | - pip install --upgrade pip 13 | - pip install $DJANGO_VERSION --upgrade 14 | - django-admin.py --version 15 | - pip install -q -e . 16 | - pip install -q -r test-requirements.txt 17 | script: 18 | - coverage run --source smuggler --branch manage.py test 19 | after_success: 20 | - coveralls 21 | branches: 22 | only: 23 | - master 24 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from unittest import TestCase 3 | 4 | from django.core.files.uploadedfile import SimpleUploadedFile 5 | 6 | from smuggler.utils import save_uploaded_file_on_disk 7 | 8 | 9 | def p(*args): 10 | return os.path.abspath( 11 | os.path.join(os.path.dirname(__file__), *args)) 12 | 13 | 14 | class TestSaveUploadedFileOnDisk(TestCase): 15 | def test_save_uploaded_file_on_disk(self): 16 | path = p('..', 'smuggler_fixtures', 'test.json') 17 | save_uploaded_file_on_disk( 18 | SimpleUploadedFile('test.json', b'[]'), path) 19 | self.assertTrue(os.path.exists(path)) 20 | os.unlink(path) 21 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_urls.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.urls import reverse 3 | 4 | 5 | class TestSmugglerUrls(TestCase): 6 | def test_can_reverse_dump_data(self): 7 | self.assertEqual(reverse('dump-data'), '/admin/dump/') 8 | 9 | def test_can_reverse_dump_app_data(self): 10 | url = reverse('dump-app-data', kwargs={'app_label': 'sites'}) 11 | self.assertEqual(url, '/admin/sites/dump/') 12 | 13 | def test_can_reverse_dump_model_data(self): 14 | url = reverse('dump-model-data', kwargs={ 15 | 'app_label': 'sites', 16 | 'model_label': 'site' 17 | }) 18 | self.assertEqual(url, '/admin/sites/site/dump/') 19 | 20 | def test_can_reverse_load_data(self): 21 | self.assertEqual(reverse('load-data'), '/admin/load/') 22 | -------------------------------------------------------------------------------- /smuggler/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Guilherme Semente and contributors 2 | # 3 | # This file is part of Django Smuggler. 4 | # 5 | # Django Smuggler is free software under terms of the GNU Lesser 6 | # General Public License version 3 (LGPLv3) as published by the Free 7 | # Software Foundation. See the file README for copying conditions. 8 | 9 | """ 10 | Django Smuggler is a pluggable application for Django Web Framework that help 11 | you dump/load fixtures via the automatically-generated admin interface. 12 | """ 13 | 14 | VERSION = (1, 0, 4) 15 | 16 | 17 | def get_version(): 18 | """ 19 | Returns the version as a human-format string. 20 | """ 21 | return ".".join([str(i) for i in VERSION]) 22 | 23 | 24 | __author__ = "See the file AUTHORS." 25 | __license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)" 26 | __url__ = "https://github.com/semente/django-smuggler" 27 | __version__ = get_version() 28 | -------------------------------------------------------------------------------- /smuggler/urls.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Guilherme Semente and contributors 2 | # 3 | # This file is part of Django Smuggler. 4 | # 5 | # Django Smuggler is free software under terms of the GNU Lesser 6 | # General Public License version 3 (LGPLv3) as published by the Free 7 | # Software Foundation. See the file README for copying conditions. 8 | 9 | from django.urls import include, path 10 | 11 | from smuggler.views import dump_app_data, dump_data, dump_model_data, load_data 12 | 13 | urlpatterns = [ 14 | path("dump/", dump_data, name="dump-data"), 15 | path( 16 | "/", 17 | include( 18 | [ 19 | path("dump/", dump_app_data, name="dump-app-data"), 20 | path( 21 | "/dump/", dump_model_data, name="dump-model-data" 22 | ), 23 | ] 24 | ), 25 | ), 26 | path("load/", load_data, name="load-data"), 27 | ] 28 | -------------------------------------------------------------------------------- /smuggler/settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Guilherme Semente and contributors 2 | # 3 | # This file is part of Django Smuggler. 4 | # 5 | # Django Smuggler is free software under terms of the GNU Lesser 6 | # General Public License version 3 (LGPLv3) as published by the Free 7 | # Software Foundation. See the file README for copying conditions. 8 | 9 | from django.conf import settings 10 | from django.dispatch import receiver 11 | from django.test.signals import setting_changed 12 | 13 | SMUGGLER_EXCLUDE_LIST = getattr(settings, "SMUGGLER_EXCLUDE_LIST", []) 14 | SMUGGLER_FIXTURE_DIR = getattr(settings, "SMUGGLER_FIXTURE_DIR", None) 15 | SMUGGLER_FORMAT = getattr(settings, "SMUGGLER_FORMAT", "json") 16 | SMUGGLER_INDENT = getattr(settings, "SMUGGLER_INDENT", 2) 17 | 18 | 19 | @receiver(setting_changed) 20 | def update_settings(setting=None, value=None, **kwargs): 21 | if setting and setting in globals(): # pragma: no branch 22 | globals()[setting] = value 23 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_dump.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from io import StringIO 4 | 5 | from django.core.management import CommandError 6 | from django.test import TestCase 7 | 8 | from smuggler import utils 9 | from tests.test_app.models import Page 10 | 11 | 12 | class BasicDumpTestCase(TestCase): 13 | SITE_DUMP = [{ 14 | "model": "sites.site", 15 | "fields": { 16 | "domain": "example.com", 17 | "name": "example.com" 18 | } 19 | }] 20 | PAGE_DUMP = [{ 21 | "pk": 1, 22 | "model": "test_app.page", 23 | "fields": { 24 | "title": "test", 25 | "path": "", 26 | "body": "test body", 27 | } 28 | }] 29 | BASIC_DUMP = SITE_DUMP + PAGE_DUMP 30 | 31 | def setUp(self): 32 | p = Page(title='test', body='test body') 33 | p.save() 34 | 35 | def normalize(self, out): 36 | return json.loads(out) 37 | 38 | def test_serialize_exclude(self): 39 | stream = StringIO() 40 | utils.serialize_to_response(exclude=['sites', 'auth', 'contenttypes'], 41 | response=stream) 42 | out = self.normalize(stream.getvalue()) 43 | self.assertEqual(out, self.PAGE_DUMP) 44 | 45 | def test_serialize_include(self): 46 | stream = StringIO() 47 | utils.serialize_to_response(app_labels=['sites'], response=stream) 48 | out = self.normalize(stream.getvalue()) 49 | self.assertEqual(out, self.SITE_DUMP) 50 | 51 | def test_serialize_unknown_app_fail(self): 52 | self.assertRaises(CommandError, utils.serialize_to_response, 53 | ['flatpages']) 54 | -------------------------------------------------------------------------------- /smuggler/templates/smuggler/load_data_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{% trans "Load data" %} {{ block.super }}{% endblock %} 5 | 6 | {% block extrahead %} 7 | {{ block.super }} 8 | 9 | {{ form.media.js }} 10 | {% endblock %} 11 | 12 | {% block extrastyle %} 13 | {{ block.super }} 14 | {{ form.media.css }} 15 | {% endblock %} 16 | 17 | {% block bodyclass %}{{ block.super }} change-form{% endblock %} 18 | 19 | {% block breadcrumbs %} 20 | 24 | {% endblock %} 25 | 26 | {% block content %} 27 |
    28 |
    29 | {% csrf_token %} 30 |

    {% trans "Load data" %}

    31 |
    32 |

    33 | {% trans 'Existing items with same primary key will be overwritten.' %} 34 |

    35 |
    36 | {% if form.errors %} 37 |

    38 | {% if form.errors|length == 1 %} 39 | {% trans "Please correct the error below." %} 40 | {% else %} 41 | {% trans "Please correct the errors below." %} 42 | {% endif %} 43 |

    44 | {{ form.non_field_errors }} 45 | {% endif %} 46 | {% for fieldset in adminform %} 47 | {% include "admin/includes/fieldset.html" %} 48 | {% endfor %} 49 |
    50 | 51 |
    52 |
    53 |
    54 | {% endblock %} 55 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import Permission, User 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | 6 | class TestAdminNormalUser(TestCase): 7 | def setUp(self): 8 | staff = User(username='staff') 9 | staff.set_password('test') 10 | staff.is_staff = True 11 | staff.save() 12 | staff.user_permissions.add( 13 | Permission.objects.get_by_natural_key( 14 | 'change_page', 'test_app', 'page')) 15 | self.url = reverse('admin:test_app_page_changelist') 16 | self.client.login(username='staff', password='test') 17 | 18 | def test_has_no_load_button(self): 19 | response = self.client.get(self.url) 20 | self.assertNotContains(response, '') 21 | 22 | def test_has_no_dump_button(self): 23 | response = self.client.get(self.url) 24 | self.assertNotContains(response, '') 25 | 26 | 27 | class TestAdminSuperUser(TestCase): 28 | def setUp(self): 29 | superuser = User(username='superuser') 30 | superuser.set_password('test') 31 | superuser.is_staff = True 32 | superuser.is_superuser = True 33 | superuser.save() 34 | self.url = reverse('admin:test_app_page_changelist') 35 | self.client.login(username='superuser', password='test') 36 | 37 | def test_has_load_button(self): 38 | response = self.client.get(self.url) 39 | self.assertContains(response, '') 40 | 41 | def test_has_dump_button(self): 42 | response = self.client.get(self.url) 43 | self.assertContains(response, '') 44 | -------------------------------------------------------------------------------- /tests/test_settings.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | 4 | def p(*args): 5 | return os.path.abspath( 6 | os.path.join(os.path.dirname(__file__), *args)) 7 | 8 | 9 | DEBUG = True 10 | STATIC_ROOT = p('') 11 | STATIC_URL = '/static/' 12 | 13 | DATABASES = { 14 | 'default': { 15 | 'ENGINE': 'django.db.backends.sqlite3', 16 | 'NAME': 'smuggler.db' 17 | } 18 | } 19 | 20 | SECRET_KEY = 'mAtTzVPOV9JY4eJQfqgW8eAS9DWKnt3MkvvpQI2MzkhAz7z3' 21 | 22 | ROOT_URLCONF = 'tests.test_app.urls' 23 | 24 | MIDDLEWARE = ( 25 | 'django.contrib.sessions.middleware.SessionMiddleware', 26 | 'django.middleware.common.CommonMiddleware', 27 | 'django.middleware.csrf.CsrfViewMiddleware', 28 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 29 | 'django.contrib.messages.middleware.MessageMiddleware', 30 | ) 31 | 32 | SITE_ID = 1 33 | 34 | LOGIN_URL = '/admin/login/' 35 | 36 | INSTALLED_APPS = [ 37 | 'django.contrib.auth', 38 | 'django.contrib.admin', 39 | 'django.contrib.sites', 40 | 'django.contrib.contenttypes', 41 | 'django.contrib.sessions', 42 | 'django.contrib.messages', 43 | 'django.contrib.staticfiles', 44 | 'smuggler', 45 | 'tests.test_app', 46 | ] 47 | 48 | TEMPLATES = [ 49 | { 50 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 51 | 'APP_DIRS': True, 52 | 'OPTIONS': { 53 | 'context_processors': ( 54 | 'django.contrib.auth.context_processors.auth', 55 | 'django.template.context_processors.debug', 56 | 'django.template.context_processors.i18n', 57 | 'django.template.context_processors.media', 58 | 'django.template.context_processors.static', 59 | 'django.template.context_processors.tz', 60 | 'django.contrib.messages.context_processors.messages', 61 | 'django.template.context_processors.request' 62 | ), 63 | }, 64 | }, 65 | ] 66 | 67 | # SMUGGLER_FIXTURE_DIR = p('test_app', 'smuggler_fixtures') 68 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_load.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from django.contrib.sites.models import Site 4 | from django.db import IntegrityError 5 | from django.test import TestCase, TransactionTestCase 6 | 7 | from smuggler.utils import load_fixtures 8 | from tests.test_app.models import Page 9 | 10 | 11 | def p(*args): 12 | return os.path.abspath( 13 | os.path.join(os.path.dirname(__file__), *args)) 14 | 15 | 16 | class SimpleLoadTestCase(TestCase): 17 | def test_load_page(self): 18 | self.assertEqual(0, Page.objects.count()) 19 | count = load_fixtures([ 20 | p('..', 'smuggler_fixtures', 'page_dump.json')]) 21 | self.assertEqual(count, 1) 22 | self.assertEqual('test', Page.objects.get(pk=1).title) 23 | 24 | def test_load_page_and_site(self): 25 | self.assertEqual(0, Page.objects.count()) 26 | self.assertEqual(Site.objects.get(pk=1).name, 'example.com') 27 | count = load_fixtures([ 28 | p('..', 'smuggler_fixtures', 'page_dump.json'), 29 | p('..', 'smuggler_fixtures', 'site_dump.json') 30 | ]) 31 | self.assertEqual(count, 2) 32 | self.assertEqual('test', Page.objects.get(pk=1).title) 33 | self.assertEqual('test.com', Site.objects.get(pk=1).name) 34 | 35 | def test_load_all(self): 36 | self.assertEqual(0, Page.objects.count()) 37 | self.assertEqual(Site.objects.get(pk=1).name, 'example.com') 38 | count = load_fixtures([ 39 | p('..', 'smuggler_fixtures', 'all_dump.json') 40 | ]) 41 | self.assertEqual(count, 2) 42 | self.assertEqual('test', Page.objects.get(pk=1).title) 43 | self.assertEqual('test.com', Site.objects.get(pk=1).name) 44 | 45 | 46 | class TestInvalidLoad(TransactionTestCase): 47 | def test_load_invalid_data(self): 48 | self.assertRaises(IntegrityError, load_fixtures, 49 | [p('..', 'smuggler_fixtures', 50 | 'garbage', 'invalid_page_dump.json')]) 51 | self.assertEqual(0, Page.objects.count()) 52 | -------------------------------------------------------------------------------- /smuggler/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Guilherme Semente and contributors 2 | # 3 | # This file is part of Django Smuggler. 4 | # 5 | # Django Smuggler is free software under terms of the GNU Lesser 6 | # General Public License version 3 (LGPLv3) as published by the Free 7 | # Software Foundation. See the file README for copying conditions. 8 | import re 9 | 10 | from io import StringIO 11 | 12 | from django.core.management import call_command 13 | from django.db.utils import DEFAULT_DB_ALIAS 14 | from django.http import HttpResponse 15 | 16 | from smuggler import settings 17 | 18 | 19 | def save_uploaded_file_on_disk(uploaded_file, destination_path): 20 | with open(destination_path, "wb") as fp: 21 | for chunk in uploaded_file.chunks(): 22 | fp.write(chunk) 23 | 24 | 25 | def serialize_to_response( 26 | app_labels=None, 27 | exclude=None, 28 | response=None, 29 | format=settings.SMUGGLER_FORMAT, 30 | indent=settings.SMUGGLER_INDENT, 31 | ): 32 | app_labels = app_labels or [] 33 | exclude = exclude or [] 34 | response = response or HttpResponse(content_type="text/plain") 35 | stream = StringIO() 36 | error_stream = StringIO() 37 | call_command( 38 | "dumpdata", 39 | *app_labels, 40 | **{ 41 | "stdout": stream, 42 | "stderr": error_stream, 43 | "exclude": exclude, 44 | "format": format, 45 | "indent": indent, 46 | "use_natural_foreign_keys": True, 47 | "use_natural_primary_keys": True, 48 | } 49 | ) 50 | response.write(stream.getvalue()) 51 | return response 52 | 53 | 54 | def load_fixtures(fixtures): 55 | stream = StringIO() 56 | error_stream = StringIO() 57 | call_command( 58 | "loaddata", 59 | *fixtures, 60 | **{ 61 | "stdout": stream, 62 | "stderr": error_stream, 63 | "ignore": True, 64 | "database": DEFAULT_DB_ALIAS, 65 | "verbosity": 1, 66 | } 67 | ) 68 | stream.seek(0) 69 | result = stream.read() 70 | return int(re.match(r"Installed\s([0-9]+)\s.*", result).groups()[0]) 71 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright (c) 2009-2013 Guilherme Gondim and contributors 4 | # 5 | # This file is part of Django Smuggler. 6 | # 7 | # Django Smuggler is free software under terms of the GNU Lesser 8 | # General Public License version 3 (LGPLv3) as published by the Free 9 | # Software Foundation. See the file README for copying conditions. 10 | 11 | import codecs 12 | import os 13 | import sys 14 | 15 | from setuptools import find_packages, setup 16 | 17 | # Dynamically calculate the version based on smuggler.VERSION. 18 | version = __import__('smuggler').get_version() 19 | 20 | 21 | def read(filepath): 22 | return codecs.open(filepath, 'r', 'utf-8').read() 23 | 24 | 25 | setup( 26 | name='django-smuggler', 27 | version=version, 28 | description=('Pluggable application for Django that helps you to ' 29 | 'import/export fixtures via the administration interface'), 30 | long_description=read(os.path.join(os.path.dirname(__file__), 'README.rst')), 31 | keywords='django apps tools backup fixtures admin', 32 | author='Guilherme Gondim', 33 | author_email='semente+django-smuggler@taurinus.org', 34 | maintainer='Guilherme Gondim', 35 | maintainer_email='semente+django-smuggler@taurinus.org', 36 | license='GNU Lesser General Public License v3 or later (LGPLv3+)', 37 | url='http://github.com/semente/django-smuggler', 38 | download_url='http://github.com/semente/django-smuggler/downloads', 39 | packages=find_packages(), 40 | zip_safe=False, 41 | include_package_data=True, 42 | python_requires='>=3.6', 43 | classifiers=[ 44 | 'Development Status :: 5 - Production/Stable', 45 | 'Environment :: Web Environment', 46 | 'Framework :: Django', 47 | 'Intended Audience :: Developers', 48 | 'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)', 49 | 'Operating System :: OS Independent', 50 | 'Topic :: Software Development :: Libraries :: Python Modules', 51 | 'Programming Language :: Python :: 3', 52 | 'Programming Language :: Python :: 3 :: Only', 53 | 'Programming Language :: Python :: 3.6', 54 | 'Programming Language :: Python :: 3.7', 55 | 'Programming Language :: Python :: 3.8', 56 | ], 57 | tests_require=[], 58 | ) 59 | -------------------------------------------------------------------------------- /smuggler/locale/en/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | #, fuzzy 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: Django Smuggler\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 10 | "PO-Revision-Date: 2012-01-31 09:57-0200\n" 11 | "Last-Translator: Guilherme Gondim \n" 12 | "Language-Team: English (http://www.transifex.net/projects/p/django-smuggler/" 13 | "team/en/)\n" 14 | "Language: en\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: forms.py:48 20 | #, python-format 21 | msgid "Invalid file extension: .%(extension)s." 22 | msgstr "" 23 | 24 | #: forms.py:106 25 | msgid "Upload" 26 | msgstr "" 27 | 28 | #: forms.py:114 29 | msgid "Save in fixture directory" 30 | msgstr "" 31 | 32 | #: forms.py:117 33 | #, python-format 34 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 35 | msgstr "" 36 | 37 | #: forms.py:123 38 | msgid "From fixture directory" 39 | msgstr "" 40 | 41 | #: forms.py:126 42 | #, python-format 43 | msgid "Data files from \"%(fixture_dir)s\"." 44 | msgstr "" 45 | 46 | #: forms.py:140 47 | msgid "At least one fixture file needs to be uploaded or selected." 48 | msgstr "" 49 | 50 | #: views.py:43 51 | #, python-format 52 | msgid "An exception occurred while dumping data: %s" 53 | msgstr "" 54 | 55 | #: views.py:113 56 | #, python-format 57 | msgid "Successfully imported %(count)d file." 58 | msgid_plural "Successfully imported %(count)d files." 59 | msgstr[0] "" 60 | msgstr[1] "" 61 | 62 | #: views.py:118 63 | #, python-format 64 | msgid "Loaded %(count)d object." 65 | msgid_plural "Loaded %(count)d objects." 66 | msgstr[0] "" 67 | msgstr[1] "" 68 | 69 | #: views.py:127 70 | #, python-format 71 | msgid "An exception occurred while loading data: %s" 72 | msgstr "" 73 | 74 | #: templates/smuggler/change_list.html:9 75 | msgid "Dump data" 76 | msgstr "" 77 | 78 | #: templates/smuggler/change_list.html:14 79 | #: templates/smuggler/load_data_form.html:6 80 | #: templates/smuggler/load_data_form.html:18 81 | #: templates/smuggler/load_data_form.html:23 82 | msgid "Load data" 83 | msgstr "" 84 | 85 | #: templates/smuggler/load_data_form.html:17 86 | msgid "Home" 87 | msgstr "" 88 | 89 | #: templates/smuggler/load_data_form.html:26 90 | msgid "" 91 | "Existing items with same primary key will be overwritten." 92 | msgstr "" 93 | 94 | #: templates/smuggler/load_data_form.html:34 95 | msgid "Please correct the error below." 96 | msgstr "" 97 | 98 | #: templates/smuggler/load_data_form.html:55 99 | msgid "Load" 100 | msgstr "" 101 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_forms.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from django.core.files.uploadedfile import SimpleUploadedFile 4 | from django.forms import BooleanField, FilePathField 5 | from django.test import TestCase 6 | from django.test.utils import override_settings 7 | from django.utils.datastructures import MultiValueDict 8 | 9 | from smuggler import settings 10 | from smuggler.forms import ImportForm 11 | 12 | 13 | def p(*args): 14 | return os.path.abspath( 15 | os.path.join(os.path.dirname(__file__), *args)) 16 | 17 | 18 | class TestForm(TestCase): 19 | def test_requires_file(self): 20 | form = ImportForm({}, {}) 21 | self.assertFalse(form.is_valid()) 22 | self.assertEqual({'uploads': ["This field is required."]}, 23 | form.errors) 24 | 25 | def test_invalid_file_extension(self): 26 | f = SimpleUploadedFile('invalid.txt', b'invalid') 27 | form = ImportForm({}, { 28 | 'uploads': f 29 | }) 30 | self.assertFalse(form.is_valid()) 31 | self.assertEqual({'uploads': ["Invalid file extension: .txt."]}, 32 | form.errors) 33 | 34 | def test_valid_file_extension(self): 35 | f = SimpleUploadedFile('valid.json', b'[]') 36 | form = ImportForm({}, { 37 | 'uploads': f 38 | }) 39 | self.assertTrue(form.is_valid()) 40 | 41 | def test_valid_uppercase_file_extension(self): 42 | f = SimpleUploadedFile('valid.JSON', b'[]') 43 | form = ImportForm({}, { 44 | 'uploads': f 45 | }) 46 | self.assertTrue(form.is_valid()) 47 | 48 | def test_mix_valid_and_invalid(self): 49 | form = ImportForm({}, MultiValueDict({ 50 | 'uploads': [ 51 | SimpleUploadedFile('valid.json', b'[]'), 52 | SimpleUploadedFile('invalid.txt', b'invalid') 53 | ] 54 | })) 55 | self.assertFalse(form.is_valid()) 56 | self.assertEqual({'uploads': ["Invalid file extension: .txt."]}, 57 | form.errors) 58 | 59 | @override_settings(SMUGGLER_FIXTURE_DIR=p('..', 'smuggler_fixtures')) 60 | def test_store_checkbox(self): 61 | form = ImportForm() 62 | self.assertIsInstance(form['store'].field, BooleanField) 63 | 64 | @override_settings(SMUGGLER_FIXTURE_DIR=p('..', 'smuggler_fixtures')) 65 | def test_picked_files(self): 66 | form = ImportForm() 67 | self.assertIsInstance(form['picked_files'].field, FilePathField) 68 | 69 | @override_settings(SMUGGLER_FIXTURE_DIR=p('..', 'smuggler_fixtures')) 70 | def test_requires_at_least_one_field(self): 71 | form = ImportForm({}, {}) 72 | self.assertFalse(form.is_valid()) 73 | self.assertEqual(form.errors, { 74 | '__all__': [ 75 | 'At least one fixture file needs to be uploaded or selected.' 76 | ]}) 77 | -------------------------------------------------------------------------------- /smuggler/locale/fo/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # Gunleif Joensen , 2011 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Django Smuggler\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 11 | "PO-Revision-Date: 2014-08-26 08:15+0000\n" 12 | "Last-Translator: Guilherme Gondim \n" 13 | "Language-Team: Faroese (http://www.transifex.com/projects/p/django-smuggler/language/fo/)\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Language: fo\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: forms.py:48 21 | #, python-format 22 | msgid "Invalid file extension: .%(extension)s." 23 | msgstr "" 24 | 25 | #: forms.py:106 26 | msgid "Upload" 27 | msgstr "" 28 | 29 | #: forms.py:114 30 | msgid "Save in fixture directory" 31 | msgstr "" 32 | 33 | #: forms.py:117 34 | #, python-format 35 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 36 | msgstr "" 37 | 38 | #: forms.py:123 39 | msgid "From fixture directory" 40 | msgstr "" 41 | 42 | #: forms.py:126 43 | #, python-format 44 | msgid "Data files from \"%(fixture_dir)s\"." 45 | msgstr "" 46 | 47 | #: forms.py:140 48 | msgid "At least one fixture file needs to be uploaded or selected." 49 | msgstr "" 50 | 51 | #: views.py:43 52 | #, python-format 53 | msgid "An exception occurred while dumping data: %s" 54 | msgstr "" 55 | 56 | #: views.py:113 57 | #, python-format 58 | msgid "Successfully imported %(count)d file." 59 | msgid_plural "Successfully imported %(count)d files." 60 | msgstr[0] "" 61 | msgstr[1] "" 62 | 63 | #: views.py:118 64 | #, python-format 65 | msgid "Loaded %(count)d object." 66 | msgid_plural "Loaded %(count)d objects." 67 | msgstr[0] "" 68 | msgstr[1] "" 69 | 70 | #: views.py:127 71 | #, python-format 72 | msgid "An exception occurred while loading data: %s" 73 | msgstr "" 74 | 75 | #: templates/smuggler/change_list.html:9 76 | msgid "Dump data" 77 | msgstr "" 78 | 79 | #: templates/smuggler/change_list.html:14 80 | #: templates/smuggler/load_data_form.html:6 81 | #: templates/smuggler/load_data_form.html:18 82 | #: templates/smuggler/load_data_form.html:23 83 | msgid "Load data" 84 | msgstr "" 85 | 86 | #: templates/smuggler/load_data_form.html:17 87 | msgid "Home" 88 | msgstr "Heim" 89 | 90 | #: templates/smuggler/load_data_form.html:26 91 | msgid "" 92 | "Existing items with same primary key will be overwritten." 93 | msgstr "" 94 | 95 | #: templates/smuggler/load_data_form.html:34 96 | msgid "Please correct the error below." 97 | msgstr "Vinarliga rætta villuna niðanfyri." 98 | 99 | #: templates/smuggler/load_data_form.html:55 100 | msgid "Load" 101 | msgstr "" 102 | -------------------------------------------------------------------------------- /smuggler/locale/ca/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # angularcircle , 2010 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Django Smuggler\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 11 | "PO-Revision-Date: 2014-08-26 08:15+0000\n" 12 | "Last-Translator: Guilherme Gondim \n" 13 | "Language-Team: Catalan (http://www.transifex.com/projects/p/django-smuggler/language/ca/)\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Language: ca\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: forms.py:48 21 | #, python-format 22 | msgid "Invalid file extension: .%(extension)s." 23 | msgstr "" 24 | 25 | #: forms.py:106 26 | msgid "Upload" 27 | msgstr "" 28 | 29 | #: forms.py:114 30 | msgid "Save in fixture directory" 31 | msgstr "" 32 | 33 | #: forms.py:117 34 | #, python-format 35 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 36 | msgstr "" 37 | 38 | #: forms.py:123 39 | msgid "From fixture directory" 40 | msgstr "" 41 | 42 | #: forms.py:126 43 | #, python-format 44 | msgid "Data files from \"%(fixture_dir)s\"." 45 | msgstr "" 46 | 47 | #: forms.py:140 48 | msgid "At least one fixture file needs to be uploaded or selected." 49 | msgstr "" 50 | 51 | #: views.py:43 52 | #, python-format 53 | msgid "An exception occurred while dumping data: %s" 54 | msgstr "" 55 | 56 | #: views.py:113 57 | #, python-format 58 | msgid "Successfully imported %(count)d file." 59 | msgid_plural "Successfully imported %(count)d files." 60 | msgstr[0] "" 61 | msgstr[1] "" 62 | 63 | #: views.py:118 64 | #, python-format 65 | msgid "Loaded %(count)d object." 66 | msgid_plural "Loaded %(count)d objects." 67 | msgstr[0] "" 68 | msgstr[1] "" 69 | 70 | #: views.py:127 71 | #, python-format 72 | msgid "An exception occurred while loading data: %s" 73 | msgstr "" 74 | 75 | #: templates/smuggler/change_list.html:9 76 | msgid "Dump data" 77 | msgstr "Treu dades" 78 | 79 | #: templates/smuggler/change_list.html:14 80 | #: templates/smuggler/load_data_form.html:6 81 | #: templates/smuggler/load_data_form.html:18 82 | #: templates/smuggler/load_data_form.html:23 83 | msgid "Load data" 84 | msgstr "Carrega dades" 85 | 86 | #: templates/smuggler/load_data_form.html:17 87 | msgid "Home" 88 | msgstr "Inici" 89 | 90 | #: templates/smuggler/load_data_form.html:26 91 | msgid "" 92 | "Existing items with same primary key will be overwritten." 93 | msgstr "" 94 | 95 | #: templates/smuggler/load_data_form.html:34 96 | msgid "Please correct the error below." 97 | msgstr "Si us plau corregeix l'error sota." 98 | 99 | #: templates/smuggler/load_data_form.html:55 100 | msgid "Load" 101 | msgstr "Carrega" 102 | -------------------------------------------------------------------------------- /smuggler/locale/nl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # jaap3 , 2014 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Django Smuggler\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 11 | "PO-Revision-Date: 2014-08-26 14:02+0000\n" 12 | "Last-Translator: jaap3 \n" 13 | "Language-Team: Dutch (http://www.transifex.com/projects/p/django-smuggler/language/nl/)\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Language: nl\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: forms.py:48 21 | #, python-format 22 | msgid "Invalid file extension: .%(extension)s." 23 | msgstr "Ongeldige bestandsextensie: .%(extension)s." 24 | 25 | #: forms.py:106 26 | msgid "Upload" 27 | msgstr "Upload" 28 | 29 | #: forms.py:114 30 | msgid "Save in fixture directory" 31 | msgstr "In fixture directory opslaan" 32 | 33 | #: forms.py:117 34 | #, python-format 35 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 36 | msgstr "Uploads worden in \"%(fixture_dir)s\" opgeslagen." 37 | 38 | #: forms.py:123 39 | msgid "From fixture directory" 40 | msgstr "Vanuit de fixture directory" 41 | 42 | #: forms.py:126 43 | #, python-format 44 | msgid "Data files from \"%(fixture_dir)s\"." 45 | msgstr "Gegevens uit de directory \"%(fixture_dir)s\"." 46 | 47 | #: forms.py:140 48 | msgid "At least one fixture file needs to be uploaded or selected." 49 | msgstr "Selecteer of upload op zijn minst één fixture bestand." 50 | 51 | #: views.py:43 52 | #, python-format 53 | msgid "An exception occurred while dumping data: %s" 54 | msgstr "Er is een fout opgetreden tijdens het dumpen van de data: %s" 55 | 56 | #: views.py:113 57 | #, python-format 58 | msgid "Successfully imported %(count)d file." 59 | msgid_plural "Successfully imported %(count)d files." 60 | msgstr[0] "%(count)d bestand met succes geïmporteerd." 61 | msgstr[1] "%(count)d bestanden met succes geïmporteerd." 62 | 63 | #: views.py:118 64 | #, python-format 65 | msgid "Loaded %(count)d object." 66 | msgid_plural "Loaded %(count)d objects." 67 | msgstr[0] "%(count)d object geladen." 68 | msgstr[1] "%(count)d objecten geladen." 69 | 70 | #: views.py:127 71 | #, python-format 72 | msgid "An exception occurred while loading data: %s" 73 | msgstr "Er is een fout opgetreden tijdens het laden van de data: %s" 74 | 75 | #: templates/smuggler/change_list.html:9 76 | msgid "Dump data" 77 | msgstr "Dump data" 78 | 79 | #: templates/smuggler/change_list.html:14 80 | #: templates/smuggler/load_data_form.html:6 81 | #: templates/smuggler/load_data_form.html:18 82 | #: templates/smuggler/load_data_form.html:23 83 | msgid "Load data" 84 | msgstr "Laad data" 85 | 86 | #: templates/smuggler/load_data_form.html:17 87 | msgid "Home" 88 | msgstr "Voorpagina" 89 | 90 | #: templates/smuggler/load_data_form.html:26 91 | msgid "" 92 | "Existing items with same primary key will be overwritten." 93 | msgstr "Bestaande objecten met dezelfde primary key worden overschreven!" 94 | 95 | #: templates/smuggler/load_data_form.html:34 96 | msgid "Please correct the error below." 97 | msgstr "Herstel de fout hieronder." 98 | 99 | #: templates/smuggler/load_data_form.html:55 100 | msgid "Load" 101 | msgstr "Importeer" 102 | -------------------------------------------------------------------------------- /smuggler/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # Tobias Paepke , 2014 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Django Smuggler\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 11 | "PO-Revision-Date: 2014-08-28 12:24+0000\n" 12 | "Last-Translator: Tobias Paepke \n" 13 | "Language-Team: German (http://www.transifex.com/projects/p/django-smuggler/language/de/)\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Language: de\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: forms.py:48 21 | #, python-format 22 | msgid "Invalid file extension: .%(extension)s." 23 | msgstr "Ungültige Datei-Endung: .%(extension)s." 24 | 25 | #: forms.py:106 26 | msgid "Upload" 27 | msgstr "Upload" 28 | 29 | #: forms.py:114 30 | msgid "Save in fixture directory" 31 | msgstr "In Fixture Verzeichnis speichern" 32 | 33 | #: forms.py:117 34 | #, python-format 35 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 36 | msgstr "Uploads werden gespeicher in \"%(fixture_dir)s\"." 37 | 38 | #: forms.py:123 39 | msgid "From fixture directory" 40 | msgstr "Vom Fixture Verzeichnis" 41 | 42 | #: forms.py:126 43 | #, python-format 44 | msgid "Data files from \"%(fixture_dir)s\"." 45 | msgstr "Daten-Dateien aus \"%(fixture_dir)s\"." 46 | 47 | #: forms.py:140 48 | msgid "At least one fixture file needs to be uploaded or selected." 49 | msgstr "Mindestens eine Fixture-Datei muss hochgeladen oder ausgewählt werden." 50 | 51 | #: views.py:43 52 | #, python-format 53 | msgid "An exception occurred while dumping data: %s" 54 | msgstr "Während dem Ausgeben der Daten ist eine Ausnahme aufgetreten: %s" 55 | 56 | #: views.py:113 57 | #, python-format 58 | msgid "Successfully imported %(count)d file." 59 | msgid_plural "Successfully imported %(count)d files." 60 | msgstr[0] "Erfolgreich %(count)d Datei importiert." 61 | msgstr[1] "Erfolgreich %(count)d Dateien importiert." 62 | 63 | #: views.py:118 64 | #, python-format 65 | msgid "Loaded %(count)d object." 66 | msgid_plural "Loaded %(count)d objects." 67 | msgstr[0] " %(count)d Objekt geladen." 68 | msgstr[1] " %(count)d Objekte geladen." 69 | 70 | #: views.py:127 71 | #, python-format 72 | msgid "An exception occurred while loading data: %s" 73 | msgstr "Eine Ausnahme ist während dem Laden der Daten aufgetreten: %s" 74 | 75 | #: templates/smuggler/change_list.html:9 76 | msgid "Dump data" 77 | msgstr "Daten ausgeben" 78 | 79 | #: templates/smuggler/change_list.html:14 80 | #: templates/smuggler/load_data_form.html:6 81 | #: templates/smuggler/load_data_form.html:18 82 | #: templates/smuggler/load_data_form.html:23 83 | msgid "Load data" 84 | msgstr "Daten einlesen" 85 | 86 | #: templates/smuggler/load_data_form.html:17 87 | msgid "Home" 88 | msgstr "Start" 89 | 90 | #: templates/smuggler/load_data_form.html:26 91 | msgid "" 92 | "Existing items with same primary key will be overwritten." 93 | msgstr "Existierende Datensätze mit dem selben Primärschlüssel werden überschrieben." 94 | 95 | #: templates/smuggler/load_data_form.html:34 96 | msgid "Please correct the error below." 97 | msgstr "Bitte den nachfolgenden Fehler korrigieren." 98 | 99 | #: templates/smuggler/load_data_form.html:55 100 | msgid "Load" 101 | msgstr "Laden" 102 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_auth.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | 6 | class TestSmugglerViewsRequireAuthentication(TestCase): 7 | def test_dump_data(self): 8 | url = reverse('dump-data') 9 | response = self.client.get(url) 10 | self.assertRedirects( 11 | response, '/admin/login/?next=/admin/dump/') 12 | 13 | def test_dump_app_data(self): 14 | url = reverse('dump-app-data', kwargs={'app_label': 'sites'}) 15 | response = self.client.get(url) 16 | self.assertRedirects( 17 | response, '/admin/login/?next=/admin/sites/dump/') 18 | 19 | def test_dump_model_data(self): 20 | url = reverse('dump-model-data', kwargs={ 21 | 'app_label': 'sites', 22 | 'model_label': 'site' 23 | }) 24 | response = self.client.get(url) 25 | self.assertRedirects( 26 | response, '/admin/login/?next=/admin/sites/site/dump/') 27 | 28 | def test_load_data(self): 29 | url = reverse('load-data') 30 | response = self.client.get(url, follow=True) 31 | self.assertRedirects( 32 | response, '/admin/login/?next=/admin/load/') 33 | 34 | 35 | class TestSmugglerViewsDeniesNonSuperuser(TestCase): 36 | def setUp(self): 37 | staff = User(username='staff') 38 | staff.set_password('test') 39 | staff.is_staff = True 40 | staff.save() 41 | self.client.login(username='staff', password='test') 42 | 43 | def test_dump_data(self): 44 | url = reverse('dump-data') 45 | response = self.client.get(url) 46 | self.assertEqual(response.status_code, 403) 47 | 48 | def test_dump_app_data(self): 49 | url = reverse('dump-app-data', kwargs={'app_label': 'sites'}) 50 | response = self.client.get(url) 51 | self.assertEqual(response.status_code, 403) 52 | 53 | def test_dump_model_data(self): 54 | url = reverse('dump-model-data', kwargs={ 55 | 'app_label': 'sites', 56 | 'model_label': 'site' 57 | }) 58 | response = self.client.get(url) 59 | self.assertEqual(response.status_code, 403) 60 | 61 | def test_load_data(self): 62 | url = reverse('load-data') 63 | response = self.client.get(url) 64 | self.assertEqual(response.status_code, 403) 65 | 66 | 67 | class TestSmugglerViewsAllowsSuperuser(TestCase): 68 | def setUp(self): 69 | superuser = User(username='superuser') 70 | superuser.set_password('test') 71 | superuser.is_staff = True 72 | superuser.is_superuser = True 73 | superuser.save() 74 | self.client.login(username='superuser', password='test') 75 | 76 | def test_dump_data(self): 77 | url = reverse('dump-data') 78 | response = self.client.get(url) 79 | self.assertEqual(response.status_code, 200) 80 | 81 | def test_dump_app_data(self): 82 | url = reverse('dump-app-data', kwargs={'app_label': 'sites'}) 83 | response = self.client.get(url) 84 | self.assertEqual(response.status_code, 200) 85 | 86 | def test_dump_model_data(self): 87 | url = reverse('dump-model-data', kwargs={ 88 | 'app_label': 'sites', 89 | 'model_label': 'site' 90 | }) 91 | response = self.client.get(url) 92 | self.assertEqual(response.status_code, 200) 93 | 94 | def test_load_data(self): 95 | url = reverse('load-data') 96 | response = self.client.get(url) 97 | self.assertEqual(response.status_code, 200) 98 | -------------------------------------------------------------------------------- /smuggler/locale/pt_BR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # Guilherme Gondim , 2010,2014 6 | # Guilherme Gondim , 2011 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: Django Smuggler\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 12 | "PO-Revision-Date: 2014-08-26 13:25+0000\n" 13 | "Last-Translator: Guilherme Gondim \n" 14 | "Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/django-smuggler/language/pt_BR/)\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Language: pt_BR\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #: forms.py:48 22 | #, python-format 23 | msgid "Invalid file extension: .%(extension)s." 24 | msgstr "Extensão de arquivo inválida: .%(extension)s." 25 | 26 | #: forms.py:106 27 | msgid "Upload" 28 | msgstr "Upload" 29 | 30 | #: forms.py:114 31 | msgid "Save in fixture directory" 32 | msgstr "Salvar no diretório de fixtures" 33 | 34 | #: forms.py:117 35 | #, python-format 36 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 37 | msgstr "Uploads serão salvos em \"%(fixture_dir)s\"." 38 | 39 | #: forms.py:123 40 | msgid "From fixture directory" 41 | msgstr "Do diretório de fixtures" 42 | 43 | #: forms.py:126 44 | #, python-format 45 | msgid "Data files from \"%(fixture_dir)s\"." 46 | msgstr "Arquivos de dados de \"%(fixture_dir)s\"." 47 | 48 | #: forms.py:140 49 | msgid "At least one fixture file needs to be uploaded or selected." 50 | msgstr "Ao menos um arquivo de fixture precisa ser enviado ou selecionado." 51 | 52 | #: views.py:43 53 | #, python-format 54 | msgid "An exception occurred while dumping data: %s" 55 | msgstr "Uma exceção ocorreu enquanto descarregava os dados: %s" 56 | 57 | #: views.py:113 58 | #, python-format 59 | msgid "Successfully imported %(count)d file." 60 | msgid_plural "Successfully imported %(count)d files." 61 | msgstr[0] "Importado %(count)d arquivo com sucesso." 62 | msgstr[1] "Importado %(count)d arquivos com sucesso." 63 | 64 | #: views.py:118 65 | #, python-format 66 | msgid "Loaded %(count)d object." 67 | msgid_plural "Loaded %(count)d objects." 68 | msgstr[0] "Carregadp %(count)d objeto." 69 | msgstr[1] "Carregado %(count)d objetos." 70 | 71 | #: views.py:127 72 | #, python-format 73 | msgid "An exception occurred while loading data: %s" 74 | msgstr "Uma exceção ocorreu enquanto carregava os dados: %s" 75 | 76 | #: templates/smuggler/change_list.html:9 77 | msgid "Dump data" 78 | msgstr "Descarregar dados" 79 | 80 | #: templates/smuggler/change_list.html:14 81 | #: templates/smuggler/load_data_form.html:6 82 | #: templates/smuggler/load_data_form.html:18 83 | #: templates/smuggler/load_data_form.html:23 84 | msgid "Load data" 85 | msgstr "Carregar dados" 86 | 87 | #: templates/smuggler/load_data_form.html:17 88 | msgid "Home" 89 | msgstr "Início" 90 | 91 | #: templates/smuggler/load_data_form.html:26 92 | msgid "" 93 | "Existing items with same primary key will be overwritten." 94 | msgstr "Itens existentes com a mesma chave primária serão sobrescritos." 95 | 96 | #: templates/smuggler/load_data_form.html:34 97 | msgid "Please correct the error below." 98 | msgstr "Por favor, corrija o erro abaixo." 99 | 100 | #: templates/smuggler/load_data_form.html:55 101 | msgid "Load" 102 | msgstr "Carregar" 103 | -------------------------------------------------------------------------------- /smuggler/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # Eugene MechanisM , 2012,2014 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Django Smuggler\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 11 | "PO-Revision-Date: 2014-09-16 12:10+0000\n" 12 | "Last-Translator: Eugene MechanisM \n" 13 | "Language-Team: Russian (http://www.transifex.com/projects/p/django-smuggler/language/ru/)\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Language: ru\n" 18 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | 20 | #: forms.py:48 21 | #, python-format 22 | msgid "Invalid file extension: .%(extension)s." 23 | msgstr "Неправильное расширение файла: .%(extension)s." 24 | 25 | #: forms.py:106 26 | msgid "Upload" 27 | msgstr "Загрузить" 28 | 29 | #: forms.py:114 30 | msgid "Save in fixture directory" 31 | msgstr "Сохранить в папке с фикстурами" 32 | 33 | #: forms.py:117 34 | #, python-format 35 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 36 | msgstr "Загружаемое будет сохранено в \"%(fixture_dir)s\"." 37 | 38 | #: forms.py:123 39 | msgid "From fixture directory" 40 | msgstr "Из папки с фикстурами" 41 | 42 | #: forms.py:126 43 | #, python-format 44 | msgid "Data files from \"%(fixture_dir)s\"." 45 | msgstr "Файлы с данными из \"%(fixture_dir)s\"." 46 | 47 | #: forms.py:140 48 | msgid "At least one fixture file needs to be uploaded or selected." 49 | msgstr "Хотя бы один файл фикстур должен быть загружен или выбран." 50 | 51 | #: views.py:43 52 | #, python-format 53 | msgid "An exception occurred while dumping data: %s" 54 | msgstr "Исключение при выгрузке данных: %s" 55 | 56 | #: views.py:113 57 | #, python-format 58 | msgid "Successfully imported %(count)d file." 59 | msgid_plural "Successfully imported %(count)d files." 60 | msgstr[0] "Успешно импортирован %(count)d файл." 61 | msgstr[1] "Успешно импортировано %(count)d файлов." 62 | msgstr[2] "Успешно импортировано %(count)d файлов." 63 | 64 | #: views.py:118 65 | #, python-format 66 | msgid "Loaded %(count)d object." 67 | msgid_plural "Loaded %(count)d objects." 68 | msgstr[0] "Загружен %(count)d объект." 69 | msgstr[1] "Загружено %(count)d объектов." 70 | msgstr[2] "Загружено %(count)d объектов." 71 | 72 | #: views.py:127 73 | #, python-format 74 | msgid "An exception occurred while loading data: %s" 75 | msgstr "Исключение при загрузке данных: %s" 76 | 77 | #: templates/smuggler/change_list.html:9 78 | msgid "Dump data" 79 | msgstr "Выгрузить данные" 80 | 81 | #: templates/smuggler/change_list.html:14 82 | #: templates/smuggler/load_data_form.html:6 83 | #: templates/smuggler/load_data_form.html:18 84 | #: templates/smuggler/load_data_form.html:23 85 | msgid "Load data" 86 | msgstr "Загрузить данные" 87 | 88 | #: templates/smuggler/load_data_form.html:17 89 | msgid "Home" 90 | msgstr "Домой" 91 | 92 | #: templates/smuggler/load_data_form.html:26 93 | msgid "" 94 | "Existing items with same primary key will be overwritten." 95 | msgstr "Существующие элементы с одинаковыми первичными ключами будут перезаписаны." 96 | 97 | #: templates/smuggler/load_data_form.html:34 98 | msgid "Please correct the error below." 99 | msgstr "Пожалуйста, исправьте следующую ошибку:" 100 | 101 | #: templates/smuggler/load_data_form.html:55 102 | msgid "Load" 103 | msgstr "Загрузить" 104 | -------------------------------------------------------------------------------- /smuggler/locale/uk/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # Sergey Lysach , 2011,2013-2014 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Django Smuggler\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 11 | "PO-Revision-Date: 2014-09-16 13:04+0000\n" 12 | "Last-Translator: Sergey Lysach \n" 13 | "Language-Team: Ukrainian (http://www.transifex.com/projects/p/django-smuggler/language/uk/)\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Language: uk\n" 18 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 19 | 20 | #: forms.py:48 21 | #, python-format 22 | msgid "Invalid file extension: .%(extension)s." 23 | msgstr "Невірне розширення файлу: .%(extension)s." 24 | 25 | #: forms.py:106 26 | msgid "Upload" 27 | msgstr "Завантажити" 28 | 29 | #: forms.py:114 30 | msgid "Save in fixture directory" 31 | msgstr "Зберегти у директорію з фікстурами" 32 | 33 | #: forms.py:117 34 | #, python-format 35 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 36 | msgstr "Завантаження будуть збережені до \"%(fixture_dir)s\"." 37 | 38 | #: forms.py:123 39 | msgid "From fixture directory" 40 | msgstr "З директорії фікстур" 41 | 42 | #: forms.py:126 43 | #, python-format 44 | msgid "Data files from \"%(fixture_dir)s\"." 45 | msgstr "Файли даних з \"%(fixture_dir)s\"." 46 | 47 | #: forms.py:140 48 | msgid "At least one fixture file needs to be uploaded or selected." 49 | msgstr "Принаймні один файл з фікстурами повинен бути завантажений або вибраний." 50 | 51 | #: views.py:43 52 | #, python-format 53 | msgid "An exception occurred while dumping data: %s" 54 | msgstr "Виникла помилка при вивантаженні даних: %s" 55 | 56 | #: views.py:113 57 | #, python-format 58 | msgid "Successfully imported %(count)d file." 59 | msgid_plural "Successfully imported %(count)d files." 60 | msgstr[0] "Успішно імпортовано %(count)d файл." 61 | msgstr[1] "Успішно імпортовано %(count)d файли." 62 | msgstr[2] "Успішно імпортовано %(count)d файлів." 63 | 64 | #: views.py:118 65 | #, python-format 66 | msgid "Loaded %(count)d object." 67 | msgid_plural "Loaded %(count)d objects." 68 | msgstr[0] "Завантажено %(count)d об'єкт." 69 | msgstr[1] "Завантажено %(count)d об'єкти." 70 | msgstr[2] "Завантажено %(count)d об'єктів." 71 | 72 | #: views.py:127 73 | #, python-format 74 | msgid "An exception occurred while loading data: %s" 75 | msgstr "Виникла помилка при завантаженні даних: %s" 76 | 77 | #: templates/smuggler/change_list.html:9 78 | msgid "Dump data" 79 | msgstr "Вивантажити дані" 80 | 81 | #: templates/smuggler/change_list.html:14 82 | #: templates/smuggler/load_data_form.html:6 83 | #: templates/smuggler/load_data_form.html:18 84 | #: templates/smuggler/load_data_form.html:23 85 | msgid "Load data" 86 | msgstr "Завантажити дані" 87 | 88 | #: templates/smuggler/load_data_form.html:17 89 | msgid "Home" 90 | msgstr "Головна" 91 | 92 | #: templates/smuggler/load_data_form.html:26 93 | msgid "" 94 | "Existing items with same primary key will be overwritten." 95 | msgstr "Існуючі записи з однаковим первинним ключем будуть перезаписані." 96 | 97 | #: templates/smuggler/load_data_form.html:34 98 | msgid "Please correct the error below." 99 | msgstr "Будь-ласка, виправте помилку, зазначену нижче." 100 | 101 | #: templates/smuggler/load_data_form.html:55 102 | msgid "Load" 103 | msgstr "Завантажити" 104 | -------------------------------------------------------------------------------- /smuggler/locale/pl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010, 2011, 2012 Guilherme Gondim and contributors 2 | # This file is distributed under the same license as the django-smuggler package. 3 | # 4 | # Translators: 5 | # angularcircle, 2011 6 | # angularcircle , 2010 7 | # angularcircle, 2014 8 | # angularcircle, 2012 9 | msgid "" 10 | msgstr "" 11 | "Project-Id-Version: Django Smuggler\n" 12 | "Report-Msgid-Bugs-To: \n" 13 | "POT-Creation-Date: 2014-08-26 10:01+0200\n" 14 | "PO-Revision-Date: 2014-09-16 12:15+0000\n" 15 | "Last-Translator: angularcircle\n" 16 | "Language-Team: Polish (http://www.transifex.com/projects/p/django-smuggler/language/pl/)\n" 17 | "MIME-Version: 1.0\n" 18 | "Content-Type: text/plain; charset=UTF-8\n" 19 | "Content-Transfer-Encoding: 8bit\n" 20 | "Language: pl\n" 21 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 22 | 23 | #: forms.py:48 24 | #, python-format 25 | msgid "Invalid file extension: .%(extension)s." 26 | msgstr "Błędne rozszerzenie pliku: .%(extension)s." 27 | 28 | #: forms.py:106 29 | msgid "Upload" 30 | msgstr "Dodaj plik" 31 | 32 | #: forms.py:114 33 | msgid "Save in fixture directory" 34 | msgstr "Zapisz w domyślnym katalogu dla zbiorów danych" 35 | 36 | #: forms.py:117 37 | #, python-format 38 | msgid "Uploads will be saved to \"%(fixture_dir)s\"." 39 | msgstr "Przesłane pliki zostaną zapisane w \"%(fixture_dir)s\". " 40 | 41 | #: forms.py:123 42 | msgid "From fixture directory" 43 | msgstr "Z katalogu zawierającego zbiory danych" 44 | 45 | #: forms.py:126 46 | #, python-format 47 | msgid "Data files from \"%(fixture_dir)s\"." 48 | msgstr "Zbiory danych z katalogu \"%(fixture_dir)s\"." 49 | 50 | #: forms.py:140 51 | msgid "At least one fixture file needs to be uploaded or selected." 52 | msgstr "Przynajmniej jeden ze zbiorów danych powinien zostać wybrany, bądź przesłany na serwer." 53 | 54 | #: views.py:43 55 | #, python-format 56 | msgid "An exception occurred while dumping data: %s" 57 | msgstr "Wystąpił wyjątek podczas eksportu danych: %s" 58 | 59 | #: views.py:113 60 | #, python-format 61 | msgid "Successfully imported %(count)d file." 62 | msgid_plural "Successfully imported %(count)d files." 63 | msgstr[0] "Import %(count)d pliku zakończył się powodzeniem." 64 | msgstr[1] "Import %(count)d plików zakończył się powodzeniem." 65 | msgstr[2] "Import %(count)d plików zakończył się powodzeniem." 66 | 67 | #: views.py:118 68 | #, python-format 69 | msgid "Loaded %(count)d object." 70 | msgid_plural "Loaded %(count)d objects." 71 | msgstr[0] "Załadowano %(count)d obiekt." 72 | msgstr[1] "Załadowano %(count)d obiekty." 73 | msgstr[2] "Załadowano %(count)d obiektów." 74 | 75 | #: views.py:127 76 | #, python-format 77 | msgid "An exception occurred while loading data: %s" 78 | msgstr "Wystąpił wyjątek podczas importu danych: %s" 79 | 80 | #: templates/smuggler/change_list.html:9 81 | msgid "Dump data" 82 | msgstr "Zapisz dane do pliku" 83 | 84 | #: templates/smuggler/change_list.html:14 85 | #: templates/smuggler/load_data_form.html:6 86 | #: templates/smuggler/load_data_form.html:18 87 | #: templates/smuggler/load_data_form.html:23 88 | msgid "Load data" 89 | msgstr "Załaduj dane" 90 | 91 | #: templates/smuggler/load_data_form.html:17 92 | msgid "Home" 93 | msgstr "Początek" 94 | 95 | #: templates/smuggler/load_data_form.html:26 96 | msgid "" 97 | "Existing items with same primary key will be overwritten." 98 | msgstr "Rekordy o takim samym identyfikatorze zostaną nadpisane." 99 | 100 | #: templates/smuggler/load_data_form.html:34 101 | msgid "Please correct the error below." 102 | msgstr "Proszę poprawić poniższe błędy." 103 | 104 | #: templates/smuggler/load_data_form.html:55 105 | msgid "Load" 106 | msgstr "Załaduj" 107 | -------------------------------------------------------------------------------- /smuggler/forms.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Guilherme Semente and contributors 2 | # 3 | # This file is part of Django Smuggler. 4 | # 5 | # Django Smuggler is free software under terms of the GNU Lesser 6 | # General Public License version 3 (LGPLv3) as published by the Free 7 | # Software Foundation. See the file README for copying conditions. 8 | import os.path 9 | 10 | from django import forms 11 | from django.conf import settings as django_settings 12 | from django.contrib.admin.widgets import FilteredSelectMultiple 13 | from django.core.serializers import get_serializer_formats 14 | from django.utils.translation import gettext_lazy as _ 15 | 16 | from smuggler import settings 17 | 18 | 19 | class MultiFileInput(forms.FileInput): 20 | def render(self, name, value, attrs=None, **kwargs): 21 | attrs = attrs or {} 22 | attrs["multiple"] = "multiple" 23 | return super().render(name, None, attrs=attrs, **kwargs) 24 | 25 | def value_from_datadict(self, data, files, name): 26 | if hasattr(files, "getlist"): 27 | return files.getlist(name) 28 | if name in files: 29 | return [files.get(name)] 30 | return [] 31 | 32 | 33 | class MultiFixtureField(forms.FileField): 34 | widget = MultiFileInput 35 | 36 | def to_python(self, data): 37 | files = [] 38 | for item in data: 39 | files.append(super().to_python(item)) 40 | return files 41 | 42 | def validate(self, data): 43 | super(MultiFixtureField, self).validate(data) 44 | for upload in data: 45 | file_format = os.path.splitext(upload.name)[1][1:].lower() 46 | if file_format not in get_serializer_formats(): 47 | raise forms.ValidationError( 48 | _("Invalid file extension: .%(extension)s.") 49 | % {"extension": file_format} 50 | ) 51 | return data 52 | 53 | 54 | class FixturePathField(forms.MultipleChoiceField, forms.FilePathField): 55 | widget = FilteredSelectMultiple(_("files"), False) 56 | 57 | def __init__(self, path, match=None, **kwargs): 58 | match = match or ( 59 | r"(?i)^.+(%s)$" 60 | % "|".join([r"\.%s" % ext for ext in get_serializer_formats()]) 61 | ) # Generate a regex string like: (?i)^.+(\.xml|\.json)$ 62 | super().__init__(path, match=match, **kwargs) 63 | if not self.required: 64 | del self.choices[0] # Remove the empty option 65 | 66 | 67 | class ImportForm(forms.Form): 68 | uploads = MultiFixtureField(label=_("Upload"), required=False) 69 | 70 | def __init__(self, *args, **kwargs): 71 | super().__init__(*args, **kwargs) 72 | if settings.SMUGGLER_FIXTURE_DIR: 73 | self.fields["store"] = forms.BooleanField( 74 | label=_("Save in fixture directory"), 75 | required=False, 76 | help_text=( 77 | _('Uploads will be saved to "%(fixture_dir)s".') 78 | % {"fixture_dir": settings.SMUGGLER_FIXTURE_DIR} 79 | ), 80 | ) 81 | self.fields["picked_files"] = FixturePathField( 82 | settings.SMUGGLER_FIXTURE_DIR, 83 | label=_("From fixture directory"), 84 | required=False, 85 | help_text=( 86 | _('Data files from "%(fixture_dir)s".') 87 | % {"fixture_dir": settings.SMUGGLER_FIXTURE_DIR} 88 | ), 89 | ) 90 | else: 91 | self.fields["uploads"].required = True 92 | 93 | def clean(self): 94 | super().clean() 95 | if settings.SMUGGLER_FIXTURE_DIR: 96 | uploads = self.cleaned_data["uploads"] 97 | picked_files = self.cleaned_data["picked_files"] 98 | if not uploads and not picked_files: 99 | raise forms.ValidationError( 100 | _("At least one fixture file needs to be" " uploaded or selected.") 101 | ) 102 | return self.cleaned_data 103 | 104 | class Media: 105 | css = {"all": ["admin/css/forms.css"]} 106 | js = [ 107 | "admin/js/vendor/jquery/jquery%s.js" 108 | % ("" if django_settings.DEBUG else ".min"), 109 | "admin/js/jquery.init.js", 110 | "admin/js/core.js", 111 | "admin/js/SelectBox.js", 112 | "admin/js/SelectFilter2.js", 113 | ] 114 | -------------------------------------------------------------------------------- /smuggler/views.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Guilherme Semente and contributors 2 | # 3 | # This file is part of Django Smuggler. 4 | # 5 | # Django Smuggler is free software under terms of the GNU Lesser 6 | # General Public License version 3 (LGPLv3) as published by the Free 7 | # Software Foundation. See the file README for copying conditions. 8 | import os.path 9 | import tempfile 10 | from datetime import datetime 11 | 12 | from django.contrib import messages 13 | from django.contrib.admin.helpers import AdminForm 14 | from django.contrib.auth.decorators import user_passes_test 15 | from django.core.exceptions import ObjectDoesNotExist, PermissionDenied 16 | from django.core.management.base import CommandError 17 | from django.core.serializers.base import DeserializationError 18 | from django.db import IntegrityError 19 | from django.http import HttpResponseRedirect 20 | from django.utils.encoding import force_str 21 | from django.utils.translation import gettext_lazy as _ 22 | from django.utils.translation import ngettext_lazy 23 | from django.views.generic.edit import FormView 24 | 25 | from smuggler import settings 26 | from smuggler.forms import ImportForm 27 | from smuggler.utils import ( 28 | load_fixtures, 29 | save_uploaded_file_on_disk, 30 | serialize_to_response, 31 | ) 32 | 33 | 34 | def dump_to_response(request, app_label=None, exclude=None, filename_prefix=None): 35 | """Utility function that dumps the given app/model to an HttpResponse.""" 36 | app_label = app_label or [] 37 | exclude = exclude 38 | try: 39 | filename = "%s.%s" % (datetime.now().isoformat(), settings.SMUGGLER_FORMAT) 40 | if filename_prefix: 41 | filename = "%s_%s" % (filename_prefix, filename) 42 | if not isinstance(app_label, list): 43 | app_label = [app_label] 44 | response = serialize_to_response(app_label, exclude) 45 | response["Content-Disposition"] = "attachment; filename=%s" % filename 46 | return response 47 | except CommandError as e: 48 | messages.error( 49 | request, _("An exception occurred while dumping data: %s") % force_str(e) 50 | ) 51 | return HttpResponseRedirect(request.build_absolute_uri().split("dump")[0]) 52 | 53 | 54 | def is_superuser(u): 55 | if u.is_authenticated: 56 | if u.is_superuser: 57 | return True 58 | raise PermissionDenied 59 | return False 60 | 61 | 62 | @user_passes_test(is_superuser) 63 | def dump_data(request): 64 | """Exports data from whole project.""" 65 | # Try to grab app_label data 66 | app_label = request.GET.get("app_label", []) 67 | if app_label: 68 | app_label = app_label.split(",") 69 | return dump_to_response( 70 | request, app_label=app_label, exclude=settings.SMUGGLER_EXCLUDE_LIST 71 | ) 72 | 73 | 74 | @user_passes_test(is_superuser) 75 | def dump_app_data(request, app_label): 76 | """Exports data from a application.""" 77 | return dump_to_response( 78 | request, app_label, settings.SMUGGLER_EXCLUDE_LIST, app_label 79 | ) 80 | 81 | 82 | @user_passes_test(is_superuser) 83 | def dump_model_data(request, app_label, model_label): 84 | """Exports data from a model.""" 85 | return dump_to_response( 86 | request, 87 | "%s.%s" % (app_label, model_label), 88 | [], 89 | "-".join((app_label, model_label)), 90 | ) 91 | 92 | 93 | class LoadDataView(FormView): 94 | form_class = ImportForm 95 | template_name = "smuggler/load_data_form.html" 96 | success_url = "." 97 | 98 | def form_valid(self, form): 99 | uploads = form.cleaned_data.get("uploads", []) 100 | store = form.cleaned_data.get("store", False) 101 | picked_files = form.cleaned_data.get("picked_files", []) 102 | fixtures = [] 103 | tmp_fixtures = [] 104 | for upload in uploads: 105 | file_name = upload.name 106 | if store: # Store the file in SMUGGLER_FIXTURE_DIR 107 | destination_path = os.path.join( 108 | settings.SMUGGLER_FIXTURE_DIR, file_name 109 | ) 110 | save_uploaded_file_on_disk(upload, destination_path) 111 | else: # Store the file in a tmp file 112 | prefix, suffix = os.path.splitext(file_name) 113 | fd, destination_path = tempfile.mkstemp(suffix=suffix, prefix=prefix + "_") 114 | save_uploaded_file_on_disk(upload, destination_path) 115 | tmp_fixtures.append((fd, destination_path)) 116 | fixtures.append(destination_path) 117 | for file_name in picked_files: 118 | fixtures.append(file_name) 119 | try: 120 | obj_count = load_fixtures(fixtures) 121 | user_msg = " ".join( 122 | [ 123 | ngettext_lazy( 124 | "Successfully imported %(count)d file.", 125 | "Successfully imported %(count)d files.", 126 | len(fixtures), 127 | ) 128 | % {"count": len(fixtures)}, 129 | ngettext_lazy( 130 | "Loaded %(count)d object.", 131 | "Loaded %(count)d objects.", 132 | obj_count, 133 | ) 134 | % {"count": obj_count}, 135 | ] 136 | ) 137 | messages.info(self.request, user_msg) 138 | except ( 139 | IntegrityError, 140 | ObjectDoesNotExist, 141 | DeserializationError, 142 | CommandError, 143 | ) as e: 144 | messages.error( 145 | self.request, _("An exception occurred while loading data: %s") % str(e) 146 | ) 147 | finally: 148 | # Remove our tmp files 149 | for fd, tmp_file in tmp_fixtures: 150 | os.close(fd) 151 | os.unlink(tmp_file) 152 | return super().form_valid(form) 153 | 154 | def get_admin_form(self, form): 155 | return AdminForm(form, self.get_fieldsets(form), {}) 156 | 157 | def get_context_data(self, **kwargs): 158 | context = super().get_context_data(**kwargs) 159 | context["adminform"] = self.get_admin_form(context["form"]) 160 | return context 161 | 162 | def get_fieldsets(self, form): 163 | fields = form.fields.keys() 164 | if "picked_files" in fields: 165 | return [ 166 | (_("Upload"), {"fields": ["uploads", "store"]}), 167 | (_("From fixture directory"), {"fields": ["picked_files"]}), 168 | ] 169 | return [(None, {"fields": fields})] 170 | 171 | 172 | load_data = user_passes_test(is_superuser)(LoadDataView.as_view()) 173 | -------------------------------------------------------------------------------- /COPYING.LESSER: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_views.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os.path 3 | 4 | from django.contrib import messages 5 | from django.contrib.auth.models import User 6 | from django.core.files.uploadedfile import SimpleUploadedFile 7 | from django.test import TestCase, TransactionTestCase 8 | from django.test.utils import override_settings 9 | from django.urls import reverse 10 | from freezegun import freeze_time 11 | 12 | from smuggler.forms import ImportForm 13 | from tests.test_app.models import Page 14 | 15 | 16 | def p(*args): 17 | return os.path.abspath( 18 | os.path.join(os.path.dirname(__file__), *args)) 19 | 20 | 21 | class SuperUserTestCase: 22 | def setUp(self): 23 | super().setUp() 24 | superuser = User(username='superuser') 25 | superuser.set_password('test') 26 | superuser.is_staff = True 27 | superuser.is_superuser = True 28 | superuser.save() 29 | self.client.login(username='superuser', password='test') 30 | 31 | 32 | class TestDumpViewsGenerateDownloadsWithSaneFilenames(SuperUserTestCase, 33 | TestCase): 34 | @freeze_time('2012-01-14') 35 | def test_dump_data(self): 36 | url = reverse('dump-data') 37 | response = self.client.get(url) 38 | self.assertEqual(response['Content-Disposition'], 39 | 'attachment; filename=2012-01-14T00:00:00.json') 40 | 41 | @freeze_time('2012-01-14') 42 | def test_dump_app_data(self): 43 | url = reverse('dump-app-data', kwargs={'app_label': 'sites'}) 44 | response = self.client.get(url) 45 | self.assertEqual(response['Content-Disposition'], 46 | 'attachment; filename=sites_2012-01-14T00:00:00.json') 47 | 48 | @freeze_time('2012-01-14') 49 | def test_dump_model_data(self): 50 | url = reverse('dump-model-data', kwargs={ 51 | 'app_label': 'sites', 52 | 'model_label': 'site' 53 | }) 54 | response = self.client.get(url) 55 | self.assertEqual(response['Content-Disposition'], 56 | 'attachment;' 57 | ' filename=sites-site_2012-01-14T00:00:00.json') 58 | 59 | 60 | class TestDumpData(SuperUserTestCase, TestCase): 61 | def test_dump_data_parameters(self): 62 | url = reverse('dump-data') 63 | response = self.client.get(url, { 64 | 'app_label': 'auth.user,sites' 65 | }) 66 | content = json.loads(response.content.decode('utf-8')) 67 | self.assertTrue([i for i in content if i['model'] == 'auth.user']) 68 | self.assertTrue([i for i in content if i['model'] == 'sites.site']) 69 | 70 | 71 | class TestDumpHandlesErrorsGracefully(SuperUserTestCase, TestCase): 72 | def test_erroneous_dump_has_error_messages(self): 73 | url = reverse('dump-app-data', kwargs={'app_label': 'flatpages'}) 74 | response = self.client.get(url, follow=True) 75 | response_messages = list(response.context['messages']) 76 | self.assertEqual(1, len(response_messages)) 77 | self.assertEqual(messages.ERROR, response_messages[0].level) 78 | self.assertRegex(response_messages[0].message, 79 | r'An exception occurred while dumping data:.*flatpages.*') 80 | 81 | def test_erroneous_dump_redirects(self): 82 | url = reverse('dump-app-data', kwargs={'app_label': 'flatpages'}) 83 | response = self.client.get(url) 84 | self.assertRedirects( 85 | response, 'http://testserver/admin/flatpages/', 86 | target_status_code=404) 87 | 88 | 89 | class TestLoadDataGet(SuperUserTestCase, TestCase): 90 | def setUp(self): 91 | super().setUp() 92 | self.url = reverse('load-data') 93 | 94 | def test_renders_correct_template(self): 95 | response = self.client.get(self.url) 96 | self.assertTemplateUsed(response, 'smuggler/load_data_form.html') 97 | 98 | def test_has_form_in_context(self): 99 | response = self.client.get(self.url) 100 | self.assertIsInstance(response.context['form'], 101 | ImportForm) 102 | 103 | 104 | class TestLoadDataPost(SuperUserTestCase, TransactionTestCase): 105 | def setUp(self): 106 | super().setUp() 107 | self.url = reverse('load-data') 108 | 109 | def test_load_fixture(self): 110 | self.assertEqual(0, Page.objects.count()) 111 | with open( 112 | p('..', 'smuggler_fixtures', 'page_dump.json'), mode='rb' 113 | ) as f: 114 | self.client.post(self.url, { 115 | 'uploads': f 116 | }, follow=True) 117 | self.assertEqual(1, Page.objects.count()) 118 | 119 | def test_load_fixture_message(self): 120 | with open( 121 | p('..', 'smuggler_fixtures', 'page_dump.json'), mode='rb' 122 | ) as f: 123 | response = self.client.post(self.url, { 124 | 'uploads': f 125 | }, follow=True) 126 | response_messages = list(response.context['messages']) 127 | self.assertEqual(1, len(response_messages)) 128 | self.assertEqual(messages.INFO, response_messages[0].level) 129 | self.assertEqual(response_messages[0].message, 130 | 'Successfully imported 1 file. Loaded 1 object.') 131 | 132 | @override_settings(FILE_UPLOAD_MAX_MEMORY_SIZE=0) 133 | def test_load_fixture_with_chunks(self): 134 | self.assertEqual(0, Page.objects.count()) 135 | with open( 136 | p('..', 'smuggler_fixtures', 'big_file.json'), mode='rb' 137 | ) as f: 138 | self.client.post(self.url, { 139 | 'uploads': f 140 | }, follow=True) 141 | self.assertEqual(1, Page.objects.count()) 142 | 143 | def test_handle_garbage_upload(self): 144 | with open( 145 | p('..', 'smuggler_fixtures', 'garbage', 'garbage.json'), mode='rb' 146 | ) as f: 147 | response = self.client.post(self.url, { 148 | 'uploads': f 149 | }, follow=True) 150 | response_messages = list(response.context['messages']) 151 | self.assertEqual(1, len(response_messages)) 152 | self.assertEqual(messages.ERROR, response_messages[0].level) 153 | self.assertRegex(response_messages[0].message, 154 | 'An exception occurred while loading data: ' 155 | 'Problem installing fixture .*') 156 | 157 | def test_handle_integrity_error(self): 158 | with open( 159 | p('..', 'smuggler_fixtures', 'garbage', 'invalid_page_dump.json'), 160 | mode='rb' 161 | ) as f: 162 | response = self.client.post(self.url, { 163 | 'uploads': f 164 | }, follow=True) 165 | response_messages = list(response.context['messages']) 166 | self.assertEqual(1, len(response_messages)) 167 | self.assertEqual(messages.ERROR, response_messages[0].level) 168 | self.assertRegex(response_messages[0].message, 169 | r'(?i)An exception occurred while loading data:.*unique.*') 170 | 171 | @override_settings(SMUGGLER_FIXTURE_DIR=p('..', 'smuggler_fixtures')) 172 | def test_load_from_disk(self): 173 | self.assertEqual(0, Page.objects.count()) 174 | self.client.post(self.url, { 175 | 'picked_files': p('..', 'smuggler_fixtures', 'page_dump.json') 176 | }, follow=True) 177 | self.assertEqual(1, Page.objects.count()) 178 | 179 | @override_settings(SMUGGLER_FIXTURE_DIR=p('..', 'smuggler_fixtures')) 180 | def test_load_from_disk_and_upload(self): 181 | with open( 182 | p('..', 'smuggler_fixtures', 'page_dump.json'), mode='rb' 183 | ) as f: 184 | response = self.client.post(self.url, { 185 | 'uploads': f, 186 | 'picked_files': p('..', 'smuggler_fixtures', 'page_dump.json') 187 | }, follow=True) 188 | response_messages = list(response.context['messages']) 189 | self.assertEqual(1, len(response_messages)) 190 | self.assertEqual(messages.INFO, response_messages[0].level) 191 | self.assertEqual(response_messages[0].message, 192 | 'Successfully imported 2 files. Loaded 2 objects.') 193 | 194 | @override_settings(SMUGGLER_FIXTURE_DIR=p('..', 'smuggler_fixtures')) 195 | def test_load_and_save(self): 196 | f = SimpleUploadedFile('uploaded.json', 197 | b'[{"pk": 1, "model": "test_app.page",' 198 | b' "fields": {"title": "test",' 199 | b' "path": "", "body": "test body"}}]') 200 | self.client.post(self.url, { 201 | 'store': True, 202 | 'uploads': f 203 | }, follow=True) 204 | self.assertTrue(os.path.exists( 205 | p('..', 'smuggler_fixtures', 'uploaded.json'))) 206 | os.unlink(p('..', 'smuggler_fixtures', 'uploaded.json')) 207 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Django Smuggler 3 | =============== 4 | 5 | .. image:: https://badge.fury.io/py/django-smuggler.svg 6 | :target: http://badge.fury.io/py/django-smuggler 7 | 8 | .. image:: https://travis-ci.org/semente/django-smuggler.svg?branch=master 9 | :target: https://travis-ci.org/semente/django-smuggler 10 | 11 | .. image:: https://coveralls.io/repos/semente/django-smuggler/badge.png?branch=master 12 | :target: https://coveralls.io/r/semente/django-smuggler?branch=master 13 | 14 | **Django Smuggler** is a pluggable application for `Django Web Framework`_ to 15 | easily dump/load fixtures via the automatically-generated administration 16 | interface. A fixture is file with model data serialized to e.g. JSON or XML 17 | that Django knows how to import to the database. 18 | 19 | Smuggler is especially useful for transporting database data between production 20 | and development environments, but can also be used as a backup tool. 21 | 22 | Project page 23 | http://github.com/semente/django-smuggler 24 | Translations 25 | https://www.transifex.com/projects/p/django-smuggler/ 26 | 27 | .. _`Django Web Framework`: http://www.djangoproject.com 28 | 29 | 30 | Installing & Setup 31 | ================== 32 | 33 | Smuggler is in the `Python Package Index (PyPI)`_ and you can easily install 34 | the latest stable version of it using the tools ``pip`` or 35 | ``easy_install``. Try:: 36 | 37 | pip install django-smuggler 38 | 39 | or:: 40 | 41 | easy_install django-smuggler 42 | 43 | .. _`Python Package Index (PyPI)`: http://pypi.python.org 44 | 45 | Alternatively, you can install Smuggler from source code running the follow 46 | command on directory that contains the file ``setup.py``:: 47 | 48 | python setup.py install 49 | 50 | After installation you need configure your project to recognizes the Smuggler 51 | application adding ``'smuggler'`` to your ``INSTALLED_APPS`` setting and setup 52 | the project *URLConf* like follow:: 53 | 54 | urlpatterns = [ 55 | # ... 56 | path('admin/', include('smuggler.urls')), # before admin url patterns! 57 | path('admin/', admin.site.urls), 58 | ] 59 | 60 | Then try access these urls: 61 | 62 | * `/admin/load/ `_, to load data from uploaded 63 | files or files on SMUGGLER_FIXTURE_DIR; 64 | 65 | * `/admin/dump/ `_, to download data from 66 | whole project; 67 | 68 | You can also pass in a querystring like 69 | ``/admin/dump/?app_label=flatpages,auth,yourapp.model`` to specify what 70 | must be dumped. 71 | 72 | * `/admin/APP_LABEL/dump/ `_, to 73 | download data from a app; 74 | 75 | * `/admin/APP_LABEL/MODEL_LABEL/dump/ 76 | `_, to download data 77 | from a model; 78 | 79 | If you can access the URLs above, the application was setup correctly. Note 80 | that these URLs are accessible only by superusers. 81 | 82 | Smuggler also provides a template to show buttons for dump and load data on 83 | change list page (``change_list.html``). You can setup the ModelAdmin you are 84 | interested like follow:: 85 | 86 | class ExampleAdmin(admin.ModelAdmin): 87 | change_list_template = 'smuggler/change_list.html' 88 | ... 89 | 90 | 91 | Settings 92 | -------- 93 | 94 | Smuggler has the following settings available. You can set them in your project 95 | ``settings.py``. If you doesn't set them it will assume the default values: 96 | 97 | SMUGGLER_EXCLUDE_LIST 98 | List of models to be excluded from dump. Use the form 'app_label.ModelName'. 99 | Default: []. 100 | 101 | SMUGGLER_FIXTURE_DIR 102 | Uploaded fixtures are stored in this directory (if requested). 103 | Default: None. 104 | 105 | SMUGGLER_FORMAT 106 | Format for dumped files. Any of the serialization formats supported by 107 | Django, json, xml and in some cases yaml. 108 | Default: 'json'. 109 | 110 | SMUGGLER_INDENT 111 | Indentation for dumped files. 112 | Default: 2. 113 | 114 | 115 | Screenshots 116 | =========== 117 | 118 | Buttons on change_list.html: 119 | 120 | .. image:: https://github.com/semente/django-smuggler/raw/master/etc/screenshot-0.png 121 | :alt: buttons on change_list.html 122 | :align: center 123 | 124 | Load form (with ``SMUGGLER_FIXTURE_DIR`` configured): 125 | 126 | .. image:: https://github.com/semente/django-smuggler/raw/master/etc/screenshot-1.png 127 | :alt: load form 128 | :align: center 129 | 130 | 131 | Release notes 132 | ============= 133 | 134 | Version 1.0.4 (2022-02-03) 135 | -------------------------- 136 | 137 | * Fix #76 138 | 139 | 140 | Version 1.0.3 (2021-08-12) 141 | -------------------------- 142 | 143 | * Fix a bug in load form template 144 | 145 | Version 1.0.2 (2020-09-28) 146 | -------------------------- 147 | 148 | * Support Django 3.1 149 | 150 | Version 1.0.1 (2020-05-15) 151 | -------------------------- 152 | 153 | * Fix Python packaging setup 154 | 155 | Version 1.0.0 (2020-04-20) 156 | -------------------------- 157 | 158 | * Support Django 3.0 159 | * Drop support for Python < 3.6 and Django < 2.2 160 | 161 | Version 0.9.1 (2018-11-05) 162 | -------------------------- 163 | 164 | * Support Django 2.1 165 | 166 | Version 0.9.0 (2018-03-22) 167 | -------------------------- 168 | 169 | * Support Django 2.0 170 | * Drop support for Django < 1.10 171 | 172 | Version 0.8.0 (2016-11-09) 173 | -------------------------- 174 | 175 | * Support Django 1.10 176 | 177 | Version 0.7.0 (2016-02-25) 178 | -------------------------- 179 | 180 | * Support Django 1.8 181 | * Support Django 1.9 182 | * Drop support for Django < 1.7 183 | * Drop support for Python < 2.7 184 | 185 | Version 0.6.1 (2015-11-25) 186 | -------------------------- 187 | 188 | * Increase Django 1.7 compatibilty by supporting 189 | use_natural_foreign_keys and use_natural_primary_keys arguments 190 | for dumpdata 191 | 192 | Version 0.6 (2014-09-18) 193 | ------------------------ 194 | 195 | * HTML5 multiple file upload is now supported for fixture uploads 196 | 197 | * Support loading fixtures from ``SMUGGLER_FIXTURE_DIR`` and upload at the same time 198 | 199 | * Recognize fixtures with upper case file extension correctly 200 | 201 | * Loading fixtures now uses loaddata management command 202 | 203 | * Removed signals.py 204 | 205 | * Removed sample templates 206 | 207 | * Cleaner code and better tests :-) 208 | 209 | 210 | Version 0.5 (2014-08-21) 211 | ------------------------ 212 | 213 | * Added an option to specify a list of app labels to the /dump/ view 214 | 215 | * Improved test suite 216 | 217 | * Dropped Django 1.3 support 218 | 219 | * Preliminary Python 3 support 220 | 221 | 222 | Version 0.4.1 (2013-11-12) 223 | -------------------------- 224 | 225 | * Changelist template is now Django 1.6 compatible 226 | 227 | 228 | Version 0.4 (2013-04-01) 229 | ------------------------ 230 | 231 | * Django 1.5+ support; 232 | 233 | * Added German translation; 234 | 235 | * Added some tests. 236 | 237 | 238 | Version 0.3 (2012-01-31) 239 | ------------------------ 240 | 241 | * Significant bug fixes and improvements when loading and exporting data; 242 | 243 | * Allow formats for import besides JSON and XML (aa105b3, needs documentation); 244 | 245 | * Added Dutch translation. 246 | 247 | 248 | Version 0.2 (2011-08-19) 249 | ------------------------ 250 | 251 | * Django 1.2+ support; 252 | 253 | * Keep uploaded files as alternative choices to import (issues #1 and #6); 254 | 255 | * Vulnerability fixed (d73cec6); 256 | 257 | * Added Polish, Russian, Catalan and Brazilian Portuguese translations. 258 | 259 | 260 | Version 0.1.1 (2010-01-20) 261 | -------------------------- 262 | 263 | * First stable version. 264 | 265 | 266 | Backwards-incompatible changes 267 | ============================== 268 | 269 | * Removed AdminFormMixin (version 0.7) 270 | 271 | * Removed signals.py (version 0.6) 272 | 273 | * Renamed urls from import/export to load/dump (version 0.1) 274 | 275 | 276 | Contributing 277 | ============ 278 | 279 | If you find any problems in the code or documentation, please take 30 seconds 280 | to fill out a issue `here `_. 281 | 282 | The contributing with code or translation is MUCH-APPRECIATED. Feel free to 283 | fork or send patches. 284 | 285 | You can translate this application to your language using Transifex. Access 286 | the `project page `_ 287 | on Transifex. 288 | 289 | See the AUTHORS file for a complete authors list of this application. 290 | 291 | Thanks to `Interaction Consortium `_ for 292 | sponsoring the first releases of the project. 293 | 294 | 295 | Tests 296 | ===== 297 | 298 | If you are contributing to django-smuggler we recommend setting up a 299 | virtualenv and running:: 300 | 301 | pip install -r test-requirements.txt 302 | 303 | You can then run the tests with:: 304 | 305 | make tests 306 | 307 | Before submitting a pull request please test against our supported versions 308 | of Python and Django by running:: 309 | 310 | tox 311 | 312 | To see if you need to add tests we use coverage. You can generate a coverage 313 | report with:: 314 | 315 | make coverage 316 | 317 | To check if your code follows the style guide you can run:: 318 | 319 | make lint 320 | 321 | Copying conditions 322 | ================== 323 | 324 | Django Smuggler is free software; you can redistribute it and/or modify it 325 | under the terms of the `GNU Lesser General Public License`_ as published by the 326 | Free Software Foundation; either version 3 of the License, or (at your option) 327 | any later version. 328 | 329 | Django Smuggler is distributed in the hope that it will be useful, but WITHOUT 330 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 331 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 332 | details. 333 | 334 | You should have received a copy of the GNU Lesser General Public License along 335 | with this program; see the file COPYING.LESSER. If not, see 336 | http://www.gnu.org/licenses/. 337 | 338 | .. _`GNU Lesser General Public License`: http://www.gnu.org/licenses/lgpl-3.0-standalone.html 339 | -------------------------------------------------------------------------------- /tests/test_app/smuggler_fixtures/big_file.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "pk": 1, 3 | "model": "test_app.page", 4 | "fields": { 5 | "title": "Lipsum", 6 | "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus volutpat, metus eget malesuada pellentesque, arcu lacus fringilla augue, quis molestie massa nunc non quam. Donec viverra sollicitudin libero sit amet ornare. Maecenas sit amet dolor arcu. Quisque diam dui, ultrices iaculis rhoncus eu, dapibus vel ipsum. Ut sed ligula ac ipsum placerat lobortis. Etiam lobortis ipsum sed justo pharetra dignissim. Sed a ultricies orci, at consectetur quam. Sed ac ornare lectus. Mauris consectetur tempus elementum.\n\nNulla hendrerit mauris sit amet orci tincidunt, in commodo ex efficitur. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec sollicitudin, velit vel imperdiet interdum, nibh lacus placerat nunc, sed lacinia ipsum mi non elit. Nunc sagittis, enim sed facilisis consectetur, erat nisl consequat felis, ac imperdiet lectus sem fringilla augue. Mauris ac lobortis tortor. In hac habitasse platea dictumst. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Maecenas a purus vel justo tempus lobortis. Integer tristique odio massa, sit amet placerat augue laoreet sit amet. Morbi semper pulvinar eros, vel tincidunt ex dapibus ac. Pellentesque sed metus laoreet magna imperdiet fermentum. Vestibulum et malesuada purus. Donec tempor risus nibh, eu convallis ipsum scelerisque quis. Vestibulum eget commodo neque. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc dolor libero, mattis vel sem sit amet, pulvinar sollicitudin arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aliquam erat volutpat. Aliquam pharetra, mauris sed condimentum bibendum, urna ipsum eleifend lacus, molestie tincidunt ante lectus id enim. Vestibulum porttitor ante in nunc efficitur auctor. Donec condimentum in risus nec luctus. Praesent placerat dapibus diam at sagittis.nnSed a magna nec massa ullamcorper hendrerit. Phasellus vitae felis a libero varius lacinia. Nulla commodo purus nunc, sit amet venenatis lacus finibus ut. Sed at mi eget magna blandit pulvinar id a nunc. Praesent at diam nec diam sagittis efficitur non vel erat. Nunc arcu nisl, laoreet eget venenatis a, sagittis id mauris. Quisque at hendrerit diam, porta auctor tellus.nnDuis diam lacus, vehicula eget porta vel, fringilla a ex. Proin accumsan sem eget pulvinar consectetur. Nunc odio tortor, viverra eu mauris sed, pretium mattis velit. Etiam lectus felis, egestas eu elit ac, feugiat congue libero. Phasellus convallis volutpat massa sed semper. Duis scelerisque diam quis dui auctor, vel pellentesque orci sollicitudin. Nam turpis nisl, posuere ac fringilla sed, lacinia sed tortor. Ut vitae mauris in turpis bibendum congue.nnPhasellus efficitur, nisi et vehicula sodales, elit lacus malesuada leo, sed hendrerit augue tellus a lorem. Vivamus eget posuere ligula. Vivamus vitae condimentum velit. Morbi non lorem mauris. Mauris vel elementum justo. Praesent elementum turpis magna. Nam eu dapibus ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nullam suscipit finibus iaculis. Morbi hendrerit quis neque nec aliquam. Aenean tincidunt pharetra quam, sit amet dignissim nisl faucibus convallis. Aliquam nec sem ut urna gravida maximus ac pellentesque lectus. Nullam et ornare mauris. Vestibulum consectetur erat imperdiet ipsum feugiat dapibus.nnNam tempor eu magna vitae mattis. Donec ultrices luctus dui, sit amet hendrerit enim. Ut non odio nibh. Curabitur ante elit, commodo sit amet pretium sit amet, pulvinar ac ante. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras ac dolor risus. Proin condimentum viverra tristique. Pellentesque ultrices mi dui, a hendrerit neque sagittis in. Vivamus non ultricies libero. Aliquam tempus justo sodales tellus porttitor rutrum. Nulla facilisi. Pellentesque porta tellus velit, ut elementum eros tincidunt non.nnAliquam hendrerit ligula eu ex aliquet, hendrerit commodo nisl bibendum. Donec sollicitudin tortor nisi, lobortis ultrices nunc dictum sit amet. In ut dolor eu erat efficitur auctor. Sed nec sapien id ante fringilla fringilla. Integer malesuada vulputate pulvinar. Sed imperdiet urna sit amet consequat interdum. Integer a hendrerit tellus. Aenean at finibus mi, sed convallis est.nnQuisque rhoncus, nisi ac ultrices suscipit, ipsum augue eleifend lacus, volutpat vehicula lorem risus sit amet tortor. Proin semper enim eget pulvinar consectetur. Sed consequat ac urna non maximus. Curabitur euismod velit mi, nec rutrum odio molestie ac. Nunc eu diam vel ipsum iaculis feugiat non nec sapien. Quisque eleifend dignissim ipsum, sed pharetra odio fermentum et. Pellentesque non ipsum sed ex pellentesque sollicitudin. Vivamus tempor ligula vitae ipsum posuere aliquet. Sed eu sapien vel velit viverra mattis. Suspendisse potenti. Donec a vestibulum magna, non ultrices neque.nnMorbi sit amet purus lorem. Quisque in sapien ac arcu commodo ultrices. Ut quis lacinia velit. Sed vel orci non eros imperdiet lacinia. Suspendisse pellentesque nunc eu vulputate ullamcorper. Vivamus pharetra quam eget risus viverra, vel feugiat mauris efficitur. Sed eu maximus lacus, at interdum tellus. Duis metus magna, suscipit non maximus ac, imperdiet non diam. Praesent a ligula lacus. Donec rutrum tristique nunc sed cursus. Fusce molestie, sem id sodales imperdiet, lectus purus fringilla sem, non faucibus dolor lorem id magna.nnCras ut mauris tortor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Duis et augue magna. Maecenas commodo sapien et aliquam luctus. Donec dignissim risus ut semper commodo. Duis ut imperdiet lectus. Etiam ullamcorper lorem tortor, quis porttitor leo efficitur nec. Nullam eu orci ac leo mattis convallis. Maecenas pellentesque laoreet sem, non feugiat arcu. Nulla facilisi. Lorem ipsum dolor sit amet, consectetur adipiscing elit.nnEtiam luctus dictum odio a fermentum. Nullam sodales lectus nibh, id commodo ligula ultrices vehicula. Cras vitae ligula quis orci aliquet sagittis. Suspendisse potenti. Morbi eget purus non libero malesuada tincidunt molestie eget erat. Vivamus suscipit ullamcorper mi quis aliquam. Nam et fermentum justo, vel molestie dui. Aliquam nisi ante, malesuada nec suscipit a, sagittis sit amet diam. Nullam interdum velit at massa viverra fermentum. Morbi semper commodo tortor, nec ornare magna laoreet ut. Nulla tristique ornare ipsum ac ultricies. Nam vestibulum quam ac erat cursus tincidunt. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non bibendum enim, ut consequat est. Quisque sollicitudin, risus facilisis aliquet posuere, sapien lorem ultricies nibh, vel placerat quam nisi ac leo. Duis vestibulum nibh vitae enim aliquam viverra.nnPhasellus non nulla vitae nisi ornare ultricies. Nunc gravida vel eros sed bibendum. Curabitur id nisl pretium, eleifend massa non, consectetur lacus. Curabitur varius viverra nulla non imperdiet. Nunc consectetur mauris sed neque bibendum lacinia. Quisque sagittis quam vitae sapien consequat lacinia. Praesent auctor enim vel urna auctor, eu pulvinar metus posuere. Aenean pellentesque augue vitae nisl congue aliquam. Quisque posuere viverra scelerisque. Integer sodales vestibulum ipsum, cursus dignissim nunc tempor et. Donec auctor volutpat erat. Vivamus ullamcorper tincidunt pulvinar. Nunc vulputate at nisi vel venenatis.nnNulla nec metus sit amet nisi maximus laoreet. Proin mattis nunc a euismod pretium. Maecenas vitae arcu suscipit, placerat enim vel, eleifend turpis. Sed vehicula sed dolor ut bibendum. Maecenas nec suscipit turpis. Vestibulum id ex libero. Maecenas accumsan ipsum vitae tortor dapibus, quis fringilla leo ultrices. Vestibulum convallis turpis felis, eu fringilla ligula vehicula vitae. Donec laoreet nec quam vel feugiat. Donec id feugiat nisl.nnCras vitae tincidunt augue. Morbi a nulla ac leo fringilla viverra. Proin ultricies ullamcorper erat, pellentesque finibus velit suscipit in. Integer arcu arcu, semper et mollis sagittis, sodales quis mi. Sed porta eros magna, sit amet tincidunt leo ultricies a. Duis viverra iaculis quam, malesuada consequat nisl porta vel. Aenean convallis, justo ac ultrices condimentum, lacus lorem vestibulum tortor, et interdum ligula sapien id urna. Sed quis posuere urna, facilisis varius metus. Etiam vel fermentum nibh. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas sed aliquam mauris, quis consequat elit. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum in eros dapibus, consequat eros sed, tristique ante. Sed eu mi ut odio posuere egestas.nnPellentesque nec purus in risus sodales dapibus. Etiam dapibus sit amet velit et interdum. Morbi lobortis efficitur efficitur. Aenean ac orci rutrum, ultricies felis vel, volutpat elit. Vestibulum magna sem, efficitur eu tincidunt eget, blandit id metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean et dolor rutrum, venenatis nisl sit amet, dapibus felis. Duis ultrices varius magna, eu facilisis sapien dictum et. In hac habitasse platea dictumst. Donec euismod ullamcorper facilisis. Etiam commodo libero id risus eleifend, eget ornare felis aliquet. Duis et posuere sem, sed varius nisi. Vivamus lacinia tristique consequat.nnPhasellus aliquam, ligula ut suscipit pellentesque, sapien quam auctor nisi, in sodales ex risus ac ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent sit amet enim justo. Vivamus lacinia facilisis rhoncus. Aenean eget nisi pellentesque, ullamcorper lectus nec, aliquet erat. Quisque eu est a nisl semper lacinia vitae nec tellus. Proin quis purus massa. Cras at lacus eget tellus ullamcorper pretium. Pellentesque eleifend scelerisque commodo.nnPhasellus placerat erat diam, nec gravida ante lacinia sit amet. Duis ultricies ipsum at convallis bibendum. Aliquam pulvinar leo id tortor lacinia pellentesque. Cras quam elit, posuere vel felis eu, egestas porttitor tortor. Suspendisse vulputate bibendum risus. Nam elementum sagittis lacinia. Proin in mauris diam. Curabitur at massa bibendum ante pretium vehicula. Donec bibendum nibh et tortor egestas, sit amet vehicula nisl varius. Aenean ac augue ligula. Vestibulum tempus mi semper, mollis nisi ut, dictum erat. Fusce mauris lacus, pulvinar sed neque non, efficitur efficitur augue. Curabitur tempor urna quis sapien egestas, nec tincidunt ex sagittis. Nullam consequat, nulla eu hendrerit porta, neque velit hendrerit tellus, vehicula condimentum ex massa tincidunt diam. Nunc in nisi maximus, lacinia dui vel, tincidunt tellus.nnPellentesque pretium dolor tellus, ultricies faucibus elit lacinia quis. Nulla facilisi. Mauris iaculis volutpat mattis. Sed vitae quam ac dui suscipit luctus eu eget tellus. Proin sagittis, leo non rutrum pellentesque, nibh lacus lobortis leo, a tincidunt erat elit vel neque. In et velit tortor. Ut accumsan porta ligula non tincidunt. Nulla at interdum nunc.nnNulla facilisi. Nulla placerat ullamcorper pellentesque. Curabitur suscipit lorem imperdiet eros rhoncus, consectetur blandit tellus dictum. Vivamus dui ante, tempus dapibus risus nec, rhoncus vehicula nisl. Nam accumsan, leo in fermentum dignissim, nunc dui ultricies nibh, quis sagittis metus purus at risus. Integer in consectetur sem, nec maximus eros. Aenean gravida ante nisi, eget faucibus felis lacinia nec. Nullam pellentesque interdum turpis, in porta mi fringilla quis. Curabitur non dui nec libero aliquet sagittis a sed ligula. Phasellus ut mollis est. Cras hendrerit eros ac ante tincidunt tincidunt. Donec molestie mollis tortor eget cursus. Vestibulum elementum lectus ipsum, sed commodo velit varius vitae. Nam congue diam eget lectus vulputate gravida.nnCras ultrices at sapien a volutpat. Cras vel purus maximus est facilisis ultricies eu sit amet massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed ullamcorper velit urna, ut consectetur purus imperdiet sed. Mauris interdum, nisl at consequat scelerisque, felis justo auctor lorem, vitae blandit purus quam sit amet mi. Fusce augue tortor, aliquet non ipsum eu, faucibus efficitur justo. Sed euismod dapibus felis, vel venenatis arcu vehicula vitae. Suspendisse potenti. Quisque nisi dui, convallis vitae est vitae, varius suscipit justo. Nullam convallis nunc ac arcu efficitur, sed placerat ligula commodo. Sed blandit aliquam hendrerit. Nulla a neque tortor. Aliquam rhoncus, felis sed porta pharetra, ex libero tempus nibh, in placerat lorem mi at ipsum.nnAliquam quis egestas risus, euismod luctus leo. Fusce cursus, nulla a hendrerit pulvinar, ipsum nisl malesuada leo, quis vehicula lorem dolor vitae quam. Suspendisse quis turpis convallis, ultrices nunc quis, mattis libero. Quisque sit amet mattis nunc. Pellentesque tristique convallis elit, id pretium nisi vehicula eu. Donec imperdiet mi ac lacus dignissim pulvinar. Suspendisse aliquam justo et ipsum finibus, id commodo augue condimentum.nnQuisque sodales nisl ut elementum eleifend. Maecenas pharetra dui at eros tempus, id tempus nunc iaculis. Integer euismod ligula id turpis accumsan placerat. Nam ac leo eros. Nulla iaculis quam ipsum, id auctor nunc facilisis ac. Vestibulum ex dui, vulputate vel magna ac, faucibus semper enim. Nullam ultricies nisi posuere malesuada finibus. Maecenas lacinia ipsum in est tincidunt, eu scelerisque lectus tincidunt. Duis dignissim non arcu ac mattis. Sed lacus velit, convallis in cursus vel, ultrices vel eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed ante lorem, maximus quis dolor vitae, fermentum ullamcorper dui. Vivamus vestibulum enim et tempus semper. Pellentesque mollis lacinia purus quis tincidunt.nnNullam congue dolor vel velit scelerisque condimentum. Proin convallis rutrum aliquet. Morbi accumsan ante eu leo cursus, vitae convallis metus volutpat. Maecenas sagittis vestibulum augue non pretium. Proin dignissim diam sit amet pretium porttitor. Nunc felis risus, tincidunt nec mollis non, congue vitae sapien. In dignissim, metus sed accumsan imperdiet, justo diam vulputate libero, a pulvinar tortor quam nec tellus. Aliquam erat volutpat.nnCurabitur quis libero non massa dignissim vestibulum. Pellentesque efficitur, odio suscipit scelerisque pharetra, felis turpis molestie dolor, in lacinia eros nulla ac elit. Integer vehicula a risus eu luctus. Praesent convallis quam mauris, eget luctus nulla aliquet ac. Nunc pellentesque felis pulvinar faucibus tristique. Nam quis augue dignissim, facilisis dolor ut, pellentesque risus. Nunc semper eros ut mi ultrices, non lobortis tellus condimentum. Fusce sagittis pretium orci, non fringilla ipsum semper vel.nnNam orci libero, facilisis eu pretium vitae, luctus non erat. Donec lobortis lorem vitae libero posuere, quis dictum quam lacinia. Suspendisse potenti. In bibendum consectetur aliquam. Donec vel orci sed tortor convallis maximus. Donec rutrum neque risus, vitae viverra est varius ac. Aliquam porta rhoncus magna, non blandit mauris. Nunc ornare sed ante ac lacinia. Sed vehicula massa sit amet dignissim placerat.nnVestibulum dolor metus, ultrices sit amet felis sed, varius malesuada velit. Mauris in sem nisi. Curabitur pellentesque pharetra ante, at mattis tellus lacinia sit amet. Duis vel purus sed odio molestie dignissim eu eu odio. Proin sit amet ligula orci. Vivamus sed neque id orci lacinia laoreet. Vivamus dignissim vitae metus ac vestibulum. In eleifend, massa id interdum efficitur, est velit semper tellus, in eleifend ex felis id nunc. Suspendisse suscipit consectetur velit in finibus. Suspendisse elit arcu, consequat quis euismod ac, vehicula nec neque. Suspendisse vel euismod libero. Phasellus non finibus diam. Morbi et lorem eget est consequat placerat. Sed in urna molestie, cursus leo id, rhoncus odio. Aliquam et euismod augue. Nullam commodo, lacus eget tempor dignissim, nunc metus convallis massa, id mattis risus urna non ipsum.nnCras tincidunt interdum erat, vel feugiat mauris aliquam nec. Nulla ac accumsan quam. Vivamus sit amet gravida magna. Pellentesque dapibus ac odio id dictum. Sed ante metus, consequat semper mi sed, sodales dignissim risus. Morbi nec purus urna. Sed nec accumsan lectus. Nunc non arcu mattis leo suscipit aliquet id non sapien. Nam fringilla pulvinar nunc. Vivamus pharetra pulvinar nisi non aliquam. Sed auctor blandit enim et tincidunt. Suspendisse potenti. Ut tristique eros id venenatis porta. Nam fringilla sem ut leo venenatis feugiat. Cras eu accumsan lacus, nec dictum leo. In hac habitasse platea dictumst.nnSuspendisse a magna malesuada elit venenatis finibus vitae id lacus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum semper est nec sapien feugiat porttitor. Proin at pulvinar nisi, ut aliquet turpis. Pellentesque vestibulum justo nec nisi placerat, at hendrerit neque scelerisque. Fusce elit velit, condimentum sit amet fringilla eget, dignissim vel nibh. Mauris sit amet justo eu turpis convallis cursus et sit amet tortor. Donec vel rhoncus nisi, et commodo enim. Aenean lorem eros, dapibus eu odio dignissim, ornare convallis purus. Suspendisse mollis tristique dictum. Phasellus non viverra augue.nnCurabitur libero dolor, fringilla eu nisl ac, rhoncus ullamcorper nunc. In lobortis, purus quis efficitur facilisis, tellus augue lobortis odio, eget varius arcu purus sed turpis. Phasellus sodales massa dolor, vitae volutpat augue pretium vitae. Morbi eu pharetra massa, at venenatis sem. Ut lacinia cursus nisi, at varius libero cursus vitae. Quisque non auctor risus, interdum dictum enim. Cras in lacus vel massa cursus lobortis in sit amet dolor.nnMaecenas iaculis, ligula ac maximus rutrum, ante nisi viverra lorem, ut pharetra nisl dui in sapien. Nulla facilisi. Fusce nisi dolor, feugiat in nunc in, convallis dictum justo. Donec imperdiet diam leo, nec egestas metus pharetra quis. Curabitur justo nisi, volutpat a luctus nec, suscipit vel diam. Duis feugiat, tortor eget tempor facilisis, sapien dui sagittis justo, eget viverra lacus magna ut dui. Suspendisse elit arcu, varius eget dictum ut, interdum in mi. Sed pellentesque, erat at egestas semper, ipsum turpis scelerisque mi, nec consequat justo leo ac mauris. Nullam lobortis vehicula enim eu lobortis. Nulla ac justo odio. Integer purus diam, mollis in orci sit amet, blandit malesuada nisl. Etiam non massa sed arcu fringilla rhoncus in ut nisi. Nam a erat pharetra, aliquam velit vel, ullamcorper diam. Quisque quis augue metus. Aenean arcu tortor, vehicula in lacinia gravida, vestibulum vel velit.nnDonec et vestibulum nisl. Integer dignissim mauris sit amet elit luctus varius. Etiam quis sem et est sagittis egestas. Donec vel tortor justo. Nulla sagittis sem sapien, vel ultricies tortor rutrum a. Cras pulvinar condimentum tortor sed pulvinar. Nam vel turpis risus. Nulla ligula purus, mattis at urna et, pretium commodo est. In at elementum est. Quisque posuere sem vel libero vulputate aliquam.nnIn eleifend, turpis sed finibus accumsan, lacus diam pellentesque magna, ut convallis magna odio ac enim. Duis neque risus, auctor sit amet lacus a, tempus aliquet urna. Nulla metus nibh, cursus nec vestibulum ac, placerat sit amet tortor. Nunc bibendum lacus vitae est pharetra consectetur. Etiam faucibus urna ut maximus pharetra. Vestibulum ipsum dui, maximus vel consectetur sed, blandit eu eros. Nam et tortor cursus, consectetur augue vitae, mattis tortor. Etiam pharetra tristique justo, vel viverra sapien molestie ac. Nam elementum viverra viverra. Fusce sodales risus eget efficitur pretium. Integer suscipit, justo ac semper pulvinar, lectus erat porttitor erat, a rhoncus velit ex vel urna. Duis tellus nisl, pretium eu semper quis, posuere eu velit. Donec interdum orci mauris, non ultricies purus rutrum quis.nnVivamus varius tempus condimentum. Suspendisse potenti. Phasellus et nibh nibh. Morbi in risus vel purus tempor sagittis. Sed tempor lectus in nisl aliquet, a efficitur erat ultricies. Nam dignissim risus quis nibh placerat suscipit. Duis mauris metus, convallis vitae est in, lacinia mollis est. Nulla facilisi. Maecenas ut cursus nulla, quis ultricies lectus.nnNullam vitae tristique ex. Nulla a justo nec lacus cursus dignissim interdum vitae tortor. Nullam sit amet bibendum ex, ut interdum velit. Pellentesque venenatis nulla eget rutrum semper. Maecenas sollicitudin purus at egestas interdum. Phasellus dui nulla, imperdiet a scelerisque ac, viverra sed ligula. Phasellus vulputate metus sit amet nibh tristique aliquet.nnDuis pretium auctor sem, sed suscipit ante tincidunt vitae. Nullam porttitor, magna vitae porta gravida, turpis magna tempus sem, vitae efficitur dolor mauris id velit. Aliquam dapibus, est id rutrum venenatis, ex sem sollicitudin risus, vel facilisis leo ipsum quis libero. Etiam mollis nisl sed sodales vulputate. Nulla tempus mauris sit amet leo egestas suscipit. Ut suscipit justo leo, sit amet placerat leo eleifend quis. Maecenas ullamcorper nisi id odio suscipit interdum. Quisque leo ante, tincidunt nec porta et, condimentum a ipsum.nnMaecenas nisi erat, sollicitudin nec dui et, feugiat cursus erat. Nullam leo augue, condimentum ut commodo quis, convallis sit amet lectus. Curabitur tempus venenatis est eu egestas. Sed quis bibendum mi. Suspendisse vulputate orci sit amet risus ullamcorper elementum. Nam risus odio, consectetur vitae porta ac, mollis nec mauris. Maecenas a mauris sit amet elit semper dictum sed vel arcu. Vestibulum rhoncus vulputate fermentum. Aliquam erat volutpat. Sed at turpis efficitur magna aliquam commodo. Donec vel eros ultricies, dapibus nunc non, laoreet dui. Vivamus dolor est, venenatis et libero eget, faucibus vestibulum risus.nnDonec varius dui elit, sit amet rhoncus tortor pellentesque sit amet. Donec faucibus efficitur eleifend. Morbi non pretium arcu, sit amet luctus dolor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer quis consequat neque, id blandit sem. Sed tempus semper tellus in faucibus. Vestibulum ultrices posuere malesuada. Vivamus pulvinar eros vel arcu ullamcorper, at consectetur dui luctus. Vestibulum ligula eros, ultrices ac lacus vel, tristique commodo erat. In dui urna, tristique et commodo a, elementum sed odio. Nullam libero libero, fermentum vel enim eget, maximus vestibulum felis. Phasellus volutpat arcu ut arcu malesuada, vel molestie leo luctus. Ut iaculis ultrices tortor nec bibendum. Vestibulum vulputate arcu a lectus auctor ultricies.nnVestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce ac lobortis arcu. In hac habitasse platea dictumst. Sed faucibus odio eu imperdiet finibus. Nam viverra vitae nisl non pharetra. Donec gravida laoreet risus, et consectetur odio euismod non. Pellentesque sit amet massa sem. Etiam sed posuere justo, in tincidunt augue. In erat eros, gravida nec tristique vel, viverra eget erat. Etiam sed lacus eget augue bibendum feugiat ut et nisl. Nullam in tristique ante, vitae sodales turpis. Nam pulvinar fermentum ullamcorper. Fusce sed turpis eget enim egestas vulputate.nnInteger ultrices velit nec urna interdum, non tincidunt ex dictum. Suspendisse interdum tortor at elit ultrices maximus. Nam purus mi, commodo in risus ut, vulputate luctus diam. Vivamus tempor consequat justo, nec suscipit ipsum ornare sit amet. Sed a nulla sed tellus lobortis scelerisque ac pulvinar erat. Sed venenatis diam vitae sapien dignissim interdum. Nulla lacus arcu, laoreet quis libero non, cursus interdum odio. Praesent mauris risus, congue ut elit a, lobortis sagittis felis. Donec vulputate velit ac felis gravida, quis vehicula mauris aliquam. Duis lacinia bibendum auctor. Praesent sit amet molestie nunc, nec dignissim elit. Fusce facilisis libero in lorem suscipit, euismod auctor purus vestibulum. Cras ante ante, lacinia ac leo et, tincidunt ultrices tellus. Pellentesque vel mauris non est suscipit blandit. Proin pretium faucibus tortor, quis hendrerit nulla lacinia nec.nnQuisque ac congue nulla. Donec non tortor dui. Quisque elit ipsum, porttitor dictum dolor vitae, dapibus molestie lorem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus porta risus non magna tristique malesuada. Vivamus laoreet quis nisl ut efficitur. Etiam enim ipsum, condimentum a elit eu, faucibus aliquam ante. Vestibulum viverra libero sit amet congue finibus. Morbi lacinia finibus felis nec pretium. Sed mollis tristique nunc, eget pulvinar ante ultricies sit amet.nnPraesent scelerisque orci quis pretium placerat. Sed non blandit elit. Proin feugiat eros nec orci placerat dignissim. Mauris mollis vitae nibh in laoreet. Maecenas non tincidunt elit, id eleifend enim. Nulla facilisi. Integer at justo arcu. Duis tincidunt neque erat, eget dapibus dolor tincidunt vel. Nunc ut arcu in elit congue varius. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi lacinia fermentum erat sed eleifend. Phasellus fermentum lectus sit amet ante faucibus mattis. Integer egestas scelerisque sem, non semper nisl iaculis vitae. Fusce malesuada, odio id elementum convallis, dui ante fringilla massa, ut sollicitudin ex quam quis purus. Proin id ornare arcu, vel condimentum mi. Nunc sagittis a sapien vitae convallis.nnNam interdum ligula et sem vestibulum varius. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras purus ligula, porttitor nec ligula in, maximus aliquam urna. Cras eget tellus a ipsum egestas accumsan faucibus ut turpis. Sed non vestibulum est. Duis a consequat ligula, non molestie diam. Sed ornare magna ut dictum scelerisque. Quisque lacinia lobortis lorem porttitor finibus. Phasellus pharetra, nulla a maximus bibendum, quam lacus efficitur lacus, sed porta diam nibh eget diam.nnMaecenas in sollicitudin ipsum. Praesent quis sollicitudin sapien, sed ultricies lacus. Suspendisse blandit massa non justo malesuada congue. Ut tristique felis augue, eu vulputate lacus interdum non. Proin efficitur ultricies iaculis. Aliquam vitae varius nulla. Proin eget elit nec ante ullamcorper tincidunt. Proin ultrices ut dolor ac pellentesque. Nulla neque ex, imperdiet sit amet orci non, porta tempor dui. In venenatis risus neque, id semper ipsum eleifend tempus. Curabitur at lacus vitae mi eleifend molestie sed sed arcu. Nullam elit augue, ultricies nec fringilla id, rhoncus eu nunc. Donec commodo velit sapien, non condimentum lorem eleifend interdum.nnPhasellus ornare, orci mattis dapibus lobortis, tortor odio fermentum felis, vitae tincidunt justo orci eget quam. Nullam congue venenatis suscipit. Nam urna sapien, dictum a ipsum eget, sagittis lacinia erat. Ut sed massa vehicula, sollicitudin mi ut, aliquam nisi. Nullam at mattis sapien. Morbi eu rutrum risus. Mauris ac sagittis ex. Donec efficitur semper urna, hendrerit fermentum quam euismod sit amet. Vivamus bibendum, eros sed congue ultricies, enim tellus consequat risus, vitae ultrices turpis magna quis lorem. Pellentesque id mi blandit erat consectetur dictum.nnCras eget purus ac urna laoreet finibus ac a tellus. Vivamus molestie elit vitae nulla consectetur pellentesque. Vivamus tincidunt vel augue vitae posuere. Quisque non odio felis. Etiam fermentum maximus suscipit. Praesent a purus consequat, congue ante sed, mollis est. Suspendisse potenti. Interdum et malesuada fames ac ante ipsum primis in faucibus.nnVestibulum eu bibendum quam. Maecenas mattis nulla ac nisi varius, vitae fermentum sapien tincidunt. Integer porta facilisis lectus eu ornare. Aenean a ullamcorper erat, vitae commodo mi. Pellentesque mattis ipsum sit amet ante pharetra, non dapibus sem maximus. Sed malesuada libero ipsum, eget convallis justo lacinia in. Vivamus eget tincidunt tellus. Aenean nisl diam, blandit quis iaculis quis, sagittis at velit.nnCras hendrerit urna nec laoreet euismod. Duis sit amet pellentesque turpis. Sed elit mauris, cursus quis arcu ut, porttitor feugiat enim. Cras sollicitudin ipsum ac gravida ultricies. Sed vel risus augue. Suspendisse consequat, libero ut ultrices maximus, est erat sagittis nulla, in sollicitudin diam tellus sed est. Vestibulum rhoncus commodo velit, vel feugiat lectus tristique eu. Integer quis dapibus nunc, ut finibus est. Nunc bibendum dolor sit amet velit gravida luctus. Sed egestas finibus tortor id fermentum. Vestibulum in mattis tortor. Donec feugiat mauris eget orci interdum fermentum. Etiam suscipit mi gravida hendrerit viverra. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent nunc turpis, congue quis commodo eget, efficitur sed justo.nnAliquam mattis nunc a rhoncus fringilla. Proin vehicula sit amet nisi ut auctor. Phasellus at euismod tortor. Mauris dignissim tempor odio, porttitor pulvinar lorem. Sed fermentum tempor dictum. Vivamus non metus ex. Aenean malesuada interdum orci vel semper. Sed in viverra orci, sed ullamcorper diam. Morbi ac purus luctus, vehicula arcu nec, vestibulum urna. Duis ullamcorper, ligula iaculis commodo dapibus, tellus quam tempor nulla, in molestie erat neque ac urna. Morbi ut lectus est. Mauris vehicula sit amet velit in mollis. Etiam ullamcorper a diam ornare tincidunt. Aenean sagittis, diam id euismod auctor, erat leo eleifend mi, id semper erat massa at risus. Integer euismod enim sit amet diam pellentesque vestibulum. Quisque vulputate maximus auctor.nnInteger nec finibus ipsum. Duis consectetur neque eu nunc hendrerit pellentesque. Maecenas dignissim ex nec ex rhoncus, at ornare libero porta. Nunc scelerisque ac odio eget pellentesque. Maecenas sollicitudin velit sit amet nunc lobortis hendrerit in quis neque. Etiam fringilla orci dolor, eget aliquet enim aliquet et. Nullam eu mattis tellus. Proin in tristique turpis, et rhoncus odio. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed vitae nibh lorem. Nullam justo nunc, fringilla non libero non, tincidunt maximus felis. Nunc fermentum elit nec libero porttitor sagittis. Duis hendrerit odio id tempus ornare. Suspendisse sem tellus, rhoncus sit amet ante non, varius vestibulum augue. Phasellus sagittis ultricies luctus. In vestibulum leo nec leo lobortis commodo.nnPhasellus porttitor ut sem non cursus. Nam nibh sapien, venenatis ac ex at, pellentesque pulvinar orci. Fusce sollicitudin aliquet semper. Aliquam erat volutpat. Nunc a laoreet quam. Nam quis aliquam massa, sed accumsan arcu. Nulla ac ligula finibus, elementum ligula ut, pretium turpis. Proin vitae scelerisque justo.nnDuis at est libero. Suspendisse convallis eleifend lorem. In convallis enim a vulputate ultricies. Quisque volutpat rutrum justo nec cursus. Nullam pretium, metus non mollis varius, massa ante tempus felis, ac finibus dolor libero id risus. Sed at neque sed tellus lobortis iaculis id in erat. Nunc a est at felis venenatis auctor. Integer tincidunt congue sem eget rutrum. Phasellus quis dictum nunc. Proin auctor, ex sed imperdiet porta, quam neque sodales lacus, ut ultricies leo risus nec libero. Etiam rutrum, ligula vel viverra euismod, mi mauris suscipit diam, in porta neque tellus quis velit. Praesent tristique rhoncus ultrices. Curabitur maximus quis neque vel vestibulum.nnInteger at dolor varius, pellentesque arcu vel, vehicula risus. Aliquam laoreet faucibus elit, eu congue lectus ornare nec. Proin sollicitudin ultrices tortor, eget mattis lectus aliquam ut. Donec est magna, condimentum non elementum at, placerat quis felis. Pellentesque feugiat dui lectus, at fringilla nulla tempus in. In elementum mauris in tristique congue. Nullam est ex, fermentum quis accumsan non, rhoncus vel tellus. Mauris at scelerisque elit. Donec a est sit amet odio pulvinar posuere vel at nisl. Integer sodales, nibh a rhoncus porta, justo massa faucibus ante, a sagittis odio ligula vitae neque. Nullam aliquam blandit finibus. Integer non rhoncus justo, nec varius arcu. Sed pharetra dui venenatis feugiat pharetra. Phasellus at sem eu justo aliquam vehicula eu quis lectus. Nunc maximus, purus nec vulputate pretium, sem felis tristique ligula, ut vestibulum neque nibh quis nisi. Nunc suscipit quis orci sed imperdiet.nnDonec cursus laoreet velit ut lobortis. Duis rhoncus urna at nisi pharetra suscipit. Etiam eu leo quis ante malesuada tincidunt. Duis leo leo, rutrum at sem eu, ultricies gravida massa. Fusce fermentum varius venenatis. Vestibulum nisi purus, aliquet ac porttitor sit amet, molestie eu nibh. Curabitur pulvinar massa massa, ultricies tempor tortor interdum vel. Cras nec auctor libero.nnUt condimentum porttitor mi sit amet consectetur. Sed sed arcu tempor, sodales risus sit amet, semper augue. Etiam porttitor dictum dictum. Phasellus eu eros sit amet ex rutrum vulputate sit amet eget odio. Praesent ut metus lobortis, vulputate elit sed, viverra odio. Donec vitae elit laoreet dui posuere pretium. Nulla imperdiet urna vel scelerisque semper. In hac habitasse platea dictumst. Nunc sit amet egestas tellus. In dapibus auctor justo non egestas. Quisque id facilisis nisl, nec suscipit eros. Donec in ex id massa vulputate vehicula id non enim. In urna ante, egestas porta placerat eget, volutpat ut lorem.nnNunc sodales metus et tortor viverra, eu imperdiet massa interdum. Maecenas ut mi et velit dictum finibus. Nunc eget bibendum neque, at ornare mi. Quisque mollis imperdiet lectus, at finibus enim sagittis ac. Pellentesque porta varius eros, non dignissim velit eleifend vel. Vivamus auctor auctor interdum. Nunc consequat enim at orci pharetra mollis. Suspendisse maximus semper est et rhoncus.nnDuis fringilla congue eros, in aliquam dolor. Duis ante lectus, tincidunt a sapien ullamcorper, suscipit consectetur sem. Fusce imperdiet faucibus magna, eu rhoncus orci fermentum vitae. Ut a mollis tortor. Proin metus est, malesuada et imperdiet sed, placerat sit amet nulla. Nullam in mi odio. Quisque ac rhoncus quam. Pellentesque vel aliquam eros. Suspendisse semper nunc vitae elit bibendum tempor. Fusce feugiat ex erat, eu sagittis quam aliquam ut. Morbi volutpat accumsan mi, sit amet tristique arcu vestibulum a. Proin ullamcorper, risus non posuere congue, est purus luctus dui, ac vestibulum lorem magna in justo. Nulla facilisi.nnSed cursus eget erat ac luctus. Suspendisse sed pharetra velit. Donec non massa dictum, dignissim dolor at, pellentesque lacus. Vestibulum nec libero eu lorem ornare varius vel at lorem. Nulla consectetur consequat orci sed volutpat. Vivamus eleifend aliquam elit, nec porta metus maximus et. Duis et nulla varius, posuere ligula nec, porttitor ipsum. In rutrum eu eros id mattis. Duis et lacinia tellus. Duis tincidunt maximus nibh, tincidunt convallis velit mattis at. Donec id lorem risus. Mauris blandit varius metus, sed bibendum metus convallis vitae.nnIn vel nibh tempor turpis mollis venenatis. Curabitur id gravida justo. Vestibulum ac libero fringilla, ultrices leo vel, pharetra metus. Phasellus vulputate tincidunt augue, sit amet malesuada arcu finibus ut. Donec ut porttitor diam. Nullam odio mauris, tincidunt sit amet cursus ut, venenatis eget purus. Etiam ultricies dui sed dui luctus, sit amet congue nibh fermentum. Proin sit amet mauris ac eros sollicitudin lobortis. Cras sed pharetra erat. Pellentesque eget egestas ligula. Maecenas mi nulla, scelerisque ac cursus eu, rhoncus eu turpis. Pellentesque at gravida odio, sit amet lobortis mauris. Nullam id erat sapien.nnPhasellus a est sit amet metus lacinia accumsan. Etiam semper congue augue, et lacinia neque venenatis in. Praesent laoreet lectus a augue accumsan hendrerit. Phasellus viverra ornare fringilla. Praesent porta, lectus vitae ornare scelerisque, arcu urna fermentum arcu, in eleifend enim dui vitae lacus. Nullam gravida iaculis elementum. Nulla in ex non orci vehicula tempor quis a lorem. Suspendisse blandit aliquam gravida. Vivamus auctor fringilla luctus. Mauris in viverra mauris, nec cursus nisi. Etiam fermentum luctus facilisis. Sed felis arcu, dapibus a enim eu, cursus aliquam leo. Praesent convallis porttitor nunc. Donec vel lacus dignissim, facilisis neque at, commodo purus. Phasellus sodales iaculis mauris laoreet hendrerit. Aenean ultricies, risus nec ultrices ultricies, velit metus vulputate nibh, et laoreet justo mauris ac metus.nnNam auctor tortor nec euismod porta. Sed tincidunt ultricies sapien vel rhoncus. Sed ut congue neque. Nam varius nulla et orci aliquam viverra. Nullam rutrum suscipit mauris vel ultricies. Etiam nec erat a elit varius semper et non sem. Cras dapibus semper iaculis. Morbi leo magna, scelerisque condimentum felis a, maximus hendrerit elit. Quisque consectetur magna eros, a rhoncus augue blandit quis.nnPraesent commodo velit bibendum tempor porta. Nulla ultrices magna laoreet orci gravida porttitor. Donec non suscipit est. Curabitur dictum id tortor ultricies elementum. Quisque diam turpis, aliquet nec tempus quis, pulvinar vel nisi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam ultrices maximus dapibus. Suspendisse vestibulum semper est vel interdum. Nam felis lorem, volutpat et feugiat luctus, scelerisque sed enim. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer at laoreet mauris, eget tincidunt lectus. Pellentesque imperdiet in nisl eu hendrerit. Ut vitae elit pharetra, pretium urna eu, aliquam felis. In et interdum leo, id feugiat ligula. Sed ac mi rutrum, mattis massa quis, ornare libero. Quisque ipsum orci, dignissim sed tempor sed, convallis a tortor.nnProin gravida sem libero, a egestas metus laoreet eu. Proin et mi mollis diam elementum vulputate sed id diam. Suspendisse et varius tortor. Donec molestie pulvinar nibh. Nulla et erat urna. Proin ultricies arcu leo, eget accumsan sem aliquam ac. Ut eu ultrices erat. Ut mi urna, mollis quis luctus non, congue in purus. Pellentesque sit amet orci vel purus tincidunt consectetur. Fusce non massa tincidunt, interdum augue malesuada, condimentum mi.nnDonec varius congue enim, vitae auctor eros mollis ut. Aenean tempor rhoncus imperdiet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nullam commodo faucibus lacus, tincidunt tincidunt sem posuere a. Quisque congue pretium metus, quis rutrum turpis suscipit ac. Donec pharetra purus augue, id ullamcorper nulla tristique sed. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;nnNullam gravida, neque condimentum dictum bibendum, velit urna consequat nisl, sit amet eleifend erat leo ut diam. Ut mollis tincidunt diam. Nullam ut facilisis libero. Aenean vitae ultricies odio. Nullam posuere sodales feugiat. Ut auctor quis mauris sit amet aliquam. Integer magna nisi, ullamcorper id nisi quis, rhoncus ultricies nisi. Donec quis facilisis ligula.nnAliquam ultricies, enim sit amet tincidunt eleifend, mauris eros malesuada turpis, vitae ornare dui dui ac ligula. Phasellus placerat, augue eu commodo semper, lorem odio porta elit, vel congue mauris metus sed lorem. Phasellus ac augue ante. Ut sagittis sodales dolor a cursus. Pellentesque vehicula, eros fermentum venenatis vulputate, neque orci porta sapien, sit amet mollis nisi lectus eu felis. Proin vehicula ut mi vel vulputate. Ut at metus pharetra, consequat tortor nec, venenatis lacus.nnMaecenas diam quam, convallis vulputate vulputate dapibus, imperdiet id purus. Aliquam suscipit, ipsum eget fringilla tincidunt, justo orci dictum magna, nec suscipit lorem libero elementum ipsum. Etiam placerat, odio in aliquam posuere, sem libero porta odio, at mattis libero purus sit amet augue. Nam commodo quam sed justo aliquam, sit amet lacinia lorem hendrerit. Proin at leo aliquet, tristique arcu sed, consectetur quam. Quisque neque sem, semper nec purus eu, egestas efficitur leo. Phasellus in elementum elit. Pellentesque bibendum vel arcu et volutpat. Aliquam aliquet interdum egestas. Cras ultrices, est lacinia efficitur ullamcorper, turpis lectus convallis odio, ac ultrices urna urna eget odio.nnAenean lobortis congue bibendum. Vestibulum tempor sapien sed nunc suscipit posuere. Mauris sed dui ligula. In a porttitor mauris. Praesent pellentesque pulvinar purus, fermentum condimentum sapien posuere vitae. Aliquam et tortor ultricies augue facilisis commodo ut a ipsum. Mauris bibendum mauris quis sem tincidunt, id laoreet nisl luctus. Fusce in ornare nisl, ut malesuada velit. Integer malesuada nulla vel diam laoreet laoreet non at elit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed vitae leo quis odio dapibus vestibulum. Donec nec libero diam.nnAenean fringilla quam id nisi consectetur porta. Fusce nec aliquam mi. Duis vitae sapien iaculis, commodo felis at, vulputate tortor. Fusce pellentesque velit nec neque cursus, eu scelerisque lorem mollis. Nam facilisis risus ut molestie bibendum. Etiam scelerisque elit quis velit ornare venenatis. Pellentesque lacus lacus, pretium vitae tincidunt nec, euismod at dui. Cras finibus tortor vel augue consectetur, vitae pulvinar sapien luctus. Donec pretium erat et nibh vehicula faucibus.nnInterdum et malesuada fames ac ante ipsum primis in faucibus. Proin malesuada mi nec urna maximus viverra. Quisque mattis dapibus sem sit amet blandit. Curabitur sagittis vitae augue eu fermentum. Etiam id congue eros, ultrices viverra libero. Sed dictum feugiat dui, vitae tempor libero semper in. Suspendisse nec pretium felis. Etiam elementum risus mauris, nec sollicitudin est egestas eu. In luctus dapibus luctus. Donec id accumsan ipsum, nec hendrerit tortor. In ac quam quis diam venenatis varius.nnMorbi id finibus mauris. Phasellus tempus tristique tristique. Quisque consectetur erat lectus, eget tincidunt lacus gravida sed. Donec mauris purus, imperdiet sed volutpat ac, euismod at tortor. Nunc in egestas purus, quis tincidunt risus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla nunc arcu, lobortis id mi eget, faucibus finibus ex. Aenean condimentum, ligula at rutrum auctor, erat purus commodo turpis, ut feugiat nisi orci sed arcu. Aenean at augue nibh. Suspendisse nibh mauris, iaculis vel justo vitae, commodo egestas risus. Fusce pulvinar odio erat, at malesuada massa varius vitae. Maecenas sit amet lorem ex. Maecenas sagittis justo sit amet molestie rutrum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;nnMauris orci diam, laoreet ut arcu ut, egestas rhoncus enim. Nam sapien justo, porta quis dui in, rutrum cursus dui. Nulla et justo imperdiet risus blandit aliquet. Sed ullamcorper accumsan ex eu venenatis. Sed aliquam non libero a pharetra. Proin vitae porta ex. Morbi non ipsum tristique, dignissim felis vel, maximus odio. Nulla vitae lorem quis tellus fermentum dignissim. Donec et neque viverra, eleifend sapien ut, malesuada felis. Maecenas eget velit sed magna finibus laoreet sit amet eu lacus.nnMauris posuere dolor non tincidunt cursus. Integer orci risus, iaculis nec sem a, pellentesque bibendum ex. Pellentesque libero sapien, dapibus ac maximus ac, ullamcorper et erat. Curabitur luctus malesuada diam, vitae sodales nunc. Nullam quis eros vel est auctor posuere. Aenean laoreet, dui eget porta commodo, sapien neque gravida elit, ut luctus nisl eros id lacus. Etiam egestas odio purus, quis efficitur lectus tincidunt vitae. Etiam sed libero vitae urna egestas pellentesque et a elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis faucibus nibh a odio venenatis mattis. Curabitur porta neque vitae neque viverra, sit amet accumsan erat consequat. Mauris dictum varius diam. Proin consectetur venenatis ultricies.nnDuis arcu leo, pulvinar eu viverra sit amet, molestie vitae purus. In orci ante, dictum nec ultricies sit amet, dapibus quis ante. Sed consequat bibendum mollis. Cras tincidunt odio at felis ultrices posuere. Nulla urna mi, convallis eu consectetur at, condimentum sed nulla. Fusce malesuada enim id pretium pharetra. Integer tincidunt porttitor dui, ut facilisis eros vehicula vitae.nnPellentesque ultrices, turpis sed vestibulum rhoncus, elit est tristique est, eu maximus orci erat tempor mi. Proin quam orci, interdum in mollis eu, consectetur vitae felis. Nulla lorem magna, aliquam ac pulvinar a, luctus at sapien. Duis rutrum varius dui vel tempor. Aenean vulputate nunc at diam lobortis, at placerat lectus tristique. Donec et porta nibh, in feugiat eros. Nulla facilisi.nnNam fringilla libero laoreet velit rutrum euismod. Cras finibus ex eu elit ultrices auctor. Maecenas vulputate vestibulum blandit. Pellentesque vitae blandit quam, non ultricies enim. Etiam diam mauris, sollicitudin molestie placerat facilisis, rhoncus in mi. Duis id vulputate elit, a rutrum lectus. Suspendisse egestas neque nulla, quis ullamcorper mauris posuere eu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque a feugiat erat.nnNam eu dapibus nibh. Vivamus semper ligula mauris. Nam porttitor, est a tempor tincidunt, diam mi suscipit nisl, sit amet ultricies lacus nunc ac justo. Nam quis justo risus. Nam nec lacus vitae velit blandit iaculis et suscipit velit. Duis at cursus quam. Nunc at mi sit amet nibh bibendum faucibus. Suspendisse potenti. In congue luctus lacinia. Suspendisse vehicula leo et pretium lacinia. Nunc ex sapien, commodo a ligula at, lacinia pellentesque urna.nnFusce porttitor consequat efficitur. Morbi volutpat massa nibh, et dignissim orci ornare ac. Duis quis purus faucibus, vehicula risus vel, molestie mi. Sed aliquet nulla eu porta commodo. In neque sem, eleifend non dolor ac, egestas interdum risus. Duis nec placerat nisi, quis egestas turpis. Nullam sit amet diam viverra, viverra ex sed, viverra enim.nnVestibulum interdum velit ut egestas posuere. Cras et lectus eu nisl efficitur egestas. Morbi dapibus, odio accumsan fermentum maximus, neque nulla mattis sapien, vitae congue leo orci ut tellus. Curabitur id accumsan felis. Donec id lacus ut massa tincidunt tempor ut quis ex. Sed condimentum tempus massa nec interdum. Praesent et tellus a lorem consequat elementum. Curabitur a placerat ligula. Sed commodo laoreet sem, ac bibendum nulla convallis iaculis. Vestibulum eu mauris pellentesque, pellentesque libero commodo, dapibus est. Vestibulum in neque ante.nnVestibulum ultrices facilisis orci, ac consequat ex ullamcorper ut. Vivamus quam augue, suscipit sed fringilla hendrerit, dictum eget ante. Vestibulum lobortis eros eget urna convallis consequat. Pellentesque lacus leo, varius in lorem id, maximus dictum neque. Nam aliquet, mi sit amet porta venenatis, dui felis suscipit nunc, eu fermentum nisi mauris et magna. Proin sed est ac odio rutrum dictum vel sed enim. Vestibulum est odio, rutrum ac lorem at, posuere maximus nunc. Curabitur laoreet, eros eu hendrerit blandit, sem ante sagittis odio, vehicula laoreet ipsum quam vel risus.nnAenean placerat massa vitae dolor vestibulum porta. Duis malesuada, turpis eget egestas fringilla, ipsum diam scelerisque arcu, pharetra auctor quam risus quis elit. Maecenas at leo nisi. Maecenas egestas rhoncus magna, vitae finibus purus viverra vitae. Etiam quam est, lobortis quis diam auctor, iaculis lobortis sapien. In urna libero, euismod et varius et, molestie eget massa. Phasellus ultrices pretium leo mollis ultricies.nnIn tincidunt massa ex, ut sodales est vehicula a. Proin varius volutpat dui. Vivamus et pellentesque nunc. Phasellus luctus velit imperdiet sapien tincidunt, non ultrices turpis porttitor. Sed suscipit ipsum at odio sollicitudin, dignissim auctor est cursus. Nulla ornare molestie felis id ornare. Suspendisse gravida enim nec diam tristique, ac condimentum dui rhoncus. Aliquam vitae venenatis ante.nnUt luctus tincidunt feugiat. In condimentum dui ac ullamcorper tincidunt. Suspendisse potenti. Maecenas non lorem auctor, facilisis dui eget, eleifend metus. Ut sagittis ac felis nec consequat. Praesent nec tristique massa, id aliquam nulla. Integer molestie eleifend egestas.nnMorbi lacinia suscipit molestie. Nulla tincidunt enim at lorem imperdiet, vel rutrum nibh tempor. Aenean sodales eget augue at posuere. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Maecenas varius justo at nibh dictum blandit. Sed id diam porttitor, maximus metus finibus, laoreet dui. Donec laoreet magna orci, a venenatis magna consequat vel. In maximus vulputate erat id vulputate. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;nnNullam gravida interdum ipsum, eget iaculis tortor venenatis vel. Etiam tempor felis sit amet risus porttitor, vitae eleifend ligula consectetur. Nam dictum interdum purus quis tincidunt. Donec eu ipsum ac magna suscipit tristique. Cras vitae diam porta risus fringilla tempus euismod tincidunt justo. Pellentesque tempus nibh a lectus semper mattis. Quisque et sapien non enim varius rhoncus quis nec neque. Etiam egestas nec nulla id venenatis. Morbi fermentum hendrerit elementum. Pellentesque purus diam, vestibulum eget volutpat a, ullamcorper et nulla. Duis porttitor turpis enim, id elementum velit sagittis non. Donec non mi tempus, cursus metus eu, venenatis turpis. Duis porttitor, lorem vehicula elementum efficitur, lectus elit tincidunt est, sed accumsan arcu est quis elit. Proin sit amet imperdiet erat, id ultricies nulla. Donec mauris nisl, blandit nec commodo non, sodales ut ligula.nnDuis ac massa in libero euismod auctor. Cras porta leo id faucibus dictum. Curabitur et pellentesque nisi. Donec ac vehicula dui. Mauris fringilla dolor id porta dignissim. Donec tempor dolor nisl, et sagittis lectus feugiat et. Pellentesque finibus nisi quis tellus semper pulvinar. Phasellus dapibus ligula imperdiet ipsum aliquet euismod. In a mi vel leo iaculis efficitur ut vitae tellus. In vel est sit amet eros porta porta. Aliquam a convallis nibh, non vulputate nisl. Suspendisse sagittis mattis enim, et scelerisque odio mollis nec. Praesent volutpat felis eu quam condimentum, lacinia scelerisque nulla luctus. Integer mollis lorem ac imperdiet porttitor. Integer semper, leo pellentesque suscipit sagittis, ligula tellus bibendum velit, id condimentum enim mi id lorem. Donec efficitur lacinia leo id sodales.nnDuis non cursus nisl, at dapibus nisl. Duis at ipsum dignissim, efficitur lectus et, sagittis turpis. Donec at lectus mauris. Pellentesque sed eleifend elit, sit amet varius diam. Praesent tempor lorem eget nulla egestas sagittis. Cras cursus, dui quis vulputate vulputate, sem est imperdiet lacus, sed elementum dui eros vitae ex. Etiam porta scelerisque leo non laoreet. Aliquam accumsan elit nec dolor dapibus, ut mattis diam condimentum. Fusce pellentesque in purus a accumsan. Pellentesque id metus a ex sodales eleifend ac eu tortor. In tincidunt, leo ut condimentum porta, sapien mauris dignissim nibh, molestie mollis tellus justo non libero. Maecenas id neque ut est varius consequat eu at nunc. Mauris vehicula tincidunt tellus, nec pulvinar massa congue nec. Nullam eu porttitor lectus. Phasellus tincidunt neque eu dignissim ullamcorper.nnDuis eget ante eleifend, auctor magna a, semper quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed ornare nec dui bibendum tincidunt. Nunc ac venenatis lorem. Fusce dapibus lorem at imperdiet volutpat. Nam accumsan sodales neque, id pellentesque arcu commodo quis. Nulla a augue dui. Mauris ex lorem, tempus et cursus et, efficitur non urna. In at dolor tellus. Nullam pulvinar condimentum tortor vel semper. Pellentesque malesuada pretium nunc, sit amet pretium tellus tincidunt sit amet. Sed eu hendrerit magna. Cras a dui lacus. Vivamus id mi nulla. Cras ut auctor justo. Pellentesque et aliquam massa.nnCurabitur ac porta est, sit amet ullamcorper arcu. Nunc et posuere dui. Cras suscipit orci non lectus dictum varius. Sed malesuada tempor fermentum. Curabitur gravida imperdiet tortor a hendrerit. Etiam molestie tristique neque, vitae fermentum turpis cursus quis. Maecenas a vulputate metus. Aliquam erat volutpat. Quisque non consectetur tortor, sit amet rutrum urna. Suspendisse lacinia mauris leo, scelerisque dictum magna tristique id. Pellentesque lobortis, magna varius venenatis faucibus, enim orci finibus magna, eu feugiat ipsum lectus sed massa. Morbi sodales urna nec vehicula aliquet.nnVestibulum enim tortor, tristique id semper sit amet, aliquet sed turpis. Integer urna elit, maximus in felis non, dignissim molestie sem. Donec iaculis sit amet nibh vitae porta. Praesent volutpat euismod auctor. Donec vitae pulvinar odio. Nulla enim velit, pretium nec tempor in, euismod ut ligula. Phasellus laoreet velit ac ex faucibus dapibus.nnSuspendisse eget quam vitae enim lacinia auctor ut non lacus. Sed tempor egestas ex non volutpat. Nullam lectus purus, auctor nec eleifend at, iaculis ut tellus. Sed ac mattis nisl. Suspendisse id vulputate magna. Vivamus metus odio, placerat interdum malesuada non, lobortis ut ante. Sed nunc sapien, molestie quis nulla in, feugiat molestie magna. Curabitur tincidunt arcu iaculis, volutpat mauris eget, dapibus dui. Ut tempor tellus id est rhoncus blandit. Aenean blandit risus in aliquam imperdiet. Curabitur mollis nisi non risus porta, id suscipit nisi efficitur. Cras suscipit eros id lectus pulvinar sagittis.nnPraesent luctus egestas faucibus. Fusce sed lobortis mi. Donec in ex a augue posuere iaculis. Proin pretium justo sed erat pharetra, maximus rutrum felis faucibus. Mauris ac urna at tellus imperdiet consequat. Nulla felis arcu, malesuada ultrices leo eu, tincidunt ultricies ante. Morbi eget dapibus velit. Proin dapibus aliquet ante eu fringilla. Vivamus id massa dolor.nnVivamus tristique tellus ex, tincidunt pulvinar sapien ultricies vitae. Nam in nunc in massa malesuada cursus. Sed porta, est sit amet blandit dapibus, purus sapien placerat elit, sit amet blandit nibh dui eget ligula. Duis tempus eros et ante placerat, in aliquam dui posuere. Praesent sollicitudin libero diam, ac egestas sem auctor nec. Nam egestas condimentum leo vitae fringilla. Suspendisse potenti. Proin fermentum sodales odio eget mattis. Maecenas rhoncus eros nec augue dapibus viverra. Ut in velit massa. Quisque sollicitudin convallis velit, eget eleifend sem lacinia vel. Suspendisse rhoncus odio tortor.nnMorbi quis finibus mauris, sit amet dignissim ante. Sed fringilla dolor nibh, vitae elementum magna tristique sed. Praesent scelerisque, lacus sed scelerisque interdum, sapien mi molestie lorem, sed lacinia augue ante id enim. Aenean elementum eu ligula eu faucibus. Cras pellentesque nisi ligula, non faucibus lacus suscipit non. Mauris nec viverra tellus, vitae posuere velit. Suspendisse potenti. Maecenas pretium efficitur vehicula. Donec vulputate auctor ipsum quis euismod. Donec egestas vestibulum finibus. Aliquam tempor arcu pharetra lacus varius efficitur. Nullam eget vestibulum elit, interdum semper dolor. Donec vitae auctor mauris. Cras eleifend tristique orci. Sed dignissim sapien ligula, at sodales massa suscipit vitae.nnSuspendisse et condimentum massa. Nulla mattis vestibulum arcu nec tristique. Vivamus vehicula bibendum sagittis. Etiam varius sollicitudin consectetur. Maecenas vel nulla eu libero blandit placerat. Mauris malesuada, tellus a lobortis ultricies, dui lorem convallis ligula, dignissim pretium diam justo nec urna. Donec euismod, mauris at dignissim interdum, ex arcu tristique nisi, non venenatis eros massa ut elit.nnVivamus tincidunt nulla ac lectus feugiat, in consequat nibh ultricies. Aenean quis metus in lorem tristique viverra sed id urna. Nullam convallis enim vitae tincidunt sagittis. Praesent dui odio, volutpat a libero ut, interdum blandit quam. Sed vel nisi cursus, sollicitudin neque et, luctus nisi. Nam consectetur ultricies placerat. Ut faucibus ipsum nec erat mollis vulputate. Donec magna diam, ultrices non enim vel, malesuada commodo elit. Fusce sed posuere velit. Duis nisl erat, fringilla ut turpis in, fermentum efficitur magna. Suspendisse purus quam, pharetra at arcu nec, aliquam euismod nulla.nnNulla at ullamcorper orci. Aliquam porta lacinia metus, vel dictum libero faucibus quis. Nulla tempus vulputate erat non lobortis. Vivamus at justo justo. Pellentesque ex purus, faucibus quis lacinia ut, ornare congue purus. Aliquam vel urna sed dolor bibendum elementum. Pellentesque venenatis accumsan tellus, sed pretium metus pharetra vitae.nnFusce accumsan malesuada ex et venenatis. Aliquam dapibus neque neque, vitae vehicula erat pellentesque ornare. Nunc condimentum dolor a mollis mollis. Nulla facilisi. Suspendisse varius nunc vel accumsan sodales. Fusce quis massa vitae mi tempor porta eget sed turpis. Praesent eu elit facilisis nunc maximus placerat eu at nunc. In consectetur leo aliquet sapien finibus aliquam. Morbi venenatis odio ut orci interdum, tristique laoreet justo tempor.nnDuis porttitor non arcu eleifend lacinia. In hac habitasse platea dictumst. Integer eget erat ligula. In lacus felis, consequat sed neque ac, fermentum tempus dolor. Fusce vel sapien ultricies, volutpat metus vel, finibus odio. Nullam sed aliquam ante, quis sollicitudin turpis. Aenean lectus enim, congue eget egestas ut, ultrices in massa.nnNulla placerat sollicitudin nulla vel egestas. Aenean laoreet congue diam, nec congue felis sagittis nec. Etiam malesuada enim turpis. Duis eget posuere magna. Fusce lobortis fringilla justo eu interdum. Vivamus urna sem, iaculis nec massa quis, dignissim ullamcorper urna. Nullam enim diam, facilisis quis quam a, iaculis congue tortor. Morbi metus enim, sodales sit amet mi ut, ullamcorper commodo odio. Donec sodales non justo sed luctus. Aliquam non hendrerit turpis. Donec quis est a enim volutpat iaculis. Etiam imperdiet dolor ipsum, id iaculis leo varius ac. Nam erat felis, iaculis vitae semper luctus, ultricies a nisl. Cras rhoncus mattis erat sollicitudin suscipit. Pellentesque imperdiet nulla lectus, eget mattis magna ultricies et. Quisque in sem convallis, aliquet sem ac, dapibus sapien.nnAenean euismod porta ante, sit amet semper ex ornare quis. Donec non placerat lorem. In hac habitasse platea dictumst. Etiam blandit placerat porttitor. Sed vitae dictum risus. Donec quis felis nec urna dictum mollis ac eu enim. Integer fermentum tellus et tincidunt luctus. Nullam vel neque sodales, lobortis massa vitae, dictum sapien. Phasellus molestie nunc sit amet faucibus ullamcorper. Interdum et malesuada fames ac ante ipsum primis in faucibus.nnIn hac habitasse platea dictumst. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed molestie nunc arcu, a dignissim purus tempor in. Proin ultrices non nisl ut lobortis. Ut eleifend eros ac risus mollis venenatis. Maecenas viverra elementum nulla nec auctor. Proin in sem lorem. Cras risus neque, pellentesque eget eros sed, scelerisque fermentum metus. Mauris pellentesque ligula quis felis ullamcorper, sed elementum velit fringilla.nnFusce accumsan, velit sit amet ultrices rhoncus, nisi dolor semper enim, ac consectetur tortor ligula ut ante. Ut consectetur mattis urna vel efficitur. Aenean eu bibendum odio, a faucibus eros. Vestibulum vel elit metus. Vivamus condimentum congue turpis quis finibus. Quisque malesuada ligula a lacus pulvinar, nec vehicula arcu sagittis. Duis quam augue, facilisis at odio et, interdum egestas tellus. Cras a imperdiet orci. Nullam ut velit posuere, porta felis sit amet, rutrum libero. Etiam varius porta pellentesque. Aenean finibus rutrum lacus, id scelerisque nulla. Quisque dui nisl, finibus tincidunt lorem pretium, bibendum vehicula lacus. Mauris mollis turpis lectus, ac viverra nunc pulvinar nec. Vestibulum porta quam eget pretium consectetur. Cras blandit gravida dui.nnPhasellus et venenatis ipsum. Etiam ac tempus urna. Morbi at mi sit amet arcu malesuada mattis ornare aliquam sem. Aliquam erat volutpat. Maecenas pulvinar, dolor sit amet cursus porttitor, lacus ipsum euismod diam, ut fermentum purus quam eu leo. Fusce gravida nisi vitae tempor volutpat. Morbi vel justo et nunc viverra finibus eget eu libero. Nunc in iaculis arcu. Suspendisse aliquam semper dui nec scelerisque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.nnPhasellus et sem ut nibh vulputate volutpat a ut nulla. Pellentesque blandit lectus quis quam viverra, at faucibus sem facilisis. Pellentesque augue elit, molestie quis hendrerit sit amet, interdum at nunc. Mauris at mauris quis arcu fringilla convallis. Fusce aliquet luctus luctus. Maecenas gravida ex id augue pretium vestibulum. Integer sed urna nisi. Vestibulum ultricies nunc ac lorem blandit consectetur sed et arcu. Duis diam massa, lacinia ut mattis sed, molestie eget dolor. Proin consequat elit in placerat mattis. Aliquam magna nibh, imperdiet ut massa ut, faucibus malesuada neque. Phasellus commodo lacus sit amet auctor finibus.nnUt aliquet dui quis quam accumsan condimentum. Curabitur porttitor rutrum rutrum. Suspendisse sed ipsum et est venenatis accumsan. Nulla condimentum fringilla metus eget lobortis. Curabitur quis sem ornare dolor finibus ultricies quis sit amet ante. Ut vitae lectus vitae ante ullamcorper fermentum. Cras euismod in quam in pretium. Quisque felis leo, auctor non fringilla vel, molestie at neque. Morbi suscipit mauris at mauris gravida convallis. In hac habitasse platea dictumst. Cras dui justo, viverra et pretium a, porta ac augue. Praesent et viverra dui.nnNullam in finibus orci. In lobortis sem id dolor sagittis dapibus. Nam porttitor efficitur rhoncus. Nunc commodo arcu vitae tristique sollicitudin. Donec sit amet ante imperdiet, eleifend metus vel, malesuada dolor. Nullam posuere euismod arcu vel accumsan. Vestibulum in est non purus gravida aliquam in vel nulla. Fusce dolor dolor, fringilla id arcu vel, commodo vulputate tortor. Proin tempor ante quis velit iaculis, vitae tristique nibh porttitor. Proin diam purus, semper sed arcu venenatis, elementum tincidunt ipsum. Duis vestibulum est a nisl maximus dictum. Quisque at quam a felis pharetra feugiat. Curabitur est odio, aliquet ac lorem ut, feugiat placerat purus. Vivamus cursus porta lectus, eu hendrerit lectus dignissim id.nnPraesent iaculis id leo sed lobortis. Pellentesque posuere euismod lorem. Donec sed tortor ligula. Cras ut ipsum convallis, placerat tortor mollis, congue est. Mauris sollicitudin sed arcu egestas bibendum. Curabitur elementum lectus quis metus mattis auctor vitae et lectus. Ut ac rhoncus velit, maximus maximus massa. Ut id neque volutpat, finibus dolor id, dignissim lectus. Sed sit amet facilisis eros, sed volutpat quam.nnIn non bibendum tellus, vel pulvinar purus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In hac habitasse platea dictumst. Pellentesque consequat orci vitae accumsan vulputate. Suspendisse interdum orci eros, nec rutrum massa imperdiet non. Nunc ornare massa non auctor fringilla. Nulla erat erat, aliquet nec ornare eget, commodo a enim. Morbi pellentesque pharetra nibh bibendum auctor. Donec varius leo cursus, facilisis ipsum eu, euismod nisl. Pellentesque ut vehicula mi, non ullamcorper libero. Aenean vel vulputate nibh, quis cursus lorem.nnNullam vel velit in tellus tincidunt gravida ut nec dui. Sed elementum nisi id leo sodales imperdiet. Donec id tempor quam. Mauris id arcu lacinia, condimentum augue et, malesuada mi. Phasellus vel ornare dolor. Fusce vulputate interdum dolor, quis ultrices est commodo ac. Curabitur egestas, neque ut porttitor tincidunt, eros elit commodo purus, eu tristique quam elit in orci. Suspendisse in urna leo. Aliquam sit amet nunc orci. Proin magna nisi, iaculis euismod lacus sed, rhoncus dapibus turpis.nnNulla nec nisl gravida, faucibus quam eu, aliquet tortor. Morbi elementum, augue eu porttitor lacinia, dolor nisi convallis lacus, et placerat orci odio in enim. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Integer congue dolor vel maximus auctor. Vivamus vitae ultricies libero. Pellentesque et lobortis ligula. Duis iaculis sed enim at ultricies. Nullam lacinia finibus fermentum. Duis varius tortor ullamcorper massa finibus pretium. Integer rutrum luctus eros vitae commodo. Vestibulum a egestas elit, ac cursus purus.nnQuisque ac finibus nibh, eu tincidunt enim. Curabitur aliquam enim in metus sagittis, id tristique dui dignissim. Integer tellus velit, hendrerit at vestibulum eget, lobortis sit amet dolor. Nulla semper diam non sapien mollis, in pretium dolor varius. Pellentesque malesuada, ex non posuere efficitur, ex est tempus metus, et dignissim ex augue vel quam. Etiam posuere, augue sed aliquam tempor, lectus urna pulvinar nisl, sed fringilla augue dolor non est. Suspendisse nec orci vitae dui ornare rutrum ut ut nibh. Vivamus justo velit, semper vitae dictum vitae, condimentum ac eros. Sed lobortis vitae orci bibendum tincidunt. Donec et orci pulvinar, scelerisque quam vitae, interdum purus. Nulla dapibus euismod imperdiet. Nullam eget est mi. Maecenas vitae ipsum eget turpis lobortis ullamcorper non fermentum purus. Pellentesque dapibus bibendum bibendum. Nulla non luctus turpis.nnSed ornare urna et mauris semper euismod. Nullam ut odio at mauris feugiat finibus ut vitae lacus. Praesent quis euismod nisi. Curabitur odio justo, dictum a hendrerit et, laoreet in erat. Sed in nisi id lacus consectetur fermentum eget pellentesque est. Mauris a justo nisi. Nulla ac urna sapien. Pellentesque ultrices turpis eget efficitur ornare. Maecenas congue venenatis elit, quis imperdiet nisi placerat vel nullam." 7 | } 8 | }] 9 | --------------------------------------------------------------------------------