{ "info": { "author": "Nando Florestan", "author_email": "nandoflorestan@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "============================\nIntroduction to *pluserable*\n============================\n\n*pluserable* provides generic user registration for the Pyramid web framework,\nif your web app uses SQLAlchemy.\n\nIt is a pluggable web application that provides user registration, login,\nlogout and change password functionality. *pluserable* follows a policy of\nminimal interference, so your app can mostly keep its existing models.\n\nThe documentation is at http://docs.nando.audio/pluserable/latest/\n\n- The last version of *pluserable* that supported Python 2 was 0.2.0.\n- *pluserable* 0.5 requires Python >= 3.4.\n- *pluserable* 0.6 requires Python >= 3.5 and has (some) type annotations.\n\n\nMinimal integration\n===================\n\n- Create a virtualenv and activate it. Install pyramid and create\n your Pyramid project.\n\n- Ensure you have some SQLAlchemy declarative initialization. This is usually\n created by the Pyramid scaffold.\n\n- Edit your *setup.py* to add \"pluserable\" to the dependencies in the\n *install_requires* list.\n\n- Run ``python setup.py develop`` on your project to install all dependencies\n into your virtualenv.\n\n- Create models inheriting from pluserable' abstract models.\n Find an example in the file `pluserable/tests/models.py\n `_.\n\n- In your Pyramid configuration file, create a section called \"kerno utilities\"\n like this::\n\n [kerno utilities]\n # Let pluserable know which model classes to use:\n activation class = some.app.models:Activation\n group class = some.app.models:Group\n user class = some.app.models:User\n\n # Give pluserable a SQLAlchemy session factory:\n session factory = some.app.models:get_sqlalchemy_session\n\n- Above you are also pointing to a session factory. Just write a function that\n returns a SQLAlchemy session instance, ready for use. Alternatively,\n it can be a scoped session.\n\n- You may write a function that returns a configuration for Pyramid routes and\n views (which is something you probably want to manipulate in code\n because it won't change between dev, staging and production environments),\n and then inform pluserable about it like this::\n\n registry.settings['pluserable_configurator'] = 'my.package:some_function'\n\n- Your ``pluserable_configurator`` function would look more or less like this::\n\n from pluserable.settings import get_default_pluserable_settings\n\n def my_pluserable(config):\n \"\"\"This function is called by pluserable during app startup.\"\"\"\n adict = get_default_pluserable_settings()\n # Manipulate adict to customize pluserable for your application, then\n return adict\n\n- Include **pluserable** into your Pyramid application,\n just after Pyramid's Configurator is instantiated::\n\n config.include('pluserable')\n\nThis does almost nothing: it only makes a new config method available.\nYou have to use it next::\n\n config.setup_pluserable( # Directive that starts pluserable up\n global_settings['__file__'], # Path to your INI configuration file\n )\n\nThe above causes **pluserable** to read certain sections of your INI file --\nespecially the ``[Kerno utilities]`` section.\n\nThe backend for database access is in a separate class, this way you can\nsubstitute the implementation. This is called the \"repository\" pattern.\nIt is recommended that you use the repository pattern in your app, too.\nThe pluserable repository is instantiated once per request. It is available\nin the ``request.repo`` variable.\n\n- Configure ``pluserable.login_redirect`` and ``pluserable.logout_redirect``\n (in your .ini configuration file) to set the redirection routes.\n\n- If you haven't done so yet, configure an HTTP session factory according to\n the Sessions chapter of the Pyramid documentation.\n\n- Create your database and tables. Maybe even an initial user.\n\n- Be sure to pass an ``authentication_policy`` argument in the\n ``config = Configurator(...)`` call. Refer to Pyramid docs for details.\n\n- By now the login form should appear at /login, but /register shouldn't.\n\n- Include the package pyramid_mailer for the validation e-mail and\n \"forgot password\" e-mail::\n\n config.include('pyramid_mailer')\n\n- The /register form should appear, though ugly. Now you have a choice\n regarding user activation by email:\n\n - You may just disable user activation by setting, in your .ini file::\n\n [pluserable]\n # (other settings, then...)\n require_activation = False\n\n - Otherwise, configure pyramid_mailer `according to its documentation\n `_\n and test the registration page.\n\n- If you are using pyramid_tm or the ZopeTransactionManager, your minimal\n integration is done. (The pages are ugly, but working. Keep reading...)\n\n\nNeed to session.commit()?\n=========================\n\n*pluserable* does not require pyramid_tm or the ZopeTransactionManager with your\nsession but if you do not use them you do have to take one extra step.\nWe don't commit transactions for you because that just wouldn't be nice!\n\nAll you have to do is subscribe to the extension events and\ncommit the session yourself. This also gives you the chance to\ndo some extra processing::\n\n from pluserable.events import (\n PasswordResetEvent, NewRegistrationEvent,\n RegistrationActivatedEvent, ProfileUpdatedEvent)\n\n def handle_request(event):\n request = event.request\n session = request.registry.getUtility(IDBSession)\n session.commit()\n\n self.config.add_subscriber(handle_request, PasswordResetEvent)\n self.config.add_subscriber(handle_request, NewRegistrationEvent)\n self.config.add_subscriber(handle_request, RegistrationActivatedEvent)\n self.config.add_subscriber(handle_request, ProfileUpdatedEvent)\n\n\nWhether or not to have a \"username\" field\n=========================================\n\nIt is important that you analyze the characteristics of your web application and decide whether you need a ``username`` field for users to log in with. pluserable provides 2 modes of operation:\n\n- **email + username:** The user chooses a username when registering and later she can log in by providing either the username or the email address. Therefore, usernames may NOT contain the @ character. **This mode is the default.** It is expressed by the configuration setting ``pluserable.handle = usermail``\n- **email only:** There is no ``username`` field and users only provide their email address. You enable this mode by:\n - Making your User model subclass NoUsernameMixin instead of UsernameMixin;\n - Adding this configuration setting: ``pluserable.handle = email``, which will make pluserable default to schemas that contain email fields instead of username fields.\n\nIf you make this change and want to keep your data you must deal with the existing (or missing) \"username\" column yourself.\n\n\nChanging the forms\n==================\n\nIf you would like to modify any of the forms, you just need\nto register the new deform class to be used.\n\nThe interfaces you have available to override from pluserable.interfaces are:\n\n- IPluserableLoginForm\n- IPluserableRegisterForm\n- IPluserableForgotPasswordForm\n- IPluserableResetPasswordForm\n- IPluserableProfileForm\n\nThis is how you would do it (*MyForm* being a custom deform Form class)::\n\n config.registry.registerUtility(MyForm, IPluserableLoginForm)\n\n\nChanging the templates\n======================\n\nIf you would like to substitute the templates you can use pyramid's\n`override_asset `_::\n\n config.override_asset(to_override='pluserable:templates/template.mako',\n override_with='your_package:templates/anothertemplate.mako')\n\nThe templates you have available to override are:\n\n- login.mako\n- register.mako\n- forgot_password.mako\n- reset_password.mako\n- profile.mako\n\nIf you would like to override the templates with Jinja2, or any other\ntemplating language, just override the view configuration::\n\n config.add_view('pluserable.views.AuthController', attr='login',\n route_name='login', renderer='yourapp:templates/login.jinja2')\n config.add_view('pluserable.views.ForgotPasswordController',\n attr='forgot_password', route_name='forgot_password',\n renderer='yourapp:templates/forgot_password.jinja2')\n config.add_view('pluserable.views.ForgotPasswordController',\n attr='reset_password', route_name='reset_password',\n renderer='yourapp:templates/reset_password.jinja2')\n config.add_view('pluserable.views.RegisterController', attr='register',\n route_name='register', renderer='yourapp:templates/register.jinja2')\n config.add_view('pluserable.views.ProfileController', attr='profile',\n route_name='profile', renderer='yourapp:templates/profile.jinja2')\n\n\nChanging strings\n================\n\nTake a look at `this class\n`_.\nThis is where we store all the strings in *pluserable*.\nIf you'd like to change one or two messages, simply create a subclass\nand configure it::\n\n [kerno utilities]\n # (...bla bla bla...)\n\n # Determining the UI strings is as easy as pointing to a class:\n string class = pluserable.strings:UIStringsBase\n\nHere is an example implementation of a strings class::\n\n class AuthStrings(UIStringsBase):\n \"\"\"Our alterations to the pluserable UI text.\"\"\"\n\n login_done = None # Do not flash a message after the user logs in\n logout_done = None # Do not flash a message after the user logs out\n\n\nChanging the primary key column name\n====================================\n\nIf you wish to override the primary key attribute name, you can do so\nby creating a new mixin class::\n\n class NullPkMixin(Base):\n abstract = True\n _idAttribute = 'pk'\n\n @declared_attr\n def pk(self):\n return Base.pk\n\n @declared_attr\n def id(self):\n return None\n\n class User(NullPkMixin, UserMixin):\n pass\n\n\nDeveloping your application\n===========================\n\nEvery request object will have a \"user\" variable containing the User instance\nof the person who logged in. This is *reified* -- meaning the query to\nretrieve the user data only happens once per request.\n\nSo do use ``request.user`` in your code.\n\n\npluserable development\n======================\n\nSee https://github.com/nandoflorestan/pluserable\n\nIf you would like to help make any changes to *pluserable*, you can run its\nunit tests with py.test:\n\n py.test\n\nTo check test coverage::\n\n py.test --cov-report term-missing --cov pluserable\n\nThe tests can also be run in parallel::\n\n py.test -n4\n\nWe are going to use this build server:\nhttp://travis-ci.org/#!/nandoflorestan/pluserable\n\n\nOrigin of the project\n=====================\n\n*pluserable* is a fork of *horus*, a project started by John Anderson:\nhttps://github.com/eventray/horus\n\nThe differences are:\n\n- *pluserable* lets you log in with an email (or a username);\n *horus* only lets you log in with a username.\n- *pluserable* does not have horus' admin views -- they were rarely used.\n- *pluserable* allows you to pick a subset of the views for your project;\n *horus* always registers all of the routes and views.\n- *horus* had a \"/profile/{user_id}/edit\" URL; but since a user can only\n edit her OWN email and password, we have a simpler URL: \"/edit_profile\".\n- *pluserable* does not include an outdated version of *bootstrap*.\n- *pluserable* does not have a scaffolding script.\n- *pluserable* no longer supports Python 2.\n- *pluserable* uses the bag library for a maintained version of FlashMessage.\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/nandoflorestan/pluserable", "keywords": "authentication", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "pluserable", "package_url": "https://pypi.org/project/pluserable/", "platform": "", "project_url": "https://pypi.org/project/pluserable/", "project_urls": { "Homepage": "https://github.com/nandoflorestan/pluserable" }, "release_url": "https://pypi.org/project/pluserable/0.6.0/", "requires_dist": null, "requires_python": "", "summary": "Generic user registration for the Pyramid web framework", "version": "0.6.0" }, "last_serial": 3850832, "releases": { "0.0.0": [], "0.1.0": [ { "comment_text": "", "digests": { "md5": "cdcc5a42d2155f128170c74751df4c90", "sha256": "06714af1ddaf3fc745f531f14d7a1477f4d07ed478c2bbe0ca52595b3f56d70d" }, "downloads": -1, "filename": "pluserable-0.1.0.tar.gz", "has_sig": false, "md5_digest": "cdcc5a42d2155f128170c74751df4c90", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 28747, "upload_time": "2016-02-24T21:25:05", "url": "https://files.pythonhosted.org/packages/66/db/e3f7c39cf0640fcca2b2fdec316679736a361b9ff24f434afaf0ac9f89ff/pluserable-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "6b593037bb683ff399c6a093fdbf09a6", "sha256": "3dface7ed64ba0d90ea752ec8b8cc7e10a99671644aa79d3c2b9b8db2415db0d" }, "downloads": -1, "filename": "pluserable-0.2.0.tar.gz", "has_sig": false, "md5_digest": "6b593037bb683ff399c6a093fdbf09a6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 29467, "upload_time": "2016-03-11T03:06:14", "url": "https://files.pythonhosted.org/packages/65/b0/b2c4c6ab25526ec3317bea7fdbd0562429c01ddfc0f124798d1566c86186/pluserable-0.2.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "03f971341fda92bf589dd054df3e0baf", "sha256": "c985a1f1e6e9a5e30b23cabc373f3485b94c21a32acfc47b16c4b073492f0e90" }, "downloads": -1, "filename": "pluserable-0.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "03f971341fda92bf589dd054df3e0baf", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 50870, "upload_time": "2017-02-10T04:29:30", "url": "https://files.pythonhosted.org/packages/74/94/9ddc8fa036c4fc971b1420d66589a0ec85f90533a6bb246ac9ac8dff74ab/pluserable-0.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3e4b960a55563d5c33c00640afd2e8e5", "sha256": "816aaf7acd303d6b983c087c8047f2789e33833f603f4e012a0f240b1c3a363c" }, "downloads": -1, "filename": "pluserable-0.3.0.tar.gz", "has_sig": false, "md5_digest": "3e4b960a55563d5c33c00640afd2e8e5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 35470, "upload_time": "2017-02-10T04:29:27", "url": "https://files.pythonhosted.org/packages/91/7e/e6b3e28645f1b3f90d2d7e8d1a0803620591448d096a995824001b5757fa/pluserable-0.3.0.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "58b4f668aab2351336d33ce05f41e7fd", "sha256": "d54156370b78841567bb27424c2f299bf667bc468bbc1d94eda6db9a2c9064c9" }, "downloads": -1, "filename": "pluserable-0.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "58b4f668aab2351336d33ce05f41e7fd", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 51389, "upload_time": "2017-10-19T19:15:16", "url": "https://files.pythonhosted.org/packages/00/b0/0d745e9a8b1c1c9b4e50fd087cf4f87aa0d755f7346726f70d16e34006ba/pluserable-0.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2a88b69a8139594400ea1b822cc5bdc3", "sha256": "5721a37597d27bfcbd3b377fd304e39bca5f47fb89acd99f234718e207f0b8f1" }, "downloads": -1, "filename": "pluserable-0.4.0.tar.gz", "has_sig": false, "md5_digest": "2a88b69a8139594400ea1b822cc5bdc3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36279, "upload_time": "2017-10-19T19:15:13", "url": "https://files.pythonhosted.org/packages/6a/10/493aa76e8c75dc3f98be578cb0684efe27234b6f99fb25963e44577afef6/pluserable-0.4.0.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "0ac357ac5e6b829361ce9b63e057f5c7", "sha256": "46b812ea751f8798100969f81fcce500d9599d7d81202decd03d132aef15b706" }, "downloads": -1, "filename": "pluserable-0.5.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "0ac357ac5e6b829361ce9b63e057f5c7", "packagetype": "bdist_wheel", "python_version": "3.5", "requires_python": null, "size": 51582, "upload_time": "2018-02-07T17:48:09", "url": "https://files.pythonhosted.org/packages/bd/4b/a54eefc2d04bbb5337ac8a4ecc0cbed94092be853da15596bb27da08ba54/pluserable-0.5.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2e047ce9077c6dee8184e810650c6aed", "sha256": "66287fe2aac8840babdab3e7218f1ab8a162233a820c947073045d84466bb5cb" }, "downloads": -1, "filename": "pluserable-0.5.0.tar.gz", "has_sig": false, "md5_digest": "2e047ce9077c6dee8184e810650c6aed", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36218, "upload_time": "2018-02-07T17:48:00", "url": "https://files.pythonhosted.org/packages/71/30/7ce98524f745bf38cb90db8ce2293b82432ae6b55ae8af7004d3b7ec7bcd/pluserable-0.5.0.tar.gz" } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "70318067029d0cb34439c0bd67e6e64c", "sha256": "b2696e77647ab438cbbd7b6aef899e6a9b22afebfd546a8edcbe41eb39d54d3b" }, "downloads": -1, "filename": "pluserable-0.6.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "70318067029d0cb34439c0bd67e6e64c", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 52196, "upload_time": "2018-05-10T15:30:00", "url": "https://files.pythonhosted.org/packages/ae/af/652b122e5ef67a39c08bb1e473b8883d07a1c2a166349494c172143cd7fa/pluserable-0.6.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1869252dffcdecb5ca424b0c90f68c86", "sha256": "107d78568863aa4c6f7f751ffc4eae364d818ea7f2211a5a08ed931bf52cea02" }, "downloads": -1, "filename": "pluserable-0.6.0.tar.gz", "has_sig": false, "md5_digest": "1869252dffcdecb5ca424b0c90f68c86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36449, "upload_time": "2018-05-10T15:29:57", "url": "https://files.pythonhosted.org/packages/cc/37/7aa45495d7b5df6ae385ac0c571f4a0a30414414e9b91300b9fa139196fa/pluserable-0.6.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "70318067029d0cb34439c0bd67e6e64c", "sha256": "b2696e77647ab438cbbd7b6aef899e6a9b22afebfd546a8edcbe41eb39d54d3b" }, "downloads": -1, "filename": "pluserable-0.6.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "70318067029d0cb34439c0bd67e6e64c", "packagetype": "bdist_wheel", "python_version": "3.6", "requires_python": null, "size": 52196, "upload_time": "2018-05-10T15:30:00", "url": "https://files.pythonhosted.org/packages/ae/af/652b122e5ef67a39c08bb1e473b8883d07a1c2a166349494c172143cd7fa/pluserable-0.6.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1869252dffcdecb5ca424b0c90f68c86", "sha256": "107d78568863aa4c6f7f751ffc4eae364d818ea7f2211a5a08ed931bf52cea02" }, "downloads": -1, "filename": "pluserable-0.6.0.tar.gz", "has_sig": false, "md5_digest": "1869252dffcdecb5ca424b0c90f68c86", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36449, "upload_time": "2018-05-10T15:29:57", "url": "https://files.pythonhosted.org/packages/cc/37/7aa45495d7b5df6ae385ac0c571f4a0a30414414e9b91300b9fa139196fa/pluserable-0.6.0.tar.gz" } ] }