Skip to content

Commit 48fc265

Browse files
committed
First commit
0 parents  commit 48fc265

15 files changed

+389
-0
lines changed

.gitignore

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
*.py?
2+
*~
3+
*.sw?
4+
\#*#
5+
/secrets.py
6+
.DS_Store
7+
._*
8+
*.egg-info
9+
/MANIFEST
10+
/_build
11+
/build
12+
dist
13+
tests/test.zip
14+
/docs/_build
15+
/.eggs
16+
.coverage
17+
htmlcov
18+
venv
19+
.pytest_cache
20+
.tox

.travis.yml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
sudo: false
2+
cache: pip
3+
language: python
4+
python:
5+
- "3.5"
6+
- "3.6"
7+
env:
8+
- DJANGO="Django>=1.11,<2.0"
9+
- DJANGO="Django>=2.0,<2.1"
10+
install:
11+
- nvm install 7
12+
- pip install -U pip wheel flake8 isort unify
13+
- pip install $DJANGO -e .[tests]
14+
- pip freeze
15+
script:
16+
- pytest
17+
- flake8
18+
- isort --check-only --diff --recursive feincms3_sites tests
19+
- unify --check-only --recursive --quote \' feincms3_sites tests

CHANGELOG.rst

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
==========
2+
Change log
3+
==========
4+
5+
`Next version`_
6+
~~~~~~~~~~~~~~~
7+
8+
`0.0`_ (2018-04-12)
9+
~~~~~~~~~~~~~~~~~~~
10+
11+
- Initial release!
12+
13+
14+
.. _1.0: https://github.com/matthiask/feincms3-sites/commit/e50451b5661
15+
.. _1.1: https://github.com/matthiask/feincms3-sites/compare/1.0...1.1
16+
.. _1.2: https://github.com/matthiask/feincms3-sites/compare/1.1...1.2
17+
.. _Next version: https://github.com/matthiask/feincms3-sites/compare/1.3...master

LICENSE

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2018, Feinheit AG and individual contributors.
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification,
5+
are permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice,
8+
this list of conditions and the following disclaimer.
9+
10+
2. Redistributions in binary form must reproduce the above copyright
11+
notice, this list of conditions and the following disclaimer in the
12+
documentation and/or other materials provided with the distribution.
13+
14+
3. Neither the name of Feinheit AG nor the names of its contributors
15+
may be used to endorse or promote products derived from this software
16+
without specific prior written permission.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

MANIFEST.in

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
include LICENSE
2+
include MANIFEST.in
3+
include README.rst
4+
recursive-include feincms3_sites/static *
5+
recursive-include feincms3_sites/locale *
6+
recursive-include feincms3_sites/templates *

README.rst

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
==============
2+
feincms3-sites
3+
==============
4+
5+
.. image:: https://travis-ci.org/matthiask/feincms3-sites.svg?branch=master
6+
:target: https://travis-ci.org/matthiask/feincms3-sites
7+
8+
Multisite support for `feincms3 <https://feincms3.readthedocs.io>`_.

feincms3_sites/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
VERSION = (0, 0, 0)
2+
__version__ = '.'.join(map(str, VERSION))

feincms3_sites/middleware.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from feincms3.apps import (
2+
AppsMiddleware as BaseMiddleware, AppsMixin, apps_urlconf,
3+
)
4+
from feincms3.utils import concrete_model
5+
6+
from .models import Site
7+
8+
9+
class AppsMiddleware(BaseMiddleware):
10+
def __init__(self, get_response):
11+
self.get_response = get_response
12+
13+
def __call__(self, request):
14+
request.site = Site.objects.for_host(request.get_host())
15+
page_model = concrete_model(AppsMixin)
16+
fields = (
17+
'path', 'application', 'app_instance_namespace', 'language_code',
18+
)
19+
apps = page_model.objects.active().filter(site=request.site).exclude(
20+
app_instance_namespace=''
21+
).values_list(*fields).order_by(*fields)
22+
request.urlconf = apps_urlconf(apps)
23+
return self.get_response(request)

feincms3_sites/models.py

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import re
2+
3+
from django.core.exceptions import ValidationError
4+
from django.core.validators import RegexValidator
5+
from django.db import models
6+
from django.db.models import Q
7+
from django.utils.translation import ugettext_lazy as _
8+
9+
from feincms3.pages import AbstractPage as BasePage
10+
11+
12+
class SiteQuerySet(models.QuerySet):
13+
def default(self):
14+
return self.filter(is_default=True).first()
15+
16+
def for_host(self, host):
17+
default = None
18+
for site in self.order_by('-is_default'):
19+
if re.match(site.host_re, host):
20+
return site
21+
elif site.is_default:
22+
default = site
23+
return default
24+
25+
26+
class Site(models.Model):
27+
is_default = models.BooleanField(
28+
_('is default'),
29+
default=False,
30+
)
31+
host = models.CharField(
32+
_('host'),
33+
max_length=200,
34+
)
35+
host_re = models.CharField(
36+
_('host regular expression'),
37+
max_length=200,
38+
blank=True,
39+
)
40+
41+
objects = SiteQuerySet.as_manager()
42+
43+
class Meta:
44+
verbose_name = _('site')
45+
verbose_name_plural = _('sites')
46+
47+
def __str__(self):
48+
return self.host
49+
50+
def clean(self):
51+
if self.is_default:
52+
if self.__class__._base_manager.filter(
53+
Q(is_default=True),
54+
~Q(pk=self.pk),
55+
).exists():
56+
raise ValidationError(
57+
_('Only one site can be the default site.'),
58+
)
59+
60+
def save(self, *args, **kwargs):
61+
if not self.host_re:
62+
self.host_re = r'^%s$' % re.escape(self.host)
63+
super().save(*args, **kwargs)
64+
save.alters_data = True
65+
66+
67+
class AbstractPage(BasePage):
68+
site = models.ForeignKey(
69+
Site,
70+
on_delete=models.CASCADE,
71+
verbose_name=_('sites'),
72+
related_name='+',
73+
)
74+
# Exactly the same as BasePage.path,
75+
# except that it is not unique:
76+
path = models.CharField(
77+
_('path'),
78+
max_length=1000,
79+
blank=True,
80+
help_text=_('Generated automatically if \'static path\' is unset.'),
81+
validators=[
82+
RegexValidator(
83+
regex=r'^/(|.+/)$',
84+
message=_('Path must start and end with a slash (/).'),
85+
),
86+
],
87+
)
88+
89+
class Meta:
90+
abstract = True
91+
unique_together = (('site', 'path'),)
92+
verbose_name = _('page')
93+
verbose_name_plural = _('pages')
94+
95+
def save(self, *args, **kwargs):
96+
if self.parent_id and self.parent.site:
97+
self.site = self.parent.site
98+
super().save(*args, **kwargs)
99+
save.alters_data = True
100+
101+
'''
102+
def get_absolute_url(self):
103+
"""
104+
Return the page's absolute URL using ``reverse()``
105+
106+
If path is ``/``, reverses ``pages:root`` without any arguments,
107+
alternatively reverses ``pages:page`` with an argument of ``path``.
108+
Note that this ``path`` is not the same as ``self.path`` -- slashes
109+
are stripped from the beginning and the end of the string to make
110+
building an URLconf more straightforward.
111+
"""
112+
if self.path == '/':
113+
return reverse('pages:root')
114+
return reverse('pages:page', kwargs={
115+
'path': self.path.strip('/'),
116+
})
117+
'''

setup.cfg

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[flake8]
2+
exclude=venv,build,docs,.tox
3+
max-complexity=10
4+
5+
[bdist_wheel]
6+
universal = 1
7+
8+
[isort]
9+
combine_as_imports = true
10+
default_section = THIRDPARTY
11+
include_trailing_comma = true
12+
known_django=django
13+
line_length = 79
14+
multi_line_output = 5
15+
not_skip = __init__.py
16+
skip = .tox,venv
17+
sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
18+
lines_after_imports = 2
19+
20+
[coverage:run]
21+
branch = True
22+
include =
23+
*feincms3_sites*
24+
omit =
25+
*migrations*
26+
*tests*
27+
*.tox*
28+
29+
[tool:pytest]
30+
addopts = tests/

setup.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env python3
2+
3+
from io import open
4+
import os
5+
from setuptools import find_packages, setup
6+
7+
8+
def read(filename):
9+
path = os.path.join(os.path.dirname(__file__), filename)
10+
with open(path, encoding='utf-8') as handle:
11+
return handle.read()
12+
13+
14+
setup(
15+
name='feincms3-sites',
16+
version=__import__('feincms3_sites').__version__,
17+
description='Multisite support for feincms3',
18+
long_description=read('README.rst'),
19+
author='Matthias Kestenholz',
20+
author_email='[email protected]',
21+
url='https://github.com/matthiask/feincms3-sites/',
22+
license='BSD License',
23+
platforms=['OS Independent'],
24+
packages=find_packages(
25+
exclude=['tests', 'testapp']
26+
),
27+
include_package_data=True,
28+
classifiers=[
29+
# 'Development Status :: 5 - Production/Stable',
30+
'Environment :: Web Environment',
31+
'Framework :: Django',
32+
'Intended Audience :: Developers',
33+
'License :: OSI Approved :: BSD License',
34+
'Operating System :: OS Independent',
35+
'Programming Language :: Python',
36+
'Programming Language :: Python :: 3',
37+
'Programming Language :: Python :: 3.4',
38+
'Programming Language :: Python :: 3.5',
39+
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
40+
'Topic :: Software Development',
41+
'Topic :: Software Development :: Libraries :: Application Frameworks',
42+
],
43+
zip_safe=False,
44+
install_requires=[
45+
'Django>=1.11',
46+
'feincms3',
47+
],
48+
extras_require={
49+
'tests': [
50+
'pytest~=3.3',
51+
'pytest-django~=3.1',
52+
'coverage~=4.4',
53+
],
54+
},
55+
)

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from django.conf import settings
2+
3+
4+
def pytest_configure():
5+
settings.configure(
6+
DATABASES={
7+
'default': {
8+
'ENGINE': 'django.db.backends.sqlite3',
9+
}
10+
},
11+
INSTALLED_APPS=[
12+
'django.contrib.auth',
13+
'django.contrib.contenttypes',
14+
'django.contrib.sessions',
15+
'django.contrib.admin',
16+
'feincms3',
17+
'feincms3_sites',
18+
],
19+
)

tests/test_meta.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from django.utils.functional import lazy
2+
3+
from feincms3_sites.utils import sites_tags
4+
5+
6+
def test_sites(rf):
7+
request = rf.get('/')
8+
assert str(sites_tags(request=request)) == '''\
9+
<sites property="og:type" content="website">
10+
<sites name="description" content="">'''
11+
12+
lazy_url = lazy(lambda: '/', str)()
13+
assert str(sites_tags(url=lazy_url, request=request)) == '''\
14+
<sites property="og:type" content="website">
15+
<sites property="og:url" content="http://testserver/">
16+
<sites name="description" content="">'''

0 commit comments

Comments
 (0)