Skip to content

zhaque/crowdsense.old

Repository files navigation

Table of Contents
=================
1 Getting started
	1.1 Dependencies
	1.2 Starting the Project
2 Overview
3 Code layout
    3.1 Third party code
        3.1.1 Downloading third-party code
        3.1.2 Updating third-party code
    3.2 Python path
    3.3 Local code
    3.4 Projects
        3.4.1 Settings
        3.4.2 Templates
		3.4.3 Static Files
        3.4.4 Sites
		3.4.5 Shadowbox JS library
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.1 Python libs
    7.2 Other code
        7.2.1 yui-app-theme
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)
	- Stable release of Django 1.0.2 or higher
	- make,
	- git,
	- mercurial,
	- subversion,
	- wget,
	- tar accepting "-j" option (any recent GNU tar).

1.2 Starting the Project
~~~~~~~~~~~~~~~~~
  To start the project, after cloning Git repository, run:

  1. make -C upstream/ init
  2. Edit/Add site-python/local_settings.py according to your enviornment. 
  3. cd projects/main
  4. python manage.py syncdb
  5. python manage.py migrate (after syncdb we migrate the necessary tables using south)
  6. python manage.py loaddata ../../exampledata.json
  7. python manage.py synccompress 
  8. hookup admin media as per settings i.e. cd media; ln -s /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media admin
  9. python manage.py runserver 8001

  (in other shell)
  10. cd projects/user_sites
      (note: once you add application that are specific to user sites, you need to run 'python manage.py syncdb' for those apps)
  11. python manage.py synccompress 
  12. python manage.py runserver
  13. Create a new group 'Editors' for templates admin (http://domain.com/admin/templates) and assign 'admin' user to the 'Editors' group.
 
  Project includes as Git sumodules or downloads from
  upstream/Makefile all needed Django applications; only requirements
  are Python (2.5 or higher, but may work on earlier versions of
  Python) and stable release of Django 1.0.2 or higher.

2 Overview
~~~~~~~~~~
  SaaSkit is a complete SaaS-type website template,
  ready for deployment.  It includes a set of integrated Django
  applications, ready to change sample templates, together with a
  Yahoo YUI-based CSS theme.  It is glued together in different 
  way: no virtualenv with locally used pip/easy_install, no
  project templates, single Git repository intended to be pulled from
  and forked to create a new project.

  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 Third party code
~~~~~~~~~~~~~~~~~
   Directory `[upstream/]' holds third-party and independently
   developed code.  Most of it are Django applications.  Code is
   downloaded primarily directly from version control systems, using
   specified tags/releases; if this is not available, stable archives
   are used.

   Third party code hosted in Git version control system is managed
   using git `submodule' feature (see `git help submodule').  For
   other VC systems and stable archives, `[upstream/Makefile]' defines
   desired release/tag numbers, repositories and commands for
   downloading.  This Makefile handles also initializing and updating
   git submodules.

3.1.1 Downloading third-party code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    To download third-party code (all code after first checkout, or
    added code after updating from git), run `make' command in
    `upstream/' directory (or `make -C upstream' from main dir, as in
    "Getting started" section).  Stamp files ensure that each package
    is downloaded only once.

3.1.2 Updating third-party code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    To update third-party code after desired version is changed:
    - for git submodules, simply run `git submodule update', or
      `make' in `upstream/' directory that will run this command;
    - for archive-based downloading, `make' command in `upstream/'
      directory will handle the update process cleanly (possibly only
      leaving old archive/directory not deleted);
    - for other version control systems, no updating support is
      currently in place.  Either re-download by deleting the
      downloaded repository and related stamp file and issuing `make'
      command in `upstream/' directory, or issue update command in
      the repository by hand.

3.2 Python path
~~~~~~~~~~~~~~~
   Directory `[site-python/]' is configured by the project code to be
   on beginning of Python module search path.  It holds only symbolic
   links to actual Python modules, mostly those located in
   `upstream/', but also to `settings.py' file (as
   `common_settings.py') and to local custom code located in
   `saaskit/' module.

3.3 Local code
~~~~~~~~~~~~~~
   Local code, packaged as single Django app (can grow to multiple
   apps/libraries if needed) is in `saaskit/' directory, linked in
   `site-python/' as `saaskit' Python module / Django app.

3.4 Projects
~~~~~~~~~~~~~
   In `[projects/]' subdirectory, two Django projects are provided.
   The `main' project serves project's main site
   (marketing/info/signup/dashboard site, common for all accounts;
   think [http://basecamphq.com/] marketing site for Basecamp).
   The `user_sites' project serves individual users' SaaS sites
   (think [http://samplecompany.basecamphq.com/] individual Basecamp
   account).

   In these projects, `manage.py' file has been altered to have the
   `site-python/' directory (see /Python path/ earlier) as first
   entry in `sys.path'.  This way, not only it sees all the included
   apps, but they take precedence over whatever is installed in the
   system.  This may be cause of some warning messages (seen with
   django-registration), but these are nothing to worry about.

   For both projects, `project_dir/media/' subdirectory holds static
   files to be served.  Most of content is symlinks to upstream
   project media files.  When run in debug mode, these media are
   served under `/media/' URL, in production they should be served
   directly by a fast HTTP daemon (nginx, lighttpd, or apache) and
   `MEDIA_URL' setting should be change accordingly (see /Deployment/
   later).

   To speed up serving CSS and JavaScript files, django-compress app
   is used.  It consolidates and minifies all needed CSS and
   JavaScript to single files and uses a single file instead of many
   small, separate files.  Usually in production server is not able
   to write the consolidated files, which causes "permission denied"
   errors.  Before deploying, and after each upgrade,
   `python manage.py synccompress' command should be run to create
   consolidated CSS and JavaScript files.

3.4.1 Settings
~~~~~~~~~~~~~~
    Shared settings are in top-level `settings.py' file; it is
    symlinked in `site-python/', as `common_settings.py' and its
    contents are imported on top of both projects' `settings.py'
    files.  Projects' `settings.py' files hold only setting specific
    for each project.  To keep the database consistent, both packages
    should have the same list of apps; the only exception is Django
    admin, used only in `main' app (and this is why
    `manage.py syncdb' should be run from `main' app directory).

3.4.2 Templates
~~~~~~~~~~~~~~~
	Most templates are shared between two projects (apps actually 
	used by both are distinct, and main theme is common). Shared 
	templates live in  templates/ dir, project-specific ones 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 'TemplateAdmins'. Create it if not present.

3.4.3 Static files
~~~~~~~~~~~~~~~~~~~
	Static files for each project is in project's 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.

	Possibly static file storage can be factored out anyway to 
	a common dir, and to separate domain. This could simplify 
	things a bit, and enable us to export static file hosting to
	the cloud, or at least out of the application server.

	The root media/ directory contains shared static files (currently,
	only a bit of local CSS). It is linked as 'saaskit' subdirectory
	of both projects' media dirs.
		
3.4.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.

3.4.5 Shadowbox JS library
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Shadowbox v3.0b has been used in the project. The stylesheet used by 
    shadowbox is located under /media/css while the images are stored 
    under /media/img/sb-resources. The entire JS library has been dumped
    under /media/js/sb/. For such an implementation, the paths to the 
    images has been modified in the stylesheet and also the colors of the
    text shown in the pop-ups. To use Shadowbox, simply include the 
    stylesheet and the js library(shadowbox.js). Then, change the link 
    markups to include the attribute <rel="shadowbox"> for single popup
    and <rel="shadowbox[Group]"> to group them as a collection/gallery.

4 Django-pages-cms integration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Django-pages-cms is integrated into project to provide dynamic
	content for otherwise static templates (e.g. registration screen).

	Two templates for this kind of content are provided:
		- `Single body' for templates using single body of data, with
	    page title and single block named `body',
	  	- `Content before and after dynamic content' for templates using a
	    block of text before and block of text after the dynamic content,
	    with page title and blocks `before' and `after'.

	For actual page-cms content the default template should be used.

	Different templates contain hard-coded references to a page with a
	specific slug.  That page can be located anywhere in django-page-cms
	hierarchy and should be created as a hidden page (so that it is not
	displayed in navigation).  Table of used slug names and desired
	page templates follows.

	   Template                              Slug                Page template     
	  -------------------------------------+-------------------+------------------
	   index.html                            main-page           Single body       
	   registration/registration_form.html   registration-form   Before and after  
	   subscription/subscription_list.html   subscription-list   Before and after  

	Based on these, template editor (webmaster) can insert content from
	page-cms pages into any other template where needed.  Other page
	templates (for e.g. three-part content) can be added by:
	  - Adding page template in `templates/page-templates/' directory,
	    basing on existing ones,
	  - Adding newly created template to `PAGE_TEMPLATES' in Django
	    settings,
	  - Documenting it in README.org and README.

	For backing up your pages execute the following command from your console:

	python manage.py dumpdata pages > pages.json 
	Store pages.json for later use or in a repo.

4.1 Adding CMS Content Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	An example using Registration_form.html, for Main Site template related pages

		1. Locate the template: templates/registration/registration_form.html
		2. Examine the template and find places in HTML that you would need to
		   fill with content. Here, we have two places: before the form and 
		   after the form.
		3. Look into templates/page-templates/ contents, see whether 
		   there exists a template for this number and kind of content fields.
		4. If it exists, remember its name. If it does not, create new template
	       with correct number and names of placeholders, based on existing ones 
		   (only placeholders count, other content is not used anywhere; 
		   they also define what kind of widget WYMEditor, plain textarea, 
		   some else WYSIWYG JS editor will be used in admin panel in CMS). 
		   Add it to PAGE_TEMPLATES in settings.py. Information about 
		   page template placeholders: 
		   http://code.google.com/p/django-page-cms/wiki/PlaceHolders
		5. Choose unique (not used yet) slug for the CMS page 
		   (here: 'registration-form'), remember it.
		   Add {% load pages_tags } after { extends 'main.html' } in the template.
		   Add { show_content %} tags where CMS content should be included, 
		   using slug chosen in step 4, and placeholder names defined by page 
		   template from step 3. 
		   See also http://code.google.com/p/django-page-cms/wiki/DisplayContentInTemplates
		6. In admin panel, go to 'Pages' ? 'Add page'. Choose title, set slug to 
		   slug chosen in step 4, template to template chosen in step 3. 
		   In Sites, choose appropriate Site object (unfortunately Pages shows 
		   only domain, not name of Site; in example data, www.example.com is
		   main site, example.com are user sites. Set Site domains correctly in
		   production database, these are used for links in e-mails!)
		7. Content should be visible on site.
		8. Page added in step 6 can be added anywhere in CMS hierarchy. 
		   I suggest using a single root placeholder Page, e.g. __templates__, 
		   keep all related pages as its children, and related templates in 
		   hierarchy (e.g. __templates__/password-change-form/password-change-successful).
		   The root -templates subdir can be then filtered in urls.py so that 
		   no one accesses these pages directly at 
		   e.g. /pages/__templates__/registration-form/ (which itself is not a problem,
		   but is not elegant and Google may see it as duplicate content).

4.2 CMS Only Pages (Main Site)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	Pages not related to any template can be edited in CMS only, they will be
	visible under /pages/hierarchy/slug URLs and their addresses can be 
	inserted in templates as {% show_absolute_url 'slug } 
	(with { load pages_tags }), or with { url pages-details-by-slug 'slug' %} 
	(without pages_tags). They will be also visible in page navigation.

	By default, templates/page.html is used to display; you may add other 
	templates as well (I suggest to place those in templates/page-templages/) 
	and use them instead. In this setup, the template actual contents will be used,
	and placeholders will be replaced with the dynamic content.

5 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).
		
6 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.

7 List of third party code
~~~~~~~~~~~~~~~~~~~~~~~~~~

7.1 Django applications
~~~~~~~~~~~~~~~~~~~~~~~

7.1.1 Used
----------
    Apps actually used by sample code.
* django-authopenid
  Used for signup/login process.
  - [http://bitbucket.org/benoitc/django-authopenid/wiki/Home]
  - [http://bitbucket.org/benoitc/django-authopenid/wiki/Getting_started]
  + django-registration
    Used internally by django-authopenid.
    - [http://bitbucket.org/ubernostrum/django-registration/wiki/Home]
  + python-openid
    Python library for OpenID, used internally by django-authopenid.
    - [http://openidenabled.com/python-openid/]
* django-contact-form
  Contact Us form for the website
  - [http://bitbucket.org/ubernostrum/django-contact-form/wiki/Home]
  - [http://bitbucket.org/ubernostrum/django-contact-form/overview/]
* 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/]
* django-faq
  Used for frequently asked question list.
  - [http://github.com/howiworkdaily/django-faq/]
* django-muaccounts
  Used for multi-user SAAS accounts.
  - [http://github.com/CrowdSense/django-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-perfect404
  A perfect 404 page, based on A List Apart's article.
  Used only when `DEBUG=False'.
  - [http://github.com/svetlyak40wt/django-perfect404/]
* django-pipes
  Used for external API consumption, by (TBD) django-mashup.
  - [http://github.com/mallipeddi/django-pipes/]
* django-prepaid
  Used to support consumable, separately paid quotas (think prepaid
  phone minutes).
  - [http://github.com/CrowdSense/django-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/  
* django-subscription
  Used for user subscription plans/levels.
  - [http://github.com/CrowdSense/django-subscription/]
  + django-paypal
    Used by django-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 yui-app-theme
~~~~~~~~~~~~~~~~~~~~
    A generic, skinnable, Yahoo YUI-based layot for web applications.
    - [http://clickontyler.com/yui-app-theme/]
    - [http://github.com/tylerhall/yui-app-theme/]

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 django-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. 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 :)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published