Browse Source

add search feature + improved document interface

Damien Accorsi 10 years ago
parent
commit
050b692cda

+ 2 - 0
bin/setup.sh View File

@@ -61,6 +61,8 @@ echo "-> psycopg2"
61 61
 echo "-> pillow"
62 62
 pip install psycopg2
63 63
 pip install pillow
64
+pip install beautifulsoup4
65
+
64 66
 echo
65 67
 echo
66 68
 

+ 3 - 3
doc/database/pod-create-database-and-user.sh View File

@@ -1,7 +1,7 @@
1 1
 #!/bin/bash
2
-POD_DB_USER='pod_master'
3
-POD_DB_USER_PASSWORD='pod_master_password'
4
-POD_DB_NAME='pod'
2
+POD_DB_USER='pod_protov1_dev'
3
+POD_DB_USER_PASSWORD='pod_protov1_dev_password'
4
+POD_DB_NAME='pod_protov1_dev'
5 5
 
6 6
 # DB_HOST='127.0.0.1'
7 7
 # DB_PORT='5432'

+ 1 - 1
pboard/development.ini View File

@@ -60,7 +60,7 @@ beaker.session.validate_key = 3283411b-1904-4554-b0e1-883863b53080
60 60
 # in development
61 61
 
62 62
 #sqlalchemy.url = postgres://pboard:pboard@127.0.0.1:5432/pboarddb
63
-sqlalchemy.url = postgres://pod_master:pod_master_password@127.0.0.1:5432/pod
63
+sqlalchemy.url = postgres://pod_protov1_dev:pod_protov1_dev_password@127.0.0.1:5432/pod_protov1_dev
64 64
 #echo shouldn't be used together with the logging module.
65 65
 sqlalchemy.echo = false
66 66
 sqlalchemy.echo_pool = false

+ 2 - 6
pboard/pboard.egg-info/SOURCES.txt View File

@@ -54,6 +54,7 @@ pboard/public/img/glyphicons.png
54 54
 pboard/public/img/turbogears_logo.png
55 55
 pboard/public/img/turbogears_logo_big.png
56 56
 pboard/public/img/under_the_hood_blue.png
57
+pboard/public/javascript/pod.js
57 58
 pboard/public/javascript/external/bootstrap-datetimepicker.min.js
58 59
 pboard/public/javascript/external/bootstrap-wysiwyg.js
59 60
 pboard/public/javascript/external/bootstrap.js
@@ -90,21 +91,16 @@ pboard/public/javascript/external/google-code-prettify/lang-yaml.js
90 91
 pboard/public/javascript/external/google-code-prettify/prettify.js
91 92
 pboard/public/javascript/external/google-code-prettify/run_prettify.js
92 93
 pboard/templates/__init__.py
93
-pboard/templates/__init__.pyc
94
+pboard/templates/about.mak
94 95
 pboard/templates/dashboard.mak
95 96
 pboard/templates/document.mak
96
-pboard/templates/document.mak~
97 97
 pboard/templates/error.mak
98 98
 pboard/templates/index.mak
99
-pboard/templates/index.mak~
100 99
 pboard/templates/login.mak
101
-pboard/templates/master.html
102 100
 pboard/templates/master.mak
103 101
 pboard/templates/pod.mak
104
-pboard/templates/pod.mak~
105 102
 pboard/templates/simple_mako.mak
106 103
 pboard/templates/debug/__init__.py
107
-pboard/templates/debug/__init__.pyc
108 104
 pboard/templates/debug/environ.mak
109 105
 pboard/templates/debug/iconset.mak
110 106
 pboard/templates/debug/identity.mak

+ 28 - 11
pboard/pboard/controllers/root.py View File

@@ -106,18 +106,18 @@ class RootController(BaseController):
106 106
     @expose('pboard.templates.dashboard')
107 107
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
108 108
     def dashboard(self):
109
-      loCurrentUser   = pld.PODStaticController.getCurrentUser()
110
-      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
109
+        loCurrentUser   = pld.PODStaticController.getCurrentUser()
110
+        loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
111 111
 
112
-      loLastModifiedNodes = loApiController.getLastModifiedNodes(10)
113
-      loWhatsHotNodes     = loApiController.getNodesByStatus('hot', 5)
114
-      loActionToDoNodes   = loApiController.getNodesByStatus('actiontodo', 5)
115
-      return dict(last_modified_nodes=loLastModifiedNodes, whats_hot_nodes=loWhatsHotNodes, action_to_do_nodes = loActionToDoNodes)
112
+        loLastModifiedNodes = loApiController.getLastModifiedNodes(10)
113
+        loWhatsHotNodes     = loApiController.getNodesByStatus('hot', 5)
114
+        loActionToDoNodes   = loApiController.getNodesByStatus('actiontodo', 5)
115
+        return dict(last_modified_nodes=loLastModifiedNodes, whats_hot_nodes=loWhatsHotNodes, action_to_do_nodes = loActionToDoNodes)
116 116
 
117 117
 
118 118
     @expose('pboard.templates.document')
119 119
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
120
-    def document(self, node=0, came_from=lurl('/')):
120
+    def document(self, node=0, came_from=lurl('/'), highlight=''):
121 121
         """show the user dashboard"""
122 122
         loCurrentUser   = pld.PODStaticController.getCurrentUser()
123 123
         loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
@@ -125,7 +125,7 @@ class RootController(BaseController):
125 125
         # loRootNodeList   = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==None).order_by(pbmd.PBNode.node_order).all()
126 126
         loRootNodeList = loApiController.buildTreeListForMenu(pbmd.PBNodeStatus.getVisibleIdsList())
127 127
         liNodeId         = int(node)
128
-        
128
+
129 129
         loCurrentNode    = None
130 130
         loNodeStatusList = None
131 131
         try:
@@ -133,12 +133,12 @@ class RootController(BaseController):
133 133
           loCurrentNode    = loApiController.getNode(liNodeId)
134 134
         except Exception as e:
135 135
           flash(_('Document not found'), 'error')
136
-        
136
+
137 137
         # FIXME - D.A - 2013-11-07 - Currently, the code build a new item if no item found for current user
138 138
         # the correct behavior should be to redirect to setup page
139 139
         if loCurrentNode is not None and "%s"%loCurrentNode.node_id!=node:
140 140
           redirect(tg.url('/document/%i'%loCurrentNode.node_id))
141
-          
141
+
142 142
         if loCurrentNode is None:
143 143
           loCurrentNode = loApiController.getNode(0) # try to get an item
144 144
           if loCurrentNode is not None:
@@ -150,5 +150,22 @@ class RootController(BaseController):
150 150
             pm.DBSession.flush()
151 151
             redirect(tg.url('/document/%i'%loCurrentNode.node_id))
152 152
 
153
-        return dict(root_node_list=loRootNodeList, current_node=loCurrentNode, node_status_list = loNodeStatusList)
153
+        return dict(
154
+            root_node_list=loRootNodeList,
155
+            current_node=loCurrentNode,
156
+            node_status_list = loNodeStatusList,
157
+            keywords = highlight
158
+        )
159
+
160
+    @expose('pboard.templates.search')
161
+    @require(predicates.in_group('user', msg=l_('Please login to access this page')))
162
+    def search(self, keywords=''):
163
+        loCurrentUser   = pld.PODStaticController.getCurrentUser()
164
+        loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
165
+
166
+        loFoundNodes = loApiController.searchNodesByText(keywords.split())
167
+
168
+        return dict(search_string=keywords, found_nodes=loFoundNodes)
169
+
170
+
154 171
 

+ 23 - 0
pboard/pboard/lib/dbapi.py View File

@@ -9,6 +9,7 @@ from sqlalchemy import Table, ForeignKey, Column
9 9
 from sqlalchemy.types import Unicode, Integer, DateTime, Text
10 10
 from sqlalchemy.orm import relation, synonym
11 11
 from sqlalchemy.orm import joinedload_all
12
+import sqlalchemy as sqla
12 13
 
13 14
 from pboard.model import DeclarativeBase, metadata, DBSession
14 15
 from pboard.model import data as pbmd
@@ -90,6 +91,28 @@ class PODUserFilteredApiController(object):
90 91
     return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).order_by(pbmd.PBNode.updated_at.desc()).limit(piMaxNodeNb).all()
91 92
 
92 93
 
94
+  def searchNodesByText(self, plKeywordList, piMaxNodeNb=100):
95
+    """
96
+    Returns a list of nodes order by type, nodes which contain at least one of the keywords
97
+    """
98
+    liOwnerIdList = self._getUserIdListForFiltering()
99
+
100
+    loKeywordFilteringClauses = []
101
+    for keyword in plKeywordList:
102
+        loKeywordFilteringClauses.append(pbmd.PBNode.data_label.ilike('%'+keyword+'%'))
103
+        loKeywordFilteringClauses.append(pbmd.PBNode.data_content.ilike('%'+keyword+'%'))
104
+
105
+    loKeywordFilteringClausesAsOr = sqla.or_(*loKeywordFilteringClauses) # Combine them with or to a BooleanClauseList
106
+
107
+    loResultsForSomeKeywords = DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren"))\
108
+        .filter(loKeywordFilteringClausesAsOr)\
109
+        .filter(pbmd.PBNode.owner_id.in_(liOwnerIdList))\
110
+        .order_by(sqla.desc(pbmd.PBNode.node_type))\
111
+        .limit(piMaxNodeNb)\
112
+        .all()
113
+
114
+    return loResultsForSomeKeywords
115
+
93 116
   def getNodesByStatus(self, psNodeStatus, piMaxNodeNb=5):
94 117
     liOwnerIdList = self._getUserIdListForFiltering()
95 118
     return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_status==psNodeStatus).order_by(pbmd.PBNode.updated_at).limit(piMaxNodeNb).all()

+ 42 - 7
pboard/pboard/model/data.py View File

@@ -6,6 +6,7 @@ import datetime as datetimeroot
6 6
 from datetime import datetime
7 7
 from hashlib import sha256
8 8
 
9
+import bs4
9 10
 from sqlalchemy import Table, ForeignKey, Column, Sequence
10 11
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
11 12
 from sqlalchemy.orm import relation, synonym, relationship
@@ -263,21 +264,44 @@ class PBNode(DeclarativeBase):
263 264
     return self.getChildrenOfType([PBNodeType.Comment], PBNode.getSortingKeyBasedOnDataDatetime, True)
264 265
 
265 266
   def getIconClass(self):
267
+    if self.node_type==PBNodeType.Data and self.getStaticChildNb()>0:
268
+      return PBNode.getIconClassForNodeType('folder')
269
+    else:
270
+      return PBNode.getIconClassForNodeType(self.node_type)
271
+
272
+  def getBreadCrumbNodes(self):
273
+    loNodes = []
274
+    if self._oParent!=None:
275
+      loNodes = self._oParent.getBreadCrumbNodes()
276
+      loNodes.append(self._oParent)
277
+    return loNodes
278
+
279
+  def getContentWithHighlightedKeywords(self, plKeywords, psPlainText):
280
+    if len(plKeywords)<=0:
281
+      return psPlainText
282
+
283
+    lsPlainText = psPlainText
284
+
285
+    for lsKeyword in plKeywords:
286
+      lsPlainText = re.sub('(?i)(%s)' % lsKeyword, '<strong>\\1</strong>', lsPlainText)
287
+
288
+    return lsPlainText
289
+
290
+
291
+  @classmethod
292
+  def getIconClassForNodeType(cls, psIconType):
266 293
     laIconClass = dict()
267 294
     laIconClass['node']   = 'fa fa-folder-open'
268 295
     laIconClass['folder'] = 'fa fa-folder-open'
269 296
     laIconClass['data']   = 'fa fa-file-text-o'
270 297
 
271
-    laIconClass['file']   = 'fa fa-file-text-o'
298
+    laIconClass['file']   = 'fa fa-paperclip'
272 299
     laIconClass['event']  = 'fa fa-calendar'
273 300
     laIconClass['contact'] = 'fa fa-user'
274 301
     laIconClass['comment'] = 'fa fa-comments-o'
302
+    return laIconClass[psIconType]
303
+
275 304
 
276
-    if self.node_type==PBNodeType.Data and self.getStaticChildNb()>0:
277
-      return laIconClass['folder']
278
-    else:
279
-      return laIconClass[self.node_type]
280
-      
281 305
   def getUserFriendlyNodeType(self):
282 306
     laNodeTypesLng = dict()
283 307
     laNodeTypesLng['node']   = 'Document' # FIXME - D.A. - 2013-11-14 - Make text translatable
@@ -304,7 +328,7 @@ class PBNode(DeclarativeBase):
304 328
   def getFormattedTime(self, poDateTime, psDateTimeFormat = '%H:%M'):
305 329
     return poDateTime.strftime(psDateTimeFormat)
306 330
 
307
-  def getStatus(self):
331
+  def getStatus(self) -> PBNodeStatusItem:
308 332
     loStatus = PBNodeStatus.getStatusItem(self.node_status)
309 333
     if loStatus.status_id!='automatic':
310 334
       return loStatus
@@ -328,6 +352,17 @@ class PBNode(DeclarativeBase):
328 352
       lsTruncatedLabel = self.data_label
329 353
     return lsTruncatedLabel
330 354
 
355
+  def getTruncatedContentAsText(self, piCharNb):
356
+    lsPlainText = ''.join(bs4.BeautifulSoup(self.data_content).findAll(text=True))
357
+    lsTruncatedContent = ''
358
+    
359
+    liMaxLength = int(piCharNb)
360
+    if len(lsPlainText)>liMaxLength:
361
+      lsTruncatedContent = lsPlainText[0:liMaxLength-1]+'…'
362
+    else:
363
+      lsTruncatedContent = lsPlainText
364
+    return lsTruncatedContent
365
+
331 366
   def getTagList(self):
332 367
     loPattern = re.compile('(^|\s|@)@(\w+)')
333 368
     loResults = re.findall(loPattern, self.data_content)

+ 12 - 3
pboard/pboard/public/css/style.css View File

@@ -122,8 +122,10 @@ body { padding-top: 60px; }
122 122
   position:fixed;
123 123
   left:0;
124 124
   top:0;
125
-  z-index:0 !important;
125
+  z-index:2001 !important;
126 126
   background-color:white;
127
+
128
+  padding: 0.5em 0.5em 0.5em 0.5em;
127 129
   
128 130
   filter: alpha(opacity=90); /* internet explorer */
129 131
   -khtml-opacity: 0.9;      /* khtml, old safari */
@@ -132,9 +134,9 @@ body { padding-top: 60px; }
132 134
 }
133 135
 
134 136
 .full-size-overlay-inner {
135
-  margin: 3.5em 0.5em 0.5em 0.5em;
137
+  margin: 0.5em 1em 0.5em 0em;
136 138
   overflow: auto;
137
-  max-height: 85%;
139
+  max-height: 90%;
138 140
 }
139 141
 
140 142
 
@@ -181,3 +183,10 @@ tr:Hover td div.pod-toolbar {
181 183
 }
182 184
 
183 185
 .navbar .nav > li > a.pod-do-not-display { display: none; }
186
+
187
+/* SEARCH RESULTS SCREEN */
188
+
189
+div.search-result-item > h5 {
190
+  margin-bottom: 0;
191
+}
192
+

+ 5 - 2
pboard/pboard/public/javascript/pod.js View File

@@ -6,6 +6,7 @@
6 6
       $('.pod-toggle-full-screen-button > i').removeClass('fa-compress')
7 7
       $('.pod-toggle-full-screen-button > i').addClass('fa-expand')
8 8
     } else {
9
+      // Toggle from normal to fullscreen
9 10
       $(outerWidgetId).addClass('full-size-overlay');
10 11
       $(innerWidgetId).addClass('full-size-overlay-inner');
11 12
       $('.pod-toggle-full-screen-button > i').removeClass('fa-expand')
@@ -94,16 +95,18 @@
94 95
     $("#current-document-content-edit-button" ).click(function() {
95 96
       $("#current-document-content" ).css("display", "none");
96 97
       $("#current-document-content-edit-form" ).css("display", "block");
98
+      $("#current-document-toobar").css("display", "none");
97 99
     });
98 100
     $("#current-document-content" ).dblclick(function() {
99 101
       $("#current-document-content" ).css("display", "none");
100 102
       $("#current-document-content-edit-form" ).css("display", "block");
101 103
     });
102
-    $("#current-document-content-edit-cancel-button" ).click(function() {
104
+    $("#current-document-content-edit-cancel-button, #current-document-content-edit-cancel-button-top" ).click(function() {
103 105
       $("#current-document-content" ).css("display", "block");
104 106
       $("#current-document-content-edit-form" ).css("display", "none");
107
+      $("#current-document-toobar").css("display", "block");
105 108
     });
106
-    $('#current-document-content-edit-save-button').on('click', function(e){
109
+    $('#current-document-content-edit-save-button, #current-document-content-edit-save-button-top').on('click', function(e){
107 110
       // We don't want this to act as a link so cancel the link action
108 111
       e.preventDefault();
109 112
       $('#current_node_textarea_wysiwyg').cleanHtml();

+ 243 - 109
pboard/pboard/templates/document.mak View File

@@ -7,15 +7,19 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
7 7
 
8 8
 <%def name="node_treeview_for_set_parent_menu(node_id, node_list, indentation=-1)">
9 9
   % if indentation==-1:
10
-    <li><a href="${tg.url('/api/set_parent_node?node_id=%i&new_parent_id=0'%(current_node.node_id))}">${_('Home')}</a>
10
+    <li>
11
+      <a href="${tg.url('/api/set_parent_node?node_id=%i&new_parent_id=0'%(current_node.node_id))}">
12
+        <i class="fa fa-file-text-o"></i> ${_('Home')}
13
+      </a>
11 14
       ${node_treeview_for_set_parent_menu(node_id, node_list, 0)}
12 15
     </li>
13 16
   % else:
14 17
     % if len(node_list)>0:
15
-      <ul>
18
+      <ul style="list-style: none;">
16 19
       % for new_parent_node in node_list:
17 20
         <li>
18
-          <a href="${tg.url('/api/set_parent_node?node_id=%i&new_parent_id=%i'%(node_id, new_parent_node.node_id))}">${new_parent_node.getTruncatedLabel(40-indentation*2)}</a>
21
+          <a href="${tg.url('/api/set_parent_node?node_id=%i&new_parent_id=%i'%(node_id, new_parent_node.node_id))}"><i class="fa fa-file-text-o"></i> ${new_parent_node.getTruncatedLabel(40-indentation*2)}
22
+          </a>
19 23
           ${node_treeview_for_set_parent_menu(node_id, new_parent_node.getStaticChildList(), indentation+1)}
20 24
         </li>
21 25
       % endfor
@@ -82,6 +86,20 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
82 86
     % endif
83 87
 </%def>
84 88
 
89
+#######
90
+##
91
+## HERE COMES THE BREADCRUMB
92
+##
93
+  <div class="row">
94
+<ul class="breadcrumb span12">
95
+  <li><span class="divider"> / Documents /</span></li>
96
+  % for breadcrumb_node in current_node.getBreadCrumbNodes():
97
+    <li><a href="${tg.url('/document/%s'%(breadcrumb_node.node_id))}">${breadcrumb_node.getTruncatedLabel(30)}</a> <span class="divider">/</span></li>
98
+  % endfor
99
+  <li class="active">${current_node.data_label}</li>
100
+</ul>
101
+  </div>
102
+
85 103
   <div class="row">
86 104
     <div id='application-left-panel' class="span3">
87 105
       <div>
@@ -89,107 +107,203 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
89 107
       </div>
90 108
     </div>
91 109
     <div id='application-main-panel' class="span9">
92
-    % if current_node.parent_id!=None and current_node.parent_id!=0:
93
-      <div class="btn-group">
94
-        <a class="btn " href="${tg.url('/document/%i'%current_node.parent_id)}" title="${_("Go to parent document")}"><i class="fa fa-hand-o-left"></i></a>
95
-      </div>
96
-    % endif
97
-      <div class="btn-group">
98
-        <button class="btn"  data-toggle="dropdown" href="#">${_("Change status")}</button>
99
-        <a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><span class="caret"></span></a>
100
-        <ul class="dropdown-menu">
101
-        % for node_status in node_status_list:
102
-          % if node_status.status_id==current_node.getStatus().status_id:
103
-          <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
104
-            <a class="${node_status.css}" href="#"  style="color: #999;">
105
-              <i class="${node_status.icon_id}"></i> ${node_status.label}
106
-            </a>
107
-          </li>
108
-          % else:
109
-          <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
110
-            <a class="${node_status.css}" href="${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, node_status.status_id))}">
111
-              <i class="${node_status.icon_id}"></i> ${node_status.label}
112
-            </a>
113
-          </li>
114
-          % endif
115
-        % endfor
116
-        </ul>
117
-      </div>
118
-      <div class="btn-group">
119
-        ${POD.EditButton('current-document-content-edit-button', True)}
120
-        <a class="btn" href="#" data-toggle="dropdown"><i class="icon-g-move"></i> ${_('Move to')} <span class="caret"></span></a>
121
-        <ul class="dropdown-menu">
122
-          ${node_treeview_for_set_parent_menu(current_node.node_id, root_node_list)}
123
-        </ul>
124
-
125
-        <a href='${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, 'deleted'))}' id='current-document-force-delete-button' class="btn" onclick="return confirm('${_('Delete current document?')}');"><i class="fa fa-trash-o"></i> ${_('Delete')}</a>
126
-        
127
-      </div>
128 110
 
129 111
       <div class="row">
130 112
         <div id='application-document-panel' class="span5">
131
-          <p>
132
-            <div id='current-document-content' class="">
133
-              <h3 id="current-document-title">#${current_node.node_id} - ${current_node.data_label}
134
-                <span class="label ${current_node.getStatus().css}" href="#">${current_node.getStatus().label}</a>
135
-              
136
-              </h3>
137
-              ${current_node.getContentWithTags()|n}
113
+          <div id='current-document-content' class="">
114
+######
115
+##
116
+## CURRENT DOCUMENT TOOLBAR - START
117
+##
118
+            <div id="current-document-toobar">
119
+              <div class="btn-group">
120
+          % if current_node.parent_id!=None and current_node.parent_id!=0:
121
+                ${POD.EditButton('current-document-content-edit-button', True)}
122
+          % endif
123
+      ##        </div>
124
+      ##        <div class="btn-group">
125
+                <button class="btn btn-small"  data-toggle="dropdown" href="#"> 
126
+                  <i class="fa  fa-signal"></i>
127
+                  ${_("Change status")}
128
+                </button>
129
+                <a class="btn btn-small dropdown-toggle" data-toggle="dropdown" href="#">
130
+                  <span class="caret"></span>
131
+                </a>
132
+                <ul class="dropdown-menu">
133
+                % for node_status in node_status_list:
134
+                  % if node_status.status_id==current_node.getStatus().status_id:
135
+                  <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
136
+                    <a class="${node_status.css}" href="#"  style="color: #999;">
137
+                      <i class="${node_status.icon_id}"></i> ${node_status.label}
138
+                    </a>
139
+                  </li>
140
+                  % else:
141
+                  <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
142
+                    <a class="${node_status.css}" href="${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, node_status.status_id))}">
143
+                      <i class="${node_status.icon_id}"></i> ${node_status.label}
144
+                    </a>
145
+                  </li>
146
+                  % endif
147
+                % endfor
148
+                </ul>
149
+              </div>
150
+              <div class="btn-group">
151
+                <button class="btn btn-small btn-success"  data-toggle="dropdown" href="#">
152
+                  <i class="fa fa-plus"></i> ${_('Add')}
153
+                </button>
154
+                <a class="btn btn-small dropdown-toggle" data-toggle="dropdown" href="#"><span class="caret"></span></a>
155
+                <ul class="dropdown-menu">
156
+                
157
+                  <li>
158
+                    <div class="btn-success strong" ><strong><i class="fa fa-magic"></i> Add New...</strong><br/></div>
159
+                    <div class="pod-grey"><i>create a totally new item...</i></div>
160
+                  </li>
161
+
162
+                  <li><a><i class="fa fa-file-text-o"></i> Document</a></li>
163
+                  <li><a><i class="fa fa-paperclip"></i> File</a></li>
164
+                  <li><a><i class="fa fa-calendar"></i> Event</a></li>
165
+                  <li><a><i class="fa fa-user"></i> Contact</a></li>
166
+                  <li><a><i class="fa fa-comments-o"></i> Comment</a></li>
167
+
168
+                  <li class="divider" role="presentation"></li>
169
+
170
+                  <li>
171
+                    <div class="btn-warning strong" ><strong><i class="fa fa-link"></i> Add Existing...</strong><br/></div>
172
+                    <div class="pod-grey"><i>link to an existing item...</i></div>
173
+                  </li>
174
+                  <li><a><i class="fa fa-file-text-o"></i> Document</a></li>
175
+                  <li><a><i class="fa fa-paperclip"></i> File</a></li>
176
+                  <li><a><i class="fa fa-calendar"></i> Event</a></li>
177
+                  <li><a><i class="fa fa-user"></i> Contact</a></li>
178
+                  <li><a><i class="fa fa-comments-o"></i> Comment</a></li>
179
+
180
+                </ul>
181
+              </div>
182
+              <div class="btn-group ">
183
+                <a
184
+                  class="btn btn-small btn-warning"
185
+                  href="#"
186
+                  data-toggle="dropdown"
187
+                  title="${_('Move to')}"
188
+                  ><i class="fa fa-arrows"></i></a>
189
+                <ul class="dropdown-menu">
190
+                  <li >
191
+                    <div class="btn-warning strong" ><strong><i class="fa fa-magic"></i> ${_("Move the document...")}</strong><br/></div>
192
+                    <div class="pod-grey"><i>move the document to...</i></div>
193
+                  </li>
194
+                  ${node_treeview_for_set_parent_menu(current_node.node_id, root_node_list)}
195
+                </ul>
196
+                <a
197
+                  class="btn btn-small btn-danger"
198
+                  href='${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(current_node.node_id, 'deleted'))}'
199
+                  id='current-document-force-delete-button' onclick="return confirm('${_('Delete current document?')}');"
200
+                  title="${_('Delete')}"
201
+                  ><i class="fa fa-trash-o"></i></a>
202
+              </div>
203
+            </div>
204
+##
205
+## CURRENT DOCUMENT TOOLBAR - END
206
+##
207
+######
208
+
209
+######
210
+##
211
+## CURRENT DOCUMENT CONTENT - START
212
+##
213
+            <h3 id="current-document-title">#${current_node.node_id} - ${current_node.data_label}
214
+              <span class="label ${current_node.getStatus().css}" href="#">${current_node.getStatus().label}</a>
215
+            </h3>
216
+            % if len(keywords)>0 and keywords!='':
217
+                ${current_node.getContentWithHighlightedKeywords(keywords.split(), current_node.getContentWithTags())|n}
218
+            % else:
219
+                ${current_node.getContentWithTags()|n}
220
+            % endif
221
+          </div>
222
+          <form style='display: none;' id="current-document-content-edit-form" method='post' action='${tg.url('/api/edit_label_and_content')}'>
223
+            <div>
224
+              ${POD.CancelButton('current-document-content-edit-cancel-button-top', True)}
225
+              ${POD.SaveButton('current-document-content-edit-save-button-top', True)}
138 226
             </div>
139
-            <form style='display: none;' id="current-document-content-edit-form" method='post' action='${tg.url('/api/edit_label_and_content')}'>
227
+            <div style="padding: 0.5em 0 0 0">
140 228
               <input type='hidden' name='node_id' value='${current_node.node_id}'/>
141 229
               <input type="hidden" name='data_content' id="current_node_textarea" />
142 230
               <input type='text' name='data_label' value='${current_node.data_label}' class="span4" placeholder="document title" />
231
+            </div>
232
+            <div>
143 233
               ${POD.RichTextEditor('current_node_textarea_wysiwyg', current_node.data_content)}
234
+            </div>
235
+            <div class="pull-right">
144 236
               ${POD.CancelButton('current-document-content-edit-cancel-button', True)}
145 237
               ${POD.SaveButton('current-document-content-edit-save-button', True)}
146
-
147
-
148
-            </form>
149
-          </p>
238
+            </div>
239
+          </form>
150 240
         </div>
151 241
         ## FIXME - D.A - 2013-11-07 - The following div should be span4 instead of span3 but some bug make it impossible
152 242
         <div id='application-metadata-panel' class="span4">
153 243
           <div class="tabbable">
154 244
             <ul class="nav nav-tabs">
155
-                ## FIXME - D.A. - 2013-11-07 - TO REMOVE OR TO REACTIVATE  <li class="active"><a href="#tags" data-toggle="tab" title="${_('Tags')}"><i class='icon-g-tags'></i></a></li>
156
-                <li class="active"><a href="#events" data-toggle="tab" title="History"><i class="pod-dark-grey fa fa-calendar"></i>${POD.ItemNb(current_node.getEvents())}</a></li>
157
-                <li><a href="#contacts" data-toggle="tab" title="Contacts"><i class="pod-dark-grey fa fa-user"></i>${POD.ItemNb(current_node.getContacts())}</a></li>
158
-                <li><a href="#comments" data-toggle="tab" title="Comments"><i class="pod-dark-grey fa fa-comments-o"></i>${POD.ItemNb(current_node.getComments())}</a></li>
159
-                <li><a href="#files" data-toggle="tab" title="Files"><i class="pod-dark-grey fa  fa-file-text-o"></i>${POD.ItemNb(current_node.getFiles())}</a></li>
245
+                <li><a href="#subdocuments" data-toggle="tab" title="${_('Subdocuments')}"><i class='pod-dark-grey fa fa-file-text-o'></i>
246
+                
247
+                ${POD.ItemNb(current_node.getChildren())}</a></li>
248
+                
249
+                <li class="active"><a href="#events" data-toggle="tab" title="${_('Calendar')}"><i class="pod-dark-grey fa fa-calendar"></i>${POD.ItemNb(current_node.getEvents())}</a></li>
250
+                <li><a href="#contacts" data-toggle="tab" title="${_('Address book')}"><i class="pod-dark-grey fa fa-user"></i>${POD.ItemNb(current_node.getContacts())}</a></li>
251
+                <li><a href="#comments" data-toggle="tab" title="${_('Comment thread')}"><i class="pod-dark-grey fa fa-comments-o"></i>${POD.ItemNb(current_node.getComments())}</a></li>
252
+                <li><a href="#files" data-toggle="tab" title="${_('Attachments')}"><i class="pod-dark-grey fa  fa-paperclip"></i>${POD.ItemNb(current_node.getFiles())}</a></li>
253
+                
254
+                <li class="pull-right"><a href="#accessmanagement" data-toggle="tab" title="${_('Access Management')}"><i class="pod-dark-grey fa fa-key"></i>${POD.ItemNb(current_node.getFiles())}</a></li>
255
+                 
160 256
             </ul>
161 257
             <div class="tab-content">
162 258
                 ################################
163 259
                 ##
164
-                ## PANEL SHOWING LIST OF TAGS
260
+                ## PANEL SHOWING LIST OF SUB DOCUMENTS
165 261
                 ##
166 262
                 ################################
167
-                <!-- DEBUG - D.A. - 2013-11-07 - Not using tags for th moment
168
-                <div class="tab-pane" id="tags">
169
-                  <div class="well">
170
-                    <p>
171
-                      <i>
172
-                        ${_('Tags are automatically extracted from document content:')}
173
-                        <ul>
174
-                          <li>${_('<code>@visible_keyword</code> is a visible keyword generating a tag.')|n}</li>
175
-                          <li>
176
-                            ${_('<code>@invisible_keyword</code> is an <u>invisible</u> keyword generating a tag.')|n}</li>
177
-                        </ul>
178
-                      </i>
179
-                    </p>
180
-                    % for tag in current_node.getTagList():
181
-                      ${POD.Badge(tag)}
263
+                <!-- DEBUG - D.A. - 2013-11-07 - Not using tags for th moment -->
264
+                <div class="tab-pane" id="subdocuments">
265
+                % if len(current_node.getChildren())<=0:
266
+                  <p class="pod-grey">
267
+                    ${_("There is currently no child documents.")}<br/>
268
+                  </p>
269
+                  <p>
270
+                    <a class="btn btn-success btn-small" href="${tg.url('/api/create_document?parent_id=%i'%current_node.node_id)}">
271
+                      <i class="fa fa-plus"></i> ${_("Add one")}
272
+                    </a>
273
+                  </p>
274
+                % else:
275
+                  <p>
276
+                    <a class="btn btn-success btn-small" href="${tg.url('/api/create_document?parent_id=%i'%current_node.node_id)}">
277
+                      <i class="fa fa-plus"></i> ${_("Add new document")}
278
+                    </a>
279
+                  </p>
280
+
281
+                  <div>
282
+                    % for subnode in current_node.getChildren():
283
+                      <p style="list-style-type:none;">
284
+                        <i class="fa-fw ${subnode.getIconClass()}"></i>
285
+                          <a href="${tg.url('/document/%i'%subnode.node_id)}">
286
+                            ${subnode.data_label}
287
+                          </a>
288
+                      </p>
182 289
                     % endfor
183 290
                   </div>
291
+                % endif
184 292
                 </div>
185
-                -->
293
+                
186 294
                 ################################
187 295
                 ##
188 296
                 ## PANEL SHOWING LIST OF EVENTS
189 297
                 ##
190 298
                 ################################
191 299
                 <div class="tab-pane active" id="events">
192
-                  ${POD.AddButton('current-document-add-event-button', True, _(' Add event'))}
300
+                % if len(current_node.getEvents())<=0:
301
+                  <p class="pod-grey">${_("The calendar is empty.")}<br/></p>
302
+                  <p>${POD.AddButton('current-document-add-event-button', True, _(' Add first event'))}</p>
303
+                % else:
304
+                  <p>${POD.AddButton('current-document-add-event-button', True, _(' Add an event'))}</p>
305
+                % endif
306
+                
193 307
                   <form style='display: none;' id='current-document-add-event-form' action='${tg.url('/api/create_event')}' method='post' class="well">
194 308
                     <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
195 309
                     <fieldset>
@@ -225,9 +339,7 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
225 339
                     </fieldset>
226 340
                   </form>
227 341
 
228
-                % if len(current_node.getEvents())<=0:
229
-                  <p><i>${_('No history for the moment.')}</i></p>
230
-                % else:
342
+                % if len(current_node.getEvents())>0:
231 343
                   <table class="table table-striped table-hover table-condensed">
232 344
                     <thead>
233 345
                       <tr>
@@ -261,9 +373,14 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
261 373
                 ##
262 374
                 ##############################
263 375
                 <div class="tab-pane" id="contacts">
264
-                
376
+                % if len(current_node.getContacts())<=0:
377
+                  <p class="pod-grey">${_("The address book is empty.")}<br/></p>
378
+                  <p>${POD.AddButton('current-document-add-contact-button', True, _(' Add first contact'), True)}</p>
379
+                % else:
380
+                  <p>${POD.AddButton('current-document-add-contact-button', True, _(' Add a contact'))}</p>
381
+                % endif
382
+
265 383
                   <!-- ADD CONTACT FORM -->
266
-                  ${POD.AddButton('current-document-add-contact-button', True, _(' Add contact'))}
267 384
                   <form style='display: none;' id='current-document-add-contact-form' action='${tg.url('/api/create_contact')}' method='post' class="well">
268 385
                     <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
269 386
                     <fieldset>
@@ -306,8 +423,14 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
306 423
                 ##
307 424
                 ################################
308 425
                 <div class="tab-pane" id="comments">
426
+                % if len(current_node.getComments())<=0:
427
+                  <p class="pod-grey">${_("The comment thread is empty.")}<br/></p>
428
+                  <p>${POD.AddButton('current-document-add-comment-button', True, _('Add first comment'), True)}</p>
429
+                % else:
430
+                  <p>${POD.AddButton('current-document-add-comment-button', True, _('Add a comment'))}</p>
431
+                % endif
432
+
309 433
                   <!-- ADD COMMENT FORM -->
310
-                  ${POD.AddButton('current-document-add-comment-button', True, _(' Add comment'))}
311 434
                   <form style='display: none;' id='current-document-add-comment-form' action='${tg.url('/api/create_comment')}' method='post' class="well">
312 435
                     <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
313 436
                     <fieldset>
@@ -327,9 +450,7 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
327 450
                   </form>
328 451
 
329 452
                   <!-- LIST OF COMMENTS -->
330
-                % if len(current_node.getComments())<=0:
331
-                  <p><i>${_('No comments.')}</i></p>
332
-                % else:
453
+                % if len(current_node.getComments())>0:
333 454
                   <table class="table table-striped table-hover table-condensed">
334 455
                     % for comment in current_node.getComments():
335 456
                       <tr title="Last updated: ${comment.updated_at}">
@@ -354,8 +475,14 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
354 475
                 ##
355 476
                 ################################
356 477
                 <div class="tab-pane" id="files">
478
+                % if len(current_node.getFiles())<=0:
479
+                  <p class="pod-grey">${_("There is currently no attachment.")}<br/></p>
480
+                  <p>${POD.AddButton('current-document-add-file-button', True, _(' Attach first file'))}</p>
481
+                % else:
482
+                  <p>${POD.AddButton('current-document-add-file-button', True, _(' Attach a file'))}</p>
483
+                % endif
484
+
357 485
                   <!-- ADD FILE FORM -->
358
-                  ${POD.AddButton('current-document-add-file-button', True, _(' Add file'))}
359 486
                   <form style='display: none;' id='current-document-add-file-form' enctype="multipart/form-data" action='${tg.url('/api/create_file')}' method='post' class="well">
360 487
                     <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
361 488
                     <fieldset>
@@ -378,35 +505,42 @@ pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id}
378 505
                   </form>
379 506
 
380 507
                   <!-- LIST OF FILES -->
381
-                % if len(current_node.getFiles())<=0:
382
-                  <p><i>${_('No files.')}</i></p>
383
-                % else:
384
-                  <table class="table table-striped table-hover table-condensed">
508
+                  <div>
509
+                % if len(current_node.getFiles())>0:
385 510
                     % for current_file in current_node.getFiles():
386
-                      <tr title="Last updated: ${current_file.updated_at}">
387
-                        <td>
388
-                          <a href="${tg.url('/api/get_file_content/%s'%(current_file.node_id))}" title="${_("Download file")}">
389
-                            <i class="fa fa-2x fa-file-text-o"></i>
390
-                          </a>
391
-                          ## FIXME - SHOW THUMBNAIL WHEN IT WILL BE OK<img src="${tg.url('/api/get_file_content_thumbnail/%s'%(current_file.node_id))}" class="img-polaroid">
392
-                        </td>
393
-                        <td>
394
-                          <b>${current_file.data_label}</b>
395
-                          <a class="pull-right" href="${tg.url('/api/get_file_content/%s'%(current_file.node_id))}" title="${_("Download file")}">
396
-                            <i class="fa fa-download"></i>
397
-                          </a>
398
-                          <a class="pull-right" href="${tg.url('/document/%i'%current_file.node_id)}" title="${_("Edit title or comment")}"><i class="fa fa-edit"></i></a>
399
-
400
-                          <br/>
401
-                          <p>
402
-                            ${current_file.data_content|n}
403
-                          </p>
404
-                        </td>
405
-                      </tr>
511
+                      <p style="list-style-type:none; margin-bottom: 0.5em;">
512
+                        <i class="fa fa-paperclip"></i>
513
+                        <a
514
+                          href="${tg.url('/document/%i'%current_file.node_id)}"
515
+                          title="${_('View the attachment')}: ${current_file.data_label}"
516
+                        >
517
+                          ${current_file.getTruncatedLabel(50)}
518
+                        </a>
519
+                        <a
520
+                          class="pull-right"
521
+                          href="${tg.url('/api/get_file_content/%s'%(current_file.node_id))}"
522
+                          title="${_('View the attachment')}"
523
+                        >
524
+                          <i class="fa fa-download"></i>
525
+                        </a>
526
+                      </p>
406 527
                     % endfor
407
-                  </table>
408 528
                 % endif
529
+                  </div>
409 530
                 </div>
531
+                
532
+                
533
+                ################################
534
+                ##
535
+                ## PANEL SHOWING ACCESS MANAGEMENT
536
+                ##
537
+                ################################
538
+                <div class="tab-pane" id="accessmanagement">
539
+                  blabla
540
+                </div>
541
+                
542
+                
543
+                
410 544
               </div>
411 545
             </div>
412 546
           </div>

+ 6 - 6
pboard/pboard/templates/master.mak View File

@@ -212,13 +212,13 @@
212 212
               
213 213
             % endif
214 214
           </ul>
215
+          <form class="navbar-search pull-right form-search" action="${tg.url('/search')}">
216
+            <div class="input-append">
217
+              <input name="keywords" type="text" class="span2 search-query" placeholder="Search" value="${context.get('search_string', '')}">
218
+              <button title="${_('Search')}" class="btn" type="submit"><i class="fa fa-search"></i></button>
219
+            </div>
220
+          </form>
215 221
 
216
-          #####################
217
-          ## FIXME - D.A. - 2013-11-07 - Make search available
218
-          ## 
219
-          ## <form class="navbar-search pull-right" action="">
220
-          ##   <input type="text" class="search-query span2" placeholder="Search">
221
-          ## </form>
222 222
         </div><!-- /.nav-collapse -->
223 223
       </div><!-- /.container -->
224 224
     </div><!-- /.navbar-inner -->

+ 83 - 30
pboard/pboard/templates/pod.mak View File

@@ -1,18 +1,55 @@
1
+<%def name="IconCssClass(psNodeType)" >
2
+  % if psNodeType=='data':
3
+    fa fa-file-text-o
4
+  % elif  psNodeType=='folder':
5
+    fa fa-folder-open
6
+  % elif  psNodeType=='node':
7
+    fa fa-file-text-o
8
+  % elif  psNodeType=='file':
9
+    fa fa-paperclip
10
+  % elif  psNodeType=='event':
11
+    fa fa-calendar
12
+  % elif  psNodeType=='contact':
13
+    fa fa-user
14
+  % elif  psNodeType=='comment':
15
+    fa fa-comments-o
16
+  % endif
17
+</%def>
18
+
19
+<%def name="DocumentTypeLabel(psNodeType)" ><%
20
+  labels = dict()
21
+  labels['data'] = 'document'
22
+  labels['folder'] = 'folder'
23
+  labels['node'] = 'node'
24
+  labels['file'] = 'file'
25
+  labels['event'] = 'event'
26
+  labels['contact'] = 'contact'
27
+  labels['comment'] = 'comment'
28
+  return labels[psNodeType]
29
+%></%def>
30
+
31
+<%def name="DocumentUrl(piNodeId, psHighlight)" >${tg.url('/document/%i?highlight=%s'%(piNodeId, psHighlight))}</%def>
32
+<%def name="DocumentUrlWithAnchor(piNodeId, psHighlight, psAnchor)" >${tg.url('/document/%i?highlight=%s#%s'%(piNodeId, psHighlight, psAnchor))}</%def>
33
+
1 34
 <%def name="Button(piId, pbWithLabel, psButtonCssClass, psButtonTitle, psButtonIcon, psButtonLabel)" >
2 35
   <button id='${piId}' type="button" class="${psButtonCssClass}" title="${psButtonTitle}"><i class="${psButtonIcon}"></i>${'' if (pbWithLabel==False) else ' %s'%(psButtonLabel)}</button>
3 36
 </%def>
4 37
 
5 38
 <%def name="SaveButton(piId, pbWithLabel=False)" >
6
-  ${Button(piId, pbWithLabel, 'btn btn-success', _('Save'), ' icon-g-ok-2 icon-g-white', _('Save'))}
39
+  ${Button(piId, pbWithLabel, 'btn btn-small btn-success', _('Save'), ' icon-g-ok-2 icon-g-white', _('Save'))}
7 40
 </%def>
8 41
 <%def name="EditButton(piId, pbWithLabel=False)" >
9
-  ${Button(piId, pbWithLabel, 'btn', _('Edit'), 'icon-g-edit', _('Edit'))}
42
+  ${Button(piId, pbWithLabel, 'btn btn-small', _('Edit'), 'fa fa-edit', _('Edit'))}
10 43
 </%def>
11 44
 <%def name='CancelButton(piId, pbWithLabel=False)'>
12
-  ${Button(piId, pbWithLabel, 'btn ', _('Cancel'), 'icon-g-ban', _('Cancel'))}
45
+  ${Button(piId, pbWithLabel, 'btn btn-small', _('Cancel'), 'icon-g-ban', _('Cancel'))}
13 46
 </%def>
14
-<%def name='AddButton(piId, pbWithLabel=False, psLabel=None)'>
15
-  ${Button(piId, pbWithLabel, 'btn', psLabel or _('New'), 'icon-g-circle-plus', psLabel or _('New'))}
47
+<%def name='AddButton(piId, pbWithLabel=False, psLabel=None, pbIsCallToAction=True)'>
48
+% if pbIsCallToAction:
49
+  ${Button(piId, pbWithLabel, 'btn btn-small btn-success', psLabel or _('New'), 'fa fa-plus', psLabel or _('New'))}
50
+% else:
51
+  ${Button(piId, pbWithLabel, 'btn btn-small', psLabel or _('New'), 'fa fa-plus', psLabel or _('New'))}
52
+% endif
16 53
 </%def>
17 54
 <%def name='Badge(psLabel, psCssClass="")'>
18 55
   <span class='badge ${psCssClass}'>${psLabel}</span>
@@ -41,15 +78,23 @@
41 78
 <%def name='RichTextEditorToolbar(psRichTextEditorNodeId, psMenuOptions="styles|boldanditalic|lists|justifiers|links|images|undoredo|fullscreen")'>
42 79
       <div class="btn-toolbar" data-role="${psRichTextEditorNodeId}-toolbar" data-target="${psRichTextEditorNodeId}">
43 80
       % if psMenuOptions.find('styles')>=0:
81
+      
44 82
         <div class="btn-group">
45
-          <a class="btn" data-edit="formatBlock p"   title="Normal paragraph">§</h1></a></li>
46
-          <a class="btn" data-edit="formatBlock pre" title="Fixed width (code)">C</h1></a>
47
-          <a class="btn" data-edit="formatBlock h1"  title="Title - level 1">h1</a>
48
-          <a class="btn" data-edit="formatBlock h2"  title="Title - level 2">h2</a>
49
-          <a class="btn" data-edit="formatBlock h3"  title="Title - level 3">h3</a>
50
-          <a class="btn" data-edit="formatBlock h4"  title="Title - level 4">h4</a>
51
-          <a class="btn" data-edit="formatBlock h5"  title="Title - level 5">h5</a>
52
-          <a class="btn" data-edit="formatBlock h6"  title="Title - level 6">h6</a>
83
+          <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
84
+            <i class="fa fa-font"></i>
85
+            <span class="caret"></span>
86
+          </a>
87
+          <ul class="dropdown-menu">
88
+          <!-- dropdown menu links -->
89
+            <li><a data-edit="formatBlock p"   title="Normal paragraph"><p style="margin: 0">text body</p></a></li>
90
+            <li><a data-edit="formatBlock pre" title="Fixed width (code)"><pre style="margin: 0">quote</pre></a></li>
91
+            <li><a data-edit="formatBlock h1"  title="Title - level 1"><h1 style="margin: 0">heading 1</h1></a></li>
92
+            <li><a data-edit="formatBlock h2"  title="Title - level 2"><h2 style="margin: 0">heading 2</h2></a></li>
93
+            <li><a data-edit="formatBlock h3"  title="Title - level 3"><h3 style="margin: 0">heading 3</h3></a></li>
94
+            <li><a data-edit="formatBlock h4"  title="Title - level 4"><h4 style="margin: 0">heading 4</h4></a></li>
95
+            <li><a data-edit="formatBlock h5"  title="Title - level 5"><h5 style="margin: 0">heading 5</h5></a></li>
96
+            <li><a data-edit="formatBlock h6"  title="Title - level 6"><h6 style="margin: 0">heading 6</h6></a></li>
97
+          </ul>
53 98
         </div>
54 99
       % endif
55 100
       % if psMenuOptions.find('boldanditalic')>=0:
@@ -76,22 +121,30 @@
76 121
           <a class="btn" data-edit="justifyfull" title="Justify (Ctrl/Cmd+J)"><i class="fa fa-align-justify"></i></a>
77 122
         </div>
78 123
       % endif
79
-      % if psMenuOptions.find('links')>=0:
80
-        <div class="btn-group">
81
-          <a class="btn dropdown-toggle" data-toggle="dropdown" title="Hyperlink"><i class="fa fa-link"></i></a>
82
-          <div class="dropdown-menu input-append">
83
-            <input class="span2" placeholder="URL" type="text" data-edit="createLink"/>
84
-            <button class="btn" type="button">Add</button>
85
-          </div>
86
-          <a class="btn" data-edit="unlink" title="Remove Hyperlink"><i class="fa fa-cut"></i></a>
87
-        </div>
88
-      % endif
89
-      % if psMenuOptions.find('images')>=0:
90
-        <div class="btn-group">
91
-          <a class="btn" title="Insert picture (or just drag & drop)" id="pictureBtn"><i class="fa fa-picture-o"></i></a>
92
-          <input type="file" data-role="magic-overlay" data-target="#pictureBtn" data-edit="insertImage" />
93
-        </div>
94
-      % endif
124
+#######
125
+##
126
+## LINK MENU ; NOT WORKING FOR NOW (links are auto-generated at render time)
127
+##
128
+##      % if psMenuOptions.find('links')>=0:
129
+##        <div class="btn-group">
130
+##          <a class="btn dropdown-toggle" data-toggle="dropdown" title="Hyperlink"><i class="fa fa-link"></i></a>
131
+##          <div class="dropdown-menu input-append">
132
+##            <input class="span2" placeholder="URL" type="text" data-edit="createLink"/>
133
+##            <button class="btn" type="button">Add</button>
134
+##          </div>
135
+##          <a class="btn" data-edit="unlink" title="Remove Hyperlink"><i class="fa fa-cut"></i></a>
136
+##        </div>
137
+##      % endif
138
+#######
139
+##
140
+## IMAGES MENU ; NOT WORKING FOR NOW
141
+##
142
+##      % if psMenuOptions.find('images')>=0:
143
+##        <div class="btn-group">
144
+##          <a class="btn" title="Insert picture (or just drag & drop)" id="pictureBtn"><i class="fa fa-picture-o"></i></a>
145
+##          <input type="file" data-role="magic-overlay" data-target="#pictureBtn" data-edit="insertImage" />
146
+##        </div>
147
+##      % endif
95 148
       % if psMenuOptions.find('undoredo')>=0:
96 149
         <div class="btn-group">
97 150
           <a class="btn" data-edit="undo" title="Undo (Ctrl/Cmd+Z)"><i class="fa fa-undo"></i></a>
@@ -122,9 +175,9 @@
122 175
 
123 176
 <%def name='RichTextEditor(psRichTextEditorNodeId, psRichTextEditorContent="", psMenuOptions="styles|boldanditalic|lists|justifiers|links|images|undoredo|fullscreen")'>
124 177
   <div id="${psRichTextEditorNodeId}-widget" class="rich-text-editor-widget">
178
+    ${RichTextEditorToolbar(psRichTextEditorNodeId, psMenuOptions)}
125 179
     <div id="${psRichTextEditorNodeId}-widget-inner" class="rich-text-editor-widget-inner">
126 180
       <div id="${psRichTextEditorNodeId}-alert-container"></div>
127
-      ${RichTextEditorToolbar(psRichTextEditorNodeId, psMenuOptions)}
128 181
       <div id="${psRichTextEditorNodeId}" class="pod-rich-text-zone pod-input-like-shadow">
129 182
         ${psRichTextEditorContent|n}
130 183
       </div>

+ 134 - 0
pboard/pboard/templates/search.mak View File

@@ -0,0 +1,134 @@
1
+<%inherit file="local:templates.master"/>
2
+<%namespace name="POD" file="pboard.templates.pod"/>
3
+
4
+<%def name="title()">
5
+pod :: your dashboard
6
+</%def>
7
+
8
+<div class="row">
9
+  <div class="span6">
10
+    <form class="form-search" action="${tg.url('/search')}">
11
+      <div class="input-append">
12
+        <input name="keywords" type="text" class="span2 search-query" placeholder="Search" value="${context.get('search_string', '')}">
13
+        <button title="${_('Search again')}" class="btn" type="submit"><i class="fa fa-search"></i></button>
14
+      </div>
15
+    </form>
16
+  </div>
17
+</div>
18
+<div class="row">
19
+  <div class="span6">
20
+    <p>
21
+      <i class="pod-blue fa fa-search"></i>
22
+      ${_("Results")}
23
+      <span class="badge badge-info">${len(found_nodes)}  result(s)</span> 
24
+      % for keyword in search_string.split():
25
+        <span class="label">${keyword}</span>
26
+      % endfor
27
+    </p>
28
+  </div>
29
+</div>
30
+<div class="row">
31
+  <div class="span6">
32
+#######
33
+## SHOW RESULT FILTERING TOOLBAR
34
+    <span>Filter results:</span>
35
+    <div class="btn-group search-result-toogle-buttons" data-toggle="buttons-checkbox">
36
+      % for data_type in ('file', 'event', 'data', 'contact', 'comment'):
37
+        <button
38
+            id="search-result-toogle-button-${data_type}"
39
+            class="btn search-result-dynamic-toogle-button"
40
+            title="Show/hide ${POD.DocumentTypeLabel(data_type)} results"
41
+        >
42
+          <i class=" ${POD.IconCssClass(data_type)}"></i>
43
+          <sup style="color: #5BB75B;"><i class="fa fa-check"></i></sup>
44
+        </button>
45
+      % endfor
46
+    </div>
47
+  </div>
48
+</div>
49
+<script>
50
+
51
+$('.search-result-toogle-buttons > button.search-result-dynamic-toogle-button').addClass('active');
52
+
53
+
54
+
55
+$('#search-result-toogle-button-file').click(function () {
56
+  $('.search-result-file').toggle();
57
+  $('#search-result-toogle-button-file > sup').toggle();
58
+});
59
+$('#search-result-toogle-button-event').click(function () {
60
+  $('.search-result-event').toggle();
61
+  $('#search-result-toogle-button-event > sup').toggle();
62
+});
63
+$('#search-result-toogle-button-data').click(function () {
64
+  $('.search-result-data').toggle();
65
+  $('#search-result-toogle-button-data > sup').toggle();
66
+});
67
+$('#search-result-toogle-button-contact').click(function () {
68
+  $('.search-result-contact').toggle();
69
+  $('#search-result-toogle-button-contact > sup').toggle();
70
+});
71
+$('#search-result-toogle-button-comment').click(function () {
72
+  $('.search-result-comment').toggle();
73
+  $('#search-result-toogle-button-comment > sup').toggle();
74
+});
75
+</script>
76
+
77
+% if found_nodes==None or len(found_nodes)<=0:
78
+#######
79
+## No result view
80
+<div class="row">
81
+  <p class="alert">
82
+    <i class="fa  fa-exclamation-triangle"></i>
83
+    ${_("No data found for keywords:")} <i>${search_string}</i>
84
+  </p>
85
+</div>
86
+% else:
87
+#######
88
+## Standard result view
89
+##  <hr/>
90
+
91
+<div class="row">
92
+  <div class="span12">
93
+  % for result_id, node in enumerate(found_nodes):
94
+  
95
+    <div class="row">
96
+      <div class="span5 search-result-item search-result-${node.node_type}">
97
+
98
+        <h5 title="${node.data_label}">
99
+    % if node.node_type=='data' or node.parent_id==None:
100
+          <a href="${POD.DocumentUrl(node.node_id, search_string)}">
101
+    % else:
102
+          <a href="${POD.DocumentUrlWithAnchor(node.parent_id, search_string, 'tab-%ss'%node.node_type)}">
103
+    % endif
104
+            <i class="${node.getIconClass()}"></i>
105
+            ${node.data_label}
106
+            <span class="label ${node.getStatus().css} pull-right" title="${node.getStatus().label}">
107
+              <i class="${node.getStatus().icon}"></i>
108
+              
109
+              
110
+            <span>
111
+          </a>
112
+        </h5>
113
+## Now show the breakcrumb in a google-like manner
114
+        <div>
115
+    % for parent_node in node.getBreadCrumbNodes():
116
+          <a style="color: #468847;" href="${POD.DocumentUrl(parent_node.node_id, search_string)}" title="${parent_node.data_label}">${parent_node.getTruncatedLabel(30)}</a>
117
+          <span style="color: #468847;" >/</span>
118
+    % endfor
119
+
120
+            <a style="color: #468847;" href="${POD.DocumentUrl(node.node_id, search_string)}" title="${node.data_label}">
121
+              ${node.getTruncatedLabel(30)}
122
+            </a>
123
+        </div>
124
+        <div class="row">
125
+          <p class="span5">${node.getContentWithHighlightedKeywords(search_string.split(), node.getTruncatedContentAsText(200))|n}</p>
126
+        </div>
127
+##      <hr/>
128
+      </div>
129
+    </div>
130
+  % endfor
131
+  </div>
132
+% endif
133
+</div>
134
+