| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | """ |
|---|
| 3 | PyLucid.index |
|---|
| 4 | ~~~~~~~~~~~~~ |
|---|
| 5 | |
|---|
| 6 | Contains all view function, except the _install views. |
|---|
| 7 | |
|---|
| 8 | - index(): Display a PyLucid CMS Page |
|---|
| 9 | - handle_command(): Answer a _command Request |
|---|
| 10 | - permalink(): redirect to the real page url |
|---|
| 11 | - redirect(): simple redirect old PyLucid URLs to the new location. |
|---|
| 12 | |
|---|
| 13 | Last commit info: |
|---|
| 14 | ~~~~~~~~~~~~~~~~~ |
|---|
| 15 | $LastChangedDate: $ |
|---|
| 16 | $Rev: $ |
|---|
| 17 | $Author: $ |
|---|
| 18 | |
|---|
| 19 | :copyleft: 2007-2008 by the PyLucid team, see AUTHORS for more details. |
|---|
| 20 | :license: GNU GPL v3 or above, see LICENSE for more details. |
|---|
| 21 | """ |
|---|
| 22 | |
|---|
| 23 | from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect, \ |
|---|
| 24 | HttpResponseRedirect |
|---|
| 25 | from django.conf import settings |
|---|
| 26 | from django.template import RequestContext, TemplateSyntaxError |
|---|
| 27 | from django.utils.safestring import mark_safe |
|---|
| 28 | from django.utils.translation import ugettext as _ |
|---|
| 29 | |
|---|
| 30 | from PyLucid.models import Page |
|---|
| 31 | from PyLucid.system import plugin_manager |
|---|
| 32 | from PyLucid.system.URLs import URLs |
|---|
| 33 | from PyLucid.system.response import SimpleStringIO |
|---|
| 34 | from PyLucid.system.exceptions import AccessDenied |
|---|
| 35 | from PyLucid.system.context_processors import add_dynamic_context, add_css_tag |
|---|
| 36 | from PyLucid.tools.utils import escape, escape_django_tags |
|---|
| 37 | from PyLucid.tools.content_processors import apply_markup, \ |
|---|
| 38 | render_string_template |
|---|
| 39 | from PyLucid.plugins_internal.page_style.page_style import replace_add_data |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | # TODO: Remove in PyLucid >v0.8.5 |
|---|
| 44 | PAGE_MSG_INFO_LINK = ( |
|---|
| 45 | '<a href="' |
|---|
| 46 | 'http://www.pylucid.org/_goto/121/changes/#20-05-2008-page_msg' |
|---|
| 47 | '">pylucid.org - Backwards-incompatible changes - page_msg</a>' |
|---|
| 48 | ) |
|---|
| 49 | |
|---|
| 50 | def _redirect_to_login(request): |
|---|
| 51 | """ |
|---|
| 52 | Redirect to the _comment auth login with the default page |
|---|
| 53 | FIXME: We should build the auth command url in a better way. |
|---|
| 54 | """ |
|---|
| 55 | path = "/%s/%s/auth/login/?next=%s" % ( |
|---|
| 56 | settings.COMMAND_URL_PREFIX, Page.objects.default_page.id, request.path |
|---|
| 57 | ) |
|---|
| 58 | return HttpResponseRedirect(path) |
|---|
| 59 | |
|---|
| 60 | def _redirect_access_denied(request): |
|---|
| 61 | """ |
|---|
| 62 | Redirect to login page, if the user is anonymous. |
|---|
| 63 | """ |
|---|
| 64 | if not request.user.is_anonymous(): |
|---|
| 65 | # The user is logged in. But he hasn't the rights to see the page |
|---|
| 66 | # or run the plugin method |
|---|
| 67 | raise AccessDenied("User can see this page content!") |
|---|
| 68 | |
|---|
| 69 | return _redirect_to_login(request) |
|---|
| 70 | |
|---|
| 71 | |
|---|
| 72 | def _render_cms_page(request, context, page_content=None): |
|---|
| 73 | """ |
|---|
| 74 | render the cms page. |
|---|
| 75 | - render a normal cms request |
|---|
| 76 | - render a _command request: The page.content is the output from the plugin. |
|---|
| 77 | """ |
|---|
| 78 | if request.anonymous_view == False: |
|---|
| 79 | # TODO: remove in v0.9, see: ticket:161 |
|---|
| 80 | # context["robots"] was set in contex_processors.static() |
|---|
| 81 | # Hide the response from search engines |
|---|
| 82 | context["robots"] = "NONE,NOARCHIVE" |
|---|
| 83 | |
|---|
| 84 | context["anonymous_view"] = request.anonymous_view |
|---|
| 85 | |
|---|
| 86 | current_page = context["PAGE"] |
|---|
| 87 | |
|---|
| 88 | if page_content: |
|---|
| 89 | # The page content comes e.g. from the _command plugin |
|---|
| 90 | # current_page.content = page_content |
|---|
| 91 | page_content = escape_django_tags(page_content) |
|---|
| 92 | else: |
|---|
| 93 | # get the current page data from the db |
|---|
| 94 | page_content = current_page.content |
|---|
| 95 | |
|---|
| 96 | markup_no = current_page.markup |
|---|
| 97 | page_content = apply_markup(page_content, context, markup_no) |
|---|
| 98 | |
|---|
| 99 | # Render only the CMS page content: |
|---|
| 100 | try: |
|---|
| 101 | page_content = render_string_template(page_content, context) |
|---|
| 102 | # If a user access a public viewable cms page, but in the page content |
|---|
| 103 | # is a lucidTag witch is a restricted method, the pylucid plugin |
|---|
| 104 | # manager would normaly raise a AccessDenied. |
|---|
| 105 | # The Problem is, if settings.TEMPLATE_DEBUG is on, we didn't get a |
|---|
| 106 | # AccessDenied directly, we always get a TemplateSyntaxError! All |
|---|
| 107 | # other errors will catched and raised a TemplateSyntaxError, too. |
|---|
| 108 | # See django/template/debug.py |
|---|
| 109 | # TODO: Instead of a redirect to the login command, we can insert |
|---|
| 110 | # the ouput from auth.login directly |
|---|
| 111 | except TemplateSyntaxError, err: |
|---|
| 112 | # Check if it was a AccessDenied exception |
|---|
| 113 | if hasattr(err, "exc_info"): |
|---|
| 114 | # sys.exc_info() added in django/template/debug.py |
|---|
| 115 | error_class = err.exc_info[1] |
|---|
| 116 | if isinstance(error_class, AccessDenied): |
|---|
| 117 | return _redirect_access_denied(request) |
|---|
| 118 | |
|---|
| 119 | raise # raise the original error |
|---|
| 120 | |
|---|
| 121 | except AccessDenied: |
|---|
| 122 | # settings.TEMPLATE_DEBUG is off |
|---|
| 123 | return _redirect_access_denied(request) |
|---|
| 124 | |
|---|
| 125 | # http://www.djangoproject.com/documentation/templates_python/#filters-and-auto-escaping |
|---|
| 126 | page_content = mark_safe(page_content) # turn djngo auto-escaping off |
|---|
| 127 | |
|---|
| 128 | context["PAGE"].content = page_content |
|---|
| 129 | |
|---|
| 130 | template = current_page.template |
|---|
| 131 | template_content = template.content |
|---|
| 132 | |
|---|
| 133 | # Render the Template to build the complete html page: |
|---|
| 134 | content = render_string_template(template_content, context) |
|---|
| 135 | |
|---|
| 136 | # insert JS/CSS data from any Plugin *after* the page rendered with the |
|---|
| 137 | # django template engine: |
|---|
| 138 | content = replace_add_data(context, content) |
|---|
| 139 | |
|---|
| 140 | # TODO: Remove in PyLucid >v0.8.5 |
|---|
| 141 | middleware = 'PyLucid.middlewares.pagemessages.PageMessagesMiddleware' |
|---|
| 142 | if middleware not in settings.MIDDLEWARE_CLASSES: |
|---|
| 143 | msg = ( |
|---|
| 144 | u"ERROR: %s not in settings.MIDDLEWARE_CLASSES!" |
|---|
| 145 | " More info: %s" |
|---|
| 146 | ) % (middleware, PAGE_MSG_INFO_LINK) |
|---|
| 147 | content = content.replace(u"<!-- page_messages -->", msg) |
|---|
| 148 | |
|---|
| 149 | return HttpResponse(content) |
|---|
| 150 | |
|---|
| 151 | |
|---|
| 152 | |
|---|
| 153 | |
|---|
| 154 | def _get_context(request, current_page_obj): |
|---|
| 155 | """ |
|---|
| 156 | Setup the context with PyLucid objects. |
|---|
| 157 | For index() and handle_command() views. |
|---|
| 158 | """ |
|---|
| 159 | # add additional attribute |
|---|
| 160 | request.anonymous_view = True |
|---|
| 161 | |
|---|
| 162 | context = RequestContext(request) |
|---|
| 163 | |
|---|
| 164 | context["PAGE"] = current_page_obj |
|---|
| 165 | context["URLs"] = URLs(context) |
|---|
| 166 | # context["URLs"].debug() |
|---|
| 167 | |
|---|
| 168 | # For additional JavaScript and StyleSheet information. |
|---|
| 169 | # JS+CSS from internal_pages or CSS data for pygments |
|---|
| 170 | # Add into the context object. Would be integraged in the page with the |
|---|
| 171 | # additional_content middleware. |
|---|
| 172 | context["js_data"] = [] |
|---|
| 173 | context["css_data"] = [] |
|---|
| 174 | |
|---|
| 175 | # A list of every used html DIV CSS-ID. |
|---|
| 176 | # used in PyLucid.defaulttags.lucidTag.lucidTagNode._add_unique_div() |
|---|
| 177 | context["CSS_ID_list"] = [] |
|---|
| 178 | |
|---|
| 179 | # add dynamic content into the context (like: login/logout link) |
|---|
| 180 | add_dynamic_context(request, context) |
|---|
| 181 | |
|---|
| 182 | # Add the context to the reponse object. |
|---|
| 183 | # Used in PyLucid middlewares |
|---|
| 184 | request.CONTEXT = context |
|---|
| 185 | |
|---|
| 186 | # TODO: Remove in PyLucid >v0.8.5 |
|---|
| 187 | msg = 'Error, see: %s' % PAGE_MSG_INFO_LINK |
|---|
| 188 | context["messages"] = [mark_safe(msg)] |
|---|
| 189 | |
|---|
| 190 | return context |
|---|
| 191 | |
|---|
| 192 | |
|---|
| 193 | def index(request, url): |
|---|
| 194 | """ |
|---|
| 195 | The main index method. |
|---|
| 196 | Return a normal cms page request. |
|---|
| 197 | Every Request will be cached for anonymous user. For the cache_key we use |
|---|
| 198 | the page shortcut from the url. |
|---|
| 199 | """ |
|---|
| 200 | try: |
|---|
| 201 | current_page_obj = Page.objects.get_by_shortcut(url, request.user) |
|---|
| 202 | except Page.DoesNotExist: |
|---|
| 203 | raise Http404(_("Page '%s' doesn't exists.") % url) |
|---|
| 204 | except Page.objects.WrongShortcut, correct_url: |
|---|
| 205 | # Some parts of the URL was wrong, but we found a right page |
|---|
| 206 | # shortcut -> redirect to the right url |
|---|
| 207 | return HttpResponseRedirect(correct_url) |
|---|
| 208 | except AccessDenied: |
|---|
| 209 | if request.user.is_anonymous(): |
|---|
| 210 | # FIXME: We should build the auth command url in a better way. |
|---|
| 211 | return _redirect_to_login(request) |
|---|
| 212 | else: |
|---|
| 213 | # User is logged in but access is denied, |
|---|
| 214 | # probably due to group restrictions. |
|---|
| 215 | request.user.message_set.create(message=_("Access denied")) |
|---|
| 216 | new_url = Page.objects.default_page.get_absolute_url() |
|---|
| 217 | return HttpResponseRedirect(new_url) |
|---|
| 218 | |
|---|
| 219 | context = _get_context(request, current_page_obj) |
|---|
| 220 | |
|---|
| 221 | # Get the response for the requested cms page: |
|---|
| 222 | response = _render_cms_page(request, context) |
|---|
| 223 | |
|---|
| 224 | if getattr(request, "_use_cache", None) == None: |
|---|
| 225 | # Set _use_cache information for the PyLucid cache middleware, but only |
|---|
| 226 | # if it was set to true or false in the past |
|---|
| 227 | request._use_cache = True |
|---|
| 228 | |
|---|
| 229 | return response |
|---|
| 230 | |
|---|
| 231 | |
|---|
| 232 | def _get_page(request, page_id): |
|---|
| 233 | """ |
|---|
| 234 | returns the page object. |
|---|
| 235 | TODO: Check int(page_id)! |
|---|
| 236 | """ |
|---|
| 237 | try: |
|---|
| 238 | current_page_obj = Page.objects.get(id=int(page_id)) |
|---|
| 239 | except Page.DoesNotExist: |
|---|
| 240 | # The ID in the url is wrong -> goto the default page |
|---|
| 241 | current_page_obj = Page.objects.default_page |
|---|
| 242 | |
|---|
| 243 | user = request.user |
|---|
| 244 | if user.is_authenticated(): |
|---|
| 245 | # The page_msg system is not initialized, yet. So we must use the |
|---|
| 246 | # low level message_set method, but this ony exist for user how are |
|---|
| 247 | # login. |
|---|
| 248 | # ToDo: How can we sent a message to anonymous users? |
|---|
| 249 | user.message_set.create( |
|---|
| 250 | message=_( |
|---|
| 251 | "Error: The page ID in the url is wrong." |
|---|
| 252 | " (goto default page.)" |
|---|
| 253 | ) |
|---|
| 254 | ) |
|---|
| 255 | |
|---|
| 256 | return current_page_obj |
|---|
| 257 | |
|---|
| 258 | |
|---|
| 259 | def handle_command(request, page_id, module_name, method_name, url_args): |
|---|
| 260 | """ |
|---|
| 261 | handle a _command request |
|---|
| 262 | """ |
|---|
| 263 | current_page_obj = _get_page(request, page_id) |
|---|
| 264 | |
|---|
| 265 | context = _get_context(request, current_page_obj) |
|---|
| 266 | |
|---|
| 267 | local_response = SimpleStringIO() |
|---|
| 268 | |
|---|
| 269 | if url_args == "": |
|---|
| 270 | url_args = () |
|---|
| 271 | else: |
|---|
| 272 | url_args = (url_args,) |
|---|
| 273 | |
|---|
| 274 | try: |
|---|
| 275 | output = plugin_manager.handle_command( |
|---|
| 276 | context, local_response, module_name, method_name, url_args |
|---|
| 277 | ) |
|---|
| 278 | except AccessDenied: |
|---|
| 279 | if request.debug: |
|---|
| 280 | # don't use errorhandling -> raise the prior error |
|---|
| 281 | raise |
|---|
| 282 | page_content = "[Permission Denied!]" |
|---|
| 283 | else: |
|---|
| 284 | if output == None: |
|---|
| 285 | # Plugin/Module has retuned the locale StringIO response object |
|---|
| 286 | page_content = local_response.getvalue() |
|---|
| 287 | elif isinstance(output, basestring): |
|---|
| 288 | page_content = output |
|---|
| 289 | elif isinstance(output, HttpResponse): |
|---|
| 290 | # e.g. send a file directly back to the client |
|---|
| 291 | return output |
|---|
| 292 | else: |
|---|
| 293 | msg = ( |
|---|
| 294 | "Error: Wrong output from Plugin!" |
|---|
| 295 | " - It should be write into the response object" |
|---|
| 296 | " or return a String/HttpResponse object!" |
|---|
| 297 | " - But %s.%s has returned: %s (%s)" |
|---|
| 298 | ) % ( |
|---|
| 299 | module_name, method_name, |
|---|
| 300 | escape(repr(output)), escape(str(type(output))) |
|---|
| 301 | ) |
|---|
| 302 | raise AssertionError(msg) |
|---|
| 303 | |
|---|
| 304 | # print module_name, method_name |
|---|
| 305 | # print page_content |
|---|
| 306 | # print "---" |
|---|
| 307 | |
|---|
| 308 | if page_content: |
|---|
| 309 | # Add the CSS Info, but only if the plugin has returned content and |
|---|
| 310 | # not when the normal cms page rendered. |
|---|
| 311 | page_content = add_css_tag( |
|---|
| 312 | context, page_content, module_name, method_name |
|---|
| 313 | ) |
|---|
| 314 | |
|---|
| 315 | return _render_cms_page(request, context, page_content) |
|---|
| 316 | |
|---|
| 317 | |
|---|
| 318 | def redirect(request, url): |
|---|
| 319 | """ |
|---|
| 320 | simple redirect old PyLucid URLs to the new location. |
|---|
| 321 | old url: |
|---|
| 322 | ".../index.py/PageShortcut/" |
|---|
| 323 | new url: |
|---|
| 324 | ".../PageShortcut/" |
|---|
| 325 | """ |
|---|
| 326 | if url == "": |
|---|
| 327 | url = "/" |
|---|
| 328 | |
|---|
| 329 | return HttpResponsePermanentRedirect(url) |
|---|
| 330 | |
|---|
| 331 | |
|---|
| 332 | def permalink(request, page_id): |
|---|
| 333 | """ |
|---|
| 334 | redirect to the real page url. |
|---|
| 335 | """ |
|---|
| 336 | current_page_obj = _get_page(request, page_id) |
|---|
| 337 | url = current_page_obj.get_absolute_url() |
|---|
| 338 | return redirect(request, url) |
|---|