Package: tgapp-userprofile
About userprofileuserprofile is a Pluggable application for TurboGears2 which provides a basic user
profile page with forms to allow users to edit their own profile or change their password or email.userprofile works with bothsqlachemyandmingbackends.Installinguserprofile can be installed both from pypi or from github:pip install tgapp-userprofileshould just work for most of the usersPlugging userprofileIn your applicationconfig/app_cfg.pyimportplug:from tgext.pluggable import plugThen at theend of the filecall plug with userprofile:plug(base_config, 'userprofile')You will be able to access your profile athttp://localhost:8080/userprofile.Optionstgapp-userprofilesupports some options that can be passed
to theplugmethod to customize various aspects of the application:user_partial- Path of a partial to display into the user profile page.Useful to add more data to the profile page without changing its templatecustom_css- Path to a CSS file which will be used for the profile pages in place of the default one.User Propertiestgapp-userprofilelooks for various properties into the User class instances
to drive its default behavior, the most important property is theprofile_dataproperty which can provide a dictionary with the user information to display
on the profile page, but other properties are available to tune the behavior:profile_dataA dictionary of entries to display into the profile page,
the default dictionary is built with:{'display_name':('Display Name', user.display_name),
 'email_address':('Email Address', user.email_address)}each key of the dictionary if the id of the field, in most
cases it will have the same name of the user property where
that field is stored. Values of the dictionary are tuples
where the first value is the name of the field which will
be displayed and the second one is the real value of the field.If anavatarkey is available that is expected to provide
the url of the avatar image of the user. If it is not available
userprofile will look for atgapp-fbauthfacebook avatar or will
falleback to the default avatar.display_namekey will be used as the profile page title.profile_formA ToscaWidgets or tw2 form that can be used to edit the user profile.
By default an autogenerated one with a text field for each entry inprofile_datais provided.save_profileA callable which will receive the user data submitted by the edit
form and is expected to update the user accordingly.By default values will be stored as they are into the user field
with the same id provided intoprofile_data.Bootstrap LayoutIf you want use bootstrap for beautify style ofUserFormorChangePasswordFormform layout, in your app_cfg:def replace_profile_form_layout():
    from axf.bootstrap import BootstrapFormLayout
    from userprofile.lib import UserForm
    from userprofile.lib import ChangePasswordForm

    UserForm.child = BootstrapFormLayout(children=UserForm.child.children)
    UserForm.submit.css_class = 'btn-primary form-control'

    ChangePasswordForm.child = BootstrapFormLayout(children=ChangePasswordForm.child.children)
    ChangePasswordForm.submit.css_class = 'btn-primary form-control'

milestones.config_ready.register(replace_profile_form_layout)Authentication ConfigurationSince withuserprofilea user can change his email address or user name, you have to configurerepoze.whoproperly.
Inapp_cfg.py, locate theauthenticatemethod ofApplicationAuthMetadata: it should return the id of the user.
Nowrepoze.whowill save the user id in a cookie to identify the user and since this id will not change, the
authentication process allows users to change email addresses and user names safely.This example illustrate a login based on user name or email address with Ming:class ApplicationAuthMetadata(TGAuthMetadata):
    def __init__(self, sa_auth):
        self.sa_auth = sa_auth

    def get_query(self, login):

        try:
            _id = ObjectId(login)
        except InvalidId:
            _id = login

        return {
            '$or': [{'email_address': login},
                    {'user_name': login},
                    {'_id': _id}],
            'blocked': {'$ne': True},
        }

    def authenticate(self, environ, identity):
        login = identity['login']
        user = self.sa_auth.user_class.query.find(self.get_query(login)).first()

        if not user:  # pragma: no cover
            login = None
        elif not user.validate_password(identity['password']):
            login = None

        if login is None:
            try:
                from urllib.parse import parse_qs, urlencode
            except ImportError:
                from urlparse import parse_qs
                from urllib import urlencode
            from tg.exceptions import HTTPFound

            params = parse_qs(environ['QUERY_STRING'])
            params.pop('password', None)  # Remove password in case it was there
            if user is None:  # pragma: no cover
                params['failure'] = 'user-not-found'
            else:
                params['login'] = identity['login']
                params['failure'] = 'invalid-password'

            # When authentication fails send user to login page.
            environ['repoze.who.application'] = HTTPFound(
                location=environ['SCRIPT_NAME'] + '?'.join(('/login', urlencode(params, True)))
            )

        return str(user._id) if user and login else login

    def get_user(self, identity, userid):
        return self.sa_auth.user_class.query.find(self.get_query(userid)).first()

    def get_groups(self, identity, userid):
        return [g.group_name for g in identity['user'].groups]

    def get_permissions(self, identity, userid):
        return [p.permission_name for p in identity['user'].permissions]


base_config.sa_auth.authmetadata = ApplicationAuthMetadata(base_config.sa_auth)