commit 71dc5cecd9911ad3b572927a65d651987f5dd47f
Author: Thomas NOEL <tnoel@entrouvert.com>
Date:   Fri Jun 17 17:39:43 2016 +0200

    general: make formdatas visible even if related formdefs are disabled (#11288)

diff --git a/extra/modules/myspace.py b/extra/modules/myspace.py
index a689006..8fc0a26 100644
--- a/extra/modules/myspace.py
+++ b/extra/modules/myspace.py
@@ -383,7 +383,7 @@ class JsonDirectory(Directory):
         return Directory._q_traverse(self, path)
 
     def forms(self):
-        formdefs = FormDef.select(lambda x: not x.is_disabled(), order_by = 'name')
+        formdefs = FormDef.select(order_by='name', ignore_errors=True)
         user_forms = []
         for formdef in formdefs:
             user_forms.extend(formdef.data_class().get_with_indexed_value(
@@ -401,7 +401,7 @@ class JsonDirectory(Directory):
         forms_output = []
         for form in user_forms:
             visible_status = form.get_visible_status(user=self.user)
-            # skip hidden forms
+            # skip drafts and hidden forms
             if not visible_status:
                 continue
             name = form.formdef.name
@@ -455,7 +455,7 @@ class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
 
         user_forms = []
         if user:
-            formdefs = FormDef.select(lambda x: not x.is_disabled(), order_by = 'name')
+            formdefs = FormDef.select(order_by='name', ignore_errors=True)
             user_forms = []
             for formdef in formdefs:
                 user_forms.extend(formdef.data_class().get_with_indexed_value(
diff --git a/extra/modules/root.py b/extra/modules/root.py
index 1c6729b..b2698da 100644
--- a/extra/modules/root.py
+++ b/extra/modules/root.py
@@ -84,7 +84,7 @@ class FormsRootDirectory(wcs.forms.root.RootDirectory):
         r = TemplateIO(html=True)
         base_url = get_publisher().get_root_url()
 
-        draft = [x for x in user_forms if x.is_draft()]
+        draft = [x for x in user_forms if x.is_draft() and not x.formdef.is_disabled()]
         if draft:
             r += htmltext('<h4 id="drafts">%s</h4>') % _('My Current Drafts')
             r += htmltext('<ul>')
diff --git a/tests/test_user_pages.py b/tests/test_user_pages.py
new file mode 100644
index 0000000..645f817
--- /dev/null
+++ b/tests/test_user_pages.py
@@ -0,0 +1,120 @@
+import os
+import shutil
+import StringIO
+import time
+
+import pytest
+
+from quixote import cleanup, get_publisher
+from wcs.qommon import errors, sessions
+from qommon.ident.password_accounts import PasswordAccount
+from wcs.qommon.http_request import HTTPRequest
+from wcs.qommon.template import get_current_theme
+from wcs.categories import Category
+from wcs.roles import Role
+from wcs.workflows import Workflow
+from wcs.formdef import FormDef
+from wcs import fields
+
+from utilities import get_app, login, create_temporary_pub
+
+def setup_module(module):
+    cleanup()
+
+    global pub
+
+    pub = create_temporary_pub()
+
+    req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
+    pub.set_app_dir(req)
+    pub.cfg['identification'] = {'methods': ['password']}
+    pub.write_cfg()
+
+def create_user():
+    if pub.user_class.has_key('user'):
+        return
+    user1 = pub.user_class(name='user')
+    user1.id = 'admin'
+    user1.is_admin = False
+    user1.store()
+
+    account1 = PasswordAccount(id='user')
+    account1.set_password('user')
+    account1.user_id = user1.id
+    account1.store()
+
+    pub.cfg['identification'] = {'methods': ['password']}
+    pub.write_cfg()
+
+    return user1
+
+def create_formdef():
+    FormDef.wipe()
+    formdef = FormDef()
+    formdef.name = 'test'
+    formdef.fields = []
+    formdef.store()
+    return formdef
+
+def teardown_module(module):
+    shutil.rmtree(pub.APP_DIR)
+
+def test_with_user():
+    user = create_user()
+    app = login(get_app(pub), username='user', password='user')
+    resp = app.get('/', status=200)
+    resp = app.get('/myspace/', status=200)
+
+def test_myspace_with_user_forms():
+    user = create_user()
+    formdef = create_formdef()
+
+    cat = Category(name='cat')
+    cat.store()
+    formdef.category_id = cat.id
+
+    wf = Workflow(name='status')
+    st1 = wf.add_status('Status1', 'st1')
+    wf.store()
+    formdef.workflow_id = wf.id
+    formdef.store()
+
+    formdef.data_class().wipe()
+    formdata = formdef.data_class()()
+    formdata.user_id = user.id
+    formdata.status = 'wf-st1'
+    formdata.data = {}
+    formdata.store()
+    draft = formdef.data_class()()
+    draft.user_id = user.id
+    draft.status = 'draft'
+    draft.data = {}
+    draft.store()
+
+    app = login(get_app(pub), username='user', password='user')
+    resp = app.get('/myspace/')
+    assert 'Status1' in resp
+    assert '<a href="/cat/test/%s/"' % formdata.id in resp
+    assert 'Draft' in resp
+    assert '<a href="/cat/test/%s"' % draft.id in resp
+    resp = app.get('/cat/test/%s' % formdata.id)
+    resp.status_int = 200
+    resp = app.get('/cat/test/%s' % draft.id, status=302)
+    resp = resp.follow(status=302)
+    resp.location.startswith('http://example.net/cat/test/?mt=')
+    resp = resp.follow(status=200)
+
+    # disable formdef: formdatas are still visible and accessible, drafts are not
+    formdef.disabled = True
+    formdef.store()
+    resp = app.get('/myspace/')
+    assert 'Status1' in resp
+    assert '<a href="/cat/test/%s/"' % formdata.id in resp
+    assert not 'Draft' in resp
+    assert not '<a href="/cat/test/%s"' % draft.id in resp
+    resp = app.get('/cat/test/%s' % formdata.id)
+    resp.status_int = 200
+    resp = app.get('/cat/test/%s' % draft.id, status=302)
+    resp = resp.follow(status=302)
+    resp.location.startswith('http://example.net/cat/test/?mt=')
+    resp = resp.follow(status=403)
