'returns a form that lets the user change his/her profile
.. method:: Auth.profile([next=DEFAULT [, onvalidation=DEFAULT
[, onaccept=DEFAULT [, log=DEFAULT]]]])
Patched for S3 to use s3_mark_required and handle opt_in mailing lists'

def profile(self, next=DEFAULT, onvalidation=DEFAULT, onaccept=DEFAULT, log=DEFAULT):

    if (not self.is_logged_in()):
        redirect(self.settings.login_url)
    messages = self.messages
    settings = self.settings
    utable = settings.table_user
    passfield = settings.password_field
    utable[passfield].writable = False
    request = current.request
    session = current.session
    deployment_settings = current.deployment_settings
    if deployment_settings.get_auth_show_utc_offset():
        utable.utc_offset.readable = True
        utable.utc_offset.writable = True
    utable.organisation_id.writable = False
    if (next == DEFAULT):
        next = (request.get_vars._next or request.post_vars._next or settings.profile_next)
    if (onvalidation == DEFAULT):
        onvalidation = settings.profile_onvalidation
    if (onaccept == DEFAULT):
        onaccept = settings.profile_onaccept
    if (log == DEFAULT):
        log = messages.profile_log
    (labels, required) = s3_mark_required(utable)
    opt_in_to_email = deployment_settings.get_auth_opt_in_to_email()
    if opt_in_to_email:
        team_list = deployment_settings.get_auth_opt_in_team_list()
        if request.post_vars:
            removed = []
            selected = []
            for opt_in in team_list:
                if (opt_in in request.post_vars):
                    selected.append(opt_in)
                else:
                    removed.append(opt_in)
            db = current.db
            s3db = current.s3db
            ptable = s3db.pr_person
            putable = s3db.pr_person_user
            query = ((putable.user_id == request.post_vars.id) & (putable.pe_id == ptable.pe_id))
            person_id = db(query).select(ptable.id, limitby=(0, 1)).first().id
            db((ptable.id == person_id)).update(opt_in=selected)
            g_table = s3db['pr_group']
            gm_table = s3db['pr_group_membership']
            for team in removed:
                query = (((g_table.name == team) & (gm_table.group_id == g_table.id)) & (gm_table.person_id == person_id))
                gm_rec = db(query).select(g_table.id, limitby=(0, 1)).first()
                if gm_rec:
                    db((gm_table.id == gm_rec.id)).delete()
            for team in selected:
                query = (((g_table.name == team) & (gm_table.group_id == g_table.id)) & (gm_table.person_id == person_id))
                gm_rec = db(query).select(g_table.id, limitby=(0, 1)).first()
                if (not gm_rec):
                    query = (g_table.name == team)
                    team_rec = db(query).select(g_table.id, limitby=(0, 1)).first()
                    if (team_rec == None):
                        team_id = g_table.insert(name=team, group_type=5)
                    else:
                        team_id = team_rec.id
                    gm_table.insert(group_id=team_id, person_id=person_id)
    formstyle = deployment_settings.get_ui_formstyle()
    current.response.form_label_separator = ''
    form = SQLFORM(utable, self.user.id, fields=settings.profile_fields, labels=labels, hidden=dict(_next=next), showid=settings.showid, submit_button=messages.profile_save_button, delete_label=messages.delete_label, upload=settings.download_url, formstyle=formstyle, separator='')
    form.add_class('auth_profile')
    if deployment_settings.get_auth_openid():
        from gluon.contrib.login_methods.openid_auth import OpenIDAuth
        openid_login_form = OpenIDAuth(self)
        form = DIV(form, openid_login_form.list_user_openids())
    if form.accepts(request, session, formname='profile', onvalidation=onvalidation, hideerror=settings.hideerror):
        self.auth_user_onaccept(form.vars.email, self.user.id)
        self.user.update(utable._filter_fields(form.vars))
        session.flash = messages.profile_updated
        if log:
            self.log_event(log, self.user)
        callback(onaccept, form)
        if (not next):
            next = self.url(args=request.args)
        elif isinstance(next, (list, tuple)):
            next = next[0]
        elif (next and (not (next[0] == '/')) and (next[:4] != 'http')):
            next = self.url(next.replace('[id]', str(form.vars.id)))
        redirect(next)
    if opt_in_to_email:
        T = current.T
        ptable = s3db.pr_person
        ltable = s3db.pr_person_user
        team_list = deployment_settings.get_auth_opt_in_team_list()
        query = ((ltable.user_id == form.record.id) & (ltable.pe_id == ptable.pe_id))
        db_opt_in_list = db(query).select(ptable.opt_in, limitby=(0, 1)).first().opt_in
        for opt_in in team_list:
            field_id = ('%s_opt_in_%s' % (utable, team_list))
            if (opt_in in db_opt_in_list):
                checked = 'selected'
            else:
                checked = None
            s3_addrow(form, LABEL((T('Receive    %(opt_in)s    updates:') % dict(opt_in=opt_in)), _for='opt_in', _id=(field_id + SQLFORM.ID_LABEL_SUFFIX)), INPUT(_name=opt_in, _id=field_id, _type='checkbox', _checked=checked), '', formstyle, (field_id + SQLFORM.ID_ROW_SUFFIX))
    return form
