Repository: web2py/web2py

Issue:
Setting map_hyphen=True in routes.py breaks codemirror editor
The codemirror breaks once I set the `map_hypen` to `True`.

In my `routes.py` I have:

``` python
routers = dict(
    BASE=dict(
        default_application='mycoolapp',
        map_hyphen=True,
    )
)
```

When I try to use the web-based web2py editor it fails to load. 

Looking at the console I see the following 404's:

![screen shot 2015-02-02 at 5 35 16 pm](https://cloud.githubusercontent.com/assets/179356/6012847/ee4b6c12-ab01-11e4-8618-5ddbfbdedb81.png)

The path goes from being an _2.9.5 to -2.9.5 which is expected behavior for the URLS, however because the directory starts with a _ and gets changed to - it causes problem. For example http://localhost:8000/admin/static/**-2.9.5**/js/typeahead.min.js 



Discussion:
To add additional info to this issue, I originally tested it on version 2.9.5, but just now tested it on version 2.9.12 and issue still exists.

To reproduce it's quite easy. Download 2.9.12. Then, to the root of the web2py directory add `routes.py` and it place the following:

``` python
routers = dict(

    # base router
    BASE=dict(
        default_application='welcome',
        map_hyphen=True,
    ),
)
```

Now in the admin interface, click on **Reload routes**. Now, try to opening and editing any file using the built-in web2py code editor (uses codemirror).

uhm. I'm not convinced this is the right fix.... and in fact I can't reproduce the original issue.......
my "_2.9.5 to -2.9.5" doesn't take place in 2.9.12. The problem is already fixed by https://github.com/web2py/web2py/commit/c0536d3b7487e73599b1f2ae1fcb963b91d41e8f . Can you please doublecheck ?

@niphlod  Hi, I tested it on a fresh copy of 2.9.12. I don't think it has much to do with commit you're referencing. This is a separate issue and goes back to _at least_ 2.9.5. It has nothing to do with allowing _map_hyphen to work for application names_ as the commit suggest. 

More system details:
OSX 10.10.2
Python 2.7.6
Web2py 2.9.12

Are you **reloading the routes** after creating the routes.py with the above code in the root dir?

yep, I am. Are you ? your problem is with urls in static/ but the code ALREADY skips static . Can you make a test that doesn't involve an app (i.e. from the shell)?

@niphlod I now feel 100% sure this is a bug. I'm sorry you can't reproduce it, maybe you're not doing something right or I'm not being clear.

I tried to further debug this. I placed a print statement to see what the value of `self.controller` would be.

![screen shot 2015-02-04 at 9 35 30 am](https://cloud.githubusercontent.com/assets/179356/6045721/40e163c8-ac51-11e4-9b9d-643ea36a8388.png)

I then re-started the web2py and refreshed the editor page and checked the terminal and it printed the following values as **self.controller**s:

``` shell
default
default
default
default
mercurial
default
default
debug
static
static
static
static
static
static
static
static
default
static
static
static
static
default
static/_2.9.12  <---- look
static/_2.9.12  <---- look
static/_2.9.12  <---- look
default
default
default
default
default
default
default
default
static/_2.9.12  <---- look
static/_2.9.12  <---- look
default
default
static
```

Note that the output contains a couple `static/_2.9.12` as _controllers_.

Now check the current code:

``` python
if self.map_hyphen:
    self.controller = self.controller.replace('_', '-') # <--- does not care if static or not, conversion for controller will still take effect, and since `static/_2.9.12` is a "controller" it will get replaced by `static/-2.9.12`
    if self.controller != 'static' and not self.controller.startswith('static/'): # <--- since static check is here
        self.application = self.application.replace('_', '-')
        self.function = self.function.replace('_', '-')
```

In my PR I placed it nested in the static/ check and this has fixed the issue for me:

``` python
if self.map_hyphen:
    if self.controller != 'static' and not self.controller.startswith('static/'):
        self.application = self.application.replace('_', '-') 
        self.controller = self.controller.replace('_', '-') # <- will not get touched since it's inside static/ check
        self.function = self.function.replace('_', '-')
```

Let me know if you have more questions. Would like to get the fix merged into master :shipit: 

if indeed this is happening, the bug is static/something being a controller, not the rewrite logic...

imho the error is the way admin is coded.

``` python
{{cm=URL('static%s' % (response.static_version and '/_' + response.static_version  or ''),'codemirror')}}
<link rel="stylesheet" href="{{=cm}}/lib/codemirror.css">
```

this doesn't pass through rewrite logic as it should (calling URL('somethingwithaslashinside') is wrong ) and so the controller passed - in some weird occasion - is 'static/_1.2.3'  and everything is screwed up.
Let's patch admin correctly instead of altering the rewrite logic that is perfect. 

moreover, I found out that there is a subtle bug handling response.static_version and response.static_version_urls ........... stay tuned for a PR that:
- adds response.static_version_urls to admin (so code is decluttered and will ALWAYS work)
- fixes the subtle bug
- cleanups admin
  if you trust me, you can go ahead and close this (as already said, the rewrite logic is perfect, it's the use inside admin of URL() that is inherently wrong)... or wait for a PR and test that one.

@mdipierro : please keep this on hold a bit


Resolving patch:
diff --git a/applications/admin/models/0.py b/applications/admin/models/0.py
--- a/applications/admin/models/0.py
+++ b/applications/admin/models/0.py
@@ -47,3 +47,4 @@
 #set static_version
 from gluon.settings import global_settings
 response.static_version = global_settings.web2py_version.split('-')[0]
+response.static_version_urls = True
diff --git a/gluon/globals.py b/gluon/globals.py
--- a/gluon/globals.py
+++ b/gluon/globals.py
@@ -29,9 +29,9 @@
 import hashlib
 import portalocker
 try:
-   import cPickle as pickle
+    import cPickle as pickle
 except:
-   import pickle
+    import pickle
 from pickle import Pickler, MARK, DICT, EMPTY_DICT
 from types import DictionaryType
 import cStringIO
@@ -234,7 +234,8 @@ def parse_post_vars(self):
             dpost = cgi.FieldStorage(fp=body, environ=env, keep_blank_values=1)
             try:
                 post_vars.update(dpost)
-            except: pass
+            except:
+                pass
             if query_string is not None:
                 env['QUERY_STRING'] = query_string
             # The same detection used by FieldStorage to detect multipart POSTs
@@ -332,8 +333,8 @@ def requires_https(self):
         and secures the session.
         """
         cmd_opts = global_settings.cmd_options
-        #checking if this is called within the scheduler or within the shell
-        #in addition to checking if it's not a cronjob
+        # checking if this is called within the scheduler or within the shell
+        # in addition to checking if it's not a cronjob
         if ((cmd_opts and (cmd_opts.shell or cmd_opts.scheduler))
                 or global_settings.cronjob or self.is_https):
             current.session.secure()
@@ -394,7 +395,7 @@ def __init__(self):
         self._custom_commit = None
         self._custom_rollback = None
         self.generic_patterns = ['*']
-        self.delimiters = ('{{','}}')
+        self.delimiters = ('{{', '}}')
         self.formstyle = 'table3cols'
         self.form_label_separator = ': '
 
@@ -490,7 +491,11 @@ def call_minify(files=files):
         for item in files:
             if isinstance(item, str):
                 f = item.lower().split('?')[0]
-                if self.static_version:
+                #  if static_version we need also to check for
+                #  static_version_urls. In that case, the _.x.x.x
+                #  bit would have already been added by the URL()
+                #  function
+                if self.static_version and not self.static_version_urls:
                     item = item.replace(
                         '/static/', '/static/_%s/' % self.static_version, 1)
                 if f.endswith('.css'):
@@ -698,7 +703,7 @@ def toolbar(self):
             DIV(BEAUTIFY(current.response), backtotop,
                 _class="w2p-toolbar-hidden", _id="response-%s" % u),
             DIV(BEAUTIFY(dbtables), backtotop,
-                _class="w2p-toolbar-hidden",_id="db-tables-%s" % u),
+                _class="w2p-toolbar-hidden", _id="db-tables-%s" % u),
             DIV(BEAUTIFY(dbstats), backtotop,
                 _class="w2p-toolbar-hidden", _id="db-stats-%s" % u),
             SCRIPT("jQuery('.w2p-toolbar-hidden').hide()"),
@@ -1007,7 +1012,7 @@ def _fixup_before_save(self):
         elif self._secure and response.session_id_name in rcookies:
             rcookies[response.session_id_name]['secure'] = True
 
-    def clear_session_cookies(sefl):
+    def clear_session_cookies(self):
         request = current.request
         response = current.response
         session = response.session
@@ -1189,6 +1194,7 @@ def _close(self, response):
             except:
                 pass
 
+
 def pickle_session(s):
     return Session, (dict(s),)
 


Test patch: