Browse Source

* Increasing after_login max string size from 100 to 1000 to accomodate larger URLs.

 * controller.Notebooks now takes (and stores) an https_url constructor parameter.
 * New controller.Notebooks.updates() method to produce an updates RSS feed for a particular notebook.
 * New controller.Notebooks.get_update_link() method to make a brief page with just a link for an updated note, referred to by the feed.
 * Implemented views for the new RSS feed.
 * Fixed bug in Rss_item's guid that caused newlines to be inserted before and after long URLs.
 * Still need to unit test new controller code.
Dan Helfman 10 years ago
parent
commit
b316b2f4a3
6 changed files with 153 additions and 5 deletions
  1. 70
    1
      controller/Notebooks.py
  2. 2
    2
      controller/Root.py
  3. 1
    1
      controller/Users.py
  4. 3
    1
      view/Rss_item.py
  5. 28
    0
      view/Update_link_page.py
  6. 49
    0
      view/Updates_rss.py

+ 70
- 1
controller/Notebooks.py View File

@@ -18,6 +18,8 @@ from view.Json import Json
18 18
 from view.Html_file import Html_file
19 19
 from view.Note_tree_area import Note_tree_area
20 20
 from view.Notebook_rss import Notebook_rss
21
+from view.Updates_rss import Updates_rss
22
+from view.Update_link_page import Update_link_page
21 23
 
22 24
 
23 25
 class Notebooks( object ):
@@ -28,7 +30,7 @@ class Notebooks( object ):
28 30
   """
29 31
   Controller for dealing with notebooks and their notes, corresponding to the "/notebooks" URL.
30 32
   """
31
-  def __init__( self, database, users, files ):
33
+  def __init__( self, database, users, files, https_url ):
32 34
     """
33 35
     Create a new Notebooks object.
34 36
 
@@ -39,11 +41,14 @@ class Notebooks( object ):
39 41
     @type files: controller.Files
40 42
     @param files: controller for all uploaded files, used here for deleting files that are no longer
41 43
                   referenced within saved notes
44
+    @type https_url: unicode
45
+    @param https_url: base URL to use for SSL http requests, or an empty string
42 46
     @return: newly constructed Notebooks
43 47
     """
44 48
     self.__database = database
45 49
     self.__users = users
46 50
     self.__files = files
51
+    self.__https_url = https_url
47 52
 
48 53
   @expose( view = Main_page, rss = Notebook_rss )
49 54
   @strongly_expire
@@ -204,6 +209,70 @@ class Notebooks( object ):
204 209
       invites = invites or [],
205 210
     )
206 211
 
212
+  @expose( view = None, rss = Updates_rss )
213
+  @strongly_expire
214
+  @end_transaction
215
+  @validate(
216
+    notebook_id = Valid_id(),
217
+    notebook_name = Valid_string(),
218
+  )
219
+  def updates( self, notebook_id, notebook_name ):
220
+    """
221
+    Provide the information necessary to display an updated notes RSS feed for the given notebook.
222
+    This method does not require any sort of login.
223
+
224
+    @type notebook_id: unicode
225
+    @param notebook_id: id of the notebook to provide updates for
226
+    @type notebook_name: unicode
227
+    @param notebook_name: name of the notebook to include in the RSS feed
228
+    @rtype: unicode
229
+    @return: rendered RSS feed
230
+    """
231
+    notebook = self.__database.load( Notebook, notebook_id )
232
+    if not notebook:
233
+      raise Access_error()
234
+
235
+    recent_notes = self.__database.select_many( Note, notebook.sql_load_notes( start = 0, count = 10 ) )
236
+
237
+    return dict(
238
+      recent_notes = [ ( note.object_id, note.revision ) for note in recent_notes ],
239
+      notebook_id = notebook_id,
240
+      notebook_name = notebook_name,
241
+      https_url = self.__https_url,
242
+    )
243
+
244
+  @expose( view = Update_link_page )
245
+  @strongly_expire
246
+  @end_transaction
247
+  @validate(
248
+    notebook_id = Valid_id(),
249
+    notebook_name = Valid_string(),
250
+    note_id = Valid_id(),
251
+    revision = Valid_revision(), 
252
+  )
253
+  def get_update_link( self, notebook_id, notebook_name, note_id, revision ):
254
+    """
255
+    Provide the information necessary to display a link to an updated note. This method does not
256
+    require any sort of login.
257
+
258
+    @type notebook_id: unicode
259
+    @param notebook_id: id of the notebook the note is in
260
+    @type notebook_name: unicode
261
+    @param notebook_name: name of the notebook
262
+    @type note_id: unicode
263
+    @param note_id: id of the note to link to
264
+    @type revision: unicode
265
+    @param revision: ignored; present so RSS feed readers distinguish between different revisions
266
+    @rtype: unicode
267
+    @return: rendered HTML page
268
+    """
269
+    return dict(
270
+      notebook_id = notebook_id,
271
+      notebook_name = notebook_name,
272
+      note_id = note_id,
273
+      https_url = self.__https_url,
274
+    )
275
+
207 276
   @expose( view = Json )
208 277
   @strongly_expire
209 278
   @end_transaction

+ 2
- 2
controller/Root.py View File

@@ -46,7 +46,7 @@ class Root( object ):
46 46
       settings[ u"global" ].get( u"luminotes.rate_plans", [] ),
47 47
     )
48 48
     self.__files = Files( database, self.__users )
49
-    self.__notebooks = Notebooks( database, self.__users, self.__files )
49
+    self.__notebooks = Notebooks( database, self.__users, self.__files, settings[ u"global" ].get( u"luminotes.https_url", u"" ) )
50 50
     self.__suppress_exceptions = suppress_exceptions # used for unit tests
51 51
 
52 52
   @expose( Main_page )
@@ -55,7 +55,7 @@ class Root( object ):
55 55
   @validate(
56 56
     note_title = unicode,
57 57
     invite_id = Valid_id( none_okay = True ),
58
-    after_login = Valid_string( min = 0, max = 100 ),
58
+    after_login = Valid_string( min = 0, max = 1000 ),
59 59
     plan = Valid_int( none_okay = True ),
60 60
     user_id = Valid_id( none_okay = True ),
61 61
   )

+ 1
- 1
controller/Users.py View File

@@ -407,7 +407,7 @@ class Users( object ):
407 407
     password = Valid_string( min = 1, max = 30 ),
408 408
     login_button = unicode,
409 409
     invite_id = Valid_id( none_okay = True ),
410
-    after_login = Valid_string( min = 0, max = 100 ),
410
+    after_login = Valid_string( min = 0, max = 1000 ),
411 411
   )
412 412
   def login( self, username, password, login_button, invite_id = None, after_login = None ):
413 413
     """

+ 3
- 1
view/Rss_item.py View File

@@ -9,5 +9,7 @@ class Rss_item( Item ):
9 9
       Link( link ),
10 10
       Description( description ),
11 11
       Dc_date( date ),
12
-      Guid( guid ),
12
+      # if we don't set the separator to empty, Node inserts newlines when the guid gets too long.
13
+      # newlines in guids make Thunderbird angry
14
+      Guid( guid, separator = u"" ),
13 15
     )

+ 28
- 0
view/Update_link_page.py View File

@@ -0,0 +1,28 @@
1
+from Tags import Html, Head, Title, Body, A
2
+
3
+
4
+class Update_link_page( Html ):
5
+  def __init__( self, notebook_id, notebook_name, note_id, https_url ):
6
+    if notebook_name == u"Luminotes":
7
+      notebook_path = u"/"
8
+    elif notebook_name == u"Luminotes user guide":
9
+      notebook_path = u"/guide"
10
+    elif notebook_name == u"Luminotes blog":
11
+      notebook_path = u"/blog"
12
+    else:
13
+      notebook_path = u"/notebooks/%s" % notebook_id
14
+
15
+    notebook_path = https_url + notebook_path
16
+
17
+    Html.__init__(
18
+      self,
19
+      Head(
20
+        Title( "Note updated" ),
21
+      ),
22
+      Body(
23
+        u"A note in ",
24
+        A( u"this notebook", href = notebook_path ),
25
+        u"has been updated.",
26
+        A( u"View the note.", href = u"%s?note_id=%s" % ( notebook_path, note_id ) ),
27
+      ),
28
+    )

+ 49
- 0
view/Updates_rss.py View File

@@ -0,0 +1,49 @@
1
+import cgi
2
+from urllib import urlencode
3
+from Rss_channel import Rss_channel
4
+from Rss_item import Rss_item
5
+
6
+
7
+class Updates_rss( Rss_channel ):
8
+  def __init__(
9
+    self,
10
+    recent_notes,
11
+    notebook_id,
12
+    notebook_name,
13
+    https_url,
14
+  ):
15
+    if notebook_name == u"Luminotes":
16
+      notebook_path = u"/"
17
+    elif notebook_name == u"Luminotes user guide":
18
+      notebook_path = u"/guide"
19
+    elif notebook_name == u"Luminotes blog":
20
+      notebook_path = u"/blog"
21
+    else:
22
+      notebook_path = u"/notebooks/%s" % notebook_id
23
+
24
+    notebook_path = https_url + notebook_path
25
+
26
+    Rss_channel.__init__(
27
+      self,
28
+      cgi.escape( notebook_name ),
29
+      notebook_path,
30
+      u"Luminotes notebook",
31
+      [ Rss_item(
32
+        title = u"Note updated",
33
+        link = self.note_link( notebook_id, notebook_name, note_id, revision, https_url ),
34
+        description = cgi.escape( u'A note in <a href="%s">this notebook</a> has been updated. <a href="%s?note_id=%s">View the note.</a>' % ( notebook_path, notebook_path, note_id ) ),
35
+        date = revision.strftime( "%Y-%m-%dT%H:%M:%SZ" ),
36
+        guid = self.note_link( notebook_id, notebook_name, note_id, revision, https_url ),
37
+      ) for ( note_id, revision ) in recent_notes ],
38
+    )
39
+
40
+  @staticmethod
41
+  def note_link( notebook_id, notebook_name, note_id, revision, https_url ):
42
+    query = urlencode( [
43
+      ( u"notebook_id", notebook_id ),
44
+      ( u"notebook_name", notebook_name ),
45
+      ( u"note_id", note_id ),
46
+      ( u"revision", unicode( revision ) ),
47
+    ] )
48
+
49
+    return cgi.escape( u"%s/notebooks/get_update_link?%s" % ( https_url, query ) )

Loading…
Cancel
Save