--- a/buildout.cfg Fri Jun 11 11:04:23 2010 +0200
+++ b/buildout.cfg Fri Jul 09 10:05:29 2010 +0200
@@ -4,6 +4,8 @@
django
python
django-extensions
+ django-piston
+ omelette
develop = .
[python]
@@ -23,11 +25,13 @@
pythonpath = src
src/cm
${django-extensions:location}
+ ${django-piston:location}
eggs =
django-flash
django-tagging
+# django-piston
# django-css
- chardet
+# chardet
feedparser
PIL
BeautifulSoup
@@ -43,4 +47,12 @@
[django-extensions]
recipe=zerokspot.recipe.git
repository=git://github.com/django-extensions/django-extensions.git
-#rev=7c73978b55fcadbe2cd6f2abbefbedb5a85c2c8c
\ No newline at end of file
+#rev=7c73978b55fcadbe2cd6f2abbefbedb5a85c2c8c
+
+[django-piston]
+recipe = mercurialrecipe
+repository = http://bitbucket.org/jespern/django-piston
+
+[omelette]
+recipe = collective.recipe.omelette
+eggs = ${django:eggs}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/api/handlers.py Fri Jul 09 10:05:29 2010 +0200
@@ -0,0 +1,251 @@
+from piston.handler import AnonymousBaseHandler, BaseHandler
+from piston.utils import rc
+
+from cm.models import Text,TextVersion, Role, UserRole
+from cm.views import get_keys_from_dict, get_textversion_by_keys_or_404, get_text_by_keys_or_404, get_textversion_by_keys_or_404, redirect
+from cm.security import get_texts_with_perm, has_perm, get_viewable_comments, \
+ has_perm_on_text_api
+from cm.security import get_viewable_comments
+from cm.utils.embed import embed_html
+from cm.views.create import CreateTextContentForm, create_text
+from piston.utils import validate
+from settings import SITE_URL
+
+URL_PREFIX = SITE_URL + '/api'
+
+class AnonymousTextHandler(AnonymousBaseHandler):
+ type = "Text methods"
+ title = "Read text info"
+ fields = ('key', 'title', 'format', 'content', 'created', 'modified', 'nb_comments', 'nb_versions', 'embed_html', ('last_text_version', ('created','modified', 'format', 'title', 'content')))
+ allowed_methods = ('GET', )
+ model = Text
+ desc = """ Read text identified by `key`."""
+ args = None
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/{key}/'
+
+
+ @has_perm_on_text_api('can_view_text')
+ def read(self, request, key):
+
+ text = get_text_by_keys_or_404(key)
+ setattr(text,'nb_comments',len(get_viewable_comments(request, text.last_text_version.comment_set.all(), text)))
+ setattr(text,'nb_versions',text.get_versions_number())
+ setattr(text,'embed_html',embed_html(text.key))
+
+ return text
+
+class TextHandler(BaseHandler):
+ type = "Text methods"
+ anonymous = AnonymousTextHandler
+ allowed_methods = ('GET',)
+ no_display = True
+
+class AnonymousTextVersionHandler(AnonymousBaseHandler):
+ type = "Text methods"
+ title = "Read text version info"
+ fields = ('key', 'title', 'format', 'content', 'created', 'modified', 'nb_comments',)
+ allowed_methods = ('GET', )
+ model = Text
+ desc = """ Read text version identified by `version_key` inside text identified by `key`."""
+ args = None
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/{key}/{version_key}/'
+
+
+ @has_perm_on_text_api('can_view_text')
+ def read(self, request, key, version_key):
+ text_version = get_textversion_by_keys_or_404(version_key, key=key)
+ setattr(text_version,'nb_comments',len(get_viewable_comments(request, text_version.comment_set.all(), text_version.text)))
+
+ return text_version
+
+class TextVersionHandler(BaseHandler):
+ type = "Text methods"
+ anonymous = AnonymousTextVersionHandler
+ allowed_methods = ('GET',)
+ no_display = True
+
+class AnonymousTextListHandler(AnonymousBaseHandler):
+ title = "List texts"
+ type = "Text methods"
+ fields = ('key', 'title', 'created', 'modified', 'nb_comments', 'nb_versions',)
+ allowed_methods = ('GET',)
+ model = Text
+ desc = """Lists texts on workspace."""
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/'
+
+ def read(self, request):
+ order_by = '-id'
+ texts = get_texts_with_perm(request, 'can_view_text').order_by(order_by)
+ return texts
+
+class TextListHandler(BaseHandler):
+ title = "Create text"
+ type = "Text methods"
+ allowed_methods = ('POST', )
+ anonymous = AnonymousTextListHandler
+ desc = "Create a text with the provided parameters."
+ args = """<br/>
+`title`: title of the text<br/>
+`format`: format content ('markdown', 'html')<br/>
+`content`: content (in specified format)<br/>
+`anon_role`: role to give to anon users: null, 4: commentator, 5: observer<br/>
+ """
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/'
+
+ def create(self, request):
+ form = CreateTextContentForm(request.POST)
+ if form.is_valid():
+ text = create_text(request.user, form.cleaned_data)
+ anon_role = request.POST.get('anon_role', None)
+ if anon_role:
+ userrole = UserRole.objects.create(user=None, role=Role.objects.get(id=anon_role), text=text)
+ return {'key' : text.key , 'version_key' : text.last_text_version.key, 'created': text.created}
+ else:
+ resp = rc.BAD_REQUEST
+ return resp
+
+from cm.exception import UnauthorizedException
+from cm.views.texts import text_delete
+
+class TextDeleteHandler(BaseHandler):
+ type = "Text methods"
+ allowed_methods = ('POST', )
+ title = "Delete text"
+ desc = "Delete the text identified by `key`."
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/{key}/delete/'
+
+ def create(self, request):
+ """
+ Delete text identified by `key`.
+ """
+ try:
+ key = request.POST.get('key')
+ text_delete(request, key=key)
+ except UnauthorizedException:
+ return rc.FORBIDDEN
+ except KeyError:
+ return rc.BAD_REQUEST
+ return rc.DELETED
+
+from cm.views.texts import text_pre_edit
+
+class TextPreEditHandler(BaseHandler):
+ type = "Text methods"
+ allowed_methods = ('POST', )
+ title = "Ask for edit impact"
+ desc = "Returns the number of impacted comments."
+ args = """<br />
+`new_format`: new format content ('markdown', 'html')<br />
+`new_content`: new content (in specified format)<br />
+ """
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/{key}/pre_edit/'
+
+ def create(self, request, key):
+ return text_pre_edit(request, key=key)
+
+from cm.views.texts import text_edit
+
+class TextEditHandler(BaseHandler):
+ allowed_methods = ('POST', )
+ type = "Text methods"
+ title = "Edit text"
+ desc = "Update text identified by `key`."
+ args = """<br />
+`title`: new title of the text<br />
+`format`: new format content ('markdown', 'html')<br />
+`content`: new content (in specified format)<br />
+`note`: note to add to edit<br />
+`new_version`: boolean: should a new version of the text be created?<br />
+`keep_comments`: boolean: should existing comments be keep (if possible)?<br />
+ """
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/text/{key}/edit/'
+
+
+ def create(self, request, key):
+ res = text_edit(request, key=key)
+ text = get_text_by_keys_or_404(key)
+ text_version = text.last_text_version
+ return {'version_key' : text_version.key , 'created': text_version.created}
+
+from django.contrib.auth import authenticate
+
+class SetUserHandler(AnonymousBaseHandler):
+ allowed_methods = ('POST',)
+ type = "User methods"
+ title = "Set username and email"
+ desc = "Set username and email to use when commenting."
+ args = """<br />
+`user_name`: user's name<br />
+`user_email`: user's email<br />
+ """
+
+ @staticmethod
+ def endpoint():
+ return URL_PREFIX + '/setuser/'
+
+ def create(self, request):
+ user_name = request.POST.get('user_name', None)
+ user_email = request.POST.get('user_email', None)
+ if user_name and user_email:
+ response = rc.ALL_OK
+ response.set_cookie('user_name', user_name)
+ response.set_cookie('user_email', user_email)
+ return response
+ else:
+ return rc.BAD_REQUEST
+
+
+
+from piston.doc import documentation_view
+
+from piston.handler import handler_tracker
+from django.template import RequestContext
+from piston.doc import generate_doc
+from django.shortcuts import render_to_response
+
+def documentation(request):
+ """
+ Generic documentation view. Generates documentation
+ from the handlers you've defined.
+ """
+ docs = [ ]
+
+ for handler in handler_tracker:
+ doc = generate_doc(handler)
+ setattr(doc,'type', handler.type)
+ docs.append(doc)
+
+ def _compare(doc1, doc2):
+ #handlers and their anonymous counterparts are put next to each other.
+ name1 = doc1.name.replace("Anonymous", "")
+ name2 = doc2.name.replace("Anonymous", "")
+ return cmp(name1, name2)
+
+ #docs.sort(_compare)
+
+ return render_to_response('api_doc.html',
+ { 'docs': docs }, RequestContext(request))
+
+from piston.doc import generate_doc
+DocHandler = generate_doc(TextPreEditHandler)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/api/urls.py Fri Jul 09 10:05:29 2010 +0200
@@ -0,0 +1,28 @@
+from django.conf.urls.defaults import *
+
+from piston.resource import Resource
+from piston.authentication import HttpBasicAuthentication
+
+from cm.api.handlers import *
+auth = HttpBasicAuthentication(realm='Comt API')
+
+text_handler = Resource(handler=TextHandler, authentication=auth)
+textversion_handler = Resource(handler=TextVersionHandler, authentication=auth)
+text_list_handler = Resource(handler=TextListHandler, authentication=auth)
+text_delete_handler = Resource(handler=TextDeleteHandler, authentication=auth)
+text_pre_edit_handler = Resource(handler=TextPreEditHandler, authentication=auth)
+text_edit_handler = Resource(handler=TextEditHandler, authentication=auth)
+setuser_handler = Resource(handler=SetUserHandler, authentication=None)
+
+#doc_handler = Resource(handler=DocHandler)
+
+urlpatterns = patterns('',
+ url(r'^text/(?P<key>\w*)/$', text_handler),
+ url(r'^text/$', text_list_handler),
+ url(r'^text/(?P<key>\w*)/delete/$', text_delete_handler),
+ url(r'^text/(?P<key>\w*)/pre_edit/$', text_pre_edit_handler),
+ url(r'^text/(?P<key>\w*)/edit/$', text_edit_handler),
+ url(r'^text/(?P<key>\w*)/(?P<version_key>\w*)/$', textversion_handler),
+ url(r'^setuser/$', setuser_handler),
+ url(r'^doc/$', documentation),
+)
--- a/src/cm/fixtures/test_content.yaml Fri Jun 11 11:04:23 2010 +0200
+++ b/src/cm/fixtures/test_content.yaml Fri Jul 09 10:05:29 2010 +0200
@@ -360,6 +360,58 @@
adminkey: "text_adminkey_3"
user: 2
+# text 4
+- model : cm.textversion
+ pk: 4
+ fields:
+ created: "2009-02-13 04:01:12"
+ modified: "2009-02-13 04:01:12"
+ title: 'title 4, public text'
+ format: 'markdown'
+ content: 'aaa bbb ccc ddd eee fff ggg'
+ text: 4
+ mod_posteriori: True
+ key: "textversion_key_4"
+ adminkey: "tv_adminkey_4"
+
+- model : cm.text
+ pk: 4
+ fields:
+ created: "2009-02-13 04:01:12"
+ modified: "2009-02-13 04:01:12"
+ last_text_version: 4
+ title: 'title 4, public text'
+ state: "approved"
+ key: "text_key_4"
+ adminkey: "text_adminkey_4"
+ user: 1
+
+# text 5
+- model : cm.textversion
+ pk: 5
+ fields:
+ created: "2009-02-13 04:01:12"
+ modified: "2009-02-13 04:01:12"
+ title: 'title 5, public text'
+ format: 'markdown'
+ content: 'aaa bbb ccc ddd eee fff ggg'
+ text: 5
+ mod_posteriori: True
+ key: "textversion_key_5"
+ adminkey: "tv_adminkey_5"
+
+- model : cm.text
+ pk: 5
+ fields:
+ created: "2009-02-13 04:01:12"
+ modified: "2009-02-13 04:01:12"
+ last_text_version: 5
+ title: 'title 5, public text'
+ state: "approved"
+ key: "text_key_5"
+ adminkey: "text_adminkey_5"
+ user: 1
+
############### userrole ###############
# user 1 is global Manager
@@ -426,6 +478,24 @@
user: 4
text: 2
+# user null (anon is Commentator on text 4)
+# userrole 8
+- model : cm.userrole
+ pk: 8
+ fields:
+ role: 4
+ user: null
+ text: 4
+
+# user null (anon is Commentator on text 5)
+# userrole 9
+- model : cm.userrole
+ pk: 9
+ fields:
+ role: 4
+ user: null
+ text: 5
+
############### comment ###############
# comment 1 (visible on text 2)
--- a/src/cm/security.py Fri Jun 11 11:04:23 2010 +0200
+++ b/src/cm/security.py Fri Jul 09 10:05:29 2010 +0200
@@ -7,7 +7,7 @@
from django.http import HttpResponseRedirect
from django.utils.http import urlquote
from django.db.models import Q
-
+from piston.utils import rc
import logging
from cm.models import *
@@ -210,8 +210,14 @@
return _check_global_perm
return _dec
+
+def has_perm_on_text_api(perm_name, must_be_logged_in=False, redirect_field_name=REDIRECT_FIELD_NAME):
+ return _has_perm_on_text(perm_name, must_be_logged_in, redirect_field_name, api=True)
-def has_perm_on_text(perm_name, must_be_logged_in=False, redirect_field_name=REDIRECT_FIELD_NAME):
+def has_perm_on_text(perm_name, must_be_logged_in=False, redirect_field_name=REDIRECT_FIELD_NAME, api=False):
+ return _has_perm_on_text(perm_name, must_be_logged_in, redirect_field_name, api)
+
+def _has_perm_on_text(perm_name, must_be_logged_in=False, redirect_field_name=REDIRECT_FIELD_NAME, api=False):
"""
decorator protection checking for perm for logged in user
force logged in (i.e. redirect to connection screen if not if must_be_logged_in
@@ -222,15 +228,24 @@
return view_func(request, *args, **kwargs)
if must_be_logged_in and not is_authenticated(request):
- login_url = reverse('login')
- return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path())))
+ if not api:
+ login_url = reverse('login')
+ return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path())))
+ else:
+ return rc.FORBIDDEN
+
if 'key' in kwargs:
text = get_object_or_404(Text, key=kwargs['key'])
else:
raise Exception('no security check possible')
-
- if has_perm(request, perm_name, text=text):
+
+ # in api, the view has an object as first parameter, request is args[0]
+ if not api:
+ req = request
+ else:
+ req = args[0]
+ if has_perm(req, perm_name, text=text):
return view_func(request, *args, **kwargs)
#else:
# TODO: (? useful ?) if some user have the perm and not logged-in : redirect to login
@@ -238,7 +253,11 @@
# return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path())))
# else : unauthorized
- raise UnauthorizedException('No perm %s' % perm_name)
+ if not api:
+ raise UnauthorizedException('No perm %s' % perm_name)
+ else:
+ return rc.FORBIDDEN
+
_check_local_perm.__doc__ = view_func.__doc__
_check_local_perm.__dict__ = view_func.__dict__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/templates/api_doc.html Fri Jul 09 10:05:29 2010 +0200
@@ -0,0 +1,75 @@
+{% extends "site/layout/base_workspace.html" %}
+{% load com %}
+{% load i18n %}
+
+{% block title %}
+{% blocktrans %}API Documentation{% endblocktrans %}
+{% endblock %}
+
+{% block head %}
+{% endblock %}
+
+{% block content %}
+
+
+
+ <style type="text/css" media="screen">
+a.reflink {
+color:#C60F0F;
+font-size:0.8em;
+padding:0 4px;
+text-decoration:none;
+visibility:hidden;
+}
+
+:hover > a.reflink {
+visibility:visible;
+}
+ </style>
+
+
+<h1>API Documentation</h1>
+
+<h2>Presentation</h2>
+
+The API exposes method for external application to deal with content store in COMT.
+
+The authentification is done using <a href="http://fr.wikipedia.org/wiki/HTTP_Authentification">HTTP Authentification</a>.
+
+The default return format is 'json', add '?format=other_format' where other_format is 'json', 'xml', 'yaml' to change the results' format.
+{% load markup %}
+
+ {% regroup docs by type as grouped_docs %}
+
+
+ {% for dd in grouped_docs %}
+ <h2>{{ dd.grouper }}</h2>
+ {% for doc in dd.list %}
+ {% if not doc.handler.no_display %}
+ <h3>{{ doc.handler.title }} <a href="#{{ doc.handler.title|iriencode }}" class="reflink" title="Permalink" name="{{ doc.handler.title|iriencode }}">ΒΆ</a></h3>
+
+ <p>{{ doc.handler.desc }}</p>
+ <p>
+ {{ doc.doc|default:""|restructuredtext }}
+ </p>
+
+ <p>
+ Endpoint: <b>{{ doc.handler.endpoint }}</b>
+ </p>
+
+ <p>
+ Method: {% for meth in doc.allowed_methods %}<b>{{ meth }}</b>{% if not forloop.last %}, {% endif %}{% endfor %}
+ </p>
+
+ <p>
+ Args: {% if doc.handler.args %}{{ doc.handler.args|safe }}{% else %}None{% endif %}
+ </p>
+
+
+ <br />
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </body>
+</html>
+{% endblock %}
--- a/src/cm/tests/__init__.py Fri Jun 11 11:04:23 2010 +0200
+++ b/src/cm/tests/__init__.py Fri Jul 09 10:05:29 2010 +0200
@@ -6,3 +6,4 @@
from cm.tests.test_history import *
from cm.tests.test_security import *
from cm.tests.test_activity import *
+from cm.tests.test_api import *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/tests/test_api.py Fri Jul 09 10:05:29 2010 +0200
@@ -0,0 +1,207 @@
+from django.test import TestCase
+from django.test.client import Client
+from django.core import management
+from datetime import datetime
+from cm.activity import *
+from cm.models import *
+from cm.security import *
+
+from django.http import HttpRequest, HttpResponse
+from django.utils import simplejson
+
+from cm.api.handlers import *
+
+#from piston.test import TestCase
+from piston.models import Consumer
+from piston.handler import BaseHandler
+from piston.utils import rc
+from piston.resource import Resource
+
+class FalseRequest(object):
+ def __init__(self, user):
+ self.user = user
+
+class APITest(TestCase):
+ fixtures = ['roles_generic', 'test_content', ]
+
+ def test_text_get(self):
+ """
+ Anonymous api call
+ """
+
+ resource = Resource(AnonymousTextHandler)
+ request = HttpRequest()
+ setattr(request, 'user' , None)
+ request.method = 'GET'
+
+ # get public text
+ response = resource(request, key='text_key_4', emitter_format='json')
+ self.assertEquals(200, response.status_code) # 401: forbidden
+ response_data = simplejson.loads(response.content)
+ self.assertEquals(response_data.get('created'), '2009-02-13 04:01:12')
+
+ # error: private text
+ response = resource(request, key='text_key_3', emitter_format='json')
+ self.assertEquals(401, response.status_code)
+
+
+ def test_text_get_logged_in(self):
+ """
+ Logged in as manager api call
+ """
+
+ resource = Resource(AnonymousTextHandler)
+ request = HttpRequest()
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'GET'
+
+ response = resource(request, key='text_key_3', emitter_format='json')
+ self.assertEquals(200, response.status_code)
+
+
+ def test_text_create(self):
+ request = FalseRequest(None)
+ nb_anon_texts = get_texts_with_perm(request, 'can_view_text').count()
+ nb_texts = Text.objects.count()
+
+ resource = Resource(TextListHandler)
+
+ # create one private text
+ request = HttpRequest()
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'POST'
+ setattr(request, 'POST' , {'content':'test content', 'format':"markdown", 'title': 'my title'})
+ response = resource(request,)
+
+ self.assertEquals(200, response.status_code)
+ self.assertTrue('key' in simplejson.loads(response.content).keys())
+
+ request = FalseRequest(None)
+ self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), nb_anon_texts) # NO more anon text
+
+ # create one text with anon observer
+ request = HttpRequest()
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'POST'
+ setattr(request, 'POST' , {'content':'test content', 'format':"markdown", 'title': 'my title', 'anon_role' : 4})
+ response = resource(request,)
+
+ self.assertEquals(200, response.status_code)
+ self.assertTrue('key' in simplejson.loads(response.content).keys())
+
+ self.assertEquals(nb_texts + 2, Text.objects.count()) # 2 more texts should have been created
+
+ request = FalseRequest(None)
+ self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), nb_anon_texts + 1) # one more anon accessible text available
+
+ def test_list_text_get(self):
+ """
+ List texts anon
+ """
+ resource = Resource(AnonymousTextListHandler)
+ request = HttpRequest()
+ setattr(request, 'user' , None)
+ request.method = 'GET'
+
+ response = resource(request, emitter_format='json')
+ self.assertEquals(200, response.status_code)
+ self.assertEquals(2, len(simplejson.loads(response.content)))
+
+ def test_list_text_logged_in(self):
+ """
+ List texts manager
+ """
+ resource = Resource(AnonymousTextListHandler)
+ request = HttpRequest()
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'GET'
+
+ response = resource(request, emitter_format='json')
+ self.assertEquals(200, response.status_code)
+ self.assertEquals(5, len(simplejson.loads(response.content)))
+
+ def test_delete_text_logged_in_works(self):
+ """
+ Delete text
+ """
+ nb_texts = Text.objects.count()
+
+ resource = Resource(TextDeleteHandler)
+ request = HttpRequest()
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'POST'
+ setattr(request, 'POST' , {'key':'text_key_3'})
+ setattr(request, 'flash' , {})
+
+ response = resource(request, emitter_format='json')
+ self.assertEquals(204, response.status_code)
+
+ # one text deleted
+ self.assertEquals(nb_texts - 1, Text.objects.count())
+
+ def test_delete_text_logged_in_fail(self):
+ """
+ Delete text (and fail: insufficient rights)
+ """
+ nb_texts = Text.objects.count()
+
+ resource = Resource(TextDeleteHandler)
+ request = HttpRequest()
+ user = User.objects.get(pk=3)
+ setattr(request, 'user' , user)
+ request.method = 'POST'
+ setattr(request, 'POST' , {'key':'text_key_3'})
+ setattr(request, 'flash' , {})
+
+ response = resource(request, emitter_format='json')
+ self.assertEquals(401, response.status_code)
+
+ # no text deleted
+ self.assertEquals(nb_texts, Text.objects.count())
+
+
+ def test_pre_edit(self):
+ """
+ Pre edit text: should return number of comments to remove
+ """
+ resource = Resource(TextPreEditHandler)
+ request = HttpRequest()
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'POST'
+ setattr(request, 'POST' , {'new_format' : 'markdown', 'new_content' : u'ggg'})
+ setattr(request, 'flash' , {})
+
+ response = resource(request, key='text_key_2', emitter_format='json')
+ self.assertEquals(response.content, '{"nb_removed": 3}')
+
+ def xtest_edit(self):
+ """
+ Edit text
+ """
+ resource = Resource(TextEditHandler)
+ request = HttpRequest()
+ setattr(request,'session',None)
+ user = User.objects.get(pk=1)
+ setattr(request, 'user' , user)
+ request.method = 'POST'
+ setattr(request, 'POST' , {'new_format' : 'markdown', 'new_content' : u'ggg'})
+ setattr(request, 'flash' , {})
+
+ response = resource(request, key='text_key_2', emitter_format='json')
+
+ self.assertEquals(Text.objects.get(pk=2).last_text_version.content , u'ggg')
+
+
+ def test_setuser(self):
+ """
+ Set username/email for commenting
+ """
+ from django.test.client import Client
+ c = Client()
+ response = c.post('/setuser/', {'username': 'my_username', 'email': 'my_email'})
--- a/src/cm/tests/test_security.py Fri Jun 11 11:04:23 2010 +0200
+++ b/src/cm/tests/test_security.py Fri Jul 09 10:05:29 2010 +0200
@@ -17,19 +17,19 @@
def test_access_rights(self):
# anon user sees no text
request = FalseRequest(None)
- self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), 0)
+ self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), 2)
# user 1 sees all texts
user1 = UserProfile.objects.get(id=1).user
request = FalseRequest(user1)
- self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), 3)
+ self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), 5)
- # user 2 sees only 2 texts
+ # user 2 sees only 4 texts
user2 = UserProfile.objects.get(id=2).user
request = FalseRequest(user2)
- self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), 2)
+ self.assertEqual(get_texts_with_perm(request, 'can_view_text').count(), 4)
- # user 4 sees only 2 texts (global manager but commentator on text 4
+ # user 4 manages only 2 texts (global manager but commentator on text 4
user4 = UserProfile.objects.get(id=4).user
request = FalseRequest(user4)
self.assertEqual(get_texts_with_perm(request, 'can_manage_text').count(), 2)
--- a/src/cm/urls.py Fri Jun 11 11:04:23 2010 +0200
+++ b/src/cm/urls.py Fri Jul 09 10:05:29 2010 +0200
@@ -157,3 +157,7 @@
urlpatterns += patterns('',
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
)
+
+urlpatterns += patterns('',
+ (r'^api/', include('cm.api.urls')),
+)