changeset 2228:c992a1dda217

Merged thomaswaldmann/moin-2.0 into default
author Ashutosh Singla <ashu1461@gmail.com>
date Mon, 22 Jul 2013 22:51:34 +0530
parents 00debb5be61a (current diff) 78ed0ce6b718 (diff)
children 63bc1dd3d321
files MoinMoin/apps/frontend/views.py MoinMoin/constants/keys.py MoinMoin/items/__init__.py MoinMoin/templates/common.js MoinMoin/templates/ticket.html
diffstat 19 files changed, 196 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sat Jul 20 23:54:16 2013 +0530
+++ b/.hgignore	Mon Jul 22 22:51:34 2013 +0530
@@ -8,6 +8,8 @@
 ^moin.egg-info/
 ^MoinMoin/_tests/wiki/data/cache/
 ^wiki/data/cache/
+^wiki/data/default/data/
+^wiki/data/default/meta/
 ^wiki/data/content/
 ^wiki/data/userprofiles/
 ^wiki/index/
--- a/MoinMoin/apps/frontend/views.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/apps/frontend/views.py	Mon Jul 22 22:51:34 2013 +0530
@@ -1168,12 +1168,29 @@
     if 'username' in request.values and 'token' in request.values:
         u = user.User(auth_username=request.values['username'])
         token = request.values['token']
-    if u and u.disabled and token and u.validate_recovery_token(token):
-        u.profile[DISABLED] = False
+    success = False
+    if u and token and u.validate_recovery_token(token):
+        unvalidated_email = u.profile[EMAIL_UNVALIDATED]
+        if (app.cfg.user_email_unique and
+            user.search_users(**{EMAIL: unvalidated_email})):
+            msg = _('This email is already in use.')
+        else:
+            if u.disabled:
+                u.profile[DISABLED] = False
+                msg = _('Your account has been activated, you can log in now.')
+            else:
+                msg = _('Your new email address has been confirmed.')
+            u.profile[EMAIL] = unvalidated_email
+            del u.profile[EMAIL_UNVALIDATED]
+            del u.profile[RECOVERPASS_KEY]
+            success = True
+    else:
+        msg = _('Your username and/or token is invalid!')
+    if success:
         u.save()
-        flash(_("Your account has been activated, you can log in now."), "info")
+        flash(msg, 'info')
     else:
-        flash(_('Your username and/or token is invalid!'), "error")
+        flash(msg, 'error')
     return redirect(url_for('.show_root'))
 
 
@@ -1539,21 +1556,19 @@
                             flaskg.user.profile[k] = v
                         if (part == 'notification' and app.cfg.user_email_verification and
                             form['email'].value != user_old_email):
-                            # disable account
-                            flaskg.user.profile[DISABLED] = True
+                            flaskg.user.profile[EMAIL] = user_old_email
+                            flaskg.user.profile[EMAIL_UNVALIDATED] = form['email'].value
                             # send verification mail
                             is_ok, msg = flaskg.user.mail_email_verification()
                             if is_ok:
-                                flaskg.user.logout_session()
-                                response['flash'].append((_('Your account has been disabled because you changed your '
-                                                            'email address. Please see the email we sent to your '
-                                                            'address to reactivate it.'), "info"))
+                                response['flash'].append(
+                                    (_('A confirmation email has been sent to your '
+                                       'newly configured email address.'), "info"))
                                 response['redirect'] = url_for('.show_root')
                             else:
-                                # sending the verification email didn't work. reset email change and alert the user.
-                                flaskg.user.profile[DISABLED] = False
-                                flaskg.user.profile[EMAIL] = user_old_email
-                                flaskg.user.save()
+                                # sending the verification email didn't work.
+                                # delete the unvalidated email and alert the user.
+                                del flaskg.user.profile[EMAIL_UNVALIDATED]
                                 response['flash'].append((_('Your email address was not changed because sending the '
                                                             'verification email failed. Please try again later.'),
                                                           "error"))
--- a/MoinMoin/config/default.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/config/default.py	Mon Jul 22 22:51:34 2013 +0530
@@ -502,6 +502,7 @@
             RESULTS_PER_PAGE: 0,
             LOCALE: None,  # None -> do browser language detection, otherwise just use this locale
             TIMEZONE: None,  # None -> use cfg.timezone_default
+            EMAIL_UNVALIDATED: None,
         }, 'Default attributes of the user object'),
     )),
     # ==========================================================================
--- a/MoinMoin/constants/keys.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/constants/keys.py	Mon Jul 22 22:51:34 2013 +0530
@@ -71,7 +71,7 @@
 QUICKLINKS = u"quicklinks"
 SESSION_KEY = u"session_key"
 SESSION_TOKEN = u"session_token"
-RECOVERPASS_KEY = u"recoverpass_key"
+RECOVERPASS_KEY = u"recoverpass_key"  # TODO: this is used for email confirmation as well, maybe it needs better name
 EDIT_ON_DOUBLECLICK = u"edit_on_doubleclick"
 SCROLL_PAGE_AFTER_EDIT = u"scroll_page_after_edit"
 SHOW_COMMENTS = u"show_comments"
@@ -82,6 +82,7 @@
 WANT_TRIVIAL = u"want_trivial"
 EMAIL_SUBSCRIBED_EVENTS = u"email_subscribed_events"
 DISABLED = u"disabled"
+EMAIL_UNVALIDATED = u"email_unvalidated"
 
 # in which backend is some revision stored?
 BACKENDNAME = u"backendname"
--- a/MoinMoin/converter/_tests/test_docbook_in.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/converter/_tests/test_docbook_in.py	Mon Jul 22 22:51:34 2013 +0530
@@ -256,13 +256,13 @@
     def test_link(self):
         data = [
             # Normal link, with conversion of all the xlink attributes
-            ('<article><para><link xlink:href="uri:test" xlink:title="title">link</link></para></article>',
-                # <page><body><div html:class="article"><p><a xlink:href="uri:test" xlink:title="title">link</a></p></div></body></page>
-                '/page/body/div/p/a[@xlink:href="uri:test"][@xlink:title="title"][text()="link"]'),
+            ('<article><para><link xlink:href="http:test" xlink:title="title">link</link></para></article>',
+                # <page><body><div html:class="article"><p><a xlink:href="http:test" xlink:title="title">link</a></p></div></body></page>
+                '/page/body/div/p/a[@xlink:href="http:test"][@xlink:title="title"][text()="link"]'),
             # Old link from DocBook v.4.X for backward compatibility
-            ('<article><para><ulink url="url:test">link</ulink></para></article>',
-                # <page><body><div html:class="article"><p><a xlink:href="url:test">link</a></p></div></body></page>
-                '/page/body/div/p/a[@xlink:href="url:test"][text()="link"]'),
+            ('<article><para><ulink url="http:test">link</ulink></para></article>',
+                # <page><body><div html:class="article"><p><a xlink:href="http:test">link</a></p></div></body></page>
+                '/page/body/div/p/a[@xlink:href="http:test"][text()="link"]'),
             # Normal link, with linkend attribute
             ('<article><para><link linkend="anchor">link</link></para></article>',
                 # <page><body><div html:class="article"><p><a xlink:href="#anchor">link</a></p></div></body></page>
@@ -273,8 +273,10 @@
                 '/page/body/div/p/a[@xlink:href="uri#anchor"][text()="link"]'),
             # Link w/ javascript: scheme
             ('<article><para><ulink url="javascript:alert(\'xss\')">link</ulink></para></article>',
-                # <page><body><div html:class="article"><p><a xlink:href="url:test">link</a></p></div></body></page>
-                '/page/body/div/p/a[@xlink:href=""][text()="link"]'),
+                # the href attribute will default to None because javascript is not an allowed url scheme
+                # we don't care how it gets rendered as long as the javascript doesn't show up
+                # <page><body><div html:class="article"><p><a xlink:href="None">link</a></p></div></body></page>
+                '/page/body/div/p/a[@xlink:href="None"][text()="link"]'),
         ]
         for i in data:
             yield (self.do, ) + i
--- a/MoinMoin/items/__init__.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/items/__init__.py	Mon Jul 22 22:51:34 2013 +0530
@@ -442,11 +442,11 @@
             # just destroy this revision
             self.rev.item.destroy_revision(self.rev.revid)
 
-    def modify(self, meta, data, comment=u'', contenttype_guessed=None, contenttype_qs=None):
-        if contenttype_qs:
-            # we use querystring param to FORCE content type
-            meta[CONTENTTYPE] = contenttype_qs
-
+    def modify(self, meta, data, comment=u'', contenttype_guessed=None, **update_meta):
+        meta = dict(meta)  # we may get a read-only dict-like, copy it
+        # get rid of None values
+        update_meta = {key:value for key, value in update_meta.items() if value is not None}
+        meta.update(update_meta)
         return self._save(meta, data, contenttype_guessed=contenttype_guessed, comment=comment)
 
     class _ModifyForm(BaseModifyForm):
@@ -790,7 +790,7 @@
                 meta, data, contenttype_guessed, comment = form._dump(self)
                 contenttype_qs = request.values.get('contenttype')
                 try:
-                    self.modify(meta, data, comment, contenttype_guessed, contenttype_qs)
+                    self.modify(meta, data, comment, contenttype_guessed, **{CONTENTTYPE: contenttype_qs})
                 except AccessDenied:
                     abort(403)
                 else:
--- a/MoinMoin/items/_tests/test_Item.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/items/_tests/test_Item.py	Mon Jul 22 22:51:34 2013 +0530
@@ -352,6 +352,25 @@
             item.meta['test_key']
         assert item.meta['another_test_key'] == another_meta['another_test_key']
         assert item.content.data == another_data
+        # add/update meta
+        another_meta = {
+            'test_key': 'test_value',
+            'another_test_key': 'another_test_value',
+        }
+        item.modify(another_meta, another_data)
+        item = Item.create(name)
+        update_meta = {
+            'another_test_key': 'updated_test_value',
+            'new_test_key': 'new_test_value',
+            'none_test_key': None,
+        }
+        item.modify(another_meta, another_data, **update_meta)
+        item = Item.create(name)
+        assert item.name == u'Test_Item'
+        assert item.meta['test_key'] == another_meta['test_key']
+        assert item.meta['another_test_key'] == update_meta['another_test_key']
+        assert item.meta['new_test_key'] == update_meta['new_test_key']
+        assert 'none_test_key' not in item.meta
 
 
 coverage_modules = ['MoinMoin.items']
--- a/MoinMoin/items/ticket.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/items/ticket.py	Mon Jul 22 22:51:34 2013 +0530
@@ -138,7 +138,8 @@
     itemtype = ITEMTYPE_TICKET
     display_name = L_('Ticket')
     description = L_('Ticket item')
-    modify_template = 'ticket.html'
+    submit_template = 'ticket/submit.html'
+    modify_template = 'ticket/modify.html'
 
     def do_show(self, revid):
         if revid != CURRENT:
@@ -169,7 +170,7 @@
         # XXX When creating new item, suppress the "foo doesn't exist. Create it?" dummy content
         data_rendered = None if is_new else Markup(self.content._render_data())
 
-        return render_template(self.modify_template,
+        return render_template(self.submit_template if is_new else self.modify_template,
                                is_new=is_new,
                                closed=closed,
                                item_name=self.name,
--- a/MoinMoin/templates/base.html	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/templates/base.html	Mon Jul 22 22:51:34 2013 +0530
@@ -41,6 +41,11 @@
     {% endblock %}
 
     {% block head_links %}
+
+    {% block bootstrap_stylesheet %}
+    <link rel="stylesheet" href="{{ url_for('serve.files', name='bootstrap', filename='bootstrap/css/bootstrap.css') }}" />
+    {% endblock %}
+
     <link rel="shortcut icon" href="{{ url_for('static', filename='logos/favicon.ico') }}" />
 
     {% block theme_stylesheets %}
--- a/MoinMoin/templates/common.js	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/templates/common.js	Mon Jul 22 22:51:34 2013 +0530
@@ -587,7 +587,7 @@
             // is option to scroll page after edit set?
             if (document.getElementById('moin-scroll-page-after-edit')) {
                 // add click handler to OK (save) button to capture position of caret in textarea
-                $("#f_submit").click(function () {
+                $("#moin-save-text-button").click(function () {
                     caretLineno = getCaretLineno(document.getElementById('f_content_form_data_text'));
                     // save lineno for use in "show" page load
                     if (caretLineno > 0) { sessionStorage.moinCaretLineNo = caretLineno; }
--- a/MoinMoin/templates/mail/account_verification.txt	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/templates/mail/account_verification.txt	Mon Jul 22 22:51:34 2013 +0530
@@ -1,10 +1,11 @@
 {{ _("""\
-Somebody has created an account with this email address.
+An account with this email address has been created or
+an existing account has been changed to use this email address.
 
 Please use the link below to verify your email address:
 
 %(link)s
 
-If you didn't create this account, please ignore this email.
+Please ignore this email if you didn't initiate this action.
 
-""", link=link) }}
\ No newline at end of file
+""", link=link) }}
--- a/MoinMoin/templates/modify.html	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/templates/modify.html	Mon Jul 22 22:51:34 2013 +0530
@@ -40,7 +40,7 @@
        POSTs originate from their respective applets.
     #}
     {% if not form['content_form'].is_draw %}
-        {{ forms.render_submit(form) }}
+        {{ forms.render_submit(form, id='moin-save-text-button') }}
         <dl>
             {{ forms.render_textcha(gen, form) }}
             {{ forms.render(form['comment']) }}
--- a/MoinMoin/templates/ticket.html	Sat Jul 20 23:54:16 2013 +0530
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-{% import "forms.html" as forms %}
-{% extends theme("layout.html") %}
-
-{% if is_new %}
-    {% set title = _("Creating new ticket: '%(item_name)s'", item_name=item_name) %}
-{% else %}
-    {% set title = _("Ticket: '%(item_name)s'", item_name=item_name) %}
-{% endif %}
-
-{% block head %}
-    {{ super() }}
-    <link media="all" rel="stylesheet" href="{{ url_for('static', filename='css/ticket.css') }}" />
-{% endblock %}
-
-{% block content %}
-<h1>
-    {{ title }}
-    {% if closed %}
-        {# TODO style .moin-ticket-closed #}
-        <span class="moin-ticket-closed">{{ _("(Closed)") }}</span>
-    {% endif %}
-</h1>
-<div class="moin-form" id="moin-ticket-form">
-    {{ gen.form.open(form, method='post', enctype='multipart/form-data') }}
-
-    {% if data_rendered %}
-        <div id="moin-content-data">
-            {{ data_rendered }}
-        </div>
-    {% endif %}
-
-    {% if is_new %}
-        <h2>Describe the ticket</h2>
-    {% else %}
-        <h2>Add comment</h2>
-    {% endif %}
-    <dl>
-    {{ forms.render(form['message']) }}
-    </dl>
-
-    <h2>Edit metadata</h2>
-    <dl>
-    {{ forms.render_errors(form) }}
-    {% for e in [
-        'summary',
-        'effort',
-        'difficulty',
-        'severity',
-        'priority',
-        'tags',
-        'assigned_to',
-        'superseded_by',
-        'depends_on',
-        ] %}
-        {{ forms.render(form['meta'][e]) }}
-    {% endfor %}
-    </dl>
-
-    {# see comments concerning the submit button within TicketForm in items/ticket.py #}
-    {% if is_new %}
-        {{ forms.render_submit(form) }}
-    {% else %}
-        {{ forms.render(form['submit']) }}
-    {% endif %}
-
-    <h2>Back references</h2>
-    <dl>
-    {% for e in [
-        'supersedes',
-        'required_by',
-        'subscribers',
-        ] %}
-        {{ forms.render(form['backrefs'][e]) }}
-    {% endfor %}
-
-    {{ gen.form.close() }}
-</div>
-{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/ticket/base.html	Mon Jul 22 22:51:34 2013 +0530
@@ -0,0 +1,58 @@
+{% import "forms.html" as forms %}
+{% extends theme("layout.html") %}
+
+{% macro render_meta() %}
+    {% for e in [
+        'summary',
+        'effort',
+        'difficulty',
+        'severity',
+        'priority',
+        'tags',
+        'assigned_to',
+        'superseded_by',
+        'depends_on',
+        ] %}
+        {{ forms.render(form['meta'][e]) }}
+    {% endfor %}
+{% endmacro %}
+
+{% macro render_backref() %}
+    {% for e in [
+        'supersedes',
+        'required_by',
+        'subscribers',
+        ] %}
+        {{ forms.render(form['backrefs'][e]) }}
+    {% endfor %}
+{% endmacro %}
+
+{% block head %}
+    {{ super() }}
+    <link media="all" rel="stylesheet" href="{{ url_for('static', filename='css/ticket.css') }}" />
+{% endblock %}
+
+{% block content %}
+<h1>
+    {% block title_text %}
+    {% endblock %}
+    {% if closed %}
+        {# TODO style .moin-ticket-closed #}
+        <span class="moin-ticket-closed">{{ _("(Closed)") }}</span>
+    {% endif %}
+</h1>
+<div class="moin-form" id="moin-ticket-form">
+    {{ gen.form.open(form, method='post', enctype='multipart/form-data') }}
+
+    {% if data_rendered %}
+        <div id="moin-content-data">
+            {{ data_rendered }}
+        </div>
+    {% endif %}
+
+    {% block form_controls %}
+    {% endblock %}
+
+    {{ gen.form.close() }}
+</div>
+{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/ticket/modify.html	Mon Jul 22 22:51:34 2013 +0530
@@ -0,0 +1,25 @@
+{% extends "ticket/base.html" %}
+
+{% block title_text %}
+    {{ _("Ticket: '%(item_name)s'", item_name=item_name) }}
+{% endblock %}
+
+{% block form_controls %}
+    <h2>{{ _("Add comment") }}</h2>
+    <dl>
+    {{ forms.render(form['message']) }}
+    </dl>
+
+    <h2>{{ _("Edit metadata") }}</h2>
+    <dl>
+    {{ forms.render_errors(form) }}
+    {{ render_meta() }}
+    </dl>
+
+    {{ forms.render(form['submit']) }}
+
+    <h2>{{ _("Back references") }}</h2>
+    <dl>
+    {{ render_backref() }}
+    </dl>
+{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/templates/ticket/submit.html	Mon Jul 22 22:51:34 2013 +0530
@@ -0,0 +1,21 @@
+{% extends "ticket/base.html" %}
+
+{% block title_text %}
+    {{ _("Creating new ticket: '%(item_name)s'", item_name=item_name) }}
+{% endblock %}
+
+{% block form_controls %}
+    <h2>{{ _("Describe the ticket") }}</h2>
+
+    <dl>
+    {{ forms.render(form['message']) }}
+    </dl>
+
+    <h2>{{ _("Provide metadata") }}</h2>
+    <dl>
+    {{ forms.render_errors(form) }}
+    {{ render_meta() }}
+    </dl>
+
+    {{ forms.render_submit(form) }}
+{% endblock %}
--- a/MoinMoin/user.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/MoinMoin/user.py	Mon Jul 22 22:51:34 2013 +0530
@@ -81,8 +81,9 @@
     if validate and email and app.cfg.user_email_unique:
         if search_users(email=email):
             return _("This email already belongs to somebody else.")
-
-    theuser.profile[EMAIL] = email
+        theuser.profile[EMAIL_UNVALIDATED] = email
+    else:
+        theuser.profile[EMAIL] = email
 
     # Openid should be unique
     if validate and openid and search_users(openid=openid):
@@ -786,5 +787,8 @@
 
         subject = _('[%(sitename)s] Please verify your email address',
                     sitename=self._cfg.sitename or "Wiki")
-        mailok, msg = sendmail.sendmail(subject, text, to=[self.email], mail_from=self._cfg.mail_from)
+        email = self.profile[EMAIL_UNVALIDATED]
+        mailok, msg = sendmail.sendmail(subject, text, to=[email], mail_from=self._cfg.mail_from)
         return mailok, msg
+
+        self._mail_email_verification(email=self.email)
--- a/setup.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/setup.py	Mon Jul 22 22:51:34 2013 +0530
@@ -107,6 +107,7 @@
         'pdfminer',  # pdf -> text/plain conversion
         'passlib>=1.6.0',  # strong password hashing (1.6 needed for consteq)
         'XStatic>=0.0.2',  # support for static file pypi packages
+        'XStatic-Bootstrap>=2.1.0.1',
         'XStatic-CKEditor>=3.6.1.2',
         'XStatic-jQuery>=1.8.2',
         'XStatic-jQuery-File-Upload>=4.4.2',
--- a/wikiconfig.py	Sat Jul 20 23:54:16 2013 +0530
+++ b/wikiconfig.py	Mon Jul 22 22:51:34 2013 +0530
@@ -66,6 +66,7 @@
     mod_names = [
         'jquery', 'jquery_file_upload',
         'json_js',
+        'bootstrap',
         'ckeditor',
         'svgweb',
         'svgedit_moin', 'twikidraw_moin', 'anywikidraw',