MobileBlur

Check-in [517f4aa073]
Login
Overview
SHA1:517f4aa073cf39995af8c701e95bf90666d0d9e8
Date: 2011-11-30 04:12:13
User: spiffy
Comment:Made the app substantially prettier
Timelines: family | ancestors | descendants | both | trunk
Downloads: Tarball | ZIP archive
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2011-12-02
03:31
[7ff27f901d] Made feed and story list divs clickable instead of just their text (user: spiffy, tags: trunk)
2011-11-30
04:12
[517f4aa073] Made the app substantially prettier (user: spiffy, tags: trunk)
02:49
[dea6297b3d] Made the story title in the story view nicely styled. Put the 'mark story read' button inside the header (user: spiffy, tags: develop, v0.4)
2011-11-20
21:41
[2ac904a27b] Added support for multiple users to use the site simultaneously (user: spiffy, tags: trunk, v0.2)
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Modified applications/mobileblur/controllers/default.py from [4edf6967cb] to [cdfea9e32e].

    19     19           Field("password", "password", requires=IS_NOT_EMPTY())
    20     20       )
    21     21       if login_form.accepts(request):
    22     22           try:
    23     23               results = newsblur.login(login_form.vars["username"], login_form.vars["password"])
    24     24               response.cookies["nb_cookie"] = newsblur.cookies["newsblur_sessionid"]
    25     25               response.cookies["nb_cookie"]["path"] = "/"
    26         -            print "cookie =", newsblur.cookies
    27     26               redirect(URL("index"))
    28     27           except Exception as ex:
    29     28               login_form.insert(-1, ex.message)
           29  +            login_form._class = "alert-message block-message error"
    30     30   
    31     31       return dict(login_form=login_form)
           32  +
           33  +
           34  +def logout():
           35  +    response.cookies["nb_cookie"] = ""
           36  +    response.cookies["nb_cookie"]["expires"] = -10
           37  +    response.cookies["nb_cookie"]["path"] = "/"
           38  +    redirect(URL("index"))

Modified applications/mobileblur/controllers/feeds.py from [b47f0eada4] to [30c965b149].

     1      1   # -*- coding: utf-8 -*-
     2      2   
     3      3   from pprint import pprint
            4  +import time
     4      5   
     5      6   def view():
     6         -    stories = newsblur.feed(request.args[0])["stories"]
     7         -    feeds = newsblur.feeds(flat=True)["feeds"]
     8         -    feed = [feed for feed in feeds.itervalues() if feed["id"]==int(request.args[0])][0]
            7  +    print ""
            8  +    s = time.time()
            9  +    feed = newsblur.feed(request.args[0])
           10  +    stories = feed["stories"]
           11  +    print time.time() - s
           12  +
           13  +    print feed.keys()
           14  +
           15  +    if not feed.has_key("feed_title"):
           16  +        s = time.time()
           17  +        feeds = newsblur.feeds(flat=True)["feeds"]
           18  +        print time.time() - s
           19  +
           20  +        s = time.time()
           21  +        feed = [feed for feed in feeds.itervalues() if feed["id"]==int(request.args[0])][0]
           22  +        print time.time() - s
           23  +
           24  +    response.title = feed["feed_title"]
           25  +
     9     26       return dict(stories=stories, feed=feed)
           27  +
    10     28   
    11     29   def mark_read():
    12         -    newsblur.mark_feed_as_read(request.vars["feed"])
           30  +    if len(request.args) > 0:
           31  +        newsblur.mark_feed_as_read(request.args[0])
    13     32       redirect(URL("default", "index"))

Modified applications/mobileblur/controllers/stories.py from [59984e848a] to [fbf9de8ba7].

     1      1   # -*- coding: utf-8 -*-
     2      2   
     3      3   from pprint import pprint
     4      4   
     5      5   def view():
     6      6       stories = newsblur.feed(request.vars["feed_id"])["stories"]
     7      7       story = [story for story in stories if story["id"]==request.vars["story"]][0]
     8         -    return dict(story=story)
            8  +    return dict(story=story, feed_id=request.vars["feed_id"])
            9  +
           10  +def mark_read():
           11  +    results = newsblur.mark_story_as_read(request.vars["story_id"], request.vars["feed_id"])
           12  +    redirect(URL("default", "index"))

Modified applications/mobileblur/models/0_helpers.py from [c67dbf684a] to [8ed26c507d].

     1      1   newsblur = local_import("newsblur")
     2      2   newsblur = newsblur.NewsBlur()
     3      3   
     4      4   threshold = 0
     5      5   thresholds = ["nt", "ps", "ng"]  # indices -1, 0, 1 for negative, neutral, and positive intelligence filters
     6      6   
     7         -print request.cookies
     8      7   if [request.application, request.controller, request.function] != [request.application, "default", "login"]:
     9      8       if "nb_cookie" not in request.cookies.keys():
    10      9           redirect(URL("default", "login"))
    11     10       else:
    12     11           newsblur.cookies["newsblur_sessionid"] = request.cookies["nb_cookie"].value

Modified applications/mobileblur/modules/newsblur.py from [a487a766ef] to [df40c3d1a1].

    19     19           Login as an existing user.
    20     20           If a user has no password set, you cannot just send any old password. 
    21     21           Required parameters, username and password, must be of string type.
    22     22           '''
    23     23   
    24     24           url = nb_url + 'api/login'
    25     25           results = requests.post(url, data={"username": username, "password": password})
    26         -        print "results.cookies =", results.cookies
    27         -        print type(results.cookies)
    28     26           self.cookies = results.cookies
    29     27           results = simplejson.loads(results.content)
    30     28           if results["authenticated"] is False:
    31     29               raise Exception("The newsblur credentials you provided are invalid")
    32     30           return results
    33     31   
    34     32       def logout(self, ):

Modified applications/mobileblur/static/css/base.css from [0353986da0] to [c4d6d0ed8b].

    23     23   - always indent the first line and add space below paragraphs
    24     24   - bullets and numbers style and indent
    25     25   - form and table padding
    26     26   - code blocks
    27     27   - left and right padding to quoted text
    28     28   - page layout alignment, width and padding (change this for spaces)
    29     29   - column widths (change this to use left_sidebar and right_sidebar)
    30         -- backrgound images and colors (change this for colors)
           30  +- background images and colors (change this for colors)
    31     31   - web2py specific (.flash, .error)
    32     32   
    33     33   Notice:
    34     34   - even if you use a different layout/css you may need classes .flash and .error
    35     35   - this is all color neutral except for #349C01 (header, links, lines)
    36         -- there are two backrgound images: images/background.png and images/header.png
           36  +- there are two background images: images/background.png and images/header.png
    37     37   
    38     38   License: This file is released under BSD and MIT
    39     39   
    40     40   */
    41     41   
    42     42   /*     
    43     43     credit is left where credit is due.
................................................................................
   141    141   h1,h2,h3,h4,h5,h6 { font-weight: bold; }
   142    142   
   143    143   /* always force a scrollbar in non-IE */ 
   144    144   html { overflow-y: scroll; }
   145    145    
   146    146   /* Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test */
   147    147   a:hover, a:active { outline: none; }
   148         -
          148  +/*
   149    149   a, a:active, a:visited { color:#607890; }
   150    150   a:hover { color:#036; }
          151  +*/
   151    152   
   152    153   ul, ol { margin-left: 1.8em; }
   153    154   ol { list-style-type: decimal; }
   154    155   
   155    156   /* Remove margins for navigation lists */
   156    157   nav ul, nav li { margin: 0; } 
   157    158   
................................................................................
   325    326   }
   326    327   
   327    328   fieldset { border: 1px solid #dedede; padding: 6px; }
   328    329   legend { font-weight: bold; }
   329    330   
   330    331   input:focus, textarea:focus { background: #fafafa; }
   331    332   
   332         -p {text-indent:30px;}
   333         -
   334         -p, blockquote {    
   335         -    margin-bottom: 10px;
   336         -}
   337         -
   338    333   h1,h2,h3,h4,h5,h6 { line-height: 170%; }
   339    334   h1 {font-size: 2.0em;}
   340    335   h2 {font-size: 1.8em;}
   341    336   h3 {font-size: 1.4em;}
   342    337   h4 {font-size: 1.2em;}
   343    338   h5 {font-size: 1.0em;}
   344    339   h6 {font-size: 0.8em;}
   345    340   
   346         -/*********** page layout alignment, width and padding ***********/
   347         -/*body {background-color: #000;}*/
   348         -#container, #header, #page, #content, #statusbar,
   349         -#footer, #wrapper { display:block; line-height: 170%; }
   350         -#wrapper {width: 900px;}
   351         -#container {
   352         -    margin: 0 auto;
   353         -    padding: 0;
   354         -}
   355         -#wrapper {margin: 0 auto;} 
   356         -#wrapper {background-color: #fff; padding: 5px;} 
   357         -#statusbar { margin: 5px 0px 20px 0px;}
   358         -#footer {    
   359         -    margin-top: 30px;    
   360         -    padding: 5px;    
   361         -}
   362         -#statusbar, #footer {    
   363         -    background: #eaeaea; 
   364         -    border-top: 1px #aaa solid;    
   365         -}
   366         -#logo {
   367         -    width: 68px;
   368         -    height: 62px;
   369         -    background: url(../images/logo.png);
   370         -}
   371         -#appname {
   372         -    color: #cccccc;
   373         -}
   374         -
   375         -#right_sidebar { width: 160px; float:right; display: none; }
   376         -#left_sidebar { width: 160px; float:left; display: none; }
   377         -#content { float: left; /*width: 740px;*//*width: 63%;*/ /*width: 640px; float:left;*/ } /* uncomment this if you are going to use sidebars */
   378         - 
   379         -.auth_navbar {
   380         -   top: 0px;
   381         -   float: right;
   382         -   padding: 3px 10px 3px 10px; 
   383         -}
   384         -
   385    341   /*********** web2py specific ***********/
   386    342   div.flash {
   387    343       font-weight: bold;
   388    344       display: none;
   389    345       position: fixed;    
   390    346       padding: 10px;
   391    347       top: 40px;
................................................................................
   531    487     
   532    488     
   533    489     /* Uncomment if you don't want iOS and WinMobile to mobile-optimize the text for you
   534    490        j.mp/textsizeadjust 
   535    491     html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
   536    492   }
   537    493   
          494  +@media handheld {
          495  +    body {
          496  +    }
          497  +}
          498  +
   538    499   
   539    500   /* 
   540    501    * print styles
   541    502    * inlined to avoid required HTTP connection www.phpied.com/delay-loading-your-print-css/ 
   542    503    */
   543    504   @media print {
   544    505     * { background: transparent !important; color: #444 !important; text-shadow: none !important; }
................................................................................
   550    511     thead { display: table-header-group; } /* css-discuss.incutio.com/wiki/Printing_Tables */ 
   551    512     tr, img { page-break-inside: avoid; }
   552    513     @page { margin: 0.5cm; }
   553    514     p, h2, h3 { orphans: 3; widows: 3; }
   554    515     h2, h3{ page-break-after: avoid; }
   555    516   }
   556    517   
          518  +body {
          519  +/*    background-color: #f2c84b;*/
          520  +    color: #493F3E
          521  +}
          522  +
          523  +header {
          524  +    background-color: #95392E;
          525  +    padding: 1%;
          526  +    border-bottom: 1px solid #95392E;
          527  +    color: white;
          528  +}
          529  +header > h1 > a {
          530  +    text-decoration: none;
          531  +    color: white;
          532  +    font-weight: bold;
          533  +}
          534  +
          535  +#story > header > a {
          536  +    color: white;
          537  +}
          538  +
          539  +header > a> h2 {
          540  +/*    border-top: 1px solid white;*/
          541  +    color: white;
          542  +}
          543  +
          544  +#story-list {
          545  +/*    background-color: #f2c84b;*/
          546  +}
          547  +
          548  +#story-list > .story > a, .feed > a {
          549  +    text-decoration: none;
          550  +    font-weight: bold;
          551  +    margin-top: 0;
          552  +}
          553  +#story-list > .story > p {
          554  +    font-size: .75em;
          555  +}
          556  +
          557  +#story-list > .story > .read {
          558  +    color: #D99B48;
          559  +}
          560  +#story-list > .story > .unread, .feed > a {
          561  +    color: #C57E3C;
          562  +}
          563  +
          564  +#story-list > .story, .feed {
          565  +    padding: .5%;
          566  +    border-top: 1px solid #95392E;
          567  +    margin-top: .33%;
          568  +}
          569  +
          570  +span.ps, span.nt, span.ng {
          571  +    padding: .05em;
          572  +    border-radius: 5px;
          573  +}
          574  +span.ps {
          575  +    background-color: #ABAA7A;
          576  +}
          577  +span.nt {
          578  +    background-color: #D99B48;
          579  +}
          580  +span.ng {
          581  +    background-color: red;
          582  +}
          583  +
          584  +footer {
          585  +    background-color: #95392E;
          586  +    padding: 1%;
          587  +    border-top: 1px solid #95392E;
          588  +    color: white;
          589  +}
          590  +footer > a {
          591  +    text-decoration: none;
          592  +    color: white;
          593  +    font-weight: bold;
          594  +}

Modified applications/mobileblur/views/default/index.html from [1ac89d52bc] to [de8ac0789f].

     1         -{{left_sidebar_enabled=right_sidebar_enabled=False}}
     2      1   {{extend 'layout.html'}}
     3      2   
     4      3   {{ for feed in feeds.itervalues(): }}
     5         -    {{ if threshold == -1 and feed["ng"] > 0: }} <span class='ng'>[ {{= feed["ng"] }} ]</span> {{ pass }}
     6         -    {{ if threshold <= 0 and feed["nt"] > 0: }} <span class='nt'>[ {{= feed["nt"] }} ]</span> {{ pass }}
     7         -    {{if feed["ps"] > 0: }}<span class='ps'>[ {{= feed["ps"] }} ]</span> {{ pass }}
     8         -    <a href="{{= URL(c="feeds", f="view", args=[feed["id"]]) }}">{{= feed["feed_title"] }}</a><br />
            4  +    <div class="feed">
            5  +        {{ if threshold == -1 and feed["ng"] > 0: }} <span class='ng'>{{= feed["ng"] }}</span> {{ pass }}
            6  +        {{ if threshold <= 0 and feed["nt"] > 0: }} <span class='nt'>{{= feed["nt"] }}</span> {{ pass }}
            7  +        {{if feed["ps"] > 0: }}<span class='ps'>{{= feed["ps"] }}</span> {{ pass }}
            8  +        <a href="{{= URL(c="feeds", f="view", args=[feed["id"]]) }}">{{= feed["feed_title"] }}</a><br />
            9  +    </div>
     9     10   {{ pass }}
    10         -
    11         -{{block left_sidebar}}New Left Sidebar Content{{end}}
    12         -{{block right_sidebar}}New Right Sidebar Content{{end}}

Modified applications/mobileblur/views/feeds/view.html from [3646a45d79] to [6526673ca3].

     1         -{{left_sidebar_enabled=right_sidebar_enabled=False}}
     2      1   {{extend 'layout.html'}}
     3      2   
     4         -<h1>{{= feed["feed_title"] }}</h1>
     5         -<a href="{{= URL("mark_read", vars=dict(feed=feed["id"])) }}"> Mark feed as read</a>
            3  +{{ block header_bonus }}
            4  +    <h2>{{= feed["feed_title"] }}</h2>
            5  +    <a href="{{= URL("mark_read", args=[feed["id"]]) }}" class="button">Mark feed as read</a>
            6  +{{ end }}
     6      7   
     7         -{{ for story in stories: }}
     8         -    <a href="{{= URL(c="stories", f="view", vars=dict(story=story["id"], feed_id=feed["id"])) }}"><h2>{{= story["story_title"] }}</h2></a>
     9         -{{ pass }}
            8  +<section id="story-list">
            9  +{{
           10  +import time 
           11  +s = time.time()
           12  +}}
           13  +    {{ 
           14  +        for story in stories: 
           15  +        if sum([v for k,v in story["intelligence"].iteritems()]) < threshold:
           16  +            continue
           17  +    }}
    10     18   
    11         -{{block left_sidebar}}New Left Sidebar Content{{end}}
    12         -{{block right_sidebar}}New Right Sidebar Content{{end}}
           19  +        {{ print story["read_status"] }}
           20  +        <div class="story">
           21  +            <a href="{{= URL(c="stories", f="view", vars=dict(story=story["id"], feed_id=feed["id"])) }}" class="{{= "unread" if story["read_status"] == 0 else "read" }}">
           22  +                {{= story["story_title"] }}
           23  +            </a>
           24  +            <p>{{= story["story_date"] }}</p>
           25  +        </div>
           26  +    {{ pass }}
           27  +{{ print time.time() - s }}
           28  +</section>

Modified applications/mobileblur/views/layout.html from [78b7d8a5ef] to [2dc8430c3d].

     6      6       <!-- www.phpied.com/conditional-comments-block-downloads/ -->
     7      7       <!--[if IE]><![endif]-->    
     8      8       <!-- Always force latest IE rendering engine 
     9      9   	 (even in intranet) & Chrome Frame 
    10     10   	 Remove this if you use the .htaccess -->
    11     11       <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    12     12       
    13         -    <title>{{=response.title or request.application}}</title>
           13  +    <title>{{ block title }}{{= " - ".join([response.title, request.application]) }}{{ end }}</title>
    14     14       
    15     15       <!-- http://dev.w3.org/html5/markup/meta.name.html -->
    16     16       <meta name="application-name" content="{{=request.application}}" />	
    17     17       
    18     18       <!-- Speaking of Google, don't forget to set your site up: 
    19     19   	 http://google.com/webmasters -->
    20     20       <meta name="google-site-verification" content="" />
................................................................................
    21     21       
    22     22       <!--  Mobile Viewport Fix
    23     23   	  j.mp/mobileviewport & davidbcalhoun.com/2010/viewport-metatag 
    24     24   	  device-width: Occupy full width of the screen in its current orientation
    25     25   	  initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
    26     26   	  maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
    27     27         -->
    28         -    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
           28  +    <meta name="viewport" content="width=320" />
    29     29       
    30     30       <!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
    31     31       <link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
    32     32       <link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
    33     33   
    34     34       <!-- For the less-enabled mobile browsers like Opera Mini -->
    35     35       <link rel="stylesheet" media="handheld" href="{{=URL('static','css/handheld.css')}}">
................................................................................
    55     55       else: width_content='100%'
    56     56       if left_sidebar_enabled: left_sidebar_style = 'style="display: block;"'
    57     57       else: left_sidebar_style = 'style="display: none;"'
    58     58       if right_sidebar_enabled: right_sidebar_style = 'style="display: block;"'
    59     59       else: right_sidebar_style = 'style="display: none;"'
    60     60       style_content = 'style="width: %s"' % width_content
    61     61       }}
           62  +<!--    <link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">-->
           63  +    <link rel="stylesheet" href="{{= URL("static", "css/1140.css") }}">
    62     64     </head>
    63     65     
    64     66     <!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->	
    65     67     <!--[if lt IE 7 ]> <body class="ie6"> <![endif]-->
    66     68     <!--[if IE 7 ]>    <body class="ie7"> <![endif]-->
    67     69     <!--[if IE 8 ]>    <body class="ie8"> <![endif]-->
    68     70     <!--[if IE 9 ]>    <body class="ie9"> <![endif]-->
    69     71     <!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
    70     72       
    71     73       <div class="flash">{{=response.flash or ''}}</div> <!-- notification div -->
    72     74       
    73         -    <div id="container">	      		      	
    74         -      
    75         -      <div id="wrapper">		
    76         -	
    77         -	<div id="header"> <!-- header and login nav -->
           75  +	<header>
    78     76   	  {{block header}} <!-- this is default header -->
    79         -	  {{try:}}{{=auth.navbar(action=URL('default','user'))}}{{except:pass}}		          
    80         -	  <h1><span id="appname">{{=request.application.capitalize()}}</span>App</h1>
    81         -	  <div style="clear: both;"></div><!-- Clear the divs -->
    82         -	  {{end}}				  					  
    83         -	</div><!-- header  -->
           77  +          <h1><a href="{{= URL("mobileblur", "default", "index") }}">MobileBlur</a></h1>
           78  +	  {{end}}
           79  +      {{ block header_bonus }}{{end}}
           80  +	</header>
    84     81   	
    85         -	<div id="statusbar"><!-- statusbar is menu zone -->
    86         -	  {{block statusbar}} <!-- this is default statusbar -->
    87         -	  {{#------ superfish menu ------}}
    88         -	  {{=MENU(response.menu,_class='sf-menu')}}
    89         -	  <script type="text/javascript">
    90         -	    jQuery(document).ready(function(){
    91         -	    jQuery('ul.sf-menu').superfish();});
    92         -	  </script>
    93         -	  <div style="clear: both;"></div><!-- Clear the divs -->	
    94         -	  {{end}}				
    95         -	</div><!-- statusbar -->
    96         -	
    97         -	<div id="page"> <!-- Here my central body -->	  
           82  +    <section id="content" class-"container" {{=XML(style_content)}} >
           83  +        {{include}}	
           84  +    </section>
    98     85   
    99         -	  {{if left_sidebar_enabled:}}
   100         -          <div id="left_sidebar" {{=XML(left_sidebar_style)}} >
   101         -            <div style="padding: 4px;">
   102         -	      {{block left_sidebar}}Content Left Sidebar{{end}}		  
   103         -            </div>
   104         -          </div><!-- left_sidebar -->
   105         -	  {{pass}}
   106         -
   107         -	  <!-- content -->
   108         -	  <div id="content" {{=XML(style_content)}} >
   109         -	    {{include}}	
   110         -	  </div>
   111         -	  <!-- content -->
   112         -
   113         -	  {{if right_sidebar_enabled:}}
   114         -          <div id="right_sidebar" {{=XML(right_sidebar_style)}} >
   115         -            <div style="padding: 4px;">
   116         -              {{block right_sidebar}}Content Right Sidebar{{end}}
   117         -            </div>
   118         -	  </div><!-- right_sidebar -->
   119         -          {{pass}}
   120         -
   121         -
   122         -	  <div style="clear: both;"></div><!-- Clear the divs -->
   123         -	  
   124         -	</div><!-- page -->							
   125         -	
   126         -	<div id="footer">
   127         -	  {{block footer}} <!-- this is default footer -->
   128         -	  <a href="http://www.web2py.com/" style="float: left; padding-right: 6px;">
   129         -	    <img src="{{=URL('static','images/poweredby.png')}}"/>
   130         -	  </a>
   131         -	  {{=T('Copyright')}} &#169; 2010				
   132         -	  <div style="clear: both;"></div><!-- Clear the divs -->
   133         -	  {{end}}
   134         -
   135         -	</div><!-- footer -->			
   136         -      </div><!-- wrapper -->
   137         -    </div><!-- container -->		
           86  +    <footer>
           87  +        <a href="{{= URL("default", "logout") }}">Log out</a>
           88  +    </footer>
   138     89       
   139     90       <!--[if lt IE 7 ]>
   140     91   	<script src="{{=URL('static','js/dd_belatedpng.js')}}"></script>
   141     92   	<script> DD_belatedPNG.fix('img, .png_bg'); //fix any <img> or .png_bg background-images </script>
   142     93   	<![endif]-->
   143     94       
   144     95       <!-- asynchronous google analytics: mathiasbynens.be/notes/async-analytics-snippet 
   145     96   	 change the UA-XXXXX-X to be your site's ID -->
   146         -    <!--   
   147     97   	   <script>
   148         -	     var _gaq = [['_setAccount', 'UA-XXXXX-X'], ['_trackPageview']];
           98  +	     var _gaq = [['_setAccount', 'UA-27222314-1'], ['_trackPageview']];
   149     99   	     (function(d, t) {
   150    100   	     var g = d.createElement(t),
   151    101   	     s = d.getElementsByTagName(t)[0];
   152    102   	     g.async = true;
   153    103   	     g.src = '//www.google-analytics.com/ga.js';
   154    104   	     s.parentNode.insertBefore(g, s);
   155    105   	     })(document, 'script');
   156    106   	   </script>
   157         -	   -->    
   158    107     </body>
   159    108   </html>

Modified applications/mobileblur/views/stories/view.html from [859afcf521] to [bcfec0acca].

     1         -{{left_sidebar_enabled=right_sidebar_enabled=False}}
     2      1   {{extend 'layout.html'}}
     3      2   
     4         -<a href="{{= story["story_permalink"] }}"><h1>{{= story["story_title"] }}</h1></a>
     5         -
     6         -{{= XML(story["story_content"]) }}
     7         -
     8         -{{block left_sidebar}}New Left Sidebar Content{{end}}
     9         -{{block right_sidebar}}New Right Sidebar Content{{end}}
            3  +{{ block header_bonus }}
            4  +    <a href="{{= story["story_permalink"] }}">
            5  +        <h2>{{= story["story_title"] }}</h2>
            6  +    </a>
            7  +    <a href="{{= URL("mark_read", vars=dict(story_id=story["id"], feed_id=feed_id)) }}" class="button">
            8  +        Mark story as read
            9  +    </a>
           10  +{{ end }}
           11  +<section id="story">
           12  +    {{= XML(story["story_content"]) }}
           13  +</section>

Added routes.py version [b42c3be42e].

            1  +#!/usr/bin/python
            2  +# -*- coding: utf-8 -*-
            3  +
            4  +# default_application, default_controller, default_function
            5  +# are used when the respective element is missing from the
            6  +# (possibly rewritten) incoming URL
            7  +#
            8  +default_application = 'mobileblur'    # ordinarily set in base routes.py
            9  +default_controller = 'default'  # ordinarily set in app-specific routes.py
           10  +default_function = 'index'      # ordinarily set in app-specific routes.py
           11  +
           12  +# routes_app is a tuple of tuples.  The first item in each is a regexp that will
           13  +# be used to match the incoming request URL. The second item in the tuple is
           14  +# an applicationname.  This mechanism allows you to specify the use of an
           15  +# app-specific routes.py. This entry is meaningful only in the base routes.py.
           16  +#
           17  +# Example: support welcome, admin, app and myapp, with myapp the default:
           18  +
           19  +
           20  +routes_app = ((r'/(?P<app>welcome|admin|app)\b.*', r'\g<app>'),
           21  +              (r'(.*)', r'myapp'),
           22  +              (r'/?(.*)', r'myapp'))
           23  +
           24  +# routes_in is a tuple of tuples.  The first item in each is a regexp that will
           25  +# be used to match the incoming request URL. The second item in the tuple is
           26  +# what it will be replaced with.  This mechanism allows you to redirect incoming
           27  +# routes to different web2py locations
           28  +#
           29  +# Example: If you wish for your entire website to use init's static directory:
           30  +#
           31  +#   routes_in=( (r'/static/(?P<file>[\w./-]+)', r'/init/static/\g<file>') )
           32  +#
           33  +
           34  +routes_in = ((r'.*:/favicon.ico', r'/examples/static/favicon.ico'),
           35  +             (r'.*:/robots.txt', r'/examples/static/robots.txt'),
           36  +             ((r'.*http://otherdomain.com.* (?P<any>.*)', r'/app/ctr\g<any>')))
           37  +
           38  +# routes_out, like routes_in translates URL paths created with the web2py URL()
           39  +# function in the same manner that route_in translates inbound URL paths.
           40  +#
           41  +
           42  +routes_out = ((r'.*http://otherdomain.com.* /app/ctr(?P<any>.*)', r'\g<any>'),
           43  +              (r'/app(?P<any>.*)', r'\g<any>'))
           44  +
           45  +# Error-handling redirects all HTTP errors (status codes >= 400) to a specified
           46  +# path.  If you wish to use error-handling redirects, uncomment the tuple
           47  +# below.  You can customize responses by adding a tuple entry with the first
           48  +# value in 'appName/HTTPstatusCode' format. ( Only HTTP codes >= 400 are
           49  +# routed. ) and the value as a path to redirect the user to.  You may also use
           50  +# '*' as a wildcard.
           51  +#
           52  +# The error handling page is also passed the error code and ticket as
           53  +# variables.  Traceback information will be stored in the ticket.
           54  +#
           55  +# routes_onerror = [
           56  +#     (r'init/400', r'/init/default/login')
           57  +#    ,(r'init/*', r'/init/static/fail.html')
           58  +#    ,(r'*/404', r'/init/static/cantfind.html')
           59  +#    ,(r'*/*', r'/init/error/index')
           60  +# ]
           61  +
           62  +# specify action in charge of error handling
           63  +#
           64  +# error_handler = dict(application='error',
           65  +#                      controller='default',
           66  +#                      function='index')
           67  +
           68  +# In the event that the error-handling page itself returns an error, web2py will
           69  +# fall back to its old static responses.  You can customize them here.
           70  +# ErrorMessageTicket takes a string format dictionary containing (only) the
           71  +# "ticket" key.
           72  +
           73  +# error_message = '<html><body><h1>%s</h1></body></html>'
           74  +# error_message_ticket = '<html><body><h1>Internal error</h1>Ticket issued: <a href="/admin/default/ticket/%(ticket)s" target="_blank">%(ticket)s</a></body></html>'
           75  +
           76  +# specify a list of apps that bypass args-checking and use request.raw_args
           77  +#
           78  +#routes_apps_raw=['myapp']
           79  +#routes_apps_raw=['myapp', 'myotherapp']
           80  +
           81  +def __routes_doctest():
           82  +    '''
           83  +    Dummy function for doctesting routes.py.
           84  +
           85  +    Use filter_url() to test incoming or outgoing routes;
           86  +    filter_err() for error redirection.
           87  +
           88  +    filter_url() accepts overrides for method and remote host:
           89  +        filter_url(url, method='get', remote='0.0.0.0', out=False)
           90  +
           91  +    filter_err() accepts overrides for application and ticket:
           92  +        filter_err(status, application='app', ticket='tkt')
           93  +
           94  +    >>> import os
           95  +    >>> import gluon.main
           96  +    >>> from gluon.rewrite import regex_select, load, filter_url, regex_filter_out, filter_err, compile_regex
           97  +    >>> regex_select()
           98  +    >>> load(routes=os.path.basename(__file__))
           99  +
          100  +    >>> os.path.relpath(filter_url('http://domain.com/favicon.ico'))
          101  +    'applications/examples/static/favicon.ico'
          102  +    >>> os.path.relpath(filter_url('http://domain.com/robots.txt'))
          103  +    'applications/examples/static/robots.txt'
          104  +    >>> filter_url('http://domain.com')
          105  +    '/init/default/index'
          106  +    >>> filter_url('http://domain.com/')
          107  +    '/init/default/index'
          108  +    >>> filter_url('http://domain.com/init/default/fcn')
          109  +    '/init/default/fcn'
          110  +    >>> filter_url('http://domain.com/init/default/fcn/')
          111  +    '/init/default/fcn'
          112  +    >>> filter_url('http://domain.com/app/ctr/fcn')
          113  +    '/app/ctr/fcn'
          114  +    >>> filter_url('http://domain.com/app/ctr/fcn/arg1')
          115  +    "/app/ctr/fcn ['arg1']"
          116  +    >>> filter_url('http://domain.com/app/ctr/fcn/arg1/')
          117  +    "/app/ctr/fcn ['arg1']"
          118  +    >>> filter_url('http://domain.com/app/ctr/fcn/arg1//')
          119  +    "/app/ctr/fcn ['arg1', '']"
          120  +    >>> filter_url('http://domain.com/app/ctr/fcn//arg1')
          121  +    "/app/ctr/fcn ['', 'arg1']"
          122  +    >>> filter_url('HTTP://DOMAIN.COM/app/ctr/fcn')
          123  +    '/app/ctr/fcn'
          124  +    >>> filter_url('http://domain.com/app/ctr/fcn?query')
          125  +    '/app/ctr/fcn ?query'
          126  +    >>> filter_url('http://otherdomain.com/fcn')
          127  +    '/app/ctr/fcn'
          128  +    >>> regex_filter_out('/app/ctr/fcn')
          129  +    '/ctr/fcn'
          130  +    >>> filter_url('https://otherdomain.com/app/ctr/fcn', out=True)
          131  +    '/ctr/fcn'
          132  +    >>> filter_url('https://otherdomain.com/app/ctr/fcn/arg1//', out=True)
          133  +    '/ctr/fcn/arg1//'
          134  +    >>> filter_url('http://otherdomain.com/app/ctr/fcn', out=True)
          135  +    '/fcn'
          136  +    >>> filter_url('http://otherdomain.com/app/ctr/fcn?query', out=True)
          137  +    '/fcn?query'
          138  +    >>> filter_url('http://otherdomain.com/app/ctr/fcn#anchor', out=True)
          139  +    '/fcn#anchor'
          140  +    >>> filter_err(200)
          141  +    200
          142  +    >>> filter_err(399)
          143  +    399
          144  +    >>> filter_err(400)
          145  +    400
          146  +    >>> filter_url('http://domain.com/welcome', app=True)
          147  +    'welcome'
          148  +    >>> filter_url('http://domain.com/', app=True)
          149  +    'myapp'
          150  +    >>> filter_url('http://domain.com', app=True)
          151  +    'myapp'
          152  +    >>> compile_regex('.*http://otherdomain.com.* (?P<any>.*)', '/app/ctr\g<any>')[0].pattern
          153  +    '^.*http://otherdomain.com.* (?P<any>.*)$'
          154  +    >>> compile_regex('.*http://otherdomain.com.* (?P<any>.*)', '/app/ctr\g<any>')[1]
          155  +    '/app/ctr\\\\g<any>'
          156  +    >>> compile_regex('/$c/$f', '/init/$c/$f')[0].pattern
          157  +    '^.*?:https?://[^:/]+:[a-z]+ /(?P<c>\\\\w+)/(?P<f>\\\\w+)$'
          158  +    >>> compile_regex('/$c/$f', '/init/$c/$f')[1]
          159  +    '/init/\\\\g<c>/\\\\g<f>'
          160  +    '''
          161  +    pass
          162  +
          163  +if __name__ == '__main__':
          164  +    import doctest
          165  +    doctest.testmod()
          166  +