Table of Contents ================= 1 Getting started 1.1 Dependencies 1.2 Starting the Project 2 Overview 3 Code layout 3.1 Settings 3.2 Templates 3.3 Static Files 3.4 Sites 4 Django-pages-cms integration 4.1 Adding CMS Content Example 4.2 CMS Only Pages 5 Plans & Pricing and Quota 6 Default Plans and Pricing 7 List of third party code 7.1 Django applications 7.1.1 Used 7.1.2 Disabled 7.2 Other code 7.2.1 Python libs 7.2.2 FAMFAM Icons 8 Runtime environment 8.1 Warnings at startup 9 Roadmap 1 Getting started ~~~~~~~~~~~~~~~~~ 1.1 Dependencies ~~~~~~~~~~~~~~~~~ Download all dependencies before proceeding to step 1, You will need the following programs: - Python (2.5 or higher, but may work on earlier versions of Python) - git, - tar accepting "-j" option (any recent GNU tar), 1.2 Starting the Project ~~~~~~~~~~~~~~~~~ 1. Create a database for SaaSkit on your postgresql i.e. "SaasKit" also create relevant "user" and "password" which will be used for connection settings. 2. Make sure postgresql in your PATH. for example your .profile file has the following PATH=$PATH:/Library/PostgreSQL/8.4/bin 3. For Postgres on Snow Leopard 64-BIT SUPPORT Version 2.6 supports 64-bit execution (which is on by default). Version 2.5 only supports 32-bit exe-cution. execution. cution. Like the version of Python, the python command can select between 32 and 64-bit execution (when both are available). Use: % defaults write com.apple.versioner.python Prefer-32-Bit -bool yes to make 32-bit execution the user default (using `/Library/Preferences/com.apple.versioner.python' will set the system-wide default). The environment variable VERSIONER_PYTHON_PREFER_32_BIT can also be used (has precedence over the preference file): % export VERSIONER_PYTHON_PREFER_32_BIT=yes # Bourne-like shells or % setenv VERSIONER_PYTHON_PREFER_32_BIT yes # C-like shells Again, the preference setting and environmental variable applies to both python and pythonw. 4. Create local setting for your development enviornment. echo " DATABASE_ENGINE = 'postgresql_psycopg2' DATABASE_NAME = 'saaskit' DATABASE_USER = 'user' DATABASE_PASSWORD = 'password' DATABASE_HOST = '' DATABASE_PORT = '5432' CACHE_BACKEND = 'dummy:///' MAIN_SITE_PORT = 8001 EMAIL_USE_TLS = True EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = '<gmail account>' EMAIL_HOST_PASSWORD = '<gmail password>' EMAIL_SUBJECT_PREFIX = '[saaskit.org]' EMAIL_PORT = 587 " > ./src/saaskit/local_settings.py 5. python ./bootstrap.py -c ./buildout.cfg # or production.cfg 6. ./bin/buildout -c ./buildout.cfg #or production.cfg 7. ./bin/main_site runserver 8001 # marketing site 8. ./bin/user_site runserver 8000 # user's site 1.3 Deployment ---------- 1. It should be working copy, i.e. buildout have done. 2. export DJANGO_SETTINGS_MODULE=saaskit.settings # replace saaskit by your package name 3. setup/modify fabfile.py with common, stage and production settings for the project. 4. ./bin/fab setup_production --fabfile=./src/saaskit/fabfile.py or setup_stage to setup the enviornment 5. user update_stage, update_production for minor updates and release_production or release_stage from version release same applies for rollback_stage and rollback_production. 2 Overview ~~~~~~~~~~ SaaSkit is a collection of reusable Django applications for creating SaaS sites. It also contains two base site projects ready for deployment: 1. Main, marketing site 2. User site with subdomain per user structure. Because of usage of symbolic links and some path mangling, kit is expected to run on POSIX systems (Linux, Mac OS X, others from UNIX family), and is expected NOT to run on Microsoft Windows (at least not without Cygwin). 3. Code layout ~~~~~~~~~~~~~ 3.1 Settings ~~~~~~~~~~~~~~ Shared settings are in saaskit-core/settings/ module. Local settings should be created in corresponding site's directory. 3.2 Templates ~~~~~~~~~~~~~~~ Templates are stored in saaskit-core/templates/ Project-specific templates to override things live in project's template/ subdir. In some instances, full override of template makes no sense as change between sites is small. In such case, I used shared template and did {% if request.muaccount %} to differentiate: if muaccount object is present, we are inside a muaccount, i.e. in user site. Root template, extended by every other template, is main.html. It contains the root theme, CSS, HTML header and so on. It is very simple, defining only a handful of blocks for child templates to fill in. Here is also defined the footer, in case of main project the sidebar 'your sites', main tabs, right-side navigation. django-templatesadmin ~~~~~~~~~~~~~~~~~~~~~ An app to edit the templates. To enable users to edit the templates add them to a group named 'Editors'. Create it if not present. By default, only 'admin' user has those rights. Listing page is accessible at /admin/templatesadmin/ Templatesadmin may only be used to edit existing templates. But, man, who implements some ui changes, should be able to work with file system without any WEB interface. He should basicaly acquaintance with django template system. 3.3 Static files ~~~~~~~~~~~~~~~~~~~ Static files for each project is in project's site_media subdir. Most of these are symlinks, and (in case of user_sites project) uploaded site logos. Site logos are available only for user_sites. Application-specific static files live in 'media' dir of application. All of them are symlinked to projects' media dir with: $ python manage.py link_app_media django-compress is used for combining and compressing of css and js: $ python manage.py synccompress django-compress uses COMPRESS_CSS, COMPRESS_JS settings to determine what styles, javascripts it should use. 3.4 Sites ~~~~~~~~~~~~ Both projects use different sites from Django's "sites" framework (see [http://docs.djangoproject.com/en/dev/ref/contrib/sites/]); `main' project uses site with ID 1 (default: www.example.com), `user_sites' use site with ID 2 (default: usersites.example.com). This allows to have separate static Pages for both projects. 4 Plans & Pricing and Quota ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Every kind of quota used in the code is listed in settings.py as QUOTAS. After the name is a series of numbers; these are values of limits. After setting or changing the values, manage.py syncdb needs to be run. For every value, permission "quotas | quotas | " is created. Additionally, permission "quotas | quotas | unlimited " is created. These permissions can be then added to users or groups in admin panel. Number set in the permission is the quota value for the group/user. To add permissions / set quotas for free users, use "Registered Members" group. To change quota values seamlessly: 1. Add new values in settings.py, without deleting old ones 2. Run python manage.py syncdb 3. In admin panel, add appropriate quota permissions to users delete unused values from settings.py (permissions may stay in the database, if this is a problem, I may address it later; if values are not in settings.py, old permissions are meaningless). 5 Default Plans and Pricing ~~~~~~~~~~~~~~~~~~~~~~~~~~~ By loading exampledata.json, data is populated with: - Silver Membership monthly recurring plan, linked to Silver Member group, having ability to change muaccount's public status; - Gold Membership monthly recurring plan, linked to Gold Member group, having ability to set custom domain and change muaccount's public status - administrative interface user, login admin, password admin - free_user user, password "free", with no paid plan selected - silver_user user, password "silver", with silver plan selected - gold_user user, password "gold", with gold plan selected - muaccount of free_user with subdomain "free" - muaccount of silver_user with subdomain "silver" - muaccount of gold_user with custom domain "www.gold-account.com" - test1 user, password "test", with no plan selected, member of "free" and "silver" muaccount - test2 user, password "test", with no plan selected, member of "silver" and "gold" account - test3 user, password "test", with no plan selected, member of "gold" account * Muaccounts StopWords ~~~~~~~~~~~~~~~~~~~~~~ Find the following setting in your Settings.py MUACCOUNTS_SUBDOMAIN_STOPWORDS - Tuple of regular expressions that cannot be used as subdomain names. Default is '("^www$",)'. Use this to stop users from using reserved domain names or using profanities as their domain name. Expressions are tested using 're.search', not 're.match', so without using '^' anchor they can match anywhere in the domain name. 6 List of third party code ~~~~~~~~~~~~~~~~~~~~~~~~~~ 6.1 Django applications ~~~~~~~~~~~~~~~~~~~~~~~ 6.1.1 Used ---------- Apps actually used by sample code. * django-registration - [http://bitbucket.org/ubernostrum/django-registration/wiki/Home] * django-contact Contact Us form for the website - [http://github.com/rcoyner/django-contact] * django-compress Consolidates and minifies static CSS and JavaScript files. - [http://github.com/pelme/django-compress/] - [http://code.google.com/p/django-compress/] - [http://code.google.com/p/django-compress/w/list] - [http://github.com/pelme/django-compress/tree/master/docs] * django-debug-toolbar Toolbar that helps debugging Django code. - [http://github.com/robhudson/django-debug-toolbar/] * saaskit-muaccounts Used for multi-user SAAS accounts. - [http://github.com/saas-kit/saaskit-muaccounts/] * django-oembed Used for embedding obejects - [http://code.google.com/p/django-oembed/] - [http://code.google.com/p/django-oembed/wiki/HowToUseIt] - [http://github.com/ericflo/django-oembed/tree/master] * django-tagging Tagging support, used by django-page-cms. - [http://code.google.com/p/django-tagging/] * django-pipes Used for external API consumption, by (TBD) django-mashup. - [git@github.com:saas-kit/django-pipes.git] * saaskit-prepaid Used to support consumable, separately paid quotas (think prepaid phone minutes). - [http://github.com/saas-kit/saaskit-prepaid/tree/master] * django-profiles Used for user profile management on main (shared/dashboard) site. - [http://bitbucket.org/ubernostrum/django-profiles/wiki/Home] - [http://bitbucket.org/ubernostrum/django-profiles/src/tip/docs/overview.txt] - [http://bitbucket.org/ubernostrum/django-profiles/src/c21962558420/docs/views.txt] * django-quotas Used for numeric hard quotas based on regular Django permission system. - [http://github.com/mpasternacki/django-quotas/] * django-notification Used for user notification support. - [http://github.com/jtauber/django-notification/] - [http://github.com/jtauber/django-notification/blob/master/docs/usage.txt] * django-rosetta Used for translating and compiling i18n translation files from Django admin panel. - [http://code.google.com/p/django-rosetta/] - [http://www.djangoproject.com/documentation/i18n/] * django-sso Allow the application to accept single sign on link from other applications and authenticate users - [http://code.google.com/p/django-sso/ * saaskit-subscription Used for user subscription plans/levels. - [http://github.com/saas-kit/saaskit-subscription/] + django-paypal Used by saaskit-subscription for PayPal payments interface. - [http://github.com/johnboxall/django-paypal/] * sorl-thumbnail Thumbnail creation utility for Django - [http://code.google.com/p/sorl-thumbnail/w/list] - [http://code.google.com/p/sorl-thumbnail/] * django-extensions Custom management extensions for Django used in production enviornment. - [http://code.google.com/p/django-command-extensions/] - [http://code.google.com/p/django-command-extensions/w/list] - [http://github.com/django-extensions/django-extensions/] - [http://github.com/django-extensions/django-extensions/tree/master/docs] 7.1.2 Disabled ~~~~~~~~~~~~~~~ Apps that are not currently used by any of sample code, but are included and ready to use. * django-ab A/B testing. - [http://github.com/johnboxall/django-ab/] * django-filter A generic system for filtering Django QuerySets based on user selections - [http://github.com/alex/django-filter/] - [http://github.com/alex/django-filter/tree/master/docs] * django-mailer Used for e-mail queuing and management. - [http://github.com/jtauber/django-mailer/] - [http://code.google.com/p/django-mailer/] - [http://github.com/jtauber/django-mailer/blob/master/docs/usage.txt] * django-page-cms Used for content management. - [http://code.google.com/p/django-page-cms/] - [http://code.google.com/p/django-page-cms/w/list] + django-mptt Django app for keeping tree structures in database, used internally by django-page-cms. - [http://code.google.com/p/django-mptt/] + html5lib Python library for HTML parsing, used internally by django-page-cms. [http://code.google.com/p/html5lib/] * django-piston Framework for creating externally accessible APIs. - [http://bitbucket.org/jespern/django-piston/wiki/Home] - [http://bitbucket.org/jespern/django-piston/wiki/Documentation] - [http://bitbucket.org/jespern/django-piston/wiki/FAQ#faq] 7.2 Other code ~~~~~~~~~~~~~~~ 7.2.1 Python libs ~~~~~~~~~~~~~~~~~ Various Python libs are used in order to support the django apps above, please refer to above apps documentation. 7.2.2 FAMFAM Icons ~~~~~~~~~~~~~~~~~~~~ Generic Icons added Thanks to FAMFAM. More details can be found here. - [http://www.famfamfam.com/lab/icons/silk/] 8 Runtime environment ~~~~~~~~~~~~~~~~~~~~~ Project is expected to run on localhost, port 8000 (or any other port set in MUACCOUNTS_PORT). For all sites to work correctly, following hosts must resolve to 127.0.0.1 (e.g. by adding entry in /etc/hosts): example.com www.example.com free.example.com silver.example.com gold.example.com www.gold-account.com. To deploy on standard port (80 for HTTP), comment out MUACCOUNTS_PORT setting. To succesfully use PayPal sandbox, you'll need to: - sign up for PayPal sandbox at [http://developer.paypal.com/] - configure PAYPAL_RECEIVER_EMAIL and possibly SUBSCRIPTION_PAYPAL_SETTINGS in project/settings.py - make sure your page is visible from outside world (necessary for IPN callbacks) - set your page's IP or root domain (MUACCOUNTS_ROOT_DOMAIN) and port, in form 12.34.56.78:8000 (when deploying on standard port, set just IP or root domain), as `example.com' Site's domain name in admin panel, so that saaskit-subscription can give correct IPN URL to PayPal. To run with live PayPal, you'll need to change {{form.sandbox}} to {{form.render}} in templates/subscription/subscription_detail.html and set PAYPAL_TEST to False in project/settings.py. 8.1 Warnings at startup ~~~~~~~~~~~~~~~~~~~~~~~ When some of dependencies are installed system-wide (especially if installed with easy_install), Django may issue warnings similar to one pasted below: Installing index for admin.LogEntry model Installing index for subscription.Transaction model /opt/local/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/simplejson-2.0.9-py2.5-macosx-10.5-i386.egg/simplejson/_speedups.py:3: UserWarning: Module registration was already imported from /Users/admin/Projects/django-saas-kit/site-python/registration/__init__.py, but /opt/local/lib/python2.5/site-packages/django_registration-0.7-py2.5.egg is being added to sys.path import sys, pkg_resources, imp Such warnings are not important, since they only indicate that system-wide installation of django-registration is not used, and project-local checkout is used instead. 9. Internationalization 1. Adding new language. Go to package folder i. e. that holds 'locale' subfolder. <local path to your project>/bin/main_site mekamessages -l <language code (en, ru, ...)> 2. Compiling messages. <local path to your project>/bin/main_site compilemessages 3. Using 'Rosetta'. Run server locally and request http://www.example.com:8001/admin/rosetta/. Login, then edit any translations you want. After that commit your message files back to repository. And, restart your server. 4. Language choise. Edit LANGUAGES contant in settings to allow or decline any languages your want. For example: ugettext = lambda s: s LANGUAGES = ( ('en', ugettext('English')), ('ru', ugettext('Russian')), ) 10. Roadmap ~~~~~~~~~~ - Support for PDF invoice and more self service account management - Support for Sales/Order data export - Paypal Pro and other payment gateways - Support for multiple user_sites templates, including more intuative theme selection - Integrate Analytics etc. - More what ever the community wants and contributes :)