--- /dev/null
+# Poll Twitter every 5 minutes
+# Comment-out if you don't use Mezzanine's Twitter app
+*/5 * * * * %(user)s %(manage)s poll_twitter
--- /dev/null
+from __future__ import unicode_literals
+import multiprocessing
+
+bind = "unix:%(proj_path)s/gunicorn.sock"
+workers = %(num_workers)s
+errorlog = "/home/%(user)s/logs/%(proj_name)s_error.log"
+loglevel = "error"
+proc_name = "%(proj_name)s"
--- /dev/null
+from __future__ import unicode_literals
+
+SECRET_KEY = "%(secret_key)s"
+NEVERCACHE_KEY = "%(nevercache_key)s"
+ALLOWED_HOSTS = [%(domains_python)s]
+
+DATABASES = {
+ "default": {
+ # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
+ "ENGINE": "django.db.backends.postgresql_psycopg2",
+ # DB name or path to database file if using sqlite3.
+ "NAME": "%(proj_name)s",
+ # Not used with sqlite3.
+ "USER": "%(proj_name)s",
+ # Not used with sqlite3.
+ "PASSWORD": "%(db_pass)s",
+ # Set to empty string for localhost. Not used with sqlite3.
+ "HOST": "127.0.0.1",
+ # Set to empty string for default. Not used with sqlite3.
+ "PORT": "",
+ }
+}
+
+SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTOCOL", "https")
+
+CACHE_MIDDLEWARE_SECONDS = 60
+
+CACHE_MIDDLEWARE_KEY_PREFIX = "%(proj_name)s"
+
+CACHES = {
+ "default": {
+ "BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
+ "LOCATION": "127.0.0.1:11211",
+ }
+}
+
+SESSION_ENGINE = "django.contrib.sessions.backends.cache"
--- /dev/null
+
+upstream %(proj_name)s {
+ server unix:%(proj_path)s/gunicorn.sock fail_timeout=0;
+}
+
+server {
+
+ listen 80;
+ %(ssl_disabled)s listen 443 ssl;
+ server_name %(domains_nginx)s;
+ client_max_body_size 10M;
+ keepalive_timeout 15;
+ error_log /home/%(user)s/logs/%(proj_name)s_error_nginx.log info;
+
+ %(ssl_disabled)s ssl_certificate conf/%(proj_name)s.crt;
+ %(ssl_disabled)s ssl_certificate_key conf/%(proj_name)s.key;
+ %(ssl_disabled)s ssl_session_cache shared:SSL:10m;
+ %(ssl_disabled)s ssl_session_timeout 10m;
+ %(ssl_disabled)s ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;
+ %(ssl_disabled)s ssl_prefer_server_ciphers on;
+
+ # Deny illegal Host headers
+ if ($host !~* ^(%(domains_regex)s)$) {
+ return 444;
+ }
+
+ location / {
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Protocol $scheme;
+ proxy_pass http://%(proj_name)s;
+ }
+
+ location /static/ {
+ root %(proj_path)s;
+ access_log off;
+ log_not_found off;
+ expires 30d;
+ }
+
+ location /robots.txt {
+ root %(proj_path)s/static;
+ access_log off;
+ log_not_found off;
+ }
+
+ location /favicon.ico {
+ root %(proj_path)s/static/img;
+ access_log off;
+ log_not_found off;
+ }
+
+}
--- /dev/null
+[program:gunicorn_%(proj_name)s]
+command=%(venv_path)s/bin/gunicorn -c gunicorn.conf.py -p gunicorn.pid %(proj_app)s.wsgi:application
+directory=%(proj_path)s
+user=%(user)s
+autostart=true
+stdout_logfile = /home/%(user)s/logs/%(proj_name)s_supervisor
+autorestart=true
+redirect_stderr=true
+environment=LANG="%(locale)s",LC_ALL="%(locale)s",LC_LANG="%(locale)s"
--- /dev/null
+
+DEBUG = True
+
+# Make these unique, and don't share it with anybody.
+SECRET_KEY = "+3b01&_6_m@@yb4f06$s0zno8vkybh81nbuj_q(xzk+xeih1+s"
+NEVERCACHE_KEY = "l11tr%#!uc@+%$51(&+%=&z6h9yrw42(jpcj$3_&6evtu6hl%z"
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'diggersdigest',
+ 'USER': 'digger',
+ 'PASSWORD': 'admin',
+ 'HOST': 'db',
+ 'PORT': 3306,
+ }
+}
+
+# FileBrowser settings
+#
+# Max. Upload Size in Bytes:
+# 10MB - 10485760
+# 20MB - 20971520
+# 50MB - 5242880
+# 100MB 104857600
+# 250MB - 214958080
+# 500MB - 429916160
+FILEBROWSER_MAX_UPLOAD_SIZE = 104857600
+
+###################
+# DEPLOY SETTINGS #
+###################
+
+# Domains for public site
+# ALLOWED_HOSTS = [""]
+
+# These settings are used by the default fabfile.py provided.
+# Check fabfile.py for defaults.
+
+# FABRIC = {
+# "DEPLOY_TOOL": "rsync", # Deploy with "git", "hg", or "rsync"
+# "SSH_USER": "", # VPS SSH username
+# "HOSTS": [""], # The IP address of your VPS
+# "DOMAINS": ALLOWED_HOSTS, # Edit domains in ALLOWED_HOSTS
+# "REQUIREMENTS_PATH": "requirements.txt", # Project's pip requirements
+# "LOCALE": "en_US.UTF-8", # Should end with ".UTF-8"
+# "DB_PASS": "", # Live database password
+# "ADMIN_PASS": "", # Live admin user password
+# "SECRET_KEY": SECRET_KEY,
+# "NEVERCACHE_KEY": NEVERCACHE_KEY,
+# }
-"""
-Django settings for diggersdigest project.
-Generated by 'django-admin startproject' using Django 1.8.3.
+from __future__ import absolute_import, unicode_literals
+import os
+from django.utils.translation import ugettext_lazy as _
-For more information on this file, see
-https://docs.djangoproject.com/en/1.8/topics/settings/
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.8/ref/settings/
-"""
+######################
+# CARTRIDGE SETTINGS #
+######################
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-import os
+# The following settings are already defined in cartridge.shop.defaults
+# with default values, but are common enough to be put here, commented
+# out, for conveniently overriding. Please consult the settings
+# documentation for a full list of settings Cartridge implements:
+# http://cartridge.jupo.org/configuration.html#default-settings
+
+# Sequence of available credit card types for payment.
+# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex")
+
+# Setting to turn on featured images for shop categories. Defaults to False.
+# SHOP_CATEGORY_USE_FEATURED_IMAGE = True
+
+# Set an alternative OrderForm class for the checkout process.
+# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm'
+
+# If True, the checkout process is split into separate
+# billing/shipping and payment steps.
+# SHOP_CHECKOUT_STEPS_SPLIT = True
+
+# If True, the checkout process has a final confirmation step before
+# completion.
+# SHOP_CHECKOUT_STEPS_CONFIRMATION = True
+
+# Controls the formatting of monetary values accord to the locale
+# module in the python standard library. If an empty string is
+# used, will fall back to the system's locale.
+#SHOP_CURRENCY_LOCALE = ''
+
+# Dotted package path and name of the function that
+# is called on submit of the billing/shipping checkout step. This
+# is where shipping calculation can be performed and set using the
+# function ``cartridge.shop.utils.set_shipping``.
+# SHOP_HANDLER_BILLING_SHIPPING = \
+# "cartridge.shop.checkout.default_billship_handler"
+
+# Dotted package path and name of the function that
+# is called once an order is successful and all of the order
+# object's data has been created. This is where any custom order
+# processing should be implemented.
+# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler"
+
+# Dotted package path and name of the function that
+# is called on submit of the payment checkout step. This is where
+# integration with a payment gateway should be implemented.
+# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler"
+
+# Sequence of value/name pairs for order statuses.
+# SHOP_ORDER_STATUS_CHOICES = (
+# (1, "Unprocessed"),
+# (2, "Processed"),
+# )
+
+# Sequence of value/name pairs for types of product options,
+# eg Size, Colour. NOTE: Increasing the number of these will
+# require database migrations!
+# SHOP_OPTION_TYPE_CHOICES = (
+# (1, "Size"),
+# (2, "Colour"),
+# )
+
+# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that
+# control how the options should be ordered in the admin,
+# eg for "Colour" then "Size" given the above:
+# SHOP_OPTION_ADMIN_ORDER = (2, 1)
+
+
+######################
+# MEZZANINE SETTINGS #
+######################
+
+# The following settings are already defined with default values in
+# the ``defaults.py`` module within each of Mezzanine's apps, but are
+# common enough to be put here, commented out, for conveniently
+# overriding. Please consult the settings documentation for a full list
+# of settings Mezzanine implements:
+# http://mezzanine.jupo.org/docs/configuration.html#default-settings
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# Controls the ordering and grouping of the admin menu.
+#
+# ADMIN_MENU_ORDER = (
+# ("Content", ("pages.Page", "blog.BlogPost",
+# "generic.ThreadedComment", (_("Media Library"), "fb_browse"),)),
+# (_("Shop"), ("shop.Product", "shop.ProductOption", "shop.DiscountCode",
+# "shop.Sale", "shop.Order")),
+# ("Site", ("sites.Site", "redirects.Redirect", "conf.Setting")),
+# ("Users", ("auth.User", "auth.Group",)),
+# )
+# A three item sequence, each containing a sequence of template tags
+# used to render the admin dashboard.
+#
+# DASHBOARD_TAGS = (
+# ("blog_tags.quick_blog", "mezzanine_tags.app_list"),
+# ("comment_tags.recent_comments",),
+# ("mezzanine_tags.recent_actions",),
+# )
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
+# A sequence of templates used by the ``page_menu`` template tag. Each
+# item in the sequence is a three item sequence, containing a unique ID
+# for the template, a label for the template, and the template path.
+# These templates are then available for selection when editing which
+# menus a page should appear in. Note that if a menu template is used
+# that doesn't appear in this setting, all pages will appear in it.
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'k#y*6^1y18xlm)y#i&v6!s26i1hgttz64&!--7ft3q-c!22y(%'
+# PAGE_MENU_TEMPLATES = (
+# (1, _("Top navigation bar"), "pages/menus/dropdown.html"),
+# (2, _("Left-hand tree"), "pages/menus/tree.html"),
+# (3, _("Footer"), "pages/menus/footer.html"),
+# )
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
+# A sequence of fields that will be injected into Mezzanine's (or any
+# library's) models. Each item in the sequence is a four item sequence.
+# The first two items are the dotted path to the model and its field
+# name to be added, and the dotted path to the field class to use for
+# the field. The third and fourth items are a sequence of positional
+# args and a dictionary of keyword args, to use when creating the
+# field instance. When specifying the field class, the path
+# ``django.models.db.`` can be omitted for regular Django model fields.
+#
+# EXTRA_MODEL_FIELDS = (
+# (
+# # Dotted path to field.
+# "mezzanine.blog.models.BlogPost.image",
+# # Dotted path to field class.
+# "somelib.fields.ImageField",
+# # Positional args for field class.
+# (_("Image"),),
+# # Keyword args for field class.
+# {"blank": True, "upload_to": "blog"},
+# ),
+# # Example of adding a field to *all* of Mezzanine's content types:
+# (
+# "mezzanine.pages.models.Page.another_field",
+# "IntegerField", # 'django.db.models.' is implied if path is omitted.
+# (_("Another name"),),
+# {"blank": True, "default": 1},
+# ),
+# )
+# Setting to turn on featured images for blog posts. Defaults to False.
+#
+# BLOG_USE_FEATURED_IMAGE = True
+
+# If True, the django-modeltranslation will be added to the
+# INSTALLED_APPS setting.
+USE_MODELTRANSLATION = False
+
+
+########################
+# MAIN DJANGO SETTINGS #
+########################
+
+# Hosts/domain names that are valid for this site; required if DEBUG is False
+# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'UTC'
-# Application definition
+# If you set this to True, Django will use timezone-aware datetimes.
+USE_TZ = True
-INSTALLED_APPS = (
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'records',
-)
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = "en"
-MIDDLEWARE_CLASSES = (
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'django.middleware.security.SecurityMiddleware',
+# Supported languages
+LANGUAGES = (
+ ('en', _('English')),
)
-ROOT_URLCONF = 'diggersdigest.urls'
+# A boolean that turns on/off debug mode. When set to ``True``, stack traces
+# are displayed for error pages. Should always be set to ``False`` in
+# production. Best set to ``True`` in local_settings.py
+DEBUG = False
-TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
-]
+# Whether a user's session cookie expires when the Web browser is closed.
+SESSION_EXPIRE_AT_BROWSER_CLOSE = True
-WSGI_APPLICATION = 'diggersdigest.wsgi.application'
+SITE_ID = 1
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = False
-# Database
-# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
+AUTHENTICATION_BACKENDS = ("mezzanine.core.auth_backends.MezzanineBackend",)
+# The numeric mode to set newly-uploaded files to. The value should be
+# a mode you'd pass directly to os.chmod.
+FILE_UPLOAD_PERMISSIONS = 0o644
+
+
+
+#############
+# DATABASES #
+#############
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'diggersdigest',
- 'USER': 'digger',
- 'PASSWORD': 'admin',
- 'HOST': 'db',
- 'PORT': 3306,
+ "default": {
+ # Ends with "postgresql_psycopg2", "mysql", "sqlite3" or "oracle".
+ "ENGINE": "django.db.backends.sqlite3",
+ # DB name or path to database file if using sqlite3.
+ "NAME": "dev.db",
+ # Not used with sqlite3.
+ "USER": "",
+ # Not used with sqlite3.
+ "PASSWORD": "",
+ # Set to empty string for localhost. Not used with sqlite3.
+ "HOST": "",
+ # Set to empty string for default. Not used with sqlite3.
+ "PORT": "",
}
}
-# Internationalization
-# https://docs.djangoproject.com/en/1.8/topics/i18n/
+#########
+# PATHS #
+#########
-LANGUAGE_CODE = 'en-us'
+# Full filesystem path to the project.
+PROJECT_APP_PATH = os.path.dirname(os.path.abspath(__file__))
+PROJECT_APP = os.path.basename(PROJECT_APP_PATH)
+PROJECT_ROOT = BASE_DIR = os.path.dirname(PROJECT_APP_PATH)
-TIME_ZONE = 'UTC'
+# Every cache key will get prefixed with this value - here we set it to
+# the name of the directory the project is in to try and use something
+# project specific.
+CACHE_MIDDLEWARE_KEY_PREFIX = PROJECT_APP
-USE_I18N = True
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = "/static/"
-USE_L10N = True
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = os.path.join(PROJECT_ROOT, STATIC_URL.strip("/"))
-USE_TZ = True
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = STATIC_URL + "media/"
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = os.path.join(PROJECT_ROOT, *MEDIA_URL.strip("/").split("/"))
+
+# Package/module name to import the root urlpatterns from for the project.
+ROOT_URLCONF = "%s.urls" % PROJECT_APP
+
+# Put strings here, like "/home/html/django_templates"
+# or "C:/www/django/templates".
+# Always use forward slashes, even on Windows.
+# Don't forget to use absolute paths, not relative paths.
+TEMPLATE_DIRS = (os.path.join(PROJECT_ROOT, "templates"),)
+
+
+################
+# APPLICATIONS #
+################
+
+INSTALLED_APPS = (
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.redirects",
+ "django.contrib.sessions",
+ "django.contrib.sites",
+ "django.contrib.sitemaps",
+ "django.contrib.staticfiles",
+ "mezzanine.boot",
+ "mezzanine.conf",
+ "mezzanine.core",
+ "mezzanine.generic",
+ "mezzanine.pages",
+ "cartridge.shop",
+ "mezzanine.blog",
+ "mezzanine.forms",
+ "mezzanine.galleries",
+ "mezzanine.twitter",
+ # "mezzanine.accounts",
+ # "mezzanine.mobile",
+ "records",
+
+)
+
+# List of processors used by RequestContext to populate the context.
+# Each one should be a callable that takes the request object as its
+# only parameter and returns a dictionary to add to the context.
+TEMPLATE_CONTEXT_PROCESSORS = (
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
+ "django.core.context_processors.debug",
+ "django.core.context_processors.i18n",
+ "django.core.context_processors.static",
+ "django.core.context_processors.media",
+ "django.core.context_processors.request",
+ "django.core.context_processors.tz",
+ "mezzanine.conf.context_processors.settings",
+ "mezzanine.pages.context_processors.page",
+)
+
+# List of middleware classes to use. Order is important; in the request phase,
+# these middleware classes will be applied in the order given, and in the
+# response phase the middleware will be applied in reverse order.
+MIDDLEWARE_CLASSES = (
+ "mezzanine.core.middleware.UpdateCacheMiddleware",
+
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ # Uncomment if using internationalisation or localisation
+ # 'django.middleware.locale.LocaleMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+
+ "cartridge.shop.middleware.ShopMiddleware",
+ "mezzanine.core.request.CurrentRequestMiddleware",
+ "mezzanine.core.middleware.RedirectFallbackMiddleware",
+ "mezzanine.core.middleware.TemplateForDeviceMiddleware",
+ "mezzanine.core.middleware.TemplateForHostMiddleware",
+ "mezzanine.core.middleware.AdminLoginInterfaceSelectorMiddleware",
+ "mezzanine.core.middleware.SitePermissionMiddleware",
+ # Uncomment the following if using any of the SSL settings:
+ # "mezzanine.core.middleware.SSLRedirectMiddleware",
+ "mezzanine.pages.middleware.PageMiddleware",
+ "mezzanine.core.middleware.FetchFromCacheMiddleware",
+)
+
+# Store these package names here as they may change in the future since
+# at the moment we are using custom forks of them.
+PACKAGE_NAME_FILEBROWSER = "filebrowser_safe"
+PACKAGE_NAME_GRAPPELLI = "grappelli_safe"
+
+#########################
+# OPTIONAL APPLICATIONS #
+#########################
+
+# These will be added to ``INSTALLED_APPS``, only if available.
+OPTIONAL_APPS = (
+ "debug_toolbar",
+ "django_extensions",
+ "compressor",
+ PACKAGE_NAME_FILEBROWSER,
+ PACKAGE_NAME_GRAPPELLI,
+)
+
+##################
+# LOCAL SETTINGS #
+##################
+
+# Allow any settings to be defined in local_settings.py which should be
+# ignored in your version control system allowing for settings to be
+# defined per machine.
+try:
+ from .local_settings import *
+except ImportError as e:
+ if "local_settings" not in str(e):
+ raise e
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.8/howto/static-files/
+####################
+# DYNAMIC SETTINGS #
+####################
-STATIC_URL = '/static/'
+# set_dynamic_settings() will rewrite globals based on what has been
+# defined so far, in order to provide some better defaults where
+# applicable. We also allow this settings module to be imported
+# without Mezzanine installed, as the case may be when using the
+# fabfile, where setting the dynamic settings below isn't strictly
+# required.
+try:
+ from mezzanine.utils.conf import set_dynamic_settings
+except ImportError:
+ pass
+else:
+ set_dynamic_settings(globals())
-"""diggersdigest URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/1.8/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
-Including another URLconf
- 1. Add an import: from blog import urls as blog_urls
- 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
-"""
-from django.conf.urls import include, url
+from __future__ import unicode_literals
+
+from django.conf.urls import patterns, include, url
+from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
-urlpatterns = [
- url(r'^admin/', include(admin.site.urls)),
-]
+from mezzanine.core.views import direct_to_template
+from mezzanine.conf import settings
+
+
+admin.autodiscover()
+
+# Add the urlpatterns for any custom Django applications here.
+# You can also change the ``home`` view to add your own functionality
+# to the project's homepage.
+
+urlpatterns = i18n_patterns("",
+ # Change the admin prefix here to use an alternate URL for the
+ # admin interface, which would be marginally more secure.
+ ("^admin/", include(admin.site.urls)),
+)
+
+if settings.USE_MODELTRANSLATION:
+ urlpatterns += patterns('',
+ url('^i18n/$', 'django.views.i18n.set_language', name='set_language'),
+ )
+
+urlpatterns += patterns('',
+
+ # Cartridge URLs.
+ ("^shop/", include("cartridge.shop.urls")),
+ url("^account/orders/$", "cartridge.shop.views.order_history",
+ name="shop_order_history"),
+
+ # We don't want to presume how your homepage works, so here are a
+ # few patterns you can use to set it up.
+
+ # HOMEPAGE AS STATIC TEMPLATE
+ # ---------------------------
+ # This pattern simply loads the index.html template. It isn't
+ # commented out like the others, so it's the default. You only need
+ # one homepage pattern, so if you use a different one, comment this
+ # one out.
+
+ url("^$", direct_to_template, {"template": "index.html"}, name="home"),
+
+ # HOMEPAGE AS AN EDITABLE PAGE IN THE PAGE TREE
+ # ---------------------------------------------
+ # This pattern gives us a normal ``Page`` object, so that your
+ # homepage can be managed via the page tree in the admin. If you
+ # use this pattern, you'll need to create a page in the page tree,
+ # and specify its URL (in the Meta Data section) as "/", which
+ # is the value used below in the ``{"slug": "/"}`` part. Make
+ # sure to uncheck all templates for the "show in menus" field
+ # when you create the page, since the link to the homepage is
+ # always hard-coded into all the page menus that display navigation
+ # on the site. Also note that the normal rule of adding a custom
+ # template per page with the template name using the page's slug
+ # doesn't apply here, since we can't have a template called
+ # "/.html" - so for this case, the template "pages/index.html" can
+ # be used.
+
+ # url("^$", "mezzanine.pages.views.page", {"slug": "/"}, name="home"),
+
+ # HOMEPAGE FOR A BLOG-ONLY SITE
+ # -----------------------------
+ # This pattern points the homepage to the blog post listing page,
+ # and is useful for sites that are primarily blogs. If you use this
+ # pattern, you'll also need to set BLOG_SLUG = "" in your
+ # ``settings.py`` module, and delete the blog page object from the
+ # page tree in the admin if it was installed.
+
+ # url("^$", "mezzanine.blog.views.blog_post_list", name="home"),
+
+ # MEZZANINE'S URLS
+ # ----------------
+ # ADD YOUR OWN URLPATTERNS *ABOVE* THE LINE BELOW.
+ # ``mezzanine.urls`` INCLUDES A *CATCH ALL* PATTERN
+ # FOR PAGES, SO URLPATTERNS ADDED BELOW ``mezzanine.urls``
+ # WILL NEVER BE MATCHED!
+
+ # If you'd like more granular control over the patterns in
+ # ``mezzanine.urls``, go right ahead and take the parts you want
+ # from it, and use them directly below instead of using
+ # ``mezzanine.urls``.
+ ("^", include("mezzanine.urls")),
+
+ # MOUNTING MEZZANINE UNDER A PREFIX
+ # ---------------------------------
+ # You can also mount all of Mezzanine's urlpatterns under a
+ # URL prefix if desired. When doing this, you need to define the
+ # ``SITE_PREFIX`` setting, which will contain the prefix. Eg:
+ # SITE_PREFIX = "my/site/prefix"
+ # For convenience, and to avoid repeating the prefix, use the
+ # commented out pattern below (commenting out the one above of course)
+ # which will make use of the ``SITE_PREFIX`` setting. Make sure to
+ # add the import ``from django.conf import settings`` to the top
+ # of this file as well.
+ # Note that for any of the various homepage patterns above, you'll
+ # need to use the ``SITE_PREFIX`` setting as well.
+
+ # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls"))
+
+)
+
+# Adds ``STATIC_URL`` to the context of error pages, so that error
+# pages can use JS, CSS and images.
+handler404 = "mezzanine.core.views.page_not_found"
+handler500 = "mezzanine.core.views.server_error"
import os
from django.core.wsgi import get_wsgi_application
+from mezzanine.utils.conf import real_project_name
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diggersdigest.settings")
+os.environ.setdefault("DJANGO_SETTINGS_MODULE",
+ "%s.settings" % real_project_name("diggersdigest"))
application = get_wsgi_application()
--- /dev/null
+from __future__ import print_function, unicode_literals
+from future.builtins import open
+
+import os
+import re
+import sys
+from contextlib import contextmanager
+from functools import wraps
+from getpass import getpass, getuser
+from glob import glob
+from importlib import import_module
+from posixpath import join
+
+from mezzanine.utils.conf import real_project_name
+
+from fabric.api import abort, env, cd, prefix, sudo as _sudo, run as _run, \
+ hide, task, local
+from fabric.context_managers import settings as fab_settings
+from fabric.contrib.console import confirm
+from fabric.contrib.files import exists, upload_template
+from fabric.contrib.project import rsync_project
+from fabric.colors import yellow, green, blue, red
+from fabric.decorators import hosts
+
+
+################
+# Config setup #
+################
+
+env.proj_app = real_project_name("diggersdigest")
+
+conf = {}
+if sys.argv[0].split(os.sep)[-1] in ("fab", "fab-script.py"):
+ # Ensure we import settings from the current dir
+ try:
+ conf = import_module("%s.settings" % env.proj_app).FABRIC
+ try:
+ conf["HOSTS"][0]
+ except (KeyError, ValueError):
+ raise ImportError
+ except (ImportError, AttributeError):
+ print("Aborting, no hosts defined.")
+ exit()
+
+env.db_pass = conf.get("DB_PASS", None)
+env.admin_pass = conf.get("ADMIN_PASS", None)
+env.user = conf.get("SSH_USER", getuser())
+env.password = conf.get("SSH_PASS", None)
+env.key_filename = conf.get("SSH_KEY_PATH", None)
+env.hosts = conf.get("HOSTS", [""])
+
+env.proj_name = conf.get("PROJECT_NAME", env.proj_app)
+env.venv_home = conf.get("VIRTUALENV_HOME", "/home/%s/.virtualenvs" % env.user)
+env.venv_path = join(env.venv_home, env.proj_name)
+env.proj_path = "/home/%s/mezzanine/%s" % (env.user, env.proj_name)
+env.manage = "%s/bin/python %s/manage.py" % (env.venv_path, env.proj_path)
+env.domains = conf.get("DOMAINS", [conf.get("LIVE_HOSTNAME", env.hosts[0])])
+env.domains_nginx = " ".join(env.domains)
+env.domains_regex = "|".join(env.domains)
+env.domains_python = ", ".join(["'%s'" % s for s in env.domains])
+env.ssl_disabled = "#" if len(env.domains) > 1 else ""
+env.vcs_tools = ["git", "hg"]
+env.deploy_tool = conf.get("DEPLOY_TOOL", "rsync")
+env.reqs_path = conf.get("REQUIREMENTS_PATH", None)
+env.locale = conf.get("LOCALE", "en_US.UTF-8")
+env.num_workers = conf.get("NUM_WORKERS",
+ "multiprocessing.cpu_count() * 2 + 1")
+
+env.secret_key = conf.get("SECRET_KEY", "")
+env.nevercache_key = conf.get("NEVERCACHE_KEY", "")
+
+# Remote git repos need to be "bare" and reside separated from the project
+if env.deploy_tool == "git":
+ env.repo_path = "/home/%s/git/%s.git" % (env.user, env.proj_name)
+else:
+ env.repo_path = env.proj_path
+
+
+##################
+# Template setup #
+##################
+
+# Each template gets uploaded at deploy time, only if their
+# contents has changed, in which case, the reload command is
+# also run.
+
+templates = {
+ "nginx": {
+ "local_path": "deploy/nginx.conf.template",
+ "remote_path": "/etc/nginx/sites-enabled/%(proj_name)s.conf",
+ "reload_command": "service nginx restart",
+ },
+ "supervisor": {
+ "local_path": "deploy/supervisor.conf.template",
+ "remote_path": "/etc/supervisor/conf.d/%(proj_name)s.conf",
+ "reload_command": "supervisorctl update gunicorn_%(proj_name)s",
+ },
+ "cron": {
+ "local_path": "deploy/crontab.template",
+ "remote_path": "/etc/cron.d/%(proj_name)s",
+ "owner": "root",
+ "mode": "600",
+ },
+ "gunicorn": {
+ "local_path": "deploy/gunicorn.conf.py.template",
+ "remote_path": "%(proj_path)s/gunicorn.conf.py",
+ },
+ "settings": {
+ "local_path": "deploy/local_settings.py.template",
+ "remote_path": "%(proj_path)s/%(proj_app)s/local_settings.py",
+ },
+}
+
+
+######################################
+# Context for virtualenv and project #
+######################################
+
+@contextmanager
+def virtualenv():
+ """
+ Runs commands within the project's virtualenv.
+ """
+ with cd(env.venv_path):
+ with prefix("source %s/bin/activate" % env.venv_path):
+ yield
+
+
+@contextmanager
+def project():
+ """
+ Runs commands within the project's directory.
+ """
+ with virtualenv():
+ with cd(env.proj_path):
+ yield
+
+
+@contextmanager
+def update_changed_requirements():
+ """
+ Checks for changes in the requirements file across an update,
+ and gets new requirements if changes have occurred.
+ """
+ reqs_path = join(env.proj_path, env.reqs_path)
+ get_reqs = lambda: run("cat %s" % reqs_path, show=False)
+ old_reqs = get_reqs() if env.reqs_path else ""
+ yield
+ if old_reqs:
+ new_reqs = get_reqs()
+ if old_reqs == new_reqs:
+ # Unpinned requirements should always be checked.
+ for req in new_reqs.split("\n"):
+ if req.startswith("-e"):
+ if "@" not in req:
+ # Editable requirement without pinned commit.
+ break
+ elif req.strip() and not req.startswith("#"):
+ if not set(">=<") & set(req):
+ # PyPI requirement without version.
+ break
+ else:
+ # All requirements are pinned.
+ return
+ pip("-r %s/%s" % (env.proj_path, env.reqs_path))
+
+
+###########################################
+# Utils and wrappers for various commands #
+###########################################
+
+def _print(output):
+ print()
+ print(output)
+ print()
+
+
+def print_command(command):
+ _print(blue("$ ", bold=True) +
+ yellow(command, bold=True) +
+ red(" ->", bold=True))
+
+
+@task
+def run(command, show=True, *args, **kwargs):
+ """
+ Runs a shell comand on the remote server.
+ """
+ if show:
+ print_command(command)
+ with hide("running"):
+ return _run(command, *args, **kwargs)
+
+
+@task
+def sudo(command, show=True, *args, **kwargs):
+ """
+ Runs a command as sudo on the remote server.
+ """
+ if show:
+ print_command(command)
+ with hide("running"):
+ return _sudo(command, *args, **kwargs)
+
+
+def log_call(func):
+ @wraps(func)
+ def logged(*args, **kawrgs):
+ header = "-" * len(func.__name__)
+ _print(green("\n".join([header, func.__name__, header]), bold=True))
+ return func(*args, **kawrgs)
+ return logged
+
+
+def get_templates():
+ """
+ Returns each of the templates with env vars injected.
+ """
+ injected = {}
+ for name, data in templates.items():
+ injected[name] = dict([(k, v % env) for k, v in data.items()])
+ return injected
+
+
+def upload_template_and_reload(name):
+ """
+ Uploads a template only if it has changed, and if so, reload the
+ related service.
+ """
+ template = get_templates()[name]
+ local_path = template["local_path"]
+ if not os.path.exists(local_path):
+ project_root = os.path.dirname(os.path.abspath(__file__))
+ local_path = os.path.join(project_root, local_path)
+ remote_path = template["remote_path"]
+ reload_command = template.get("reload_command")
+ owner = template.get("owner")
+ mode = template.get("mode")
+ remote_data = ""
+ if exists(remote_path):
+ with hide("stdout"):
+ remote_data = sudo("cat %s" % remote_path, show=False)
+ with open(local_path, "r") as f:
+ local_data = f.read()
+ # Escape all non-string-formatting-placeholder occurrences of '%':
+ local_data = re.sub(r"%(?!\(\w+\)s)", "%%", local_data)
+ if "%(db_pass)s" in local_data:
+ env.db_pass = db_pass()
+ local_data %= env
+ clean = lambda s: s.replace("\n", "").replace("\r", "").strip()
+ if clean(remote_data) == clean(local_data):
+ return
+ upload_template(local_path, remote_path, env, use_sudo=True, backup=False)
+ if owner:
+ sudo("chown %s %s" % (owner, remote_path))
+ if mode:
+ sudo("chmod %s %s" % (mode, remote_path))
+ if reload_command:
+ sudo(reload_command)
+
+
+def rsync_upload():
+ """
+ Uploads the project with rsync excluding some files and folders.
+ """
+ excludes = ["*.pyc", "*.pyo", "*.db", ".DS_Store", ".coverage",
+ "local_settings.py", "/static", "/.git", "/.hg"]
+ local_dir = os.getcwd() + os.sep
+ return rsync_project(remote_dir=env.proj_path, local_dir=local_dir,
+ exclude=excludes)
+
+
+def vcs_upload():
+ """
+ Uploads the project with the selected VCS tool.
+ """
+ if env.deploy_tool == "git":
+ remote_path = "ssh://%s@%s%s" % (env.user, env.host_string,
+ env.repo_path)
+ if not exists(env.repo_path):
+ run("mkdir -p %s" % env.repo_path)
+ with cd(env.repo_path):
+ run("git init --bare")
+ local("git push -f %s master" % remote_path)
+ with cd(env.repo_path):
+ run("GIT_WORK_TREE=%s git checkout -f master" % env.proj_path)
+ run("GIT_WORK_TREE=%s git reset --hard" % env.proj_path)
+ elif env.deploy_tool == "hg":
+ remote_path = "ssh://%s@%s/%s" % (env.user, env.host_string,
+ env.repo_path)
+ with cd(env.repo_path):
+ if not exists("%s/.hg" % env.repo_path):
+ run("hg init")
+ print(env.repo_path)
+ with fab_settings(warn_only=True):
+ push = local("hg push -f %s" % remote_path)
+ if push.return_code == 255:
+ abort()
+ run("hg update")
+
+
+def db_pass():
+ """
+ Prompts for the database password if unknown.
+ """
+ if not env.db_pass:
+ env.db_pass = getpass("Enter the database password: ")
+ return env.db_pass
+
+
+@task
+def apt(packages):
+ """
+ Installs one or more system packages via apt.
+ """
+ return sudo("apt-get install -y -q " + packages)
+
+
+@task
+def pip(packages):
+ """
+ Installs one or more Python packages within the virtual environment.
+ """
+ with virtualenv():
+ return run("pip install %s" % packages)
+
+
+def postgres(command):
+ """
+ Runs the given command as the postgres user.
+ """
+ show = not command.startswith("psql")
+ return sudo(command, show=show, user="postgres")
+
+
+@task
+def psql(sql, show=True):
+ """
+ Runs SQL against the project's database.
+ """
+ out = postgres('psql -c "%s"' % sql)
+ if show:
+ print_command(sql)
+ return out
+
+
+@task
+def backup(filename):
+ """
+ Backs up the project database.
+ """
+ tmp_file = "/tmp/%s" % filename
+ # We dump to /tmp because user "postgres" can't write to other user folders
+ # We cd to / because user "postgres" might not have read permissions
+ # elsewhere.
+ with cd("/"):
+ postgres("pg_dump -Fc %s > %s" % (env.proj_name, tmp_file))
+ run("cp %s ." % tmp_file)
+ sudo("rm -f %s" % tmp_file)
+
+
+@task
+def restore(filename):
+ """
+ Restores the project database from a previous backup.
+ """
+ return postgres("pg_restore -c -d %s %s" % (env.proj_name, filename))
+
+
+@task
+def python(code, show=True):
+ """
+ Runs Python code in the project's virtual environment, with Django loaded.
+ """
+ setup = "import os;" \
+ "os.environ[\'DJANGO_SETTINGS_MODULE\']=\'%s.settings\';" \
+ "import django;" \
+ "django.setup();" % env.proj_app
+ full_code = 'python -c "%s%s"' % (setup, code.replace("`", "\\\`"))
+ with project():
+ if show:
+ print_command(code)
+ result = run(full_code, show=False)
+ return result
+
+
+def static():
+ """
+ Returns the live STATIC_ROOT directory.
+ """
+ return python("from django.conf import settings;"
+ "print(settings.STATIC_ROOT)", show=False).split("\n")[-1]
+
+
+@task
+def manage(command):
+ """
+ Runs a Django management command.
+ """
+ return run("%s %s" % (env.manage, command))
+
+
+###########################
+# Security best practices #
+###########################
+
+@task
+@log_call
+@hosts(["root@%s" % host for host in env.hosts])
+def secure(new_user=env.user):
+ """
+ Minimal security steps for brand new servers.
+ Installs system updates, creates new user (with sudo privileges) for future
+ usage, and disables root login via SSH.
+ """
+ run("apt-get update -q")
+ run("apt-get upgrade -y -q")
+ run("adduser --gecos '' %s" % new_user)
+ run("usermod -G sudo %s" % new_user)
+ run("sed -i 's:RootLogin yes:RootLogin no:' /etc/ssh/sshd_config")
+ run("service ssh restart")
+ print(green("Security steps completed. Log in to the server as '%s' from "
+ "now on." % new_user, bold=True))
+
+
+#########################
+# Install and configure #
+#########################
+
+@task
+@log_call
+def install():
+ """
+ Installs the base system and Python requirements for the entire server.
+ """
+ # Install system requirements
+ sudo("apt-get update -y -q")
+ apt("nginx libjpeg-dev python-dev python-setuptools git-core "
+ "postgresql libpq-dev memcached supervisor python-pip")
+ run("mkdir -p /home/%s/logs" % env.user)
+
+ # Install Python requirements
+ sudo("pip install -U pip virtualenv virtualenvwrapper mercurial")
+
+ # Set up virtualenv
+ run("mkdir -p %s" % env.venv_home)
+ run("echo 'export WORKON_HOME=%s' >> /home/%s/.bashrc" % (env.venv_home,
+ env.user))
+ run("echo 'source /usr/local/bin/virtualenvwrapper.sh' >> "
+ "/home/%s/.bashrc" % env.user)
+ print(green("Successfully set up git, mercurial, pip, virtualenv, "
+ "supervisor, memcached.", bold=True))
+
+
+@task
+@log_call
+def create():
+ """
+ Creates the environment needed to host the project.
+ The environment consists of: system locales, virtualenv, database, project
+ files, SSL certificate, and project-specific Python requirements.
+ """
+ # Generate project locale
+ locale = env.locale.replace("UTF-8", "utf8")
+ with hide("stdout"):
+ if locale not in run("locale -a"):
+ sudo("locale-gen %s" % env.locale)
+ sudo("update-locale %s" % env.locale)
+ sudo("service postgresql restart")
+ run("exit")
+
+ # Create project path
+ run("mkdir -p %s" % env.proj_path)
+
+ # Set up virtual env
+ run("mkdir -p %s" % env.venv_home)
+ with cd(env.venv_home):
+ if exists(env.proj_name):
+ if confirm("Virtualenv already exists in host server: %s"
+ "\nWould you like to replace it?" % env.proj_name):
+ run("rm -rf %s" % env.proj_name)
+ else:
+ abort()
+ run("virtualenv %s" % env.proj_name)
+
+ # Upload project files
+ if env.deploy_tool in env.vcs_tools:
+ vcs_upload()
+ else:
+ rsync_upload()
+
+ # Create DB and DB user
+ pw = db_pass()
+ user_sql_args = (env.proj_name, pw.replace("'", "\'"))
+ user_sql = "CREATE USER %s WITH ENCRYPTED PASSWORD '%s';" % user_sql_args
+ psql(user_sql, show=False)
+ shadowed = "*" * len(pw)
+ print_command(user_sql.replace("'%s'" % pw, "'%s'" % shadowed))
+ psql("CREATE DATABASE %s WITH OWNER %s ENCODING = 'UTF8' "
+ "LC_CTYPE = '%s' LC_COLLATE = '%s' TEMPLATE template0;" %
+ (env.proj_name, env.proj_name, env.locale, env.locale))
+
+ # Set up SSL certificate
+ if not env.ssl_disabled:
+ conf_path = "/etc/nginx/conf"
+ if not exists(conf_path):
+ sudo("mkdir %s" % conf_path)
+ with cd(conf_path):
+ crt_file = env.proj_name + ".crt"
+ key_file = env.proj_name + ".key"
+ if not exists(crt_file) and not exists(key_file):
+ try:
+ crt_local, = glob(join("deploy", "*.crt"))
+ key_local, = glob(join("deploy", "*.key"))
+ except ValueError:
+ parts = (crt_file, key_file, env.domains[0])
+ sudo("openssl req -new -x509 -nodes -out %s -keyout %s "
+ "-subj '/CN=%s' -days 3650" % parts)
+ else:
+ upload_template(crt_local, crt_file, use_sudo=True)
+ upload_template(key_local, key_file, use_sudo=True)
+
+ # Install project-specific requirements
+ upload_template_and_reload("settings")
+ with project():
+ if env.reqs_path:
+ pip("-r %s/%s" % (env.proj_path, env.reqs_path))
+ pip("gunicorn setproctitle psycopg2 "
+ "django-compressor python-memcached")
+ # Bootstrap the DB
+ manage("createdb --noinput --nodata")
+ python("from django.conf import settings;"
+ "from django.contrib.sites.models import Site;"
+ "Site.objects.filter(id=settings.SITE_ID).update(domain='%s');"
+ % env.domains[0])
+ for domain in env.domains:
+ python("from django.contrib.sites.models import Site;"
+ "Site.objects.get_or_create(domain='%s');" % domain)
+ if env.admin_pass:
+ pw = env.admin_pass
+ user_py = ("from django.contrib.auth import get_user_model;"
+ "User = get_user_model();"
+ "u, _ = User.objects.get_or_create(username='admin');"
+ "u.is_staff = u.is_superuser = True;"
+ "u.set_password('%s');"
+ "u.save();" % pw)
+ python(user_py, show=False)
+ shadowed = "*" * len(pw)
+ print_command(user_py.replace("'%s'" % pw, "'%s'" % shadowed))
+
+ return True
+
+
+@task
+@log_call
+def remove():
+ """
+ Blow away the current project.
+ """
+ if exists(env.venv_path):
+ run("rm -rf %s" % env.venv_path)
+ if exists(env.proj_path):
+ run("rm -rf %s" % env.proj_path)
+ for template in get_templates().values():
+ remote_path = template["remote_path"]
+ if exists(remote_path):
+ sudo("rm %s" % remote_path)
+ if exists(env.repo_path):
+ run("rm -rf %s" % env.repo_path)
+ sudo("supervisorctl update")
+ psql("DROP DATABASE IF EXISTS %s;" % env.proj_name)
+ psql("DROP USER IF EXISTS %s;" % env.proj_name)
+
+
+##############
+# Deployment #
+##############
+
+@task
+@log_call
+def restart():
+ """
+ Restart gunicorn worker processes for the project.
+ If the processes are not running, they will be started.
+ """
+ pid_path = "%s/gunicorn.pid" % env.proj_path
+ if exists(pid_path):
+ run("kill -HUP `cat %s`" % pid_path)
+ else:
+ sudo("supervisorctl update")
+
+
+@task
+@log_call
+def deploy():
+ """
+ Deploy latest version of the project.
+ Backup current version of the project, push latest version of the project
+ via version control or rsync, install new requirements, sync and migrate
+ the database, collect any new static assets, and restart gunicorn's worker
+ processes for the project.
+ """
+ if not exists(env.proj_path):
+ if confirm("Project does not exist in host server: %s"
+ "\nWould you like to create it?" % env.proj_name):
+ create()
+ else:
+ abort()
+
+ # Backup current version of the project
+ with cd(env.proj_path):
+ backup("last.db")
+ if env.deploy_tool in env.vcs_tools:
+ with cd(env.repo_path):
+ if env.deploy_tool == "git":
+ run("git rev-parse HEAD > %s/last.commit" % env.proj_path)
+ elif env.deploy_tool == "hg":
+ run("hg id -i > last.commit")
+ with project():
+ static_dir = static()
+ if exists(static_dir):
+ run("tar -cf static.tar --exclude='*.thumbnails' %s" %
+ static_dir)
+ else:
+ with cd(join(env.proj_path, "..")):
+ excludes = ["*.pyc", "*.pio", "*.thumbnails"]
+ exclude_arg = " ".join("--exclude='%s'" % e for e in excludes)
+ run("tar -cf {0}.tar {1} {0}".format(env.proj_name, exclude_arg))
+
+ # Deploy latest version of the project
+ with update_changed_requirements():
+ if env.deploy_tool in env.vcs_tools:
+ vcs_upload()
+ else:
+ rsync_upload()
+ with project():
+ manage("collectstatic -v 0 --noinput")
+ manage("syncdb --noinput")
+ manage("migrate --noinput")
+ for name in get_templates():
+ upload_template_and_reload(name)
+ restart()
+ return True
+
+
+@task
+@log_call
+def rollback():
+ """
+ Reverts project state to the last deploy.
+ When a deploy is performed, the current state of the project is
+ backed up. This includes the project files, the database, and all static
+ files. Calling rollback will revert all of these to their state prior to
+ the last deploy.
+ """
+ with update_changed_requirements():
+ if env.deploy_tool in env.vcs_tools:
+ with cd(env.repo_path):
+ if env.deploy_tool == "git":
+ run("GIT_WORK_TREE={0} git checkout -f "
+ "`cat {0}/last.commit`".format(env.proj_path))
+ elif env.deploy_tool == "hg":
+ run("hg update -C `cat last.commit`")
+ with project():
+ with cd(join(static(), "..")):
+ run("tar -xf %s/static.tar" % env.proj_path)
+ else:
+ with cd(env.proj_path.rsplit("/", 1)[0]):
+ run("rm -rf %s" % env.proj_name)
+ run("tar -xf %s.tar" % env.proj_name)
+ with cd(env.proj_path):
+ restore("last.db")
+ restart()
+
+
+@task
+@log_call
+def all():
+ """
+ Installs everything required on a new system and deploy.
+ From the base software, up to the deployed project.
+ """
+ install()
+ if create():
+ deploy()
import sys
if __name__ == "__main__":
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "diggersdigest.settings")
+
+ from mezzanine.utils.conf import real_project_name
+
+ settings_module = "%s.settings" % real_project_name("diggersdigest")
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings_module)
from django.core.management import execute_from_command_line
--- /dev/null
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import mezzanine.core.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('shop', '0005_auto_20150527_1127'),
+ ('records', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Artist',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Country',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Label',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Record',
+ fields=[
+ ('product_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='shop.Product')),
+ ('artiste', models.CharField(max_length=128)),
+ ('new', models.IntegerField()),
+ ('label', models.CharField(max_length=128)),
+ ('date', models.CharField(max_length=8)),
+ ('pays', models.CharField(max_length=128)),
+ ('desc', models.TextField()),
+ ('cover', models.IntegerField()),
+ ('vinyl', models.IntegerField()),
+ ('audio', mezzanine.core.fields.FileField(max_length=200, verbose_name='Audio File')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('shop.product',),
+ ),
+ ]
from __future__ import unicode_literals
from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from cartridge.shop.models import Product
+
+from mezzanine.core.fields import FileField
+from mezzanine.utils.models import upload_to
+
+# Auto-generated Django models with manage.py inspectdb on the old database
+# You'll have to do the following manually to clean this up:
+# * Make sure each model has one field with primary_key=True
+# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
+# Feel free to rename the models, but don't rename db_table values or field names.
class Gallery(models.Model):
titre = models.CharField(max_length=124)
managed = False
db_table = 'gallery'
+ def __unicode__(self):
+ return self.titre
+
+
class Grading(models.Model):
nom = models.CharField(max_length=5)
managed = False
db_table = 'grading'
+ def __unicode__(self):
+ return self.nom
+
+
class Link(models.Model):
section = models.IntegerField()
managed = False
db_table = 'links'
+ def __unicode__(self):
+ return self.titre
+
class Mix(models.Model):
genre = models.CharField(max_length=128)
managed = False
db_table = 'mix'
+ def __unicode__(self):
+ return self.titre
+
class News(models.Model):
titre = models.CharField(max_length=128)
managed = False
db_table = 'news'
+ def __unicode__(self):
+ return self.titre
+
class Shop(models.Model):
theme = models.IntegerField()
managed = False
db_table = 'shop'
+ def __unicode__(self):
+ return self.titre
+
class Theme(models.Model):
nom = models.CharField(max_length=124)
managed = False
db_table = 'theme'
+ def __unicode__(self):
+ return self.nom
+
class User(models.Model):
username = models.CharField(max_length=10)
class Meta:
managed = False
db_table = 'user'
+
+ def __unicode__(self):
+ return self.username
+
+
+# New models for the 'records' app
+
+class Artist(models.Model):
+ name = models.CharField(max_length=128)
+ def __unicode__(self):
+ return self.name
+
+class Label(models.Model):
+ name = models.CharField(max_length=128)
+ def __unicode__(self):
+ return self.name
+
+class Country(models.Model):
+ name = models.CharField(max_length=128)
+ def __unicode__(self):
+ return self.name
+
+
+
+class Record(Product):
+ """
+ Model for Record
+ """
+ # herited fields (from Product):
+ # title --> shop.titre
+ # categories --> shop.theme
+ # price --> shop.prix
+
+ artiste = models.CharField(max_length=128)
+ new = models.IntegerField()
+ label = models.CharField(max_length=128)
+ date = models.CharField(max_length=8)
+ pays = models.CharField(max_length=128)
+ desc = models.TextField()
+ cover = models.IntegerField() # TODO : choices=GRADINGS)
+ vinyl = models.IntegerField() # TODO : choices=GRADING)
+ audio = FileField(_("Audio File"), max_length=200, format="Audio",
+ upload_to=upload_to("dig2site.Record.audio", "audio"))
+
+ def __unicode__(self):
+ return " - ".join([self.artiste, self.title])
+
--- /dev/null
+Django==1.8.3
+MySQL-python==1.2.5
+cartridge==0.10.0