Pārlūkot izejas kodu

Merge branch 'master' of bitbucket.org:lebouquetin/protov1

sferot 11 gadus atpakaļ
vecāks
revīzija
f9d574446c

+ 21 - 0
doc/database/pod-upgrade-0.3.0_to_0.4.0.sql Parādīt failu

11
     FROM pod_nodes_history
11
     FROM pod_nodes_history
12
     ORDER BY node_id, updated_at DESC;
12
     ORDER BY node_id, updated_at DESC;
13
 
13
 
14
+CREATE OR REPLACE RULE pod_insert_new_node AS ON INSERT
15
+TO pod_nodes
16
+DO INSTEAD INSERT INTO pod_nodes_history (node_id, parent_id, node_order, node_type, created_at, updated_at, 
17
+       data_label, data_content, data_datetime, node_status, data_reminder_datetime, 
18
+       data_file_name, data_file_content, data_file_mime_type, parent_tree_path, 
19
+       node_depth, owner_id, version_id, is_shared, is_public, public_url_key) VALUES (nextval('pod_nodes__node_id__sequence'), NEW.parent_id, NEW.node_order, NEW.node_type, NEW.created_at, NEW.updated_at, NEW.data_label, NEW.data_content, NEW.data_datetime, NEW.node_status, NEW.data_reminder_datetime, NEW.data_file_name, NEW.data_file_content, NEW.data_file_mime_type, NEW.parent_tree_path, NEW.node_depth, NEW.owner_id, nextval('pod_nodes_version_id_sequence'), NEW.is_shared, NEW.is_public, NEW.public_url_key)
20
+RETURNING node_id, parent_id, node_order, node_type, created_at, updated_at, 
21
+       data_label, data_content, data_datetime, node_status, data_reminder_datetime, 
22
+       data_file_name, data_file_content, data_file_mime_type, parent_tree_path, 
23
+       node_depth, owner_id, is_shared, is_public, public_url_key;
14
 
24
 
25
+CREATE OR REPLACE FUNCTION pod_update_node() RETURNS trigger AS $$
26
+BEGIN
27
+INSERT INTO pod_nodes_history (node_id, parent_id, node_order, node_type, created_at, updated_at, 
28
+       data_label, data_content, data_datetime, node_status, data_reminder_datetime, 
29
+       data_file_name, data_file_content, data_file_mime_type, parent_tree_path, 
30
+       node_depth, owner_id, version_id, is_shared, is_public, public_url_key) VALUES (NEW.node_id, NEW.parent_id, NEW.node_order, NEW.node_type, NEW.created_at, NEW.updated_at, NEW.data_label, NEW.data_content, NEW.data_datetime, NEW.node_status, NEW.data_reminder_datetime, NEW.data_file_name, NEW.data_file_content, NEW.data_file_mime_type, NEW.parent_tree_path, NEW.node_depth, NEW.owner_id, nextval('pod_nodes_version_id_sequence'), NEW.is_shared, NEW.is_public, NEW.public_url_key);
31
+return new;
32
+END;
33
+$$ LANGUAGE plpgsql;
34
+
35
+CREATE TRIGGER pod_update_node_tg INSTEAD OF UPDATE ON pod_nodes FOR EACH ROW EXECUTE PROCEDURE pod_update_node();

+ 1 - 1
pboard/pboard/config/app_cfg.py Parādīt failu

22
 base_config = AppConfig()
22
 base_config = AppConfig()
23
 base_config.renderers = []
23
 base_config.renderers = []
24
 base_config.use_toscawidgets = False
24
 base_config.use_toscawidgets = False
25
-base_config.use_toscawidgets2 = False
25
+base_config.use_toscawidgets2 = True
26
 
26
 
27
 base_config.package = pboard
27
 base_config.package = pboard
28
 
28
 

+ 30 - 8
pboard/pboard/controllers/api.py Parādīt failu

93
       redirect(lurl('/document/%i'%(loNewNode.parent_id)))
93
       redirect(lurl('/document/%i'%(loNewNode.parent_id)))
94
 
94
 
95
     @expose()
95
     @expose()
96
-    def create_comment(self, parent_id=None, data_label='', data_content='', **kw):
96
+    def create_comment(self, parent_id=None, data_label='', data_content='', is_shared='', **kw):
97
       loCurrentUser   = pld.PODStaticController.getCurrentUser()
97
       loCurrentUser   = pld.PODStaticController.getCurrentUser()
98
       loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
98
       loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
99
-      
99
+
100
       loNewNode = loApiController.createNode()
100
       loNewNode = loApiController.createNode()
101
       loNewNode.parent_id     = int(parent_id)
101
       loNewNode.parent_id     = int(parent_id)
102
       loNewNode.node_type     = pmd.PBNodeType.Comment
102
       loNewNode.node_type     = pmd.PBNodeType.Comment
103
       loNewNode.data_label    = data_label
103
       loNewNode.data_label    = data_label
104
       loNewNode.data_content  = data_content
104
       loNewNode.data_content  = data_content
105
+      if is_shared=='on':
106
+        loNewNode.is_shared = True
105
 
107
 
106
       pm.DBSession.flush()
108
       pm.DBSession.flush()
107
       redirect(lurl('/document/%i'%(loNewNode.parent_id)))
109
       redirect(lurl('/document/%i'%(loNewNode.parent_id)))
193
       redirect(lurl('/document/%s'%(node_id)))
195
       redirect(lurl('/document/%s'%(node_id)))
194
 
196
 
195
     @expose()
197
     @expose()
196
-    def create_document(self, parent_id=None):
198
+    def create_document(self, parent_id=None, data_label='', data_content=''):
197
       loCurrentUser   = pld.PODStaticController.getCurrentUser()
199
       loCurrentUser   = pld.PODStaticController.getCurrentUser()
198
       loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
200
       loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
199
-      
201
+
202
+      lsNodeName = 'Document with no name...'
203
+      if int(parent_id)!=0:
204
+        loParent = loApiController.getNode(parent_id)
205
+        lsNodeName = 'Subdocument of (%s)' % loParent.data_label
206
+
200
       loNewNode = loApiController.createDummyNode()
207
       loNewNode = loApiController.createDummyNode()
201
-      loNewNode.data_label   = 'New document'
208
+      loNewNode.data_label   = lsNodeName
202
       loNewNode.data_content = 'insert content...'
209
       loNewNode.data_content = 'insert content...'
203
-      if int(parent_id)==0:
204
-        loNewNode.parent_id = None
205
-      else:
210
+
211
+      if data_label!='':
212
+        loNewNode.data_label = data_label
213
+      if data_content!='':
214
+        loNewNode.data_content = data_content
215
+
216
+      if int(parent_id)!=0:
206
         loNewNode.parent_id = parent_id
217
         loNewNode.parent_id = parent_id
207
 
218
 
208
       pm.DBSession.flush()
219
       pm.DBSession.flush()
258
       flash(_('Documents re-indexed'), 'info')
269
       flash(_('Documents re-indexed'), 'info')
259
       redirect(lurl('/document/%s'%(back_to_node_id)))
270
       redirect(lurl('/document/%s'%(back_to_node_id)))
260
 
271
 
272
+    @expose()
273
+    def toggle_share_status(self, node_id):
274
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
275
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
276
+      loNode = loApiController.getNode(node_id)
277
+      if loNode.owner_id==loCurrentUser.user_id:
278
+        loNode.is_shared = not loNode.is_shared
279
+      # FIXME - DA. - 2014-05-06
280
+      # - if root node, then exception
281
+      # - this redirect is done in order to be adapted to comment share status toggle
282
+      redirect(lurl('/document/%s#tab-comments'%(loNode._oParent.node_id)))

+ 1 - 14
pboard/pboard/controllers/root.py Parādīt failu

119
         loCurrentUser   = pld.PODStaticController.getCurrentUser()
119
         loCurrentUser   = pld.PODStaticController.getCurrentUser()
120
         loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
120
         loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
121
 
121
 
122
-        # loRootNodeList   = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==None).order_by(pbmd.PBNode.node_order).all()
123
         loRootNodeList = loApiController.buildTreeListForMenu(pbmd.PBNodeStatus.getVisibleIdsList())
122
         loRootNodeList = loApiController.buildTreeListForMenu(pbmd.PBNodeStatus.getVisibleIdsList())
124
         liNodeId         = int(node)
123
         liNodeId         = int(node)
125
 
124
 
126
         loCurrentNode    = None
125
         loCurrentNode    = None
127
         loNodeStatusList = None
126
         loNodeStatusList = None
127
+
128
         try:
128
         try:
129
           loNodeStatusList = pbmd.PBNodeStatus.getChoosableList()
129
           loNodeStatusList = pbmd.PBNodeStatus.getChoosableList()
130
           loCurrentNode    = loApiController.getNode(liNodeId)
130
           loCurrentNode    = loApiController.getNode(liNodeId)
133
 
133
 
134
         # FIXME - D.A - 2013-11-07 - Currently, the code build a new item if no item found for current user
134
         # FIXME - D.A - 2013-11-07 - Currently, the code build a new item if no item found for current user
135
         # the correct behavior should be to redirect to setup page
135
         # the correct behavior should be to redirect to setup page
136
-        if loCurrentNode is not None and "%s"%loCurrentNode.node_id!=node:
137
-          redirect(tg.url('/document/%i'%loCurrentNode.node_id))
138
-
139
-        if loCurrentNode is None:
140
-          loCurrentNode = loApiController.getNode(0) # try to get an item
141
-          if loCurrentNode is not None:
142
-            flash(_('Document not found. Randomly showing item #%i')%(loCurrentNode.node_id), 'warning')
143
-            redirect(tg.url('/document/%i'%loCurrentNode.node_id))
144
-          else:
145
-            flash(_('Your first document has been automatically created'), 'info')
146
-            loCurrentNode = loApiController.createDummyNode()
147
-            pm.DBSession.flush()
148
-            redirect(tg.url('/document/%i'%loCurrentNode.node_id))
149
 
136
 
150
         return dict(
137
         return dict(
151
             root_node_list=loRootNodeList,
138
             root_node_list=loRootNodeList,

+ 4 - 5
pboard/pboard/lib/dbapi.py Parādīt failu

70
 
70
 
71
   def createDummyNode(self):
71
   def createDummyNode(self):
72
     loNewNode = self.createNode()
72
     loNewNode = self.createNode()
73
-    loNewNode.data_label   = 'New document'
74
-    loNewNode.data_content = 'insert content...'
73
+    loNewNode.data_label   = ''
74
+    loNewNode.data_content = ''
75
     return loNewNode
75
     return loNewNode
76
 
76
 
77
 
77
 
78
   def getNode(self, liNodeId):
78
   def getNode(self, liNodeId):
79
     liOwnerIdList = self._getUserIdListForFiltering()
79
     liOwnerIdList = self._getUserIdListForFiltering()
80
-    if liNodeId==0:
81
-      return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).first()
82
-    else:
80
+    if liNodeId!=0:
83
       return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.node_id==liNodeId).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).one()
81
       return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.node_id==liNodeId).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).one()
82
+    return None
84
 
83
 
85
 
84
 
86
   def getLastModifiedNodes(self, piMaxNodeNb):
85
   def getLastModifiedNodes(self, piMaxNodeNb):

+ 41 - 0
pboard/pboard/lib/helpers.py Parādīt failu

39
       return _("Close the item if you want not to see it anymore. The data won't be deleted")
39
       return _("Close the item if you want not to see it anymore. The data won't be deleted")
40
     if psStatusId=='deleted':
40
     if psStatusId=='deleted':
41
       return _("This status tells that the item has been deleted.")
41
       return _("This status tells that the item has been deleted.")
42
+
43
+class ID(object):
44
+  """ Helper class that will manage html items ids that need to be shared"""
45
+
46
+  @classmethod
47
+  def AddDocumentModalForm(cls, poNode=None):
48
+    if poNode:
49
+      return 'add-document-modal-form-%d'%poNode.node_id
50
+    else:
51
+      return 'add-document-modal-form'
52
+
53
+  @classmethod
54
+  def AddContactModalForm(cls, poNode=None):
55
+    if poNode:
56
+      return 'add-contact-modal-form-%d'%poNode.node_id
57
+    else:
58
+      return 'add-contact-modal-form'
59
+
60
+  @classmethod
61
+  def AddFileModalForm(cls, poNode=None):
62
+    if poNode:
63
+      return 'add-file-modal-form-%d'%poNode.node_id
64
+    else:
65
+      return 'add-file-modal-form'
66
+
67
+  @classmethod
68
+  def AddEventModalForm(cls, poNode=None):
69
+    if poNode:
70
+      return 'add-event-modal-form-%d'%poNode.node_id
71
+    else:
72
+      return 'add-event-modal-form'
73
+    ## Original id is 'current-document-add-event-form'
74
+
75
+  @classmethod
76
+  def AddCommentInlineForm(cls):
77
+    return 'current-document-add-comment-form'
78
+
79
+class ICON(object):
80
+  Shared = '<i class="fa fa-group"></i>'
81
+  Private = '<i class="fa fa-key"></i>'
82
+

+ 45 - 36
pboard/pboard/model/data.py Parādīt failu

9
 import bs4
9
 import bs4
10
 from sqlalchemy import Table, ForeignKey, Column, Sequence
10
 from sqlalchemy import Table, ForeignKey, Column, Sequence
11
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
11
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
12
+import sqlalchemy.types as sqlat
12
 from sqlalchemy.orm import relation, synonym, relationship
13
 from sqlalchemy.orm import relation, synonym, relationship
13
 from sqlalchemy.orm import backref
14
 from sqlalchemy.orm import backref
14
 import sqlalchemy.orm as sqlao
15
 import sqlalchemy.orm as sqlao
18
 
19
 
19
 import tg
20
 import tg
20
 from pboard.model import DeclarativeBase, metadata, DBSession
21
 from pboard.model import DeclarativeBase, metadata, DBSession
22
+from pboard.model import auth as pma
21
 
23
 
22
 # This is the association table for the many-to-many relationship between
24
 # This is the association table for the many-to-many relationship between
23
 # groups and permissions.
25
 # groups and permissions.
143
       PBNodeStatus.StatusList['closed'],
145
       PBNodeStatus.StatusList['closed'],
144
       PBNodeStatus.StatusList['deleted']
146
       PBNodeStatus.StatusList['deleted']
145
     ]
147
     ]
146
-    
147
-    PBNodeStatus.StatusList.values()
148
+
148
     
149
     
149
   @classmethod
150
   @classmethod
150
   def getStatusItem(cls, psStatusId):
151
   def getStatusItem(cls, psStatusId):
182
     return len(self._lStaticChildList)
183
     return len(self._lStaticChildList)
183
 
184
 
184
   __tablename__ = 'pod_nodes'
185
   __tablename__ = 'pod_nodes'
186
+
185
   node_id          = Column(Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True)
187
   node_id          = Column(Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True)
186
   parent_id        = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None)
188
   parent_id        = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None)
187
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
189
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
195
   created_at = Column(DateTime, unique=False, nullable=False)
197
   created_at = Column(DateTime, unique=False, nullable=False)
196
   updated_at = Column(DateTime, unique=False, nullable=False)
198
   updated_at = Column(DateTime, unique=False, nullable=False)
197
 
199
 
200
+  """
201
+    if 1, the document is available for other users logged into pod.
202
+    default is 0 (private document)
203
+  """
204
+  is_shared = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
205
+  """
206
+    if 1, the document is available through a public - but obfuscated, url
207
+    default is 0 (document not publicly available)
208
+  """
209
+  is_public = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
210
+  """
211
+    here is the hash allowing to get the document publicly
212
+  """
213
+  public_url_key = Column(Unicode(1024), unique=False, nullable=False, default='')
214
+
198
   data_label   = Column(Unicode(1024), unique=False, nullable=False, default='')
215
   data_label   = Column(Unicode(1024), unique=False, nullable=False, default='')
199
   data_content = Column(Text(),        unique=False, nullable=False, default='')
216
   data_content = Column(Text(),        unique=False, nullable=False, default='')
200
   
217
   
208
 
225
 
209
   _oParent = relationship('PBNode', remote_side=[node_id], backref='_lAllChildren')
226
   _oParent = relationship('PBNode', remote_side=[node_id], backref='_lAllChildren')
210
   rights = relation('Rights', secondary=group_node_table, backref='nodes')
227
   rights = relation('Rights', secondary=group_node_table, backref='nodes')
228
+  _oOwner = relationship('User', remote_side=[pma.User.user_id], backref='_lAllNodes')
211
 
229
 
212
   def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
230
   def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
213
     """return all children nodes of type 'data' or 'node' or 'folder'"""
231
     """return all children nodes of type 'data' or 'node' or 'folder'"""
231
   def getChildNb(self):
249
   def getChildNb(self):
232
     return self.getChildNbOfType([PBNodeType.Data])
250
     return self.getChildNbOfType([PBNodeType.Data])
233
 
251
 
234
-  def getChildren(self):
252
+  def getChildren(self, pbIncludeDeleted=False):
235
     """return all children nodes of type 'data' or 'node' or 'folder'"""
253
     """return all children nodes of type 'data' or 'node' or 'folder'"""
236
-    return self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
254
+    # return self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
255
+    items = self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
256
+    items2 = list()
257
+    for item in items:
258
+      if pbIncludeDeleted==True or item.node_status!='deleted':
259
+        items2.append(item)
260
+    return items2
237
 
261
 
238
   def getContacts(self):
262
   def getContacts(self):
239
     """return all children nodes of type 'data' or 'node' or 'folder'"""
263
     """return all children nodes of type 'data' or 'node' or 'folder'"""
344
           break
368
           break
345
       return PBNodeStatus.getStatusItem(lsRealStatusId)
369
       return PBNodeStatus.getStatusItem(lsRealStatusId)
346
 
370
 
347
-  def getTruncatedLabel(self, piCharNb):
348
-    lsTruncatedLabel = ''
371
+  def getTruncatedLabel(self, piCharNb: int):
372
+    """
373
+    return a truncated version of the data_label property.
374
+    if piCharNb is not > 0, then the full data_label is returned
375
+    note: if the node is a file and the data_label is empty, the file name is returned
376
+    """
377
+    lsTruncatedLabel = self.data_label
378
+
379
+    # 2014-05-06 - D.A. - HACK
380
+    # if the node is a file and label empty, then use the filename as data_label
381
+    if self.node_type==PBNodeType.File and lsTruncatedLabel=='':
382
+      lsTruncatedLabel = self.data_file_name
383
+
349
     liMaxLength = int(piCharNb)
384
     liMaxLength = int(piCharNb)
350
-    if len(self.data_label)>liMaxLength:
351
-      lsTruncatedLabel = self.data_label[0:liMaxLength-1]+'…'
352
-    else:
353
-      lsTruncatedLabel = self.data_label
385
+    if liMaxLength>0 and len(lsTruncatedLabel)>liMaxLength:
386
+      lsTruncatedLabel = lsTruncatedLabel[0:liMaxLength-1]+'…'
387
+
354
     return lsTruncatedLabel
388
     return lsTruncatedLabel
355
 
389
 
356
   def getTruncatedContentAsText(self, piCharNb):
390
   def getTruncatedContentAsText(self, piCharNb):
389
 
423
 
390
 
424
 
391
 
425
 
392
-"""from sqlalchemy.orm import mapper
393
-mapper(
394
-  PBNode,
395
-  pod_node_table,
396
-  properties = {'_lAllChildren' : relationship(PBNode, backref=backref('_oParent', remote_side=PBNode.parent_id)) }
397
-)"""
398
-
399
-
400
-
401
-"""    children = relationship('TreeNode',
402
-
403
-                        # cascade deletions
404
-                        cascade="all",
405
-
406
-                        # many to one + adjacency list - remote_side
407
-                        # is required to reference the 'remote' 
408
-                        # column in the join condition.
409
-                        backref=backref("parent", remote_side='TreeNode.id'),
410
-
411
-                        # children will be represented as a dictionary
412
-                        # on the "name" attribute.
413
-                        collection_class=attribute_mapped_collection('name'),
414
-                    ) 
415
-"""
416
-
417
 # This is the association table for the many-to-many relationship between groups and nodes
426
 # This is the association table for the many-to-many relationship between groups and nodes
418
 group_node_table = Table('pod_group_node', metadata,
427
 group_node_table = Table('pod_group_node', metadata,
419
         Column('group_id', Integer, ForeignKey('pod_group.group_id',
428
         Column('group_id', Integer, ForeignKey('pod_group.group_id',
421
         Column('node_id', Integer, ForeignKey('pod_nodes.node_id',
430
         Column('node_id', Integer, ForeignKey('pod_nodes.node_id',
422
             onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
431
             onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
423
         Column('rights', Integer)
432
         Column('rights', Integer)
424
-)
433
+)

+ 6 - 4
pboard/pboard/public/css/style.css Parādīt failu

127
 
127
 
128
   padding: 0.5em 0.5em 0.5em 0.5em;
128
   padding: 0.5em 0.5em 0.5em 0.5em;
129
   
129
   
130
-  filter: alpha(opacity=90); /* internet explorer */
131
-  -khtml-opacity: 0.9;      /* khtml, old safari */
132
-  -moz-opacity: 0.9;       /* mozilla, netscape */
133
-  opacity: 0.9;           /* fx, safari, opera */
130
+  filter: alpha(opacity=95); /* internet explorer */
131
+  -khtml-opacity: 0.95;      /* khtml, old safari */
132
+  -moz-opacity: 0.95;       /* mozilla, netscape */
133
+  opacity: 0.95;           /* fx, safari, opera */
134
 }
134
 }
135
 
135
 
136
 .full-size-overlay-inner {
136
 .full-size-overlay-inner {
190
   margin-bottom: 0;
190
   margin-bottom: 0;
191
 }
191
 }
192
 
192
 
193
+ul.nav-tabs li.active > a { background-color: #efefef; }
194
+div.tab-pane > h4 { background-color: #efefef; padding: 0.5em; margin: 0 0 0.5em 0;}

+ 12 - 148
pboard/pboard/public/javascript/pod.js Parādīt failu

1
+  function generateStringId(charNb = 32, allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
2
+    var text = "";
3
+
4
+    for( var i=0; i < charNb; i++ ) {
5
+      text += allowedChars.charAt(Math.floor(Math.random() * allowedChars.length));
6
+    }
7
+
8
+    return text;
9
+  }
10
+
1
   function toggleFullScreen(outerWidgetId, innerWidgetId) {
11
   function toggleFullScreen(outerWidgetId, innerWidgetId) {
2
     if($(outerWidgetId).hasClass('full-size-overlay')) {
12
     if($(outerWidgetId).hasClass('full-size-overlay')) {
3
       // Toggle from fullscreen to "normal"
13
       // Toggle from fullscreen to "normal"
31
       $('#voiceBtn').hide();
41
       $('#voiceBtn').hide();
32
     }
42
     }
33
   };
43
   };
44
+  
34
   function showErrorAlert (reason, detail) {
45
   function showErrorAlert (reason, detail) {
35
     var msg='';
46
     var msg='';
36
     if (reason==='unsupported-file-type') { msg = "Unsupported format " +detail; }
47
     if (reason==='unsupported-file-type') { msg = "Unsupported format " +detail; }
43
 
54
 
44
   $(document).ready(function() {
55
   $(document).ready(function() {
45
 
56
 
46
-    $('#create_document_save_button').on('click', function(e){
47
-      // We don't want this to act as a link so cancel the link action
48
-      e.preventDefault();
49
-
50
-      // Find form and submit it
51
-      $('#create_document_form').submit();
52
-    });
53
-
54
-// ## FIXME                $('#current_node_textarea').wysihtml5({
55
-// ## FIXME                  "font-styles": true, //Font styling, e.g. h1, h2, etc. Default true
56
-// ## FIXME                  "emphasis": true, //Italics, bold, etc. Default true
57
-// ## FIXME                  "lists": true, //(Un)ordered lists, e.g. Bullets, Numbers. Default true
58
-// ## FIXME                  "html": true, //Button which allows you to edit the generated HTML. Default false
59
-// ## FIXME                  "link": true, //Button to insert a link. Default true
60
-// ## FIXME                  "image": true, //Button to insert an image. Default true,
61
-// ## FIXME                  // "color": true //Button to change color of font  
62
-// ## FIXME                });
63
-// ## FIXME                $('#current_node_textarea').css('margin-bottom', '0');
64
-// ## FIXME                $('#current_node_textarea').css("min-height", "12em");
65
-// ## FIXME                $('#current_node_textarea').addClass("span5");
66
-
67
-// ###################
68
-// ##
69
-// ## HERE
70
-// ##
71
-// ###################
72
-
73
-// ##
74
-// ## RE-IMPLEMENT THIS SOON !!!
75
-// ##
76
-// ##                /* Edit title form */
77
-// ##                $("#current-document-title-edit-form" ).css("display", "none");
78
-// ##                $("#current-document-title" ).dblclick(function() {
79
-// ##                  $("#current-document-title" ).css("display", "none");
80
-// ##                  $("#current-document-title-edit-form" ).css("display", "block");
81
-// ##                });
82
-// ##                $("#current-document-title-edit-cancel-button" ).click(function() {
83
-// ##                  $("#current-document-title" ).css("display", "block");
84
-// ##                  $("#current-document-title-edit-form" ).css("display", "none");
85
-// ##                });
86
-// ##                $('#current-document-title-save-cancel-button').on('click', function(e){
87
-// ##                  // We don't want this to act as a link so cancel the link action
88
-// ##                  e.preventDefault();
89
-// ##                  $('#current-document-title-edit-form').submit();
90
-// ##                });
91
-
92
-
93
     /* EDIT CONTENT FORM */
57
     /* EDIT CONTENT FORM */
94
     $("#current-document-content-edit-form" ).css("display", "none");
58
     $("#current-document-content-edit-form" ).css("display", "none");
95
     $("#current-document-content-edit-button" ).click(function() {
59
     $("#current-document-content-edit-button" ).click(function() {
97
       $("#current-document-content-edit-form" ).css("display", "block");
61
       $("#current-document-content-edit-form" ).css("display", "block");
98
       $("#current-document-toobar").css("display", "none");
62
       $("#current-document-toobar").css("display", "none");
99
     });
63
     });
100
-    $("#current-document-content" ).dblclick(function() {
101
-      $("#current-document-content" ).css("display", "none");
102
-      $("#current-document-content-edit-form" ).css("display", "block");
103
-    });
64
+
104
     $("#current-document-content-edit-cancel-button, #current-document-content-edit-cancel-button-top" ).click(function() {
65
     $("#current-document-content-edit-cancel-button, #current-document-content-edit-cancel-button-top" ).click(function() {
105
       $("#current-document-content" ).css("display", "block");
66
       $("#current-document-content" ).css("display", "block");
106
       $("#current-document-content-edit-form" ).css("display", "none");
67
       $("#current-document-content-edit-form" ).css("display", "none");
114
       $('#current-document-content-edit-form').submit();
75
       $('#current-document-content-edit-form').submit();
115
     });
76
     });
116
 
77
 
117
-    /* ADD EVENT => FORM */
118
-    $('#add_event_data_content_textarea').wysiwyg();
119
-    $('#add_event_data_content_textarea').css('margin-bottom', '0');
120
-    $('#add_event_data_content_textarea').css("height", "4em");
121
-    $('#add_event_data_content_textarea').addClass("span3");
122
-    /* ADD EVENT => SHOW/HIDE/SUBMIT BUTTONS */
123
-    $("#current-document-add-event-button" ).click(function() {
124
-      $("#current-document-add-event-form" ).css("display", "block");
125
-      $("#current-document-add-event-button" ).css("display", "none");
126
-    });
127
-    $('#current-document-add-event-cancel-button').on('click', function(e){
128
-      $("#current-document-add-event-form" ).css("display", "none");
129
-      $("#current-document-add-event-button" ).css("display", "block");
130
-    });
131
-    $('#current-document-add-event-save-button').on('click', function(e){
132
-      e.preventDefault(); // We don't want this to act as a link so cancel the link action
133
-      $('#add_event_data_content_textarea_wysiwyg').cleanHtml();
134
-      $('#add_event_data_content_textarea').val($('#add_event_data_content_textarea_wysiwyg').html());
135
-      $('#current-document-add-event-form').submit();
136
-    });
137
-
138
-    /* ADD CONTACT => FORM */
139
-    $('#add_contact_data_content_textarea').wysiwyg();
140
-    $('#add_contact_data_content_textarea').css('margin-bottom', '0');
141
-    $('#add_contact_data_content_textarea').css("height", "4em");
142
-    $('#add_contact_data_content_textarea').addClass("span3");
143
-    /* ADD CONTACT => SHOW/HIDE/SUBMIT BUTTONS */
144
-    $("#current-document-add-contact-button" ).click(function() {
145
-      $("#current-document-add-contact-form" ).css("display", "block");
146
-      $("#current-document-add-contact-button" ).css("display", "none");
147
-    });
148
-    $('#current-document-add-contact-cancel-button').on('click', function(e){
149
-      $("#current-document-add-contact-form" ).css("display", "none");
150
-      $("#current-document-add-contact-button" ).css("display", "block");
151
-    });
152
-    $('#current-document-add-contact-save-button').on('click', function(e){
153
-      e.preventDefault(); // We don't want this to act as a link so cancel the link action
154
-      $('#add_contact_data_content_textarea_wysiwyg').cleanHtml();
155
-      $('#add_contact_data_content_textarea').val($('#add_contact_data_content_textarea_wysiwyg').html());
156
-      $('#current-document-add-contact-form').submit();
157
-    });
158
-
159
-
160
-    /* ADD COMMENT => FORM */
161
-    $('#add_comment_data_content_textarea').wysiwyg();
162
-    $('#add_comment_data_content_textarea').css('margin-bottom', '0');
163
-    $('#add_comment_data_content_textarea').css("height", "4em");
164
-    $('#add_comment_data_content_textarea').addClass("span3");
165
-    /* ADD COMMENT => SHOW/HIDE/SUBMIT BUTTONS */
166
-    $("#current-document-add-comment-button" ).click(function() {
167
-      $("#current-document-add-comment-form" ).css("display", "block");
168
-      $("#current-document-add-comment-button" ).css("display", "none");
169
-    });
170
-    $('#current-document-add-comment-cancel-button').on('click', function(e){
171
-      $("#current-document-add-comment-form" ).css("display", "none");
172
-      $("#current-document-add-comment-button" ).css("display", "block");
173
-    });
174
-    $('#current-document-add-comment-save-button').on('click', function(e){
175
-      e.preventDefault(); // We don't want this to act as a link so cancel the link action
176
-      $('#add_comment_data_content_textarea_wysiwyg').cleanHtml();
177
-      $('#add_comment_data_content_textarea').val($('#add_comment_data_content_textarea_wysiwyg').html());
178
-      $('#current-document-add-comment-form').submit();
179
-    });
180
-
181
-    /* ADD FILE => FORM */
182
-    $('#add_file_data_content_textarea').wysiwyg();
183
-    $('#add_file_data_content_textarea').css('margin-bottom', '0');
184
-    $('#add_file_data_content_textarea').css("height", "4em");
185
-    $('#add_file_data_content_textarea').addClass("span3");
186
-    /* ADD FILE => SHOW/HIDE/SUBMIT BUTTONS */
187
-    $("#current-document-add-file-button" ).click(function() {
188
-      $("#current-document-add-file-form" ).css("display", "block");
189
-      $("#current-document-add-file-button" ).css("display", "none");
190
-    });
191
-    $('#current-document-add-file-cancel-button').on('click', function(e){
192
-      $("#current-document-add-file-form" ).css("display", "none");
193
-      $("#current-document-add-file-button" ).css("display", "block");
194
-    });
195
-    $('#current-document-add-file-save-button').on('click', function(e){
196
-      e.preventDefault(); // We don't want this to act as a link so cancel the link action
197
-      $('#add_file_data_content_textarea_wysiwyg').cleanHtml();
198
-      $('#add_file_data_content_textarea').val($('#add_file_data_content_textarea_wysiwyg').html());
199
-      $('#current-document-add-file-form').submit();
200
-    });
201
 
78
 
202
     $(function() {
79
     $(function() {
203
       $('.datetime-picker-input-div').datetimepicker({
80
       $('.datetime-picker-input-div').datetimepicker({
206
       });
83
       });
207
     });
84
     });
208
 
85
 
209
-/*
210
-    // Allow to go directly to required tab on load
211
-    // Javascript to enable link to tab
212
-    var url = document.location.toString();
213
-    if (url.match('#')) {
214
-      $('.nav-tabs a[href=#'+url.split('#')[1]+']').tab('show') ;
215
-    } 
216
-
217
-    // Change hash for page-reload
218
-    $('.nav-tabs a').on('shown', function (e) {
219
-      window.location.hash = e.target.hash;
220
-    })
221
-*/
222
     // #################################
86
     // #################################
223
     // ##
87
     // ##
224
     // ## The following JS code allow t
88
     // ## The following JS code allow t

+ 483 - 0
pboard/pboard/templates/document-widgets-tabs.mak Parādīt failu

1
+<%inherit file="local:templates.master"/>
2
+<%namespace name="POD" file="pboard.templates.pod"/>
3
+<%namespace name="DOC" file="pboard.templates.document-widgets"/>
4
+
5
+<%def name="AccessManagementTab(poNode)">
6
+  ######
7
+  ##
8
+  ## THIS WIDGET IS INTENDED TO BE USED ONE TIME ONLY IN A PAGE
9
+  ##
10
+  <h4>${_('Share options')}</h4> 
11
+  <p>
12
+    This document is
13
+    % if poNode.is_shared==False:
14
+      <span class="label label-info">
15
+        <i class="fa fa-user"></i>
16
+        ${_('private')}
17
+      </span>
18
+    % else:
19
+      <span class="label label-info">
20
+        <i class="fa fa-group"></i>
21
+        ${_('collaborative')}
22
+      </span>
23
+    % endif
24
+  </p>
25
+  <p>
26
+    % if poNode.is_shared==True or poNode.is_shared==False:
27
+      ${_('People working on it are:')}
28
+######
29
+##
30
+## FIXME - SHOW LIST OF GROUPS ALLOWED TO WORK ON THE DOCUMENT
31
+##
32
+    <table class="table table-striped table-hover table-condensed">
33
+      <thead>
34
+        <tr>
35
+          <th><i class="fa fa-group"></i> ${_('Groups')}</th>
36
+          <th></th>
37
+        </tr>
38
+      </thead>
39
+      <tr>
40
+        <td>Recherche et Développement</td>
41
+        <td>
42
+          <span class="label label-success" title="${_('Read access')}">R</span>
43
+          <span class="label label-warning" title="${_('Write access')}">W</span>
44
+        </td>
45
+      </tr>
46
+      <thead>
47
+        <tr>
48
+          <th><i class="fa fa-user"></i> ${_('Users')}</th>
49
+          <th></th>
50
+        </tr>
51
+      </thead>
52
+      <tr>
53
+        <td>Damien Accorsi</td>
54
+        <td>
55
+          <span class="label label-success">R</span>
56
+        </td>
57
+      </tr>
58
+      <tr>
59
+        <td>Sylvain Ferot</td>
60
+        <td>
61
+          <span class="label label-success">R</span>
62
+          <span class="label label-warning">W</span>
63
+        </td>
64
+      </tr>
65
+    </table>
66
+    
67
+    % endif
68
+  <p>
69
+
70
+######
71
+##
72
+## 2014-05-06 - D.A. We do not share documents on internet yet.
73
+##
74
+##  <p>
75
+##    % if poNode.is_public==False:
76
+##      ${_('This document is not shared on internet')|n}
77
+##    % else:
78
+##      ${_('This document is <span class="label label-warning"><i class="fa fa-globe"></i><span>shared</span></span> on internet')|n}.
79
+##      ${_('The associated url is:')} <a href="FIXME">${poNode.public_url_key}</a>
80
+##    % endif
81
+##  </p>
82
+  <!-- Button to trigger modal -->
83
+  <a href="#edit-document-share-properties" role="button" class="btn btn-success" data-toggle="modal">
84
+    <i class="fa fa-edit"></i>
85
+    ${_('Edit share options')}
86
+  </a>
87
+     
88
+  <!-- Modal -->
89
+  <div
90
+    id="edit-document-share-properties"
91
+    class="modal hide"
92
+    tabindex="-1"
93
+    role="dialog"
94
+    aria-labelledby="myModalLabel"
95
+    aria-hidden="true">
96
+    
97
+    <div class="modal-header">
98
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
99
+      <h3 id="myModalLabel">Document sharing options</h3>
100
+    </div>
101
+    <div class="modal-body">
102
+
103
+      <form id='document-share-form' method="GET" action="${tg.url('/api/set_access_management?node_id=%d'%poNode.node_id)}">
104
+        <fieldset>
105
+          <label class="checkbox">
106
+            <input name="is_shared" type="checkbox" id="document-share-selector" ${('', 'checked')[poNode.is_shared]}/>
107
+            ${_('Share document with collaborators.')} <i class="fa fa-group"></i>
108
+          </label>
109
+          <div id="document-share-people-selector">
110
+            <p>
111
+              ${_('Select read and write access for each group or people...')}</p>
112
+            <script>
113
+            function updateRights(psUserId) {
114
+              var ACCESS_NONE = '';
115
+              var ACCESS_READ = 'R';
116
+              var ACCESS_WRITE = 'RW';
117
+              
118
+              var nodeIdForSelectedUser = 'user-'+psUserId+'-value';
119
+              var widget = $('#'+nodeIdForSelectedUser);
120
+              var oldValue = widget.val();
121
+              var newValue = '';
122
+              if(oldValue==ACCESS_NONE) {
123
+                newValue = ACCESS_READ;
124
+                newHtml = '<span class="label label-success">R</span>';
125
+              } else if(oldValue==ACCESS_READ) {
126
+                newValue = ACCESS_WRITE;
127
+                newHtml = '<span class="label label-success">R</span> <span class="label label-warning">W</span>';
128
+              } else if (oldValue==ACCESS_WRITE) {
129
+                newValue = ACCESS_NONE;
130
+                newHtml = '';
131
+              } else {
132
+                newValue = ACCESS_READ;
133
+                newHtml = '<span class="label label-success">R</span>';
134
+              }
135
+              
136
+              widget.val(newValue);
137
+              visibleid = 'user-'+psUserId+'-rights';
138
+              $("#"+visibleid).html(newHtml);
139
+            }
140
+            </script>
141
+            
142
+            <table class="table table-striped table-hover table-condensed">
143
+              <thead>
144
+                <tr>
145
+                  <th></th>
146
+                  <th>${_('Group')}</th>
147
+                  <th>${_('Access')}</th>
148
+                </tr>
149
+              </thead>
150
+    ######
151
+    ##
152
+    ## FIXME - SET A DYNAMIC SELECT LIST HERE
153
+    ##
154
+              % for loCurrentUser in ((3, 'Research and Development'), (4, 'Sylvain Ferot'), (5, 'Damien Accorsi')):
155
+              <tr id='user-${loCurrentUser[0]}-rights-row'>
156
+                <td>
157
+                  <a
158
+                    class="btn btn-mini"
159
+                    onclick="updateRights(${loCurrentUser[0]})"
160
+                  >
161
+                    <i class="fa fa-key"></i>
162
+                  </a>
163
+                </td>
164
+                <td class='pod-highlightable-access-management-cell'>
165
+                  ${loCurrentUser[1]}
166
+                  <input
167
+                    type="hidden"
168
+                    id="user-${loCurrentUser[0]}-value"
169
+                    name="user[${loCurrentUser[0]}]"
170
+                    value=""
171
+                  />
172
+                </td>
173
+                <td id="user-${loCurrentUser[0]}-rights" class="pod-right-cell"></td>
174
+              </tr>
175
+              % endfor
176
+            </table>
177
+          </div>
178
+        </fieldset>
179
+######
180
+##
181
+## 2014-05-06 - D.A. The documents are not yet sharable through internet
182
+##
183
+##        <fieldset>
184
+##          <label class="checkbox">
185
+##            <input name="is_public" type="checkbox" id="document-public-selector" ${('', 'checked')[poNode.is_public]}/>
186
+##            ${_('Internet shared document')}
187
+##            <i class="fa fa-globe"></i>
188
+##          </label>
189
+##          <label id="document-public-key-selector">
190
+##            ${_('Key')}
191
+##            <div class="input-append">
192
+##              <input name="url_public_key" id="document-public-key" type="text">
193
+##              <span id="document-public-key-refresh-button" class="add-on btn" title="${_('Regenerate key')}">
194
+##                <i class="fa fa-refresh"></i>
195
+##              </span>
196
+##            </div>
197
+##            <p><a id='document-public-key-url' href="">http://share.pod.com/document/azefnzeioguneriugnreiugnre</a></p>
198
+##          </label>
199
+
200
+        </fieldset>
201
+####
202
+## Button replaced by modal dialog button
203
+##        <button type="submit" class="btn btn-success">
204
+##          <i class="fa fa-check"></i>
205
+##          ${_('Save')}
206
+##        </button>
207
+      </form>
208
+    </div>
209
+    <div class="modal-footer">
210
+    <button class="btn" data-dismiss="modal" aria-hidden="true">
211
+      <i class="fa fa-ban"></i> ${_('Cancel')}
212
+    </button>
213
+    <button class="btn btn-success" id="document-share-form-submit-button">
214
+      <i class="fa fa-check"></i> ${_('Save changes')}
215
+    </button>
216
+    </div>
217
+    <script>
218
+##
219
+## 2014-05-06 - D.A. - Documents are not yet sharable through internet
220
+##
221
+##        function refreshDocumentPublicKey(psNewPublicKey) {
222
+##          var lsNewUrl = 'http://share.pod.com/document/'+psNewPublicKey;
223
+##          $('#document-public-key').val(psNewPublicKey);
224
+##          $('#document-public-key-url').attr('href', lsNewUrl);
225
+##          $('#document-public-key-url').text(lsNewUrl);
226
+##        }
227
+      
228
+      function toggleDocumentSharePeopleSelector(pbShowIt) {
229
+        if (pbShowIt) {
230
+          $('#document-share-people-selector').show();
231
+          // $('#document-share-people-selector input').removeAttr("disabled");
232
+        } else {
233
+          $('#document-share-people-selector').hide();
234
+          // $('#document-share-people-selector input').prop('disabled', 'disabled');
235
+        }
236
+      }
237
+
238
+##
239
+## 2014-05-06 - D.A. - Documents are not yet sharable through internet
240
+##
241
+##        function toggleDocumentPublicKeyGenerator(pbShowIt) {
242
+##          if (pbShowIt) {
243
+##            $('#document-public-key-selector input').removeAttr("disabled");
244
+##            $('#document-public-key-refresh-button').removeProp('disabled');
245
+##            $('#document-public-key-refresh-button').removeClass('btn-disabled');
246
+##            $('#document-public-key-selector a').show();
247
+##            $('#document-public-key-refresh-button').on("click").click(function () {
248
+##              refreshDocumentPublicKey(generateStringId()); // New random 32-char id
249
+##            });
250
+##            if($('#document-public-key-selector input').val()=='') {
251
+##              refreshDocumentPublicKey(generateStringId());
252
+##            }
253
+##          } else {
254
+##            $('#document-public-key-refresh-button').prop('disabled', true);
255
+##            $('#document-public-key-refresh-button').addClass('btn-disabled');
256
+##            $('#document-public-key-selector input').prop('disabled', 'disabled');
257
+##            $('#document-public-key-refresh-button').off("click");
258
+##            $('#document-public-key-selector a').hide();
259
+##          }
260
+##        }
261
+##
262
+##
263
+##
264
+
265
+      // Callbacks setup
266
+      $('#document-share-selector').change(function () {
267
+        var checkedValue = $('#document-share-selector').prop("checked");
268
+        toggleDocumentSharePeopleSelector(checkedValue);
269
+      });
270
+
271
+##        $('#document-public-selector').change(function () {
272
+##          var checkedValue = $('#document-public-selector').prop("checked");
273
+##          toggleDocumentPublicKeyGenerator(checkedValue);
274
+##        });
275
+
276
+      // Submit access-management modal dialog form
277
+      $('#document-share-form-submit-button').click(function(){
278
+        $('#document-share-form')[0].submit();
279
+      });
280
+
281
+      // Initial setup
282
+      // Activate or disactivate users selector according
283
+      // to current state of the is_shared property
284
+      //
285
+      // FIXME - 2014-05-06 - This is not working (should be done at document.ready time)
286
+      // note: putting this in a document.ready callback does not work.
287
+      //
288
+      $('#document-share-form')[0].reset();
289
+      toggleDocumentSharePeopleSelector($('#document-share-selector').prop("checked"));
290
+##        toggleDocumentPublicKeyGenerator($('#document-public-selector').prop("checked"));  
291
+##        
292
+##        refreshDocumentPublicKey($('#document-public-key').val()); // First init
293
+
294
+    </script>
295
+  </div>
296
+</%def>
297
+
298
+<%def name="FileTabContent(poNode)">
299
+  <h4>${_('Attachments')}</h4>
300
+  
301
+  % if len(poNode.getFiles())<=0:
302
+    <p class="pod-grey">${_("There is currently no attachment.")}<br/></p>
303
+    <p>${POD.OpenModalButton(h.ID.AddFileModalForm(poNode), _(' Attach first file'))}</p>
304
+  % else:
305
+    <p>${POD.OpenModalButton(h.ID.AddFileModalForm(poNode), _(' Attach a file'))}</p>
306
+  % endif
307
+
308
+  <div>
309
+    % if len(poNode.getFiles())>0:
310
+      % for loFile in poNode.getFiles():
311
+        <p style="list-style-type:none; margin-bottom: 0.5em;">
312
+          <i class="fa fa-paperclip"></i>
313
+          <a
314
+            href="${tg.url('/document/%i'%loFile.node_id)}"
315
+            title="${_('View the attachment')}: ${loFile.getTruncatedLabel(-1)}"
316
+          >
317
+            ${loFile.getTruncatedLabel(50)}
318
+          </a>
319
+          <a
320
+            class="pull-right"
321
+            href="${tg.url('/api/get_file_content/%s'%(loFile.node_id))}"
322
+            title="${_('View the attachment')}"
323
+          >
324
+            <i class="fa fa-download"></i>
325
+          </a>
326
+        </p>
327
+      % endfor
328
+    % endif
329
+  </div>
330
+</%def>
331
+
332
+<%def name="SubdocumentContent(poNode)">
333
+  <h4>${_('Sub-documents')}</h4>
334
+  
335
+  % if len(poNode.getChildren())<=0:
336
+    <p class="pod-grey">${_("There is currently no child documents.")}</p>
337
+  % endif
338
+  <p>${POD.OpenModalButton(h.ID.AddDocumentModalForm(poNode), _('Add a document'))}</p>
339
+
340
+  % if len(poNode.getChildren())>0:
341
+    <div>
342
+      % for subnode in poNode.getChildren():
343
+        <p style="list-style-type:none;">
344
+          <i class="fa-fw ${subnode.getIconClass()}"></i>
345
+            <a href="${tg.url('/document/%i'%subnode.node_id)}">
346
+              ${subnode.data_label}
347
+            </a>
348
+        </p>
349
+      % endfor
350
+    </div>
351
+  % endif
352
+</%def>
353
+
354
+<%def name="EventTabContent(poNode)">
355
+  <h4>${_('Calendar')}</h4>
356
+  
357
+  % if len(poNode.getEvents())<=0:
358
+    <p class="pod-grey">${_("The calendar is empty.")}<br/></p>
359
+    <p>${POD.OpenModalButton(h.ID.AddEventModalForm(poNode), _(' Add first event'))}</p>
360
+  % else:
361
+    <p>${POD.OpenModalButton(h.ID.AddEventModalForm(poNode), _(' Add an event'))}</p>
362
+  % endif
363
+
364
+  % if len(poNode.getEvents())>0:
365
+    <table class="table table-striped table-hover table-condensed">
366
+      <thead>
367
+        <tr>
368
+          <th>Date</th>
369
+          <th>Time</th>
370
+          <th>
371
+            Event
372
+          </th>
373
+          <th>
374
+            <a href="" title="Add an event"><i class="icon-g-plus"></i></a>
375
+          </th>
376
+        </tr>
377
+      </thead>
378
+      % for event in poNode.getEvents():
379
+        <tr class="item-with-data-popoverable" data-content="${event.data_content}" rel="popover" data-placement="left" data-trigger="hover">
380
+          <td>${event.getFormattedDate(event.data_datetime)}</td>
381
+          <td>${event.getFormattedTime(event.data_datetime)}</td>
382
+          <td>${event.data_label}</td>
383
+        </tr>
384
+  ## FIXME                    <script>
385
+  ##                      $('.item-with-data-popoverable').popover({ html: true});
386
+  ##                    </script>
387
+
388
+      % endfor
389
+    </table>
390
+  % endif
391
+</%def>
392
+
393
+<%def name="ContactTabContent(poNode)">
394
+  <h4>${_('Address book')}</h4> 
395
+  % if len(poNode.getContacts())<=0:
396
+    <p class="pod-grey">${_("The address book is empty.")}<br/></p>
397
+    <p>${POD.OpenModalButton(h.ID.AddContactModalForm(poNode), _('Add first contact'))}</p>
398
+  % else:
399
+    <p>${POD.OpenModalButton(h.ID.AddContactModalForm(poNode), _('Add a contact'))}</p>
400
+  % endif
401
+
402
+  <!-- LIST OF CONTACT NODES -->
403
+  % for contact in poNode.getContacts():
404
+    <div class="well">
405
+      <legend class="text-info">
406
+        ${contact.data_label}
407
+        ## TODO - 2013-11-20 - Use the right form in order to update meta-data
408
+        <a class="pull-right" href="${tg.url('/document/%i'%contact.node_id)}"><i class="fa fa-edit"></i></a>
409
+      </legend>
410
+      
411
+      <div>
412
+        ## FIXME - D.A. - 2013-11-15 - Implement localisation stuff <a style='float: right;' href="" title='${_('Search on google maps')}'><i class='icon-g-google-maps'></i></a>
413
+        ${contact.data_content|n}
414
+      </div>
415
+    </div>
416
+  % endfor
417
+</%def>
418
+
419
+<%def name="CommentTabContent(poNode)">
420
+  <h4>${_('Comment thread')}</h4>
421
+  
422
+  % if len(poNode.getComments())<=0:
423
+    <p class="pod-grey">${_("The comment thread is empty.")}<br/></p>
424
+  % endif
425
+
426
+  % if len(poNode.getComments())>0:
427
+    % if len(poNode.getComments())>5:
428
+      ##
429
+      ## We show a "direct down" button in case the page is too long
430
+      ##
431
+      <p>${POD.OpenLinkButton(h.ID.AddCommentInlineForm(), _('Add a comment'))}</p>
432
+    % endif
433
+    <div>
434
+      % for comment in poNode.getComments():
435
+        <p>
436
+          <a href="${tg.url('/api/toggle_share_status', dict(node_id=comment.node_id))}">
437
+            % if comment.is_shared:
438
+              <span class="label label-warning" title="${_('Shared comment. Click to make private.')}">${h.ICON.Shared|n}</span>
439
+            % else:
440
+              <span class="label label-info" title="${_('Private comment. Click to share.')}">${h.ICON.Private|n}</span>
441
+            % endif
442
+          </a>
443
+          <strong>${comment._oOwner.display_name}</strong>
444
+          <i class="pull-right">
445
+            The ${comment.getFormattedDate(comment.updated_at)} 
446
+            at ${comment.getFormattedTime(comment.updated_at)}
447
+          </i>
448
+          <br/>
449
+          ${comment.data_content|n}
450
+          <hr style="border-top: 1px dotted #ccc; margin: 0;"/>
451
+        </p>
452
+      % endfor
453
+    </div>
454
+  % endif
455
+
456
+  <form class="form" id="${h.ID.AddCommentInlineForm()}" action="${tg.url('/api/create_comment')}" method="POST">
457
+    <input type="hidden" name='parent_id' value='${poNode.node_id}'/>
458
+    <input type="hidden" name='data_label' value=""/>
459
+    <input type="hidden" id="add_comment_data_content_textarea" name='data_content' />
460
+    <label>
461
+      ${_('Write your comment below:')}
462
+      ${POD.RichTextEditor('add_comment_data_content_textarea_wysiwyg', '', 'boldanditalic')}
463
+    </label>
464
+    <label>
465
+      <input type="checkbox" name='is_shared'/> ${_('Share this comment')}
466
+    </label>
467
+    <span class="pull-right">
468
+      % if len(poNode.getComments())<=0:
469
+        ${POD.SaveButton('current-document-add-comment-save-button', True, _('Add first comment'))}
470
+      % else:
471
+        ${POD.SaveButton('current-document-add-comment-save-button', True, _('Comment'))}
472
+      % endif
473
+    </span>
474
+  </form>
475
+  <script>
476
+      $('#current-document-add-comment-save-button').on('click', function(e){
477
+      e.preventDefault(); // We don't want this to act as a link so cancel the link action
478
+      $('#add_comment_data_content_textarea_wysiwyg').cleanHtml();
479
+      $('#add_comment_data_content_textarea').val($('#add_comment_data_content_textarea_wysiwyg').html());
480
+      $('#current-document-add-comment-form').submit();
481
+    });
482
+  </script>
483
+</%def>

+ 387 - 42
pboard/pboard/templates/document-widgets.mak Parādīt failu

4
 <%def name="node_treeview_for_set_parent_menu(node_id, node_list, indentation=-1)">
4
 <%def name="node_treeview_for_set_parent_menu(node_id, node_list, indentation=-1)">
5
   % if indentation==-1:
5
   % if indentation==-1:
6
     <li>
6
     <li>
7
-      <a href="${tg.url('/api/set_parent_node?node_id=%i&new_parent_id=0'%(current_node.node_id))}">
7
+      <a href="${tg.url('/api/set_parent_node', dict(node_id=node_id, new_parent_id=0))}">
8
         <i class="fa fa-file-text-o"></i> ${_('Home')}
8
         <i class="fa fa-file-text-o"></i> ${_('Home')}
9
       </a>
9
       </a>
10
       ${node_treeview_for_set_parent_menu(node_id, node_list, 0)}
10
       ${node_treeview_for_set_parent_menu(node_id, node_list, 0)}
14
       <ul style="list-style: none;">
14
       <ul style="list-style: none;">
15
       % for new_parent_node in node_list:
15
       % for new_parent_node in node_list:
16
         <li>
16
         <li>
17
-          <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)}
17
+          <a href="${tg.url('/api/set_parent_node', dict(node_id=node_id, new_parent_id=new_parent_node.node_id))}"><i class="fa fa-file-text-o"></i> ${new_parent_node.getTruncatedLabel(40-indentation*2)}
18
           </a>
18
           </a>
19
           ${node_treeview_for_set_parent_menu(node_id, new_parent_node.getStaticChildList(), indentation+1)}
19
           ${node_treeview_for_set_parent_menu(node_id, new_parent_node.getStaticChildList(), indentation+1)}
20
         </li>
20
         </li>
24
   % endif
24
   % endif
25
 </%def>
25
 </%def>
26
 
26
 
27
+<%def name="ToolbarMenuItemModal(psTargetModalId, psIconClasses, psMenuLabel)">
28
+  <li><a href="#${psTargetModalId}" role="button" data-toggle="modal"><i class="${psIconClasses}"></i> ${psMenuLabel}</a></li>
29
+</%def>
30
+
31
+<%def name="ToolbarMenuItemInline(psTargetId, psIconClasses, psMenuLabel)">
32
+  <li><a href="#${psTargetId}"><i class="${psIconClasses}"></i> ${psMenuLabel}</a></li>
33
+</%def>
34
+<%def name="ToolbarMenuItemLink(psTargetUrl, psIconClasses, psMenuLabel, psLinkCss='', psLinkTitle='')">
35
+  % if psTargetUrl=='#':
36
+    <li class="disabled"><a href="${psTargetUrl}" class="${psLinkCss}" title="${psLinkTitle}"><i class="${psIconClasses}"></i> ${psMenuLabel}</a></li>
37
+  % else:
38
+    <li><a href="${psTargetUrl}" class="${psLinkCss}" title="${psLinkTitle}"><i class="${psIconClasses}"></i> ${psMenuLabel}</a></li>
39
+  % endif
40
+</%def>
41
+
42
+        
27
 <%def name="Toolbar(poNode, plNodeStatusList, plRootNodes, psDivId)">
43
 <%def name="Toolbar(poNode, plNodeStatusList, plRootNodes, psDivId)">
28
   <div id="${psDivId}">
44
   <div id="${psDivId}">
29
     <div class="btn-group">
45
     <div class="btn-group">
30
-  % if poNode.parent_id!=None and poNode.parent_id!=0:
31
       ${POD.EditButton('current-document-content-edit-button', True)}
46
       ${POD.EditButton('current-document-content-edit-button', True)}
32
-  % endif
33
       <button class="btn btn-small"  data-toggle="dropdown" href="#"> 
47
       <button class="btn btn-small"  data-toggle="dropdown" href="#"> 
34
         <i class="fa  fa-signal"></i>
48
         <i class="fa  fa-signal"></i>
35
         ${_("Change status")}
49
         ${_("Change status")}
38
         <span class="caret"></span>
52
         <span class="caret"></span>
39
       </a>
53
       </a>
40
       <ul class="dropdown-menu">
54
       <ul class="dropdown-menu">
55
+        <li>
56
+          <div class="pod-grey strong" ><strong><i class="fa fa-magic"></i> ${_('Current status is...')}</strong><br/></div>
57
+        </li>
41
       % for node_status in plNodeStatusList:
58
       % for node_status in plNodeStatusList:
42
         % if node_status.status_id==poNode.getStatus().status_id:
59
         % if node_status.status_id==poNode.getStatus().status_id:
43
-        <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
44
-          <a class="${node_status.css}" href="#"  style="color: #999;">
45
-            <i class="${node_status.icon_id}"></i> ${node_status.label}
46
-          </a>
47
-        </li>
48
-        % else:
49
-        <li title="${h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id)}">
50
-          <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))}">
51
-            <i class="${node_status.icon_id}"></i> ${node_status.label}
52
-          </a>
60
+          ${ToolbarMenuItemLink('#', node_status.icon_id, node_status.label, 'disabled '+node_status.css, h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id))}
61
+        % endif
62
+      % endfor
63
+        <li class="divider" role="presentation"></li>
64
+        <li>
65
+          <div class=" strong" ><strong><i class="fa fa-magic"></i> ${_('Change to...')}</strong><br/></div>
66
+          <div class="pod-grey"><i>${_('change the status to...')}</i></div>
53
         </li>
67
         </li>
68
+      % for node_status in plNodeStatusList:
69
+        % if node_status.status_id!=poNode.getStatus().status_id:
70
+          ${ToolbarMenuItemLink(tg.url('/api/edit_status', dict(node_id=current_node.node_id, node_status=node_status.status_id)), node_status.icon_id, node_status.label, node_status.css, h.getExplanationAboutStatus(node_status.status_id, current_node.getStatus().status_id))}
54
         % endif
71
         % endif
55
       % endfor
72
       % endfor
56
       </ul>
73
       </ul>
63
       <ul class="dropdown-menu">
80
       <ul class="dropdown-menu">
64
       
81
       
65
         <li>
82
         <li>
66
-          <div class="btn-success strong" ><strong><i class="fa fa-magic"></i> Add New...</strong><br/></div>
67
-          <div class="pod-grey"><i>create a totally new item...</i></div>
83
+          <div class="btn-success strong" ><strong><i class="fa fa-magic"></i> ${_('Add New...')}</strong><br/></div>
84
+          <div class="pod-grey"><i>${_('create a totally new item...')}</i></div>
68
         </li>
85
         </li>
69
 
86
 
70
-        <li><a><i class="fa fa-file-text-o"></i> Document</a></li>
71
-        <li><a><i class="fa fa-paperclip"></i> File</a></li>
72
-        <li><a><i class="fa fa-calendar"></i> Event</a></li>
73
-        <li><a><i class="fa fa-user"></i> Contact</a></li>
74
-        <li><a><i class="fa fa-comments-o"></i> Comment</a></li>
87
+        ${ToolbarMenuItemModal(h.ID.AddDocumentModalForm(current_node), 'fa fa-file-text-o', _('Document'))}
88
+        ${ToolbarMenuItemModal(h.ID.AddFileModalForm(current_node), 'fa fa-paperclip', _('File'))}
89
+        ${ToolbarMenuItemModal(h.ID.AddEventModalForm(current_node), 'fa fa-calendar', _('Event'))}
90
+        ${ToolbarMenuItemModal(h.ID.AddContactModalForm(current_node), 'fa fa-user', _('Contact'))}
91
+##
92
+## FIXME - DA - 07-05-2014 - The link below is not working clean
93
+##
94
+        ${ToolbarMenuItemInline(h.ID.AddCommentInlineForm(), 'fa fa-comments-o', _('Comment'))}
75
 
95
 
76
         <li class="divider" role="presentation"></li>
96
         <li class="divider" role="presentation"></li>
77
 
97
 
79
           <div class="btn-warning strong" ><strong><i class="fa fa-link"></i> Add Existing...</strong><br/></div>
99
           <div class="btn-warning strong" ><strong><i class="fa fa-link"></i> Add Existing...</strong><br/></div>
80
           <div class="pod-grey"><i>link to an existing item...</i></div>
100
           <div class="pod-grey"><i>link to an existing item...</i></div>
81
         </li>
101
         </li>
82
-        <li><a><i class="fa fa-file-text-o"></i> Document</a></li>
83
-        <li><a><i class="fa fa-paperclip"></i> File</a></li>
84
-        <li><a><i class="fa fa-calendar"></i> Event</a></li>
85
-        <li><a><i class="fa fa-user"></i> Contact</a></li>
86
-        <li><a><i class="fa fa-comments-o"></i> Comment</a></li>
87
-
102
+        <li><p class="pod-grey"><i class="fa fa-danger"></i> coming soon!</p></li>
88
       </ul>
103
       </ul>
89
     </div>
104
     </div>
90
     <div class="btn-group ">
105
     <div class="btn-group ">
103
       </ul>
118
       </ul>
104
       <a
119
       <a
105
         class="btn btn-small btn-danger"
120
         class="btn btn-small btn-danger"
106
-        href='${tg.url('/api/edit_status?node_id=%i&node_status=%s'%(poNode.node_id, 'deleted'))}'
121
+        href='${tg.url('/api/edit_status', dict(node_id=poNode.node_id, node_status='deleted'))}'
107
         id='current-document-force-delete-button' onclick="return confirm('${_('Delete current document?')}');"
122
         id='current-document-force-delete-button' onclick="return confirm('${_('Delete current document?')}');"
108
         title="${_('Delete')}"
123
         title="${_('Delete')}"
109
         ><i class="fa fa-trash-o"></i></a>
124
         ><i class="fa fa-trash-o"></i></a>
114
 <%def name="BreadCrumb(poNode)">
129
 <%def name="BreadCrumb(poNode)">
115
   <ul class="breadcrumb span12">
130
   <ul class="breadcrumb span12">
116
     <li>
131
     <li>
117
-      <span class="divider"> / Documents /</span>
118
-    </li>
119
-    % for breadcrumb_node in poNode.getBreadCrumbNodes():
120
-    <li>
121
-      <a href="${tg.url('/document/%s'%(breadcrumb_node.node_id))}">${breadcrumb_node.getTruncatedLabel(30)}</a>
122
       <span class="divider">/</span>
132
       <span class="divider">/</span>
133
+      <a href="${tg.url('/document/')}">Documents</a>
123
     </li>
134
     </li>
124
-    % endfor
125
-    <li class="active">${poNode.data_label}</li>
135
+    % if poNode!=None:
136
+      % for breadcrumb_node in poNode.getBreadCrumbNodes():
137
+      <li>
138
+        <span class="divider">/</span>
139
+        <a href="${tg.url('/document/%s'%(breadcrumb_node.node_id))}">${breadcrumb_node.getTruncatedLabel(30)}</a>
140
+      </li>
141
+      % endfor
142
+      <li class="active">
143
+        <span class="divider">/</span>
144
+        ${poNode.data_label}
145
+      </li>
146
+    % endif
126
   </ul>
147
   </ul>
127
 </%def>
148
 </%def>
128
 
149
 
140
     <div style="padding: 0.5em 0 0 0">
161
     <div style="padding: 0.5em 0 0 0">
141
       <input type="hidden" name="node_id" value="${current_node.node_id}"/>
162
       <input type="hidden" name="node_id" value="${current_node.node_id}"/>
142
       <input type="hidden" name="data_content" id="current_node_textarea" />
163
       <input type="hidden" name="data_content" id="current_node_textarea" />
143
-      <input
144
-        type="text"
145
-        name="data_label"
146
-        value="${current_node.data_label}"
147
-        class="span4"
148
-        placeholder="${_('document title')}"
149
-      />
164
+      <label>
165
+        ${_('Title')}
166
+        <input
167
+          type="text"
168
+          name="data_label"
169
+          value="${current_node.data_label}"
170
+          class="span4"
171
+          placeholder="${_('document title')}"
172
+        />
173
+      </label>
150
     </div>
174
     </div>
151
     <div>
175
     <div>
152
       ${POD.RichTextEditor('current_node_textarea_wysiwyg', current_node.data_content)}
176
       ${POD.RichTextEditor('current_node_textarea_wysiwyg', current_node.data_content)}
172
   <h3 id="${psId}" title="Document ${poNode.node_id}: ${poNode.data_label}">
196
   <h3 id="${psId}" title="Document ${poNode.node_id}: ${poNode.data_label}">
173
     ${poNode.data_label}
197
     ${poNode.data_label}
174
     <sup class="label ${poNode.getStatus().css}" href="#">
198
     <sup class="label ${poNode.getStatus().css}" href="#">
199
+      <i class="${poNode.getStatus().icon_id}"></i>
175
       ${poNode.getStatus().label}
200
       ${poNode.getStatus().label}
176
     </sup>
201
     </sup>
202
+    
203
+    % if poNode.is_shared==False:
204
+      <sup class="label label-info" title="${_('This document is private')}">
205
+        <i class="fa fa-key"></i>
206
+        ${_('private')}
207
+      </sup>
208
+    % else:
209
+      <sup class="label label-warning" title="${_('This document is collaborative')}">
210
+        <i class="fa fa-group"></i>
211
+        ${_('collaborative')}
212
+      </sup>
213
+    % endif
214
+######
215
+##
216
+## 2014-05-06 - D.A. - The document is not yet internet-sharable
217
+##
218
+##    % if poNode.is_public==True:
219
+##      <sup class="label label-warning" href="#">
220
+##        <i class="fa fa-globe"></i>
221
+##        <span title="${_('This document is published through internet at %s')%poNode.public_url_key}">${_('shared')}</span>
222
+##      </sup>
223
+##    % endif
224
+
177
   </h3>
225
   </h3>
178
 </%def>
226
 </%def>
179
 
227
 
192
   </a>
240
   </a>
193
 </%def>
241
 </%def>
194
 
242
 
243
+
244
+<%def name="FirstTimeFakeDocument()">
245
+  <div id='application-document-panel' class="span5">
246
+    <div id='current-document-content' class="">
247
+      <p class="well">
248
+        <strong>${_('Welcome aboard')}</strong>
249
+        <i class="fa fa-smile-o fa-2x"></i>
250
+      </p>
251
+      ${_('<p>We suggest you to...<br/><br/></p>')|n}
252
+      <h4>
253
+        <i class="fa fa-angle-double-left fa-3x fa-fw pod-blue" style="vertical-align: middle"></i>
254
+        ${_('work on existing documents')}
255
+      </h4>
256
+      <p class="text-center">${_('or')}</p>
257
+      <h4 class="text-right">
258
+        ${_('create a new document')}
259
+        <i class="fa fa-angle-double-down fa-3x fa-fw pod-blue" style="vertical-align: middle"></i>
260
+      </h4>
261
+      <p class="pull-right">
262
+        <a href="#${h.ID.AddDocumentModalForm()}" role="button" class="btn btn-success" data-toggle="modal">
263
+          <i class="fa fa-plus"></i>
264
+          ${_('Create a new document')}
265
+        </a>
266
+      </p>
267
+  
268
+      ${DocumentEditModalDialog(None, None, tg.url('/api/create_document'), h.ID.AddDocumentModalForm(), 'Create your first document')}
269
+      <div style="clear: both;"></div>
270
+      <p class="alert alert-info" style="margin-top: 2em;">
271
+        <i class="fa fa-info-circle"></i> ${_('<strong>Note :</strong> You can even create a dummy document: you will be able to remove it later.')|n}
272
+      </p>
273
+    </div>
274
+    <script>
275
+    </script>
276
+  </div>
277
+</%def>
278
+
279
+<%def name="DocumentEditModalDialog(piParentNodeId, poNode, psPostUrl, psModalId, psTitle)">
280
+  <div
281
+    id="${psModalId}"
282
+    class="modal hide"
283
+    tabindex="-1"
284
+    role="dialog"
285
+    aria-labelledby="myModalLabel"
286
+    aria-hidden="true">
287
+    
288
+   <div class="modal-header">
289
+## MODAL HEADER
290
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
291
+      <h3 id="myModalLabel">${psTitle}</h3>
292
+## MODAL HEADER [END]
293
+    </div>
294
+
295
+    <div class="modal-body">
296
+## MODAL BODY
297
+      <form id='${psModalId}-form' method="GET" action="${psPostUrl}">
298
+        <div style="padding: 0.5em 0 0 0">
299
+          % if poNode!=None:
300
+            <input type="hidden" name="node_id" value="${poNode.node_id}"/>
301
+          % endif
302
+          <input type="hidden" name="parent_id" value="${piParentNodeId if piParentNodeId else 0}"/>
303
+          
304
+          <input type="hidden" name="data_content" id="${psModalId}-textarea" />
305
+          <input
306
+            type="text"
307
+            name="data_label"
308
+            value="${poNode.data_label if poNode!=None else ''}"
309
+            class="span4"
310
+            placeholder="${_('document title')}"
311
+          />
312
+        </div>
313
+        <div>
314
+          ${POD.RichTextEditor(psModalId+'-textarea-wysiwyg', poNode.data_content if poNode!=None else '')}
315
+        </div>
316
+      </form>
317
+
318
+## MODAL BODY [END]
319
+    </div>
320
+    
321
+    <div class="modal-footer">
322
+## MODAL FOOTER
323
+      <button class="btn" data-dismiss="modal" aria-hidden="true">
324
+        <i class="fa fa-ban"></i> ${_('Cancel')}
325
+      </button>
326
+      <button class="btn btn-success" id="${psModalId}-form-submit-button">
327
+        <i class="fa fa-check"></i> ${_('Save changes')}
328
+      </button>
329
+## MODAL FOOTER [END]
330
+      <script>
331
+        $('#${psModalId}-form-submit-button').click(function(){
332
+          $('#${psModalId}-textarea-wysiwyg').cleanHtml();
333
+          $('#${psModalId}-textarea').val($('#${psModalId}-textarea-wysiwyg').html());
334
+          $('#${psModalId}-form')[0].submit();
335
+        });
336
+      </script>
337
+    </div>
338
+  </div>
339
+</%def>
340
+
341
+
342
+<%def name="FileEditModalDialog(piParentNodeId, poNode, psPostUrl, psModalId, psTitle)">
343
+  <div
344
+    id="${psModalId}"
345
+    class="modal hide"
346
+    tabindex="-1"
347
+    role="dialog"
348
+    aria-labelledby="myModalLabel"
349
+    aria-hidden="true">
350
+    
351
+    <div class="modal-header">
352
+    ## MODAL HEADER
353
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
354
+      <h3 id="myModalLabel">${psTitle}</h3>
355
+    ## MODAL HEADER [END]
356
+    </div>
357
+
358
+    <div class="modal-body">
359
+    ## MODAL BODY
360
+      <form id='${psModalId}-form' method="POST" action="${psPostUrl}" enctype="multipart/form-data">
361
+          % if poNode!=None:
362
+            <input type="hidden" name="node_id" value="${poNode.node_id}"/>
363
+          % endif
364
+          <input type="hidden" name="parent_id" value="${piParentNodeId if piParentNodeId else 0}"/>
365
+          <input type="hidden" name="data_content" id="${psModalId}-textarea" />
366
+        <div>
367
+          <label>
368
+            ${_('Title')}
369
+            <input
370
+              type="text"
371
+              name="data_label"
372
+              value="${poNode.data_label if poNode!=None else ''}"
373
+              class="span4"
374
+              placeholder="${_('this field is optionnal')}"
375
+            />
376
+          </label>
377
+          <label>
378
+            ${_('Choose a file...')}
379
+            <input type="file" class="span4" placeholder="${_('choose a file...')}" name="data_file"/>
380
+          </label>
381
+          
382
+        </div>
383
+        <div>
384
+          <label>${_('File description (optionnal)')}</label>
385
+          ${POD.RichTextEditor(psModalId+'-textarea-wysiwyg', poNode.data_content if poNode!=None else '', '')}
386
+        </div>
387
+      </form>
388
+    ## MODAL BODY [END]
389
+    </div>
390
+    
391
+    <div class="modal-footer">
392
+    ## MODAL FOOTER
393
+      <button class="btn" data-dismiss="modal" aria-hidden="true">
394
+        <i class="fa fa-ban"></i> ${_('Cancel')}
395
+      </button>
396
+      <button class="btn btn-success" id="${psModalId}-form-submit-button">
397
+        <i class="fa fa-check"></i> ${_('Save changes')}
398
+      </button>
399
+      <script>
400
+        $('#${psModalId}-form-submit-button').click(function(){
401
+          $('#${psModalId}-textarea-wysiwyg').cleanHtml();
402
+          $('#${psModalId}-textarea').val($('#${psModalId}-textarea-wysiwyg').html());
403
+          $('#${psModalId}-form')[0].submit();
404
+        });
405
+      </script>
406
+    ## MODAL FOOTER [END]
407
+    </div>
408
+  </div>
409
+</%def>
410
+
411
+<%def name="EventEditModalDialog(piParentNodeId, poNode, psPostUrl, psModalId, psTitle)">
412
+  <div
413
+    id="${psModalId}"
414
+    class="modal hide"
415
+    tabindex="-1"
416
+    role="dialog"
417
+    aria-labelledby="myModalLabel"
418
+    aria-hidden="true">
419
+    
420
+    <div class="modal-header">
421
+    ## MODAL HEADER
422
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
423
+      <h3 id="myModalLabel">
424
+        ${psTitle}
425
+      </h3>
426
+    ## MODAL HEADER [END]
427
+    </div>
428
+
429
+    <div class="modal-body">
430
+    ###### MODAL BODY
431
+      <form id='${psModalId}-form' action='${psPostUrl}' method='POST'>
432
+        <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
433
+        <fieldset>
434
+          <label>
435
+            ${_('Event')}
436
+            <input type="text" name='data_label' placeholder="Event"/>
437
+          </label>
438
+          <label>
439
+            ${_('Date and time')}
440
+            <div class="datetime-picker-input-div input-append date">
441
+              <input name='data_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
442
+              <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
443
+            </div>
444
+          </label>
445
+          <label>
446
+            ${_('Event description:')}
447
+            <div>
448
+              <input type="hidden" name="data_content" id="${psModalId}-textarea" />
449
+              ${POD.RichTextEditor(psModalId+'-textarea-wysiwyg', poNode.data_content if poNode!=None else '', 'boldanditalic')}
450
+            </div>
451
+          </label>
452
+        </fieldset>
453
+      </form>
454
+    ###### MODAL BODY [END]
455
+    </div>
456
+    
457
+    <div class="modal-footer">
458
+    ###### MODAL FOOTER
459
+      <button class="btn" data-dismiss="modal" aria-hidden="true">
460
+        <i class="fa fa-ban"></i> ${_('Cancel')}
461
+      </button>
462
+      <button class="btn btn-success" id="${psModalId}-form-submit-button">
463
+        <i class="fa fa-check"></i> ${_('Save changes')}
464
+      </button>
465
+      <script>
466
+        $('#${psModalId}-form-submit-button').click(function(){
467
+          $('#${psModalId}-textarea-wysiwyg').cleanHtml();
468
+          $('#${psModalId}-textarea').val($('#${psModalId}-textarea-wysiwyg').html());
469
+          $('#${psModalId}-form')[0].submit();
470
+        });
471
+      </script>
472
+    ###### MODAL FOOTER [END]
473
+    </div>
474
+  </div>
475
+</%def>
476
+
477
+<%def name="ContactEditModalDialog(piParentNodeId, poNode, psPostUrl, psModalId, psTitle)">
478
+  <div
479
+    id="${psModalId}"
480
+    class="modal hide"
481
+    tabindex="-1"
482
+    role="dialog"
483
+    aria-labelledby="myModalLabel"
484
+    aria-hidden="true">
485
+    
486
+    <div class="modal-header">
487
+    ## MODAL HEADER
488
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
489
+      <h3 id="myModalLabel">${psTitle}</h3>
490
+    ## MODAL HEADER [END]
491
+    </div>
492
+
493
+    <div class="modal-body">
494
+    ## MODAL BODY
495
+      <form id='${psModalId}-form' method="POST" action="${psPostUrl}">
496
+          % if poNode!=None:
497
+            <input type="hidden" name="node_id" value="${poNode.node_id}"/>
498
+          % endif
499
+          <input type="hidden" name="parent_id" value="${piParentNodeId if piParentNodeId else 0}"/>
500
+          <input type="hidden" name="data_content" id="${psModalId}-textarea" />
501
+        <div>
502
+          <label>
503
+            ${_('Contact name and firstname')}
504
+            <input
505
+              type="text"
506
+              name="data_label"
507
+              value="${poNode.data_label if poNode!=None else ''}"
508
+              class="span4"
509
+              placeholder="${_('name, firstname, title...')}"
510
+            />
511
+          </label>
512
+        </div>
513
+        <div>
514
+          <label>${_('Address, phone, email, company...')}</label>
515
+          ${POD.RichTextEditor(psModalId+'-textarea-wysiwyg', poNode.data_content if poNode!=None else '', 'boldanditalic')}
516
+        </div>
517
+      </form>
518
+    ## MODAL BODY [END]
519
+    </div>
520
+    
521
+    <div class="modal-footer">
522
+    ## MODAL FOOTER
523
+      <button class="btn" data-dismiss="modal" aria-hidden="true">
524
+        <i class="fa fa-ban"></i> ${_('Cancel')}
525
+      </button>
526
+      <button class="btn btn-success" id="${psModalId}-form-submit-button">
527
+        <i class="fa fa-check"></i> ${_('Save changes')}
528
+      </button>
529
+      <script>
530
+        $('#${psModalId}-form-submit-button').click(function(){
531
+          $('#${psModalId}-textarea-wysiwyg').cleanHtml();
532
+          $('#${psModalId}-textarea').val($('#${psModalId}-textarea-wysiwyg').html());
533
+          $('#${psModalId}-form')[0].submit();
534
+        });
535
+      </script>
536
+    ## MODAL FOOTER [END]
537
+    </div>
538
+  </div>
539
+</%def>

+ 41 - 297
pboard/pboard/templates/document.mak Parādīt failu

1
 <%inherit file="local:templates.master"/>
1
 <%inherit file="local:templates.master"/>
2
 <%namespace name="POD" file="pboard.templates.pod"/>
2
 <%namespace name="POD" file="pboard.templates.pod"/>
3
 <%namespace name="DOC" file="pboard.templates.document-widgets"/>
3
 <%namespace name="DOC" file="pboard.templates.document-widgets"/>
4
+<%namespace name="DOCTABS" file="pboard.templates.document-widgets-tabs"/>
4
 
5
 
5
 <%def name="title()">
6
 <%def name="title()">
6
-pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id} / ${current_node.getStatus().label}]
7
+  % if current_node!=None:
8
+    pod :: document ${current_node.getTruncatedLabel(40)} [#${current_node.node_id} / ${current_node.getStatus().label}]
9
+  % else:
10
+    pod :: document root
11
+  % endif
7
 </%def>
12
 </%def>
8
 
13
 
9
 <%def name="node_treeview(node_list, indentation=-1)">
14
 <%def name="node_treeview(node_list, indentation=-1)">
10
   % if indentation==-1:
15
   % if indentation==-1:
11
     <div id='pod-menu-item-0' class="pod-toolbar-parent" style="padding-left: 0.5em; position: relative;">
16
     <div id='pod-menu-item-0' class="pod-toolbar-parent" style="padding-left: 0.5em; position: relative;">
12
       <a class="toggle-child-menu-items"><i class='fa fa-folder-open'></i></a>
17
       <a class="toggle-child-menu-items"><i class='fa fa-folder-open'></i></a>
13
-      <a href="?node=0" title="${_('Root')}">
18
+      <a href="${tg.url('/document')}" title="${_('Root')}">
14
         ${_('Root')}
19
         ${_('Root')}
15
       </a>
20
       </a>
16
-      <div class="pod-toolbar">
17
-        <a href="${tg.url('/api/create_document?parent_id=0')}" title="${_('Add child document')}"><i class="fa fa-plus-circle"></i></a>
18
-      </div>
19
     </div>
21
     </div>
20
     <div id="pod-menu-item-0-children">${node_treeview(node_list, 0)}</div>
22
     <div id="pod-menu-item-0-children">${node_treeview(node_list, 0)}</div>
21
     
23
     
22
   % else:
24
   % else:
25
+    % if len(node_list)<=0 and indentation==0:
26
+      <p class="pod-grey">${_('You have no document yet.')}</p>
27
+    % endif
28
+    
23
     % if len(node_list)>0:
29
     % if len(node_list)>0:
24
       % for node in node_list:
30
       % for node in node_list:
25
-        <div id='pod-menu-item-${node.node_id}' class="pod-toolbar-parent ${'pod-status-active' if node.node_id==current_node.node_id else ''}" style="padding-left: ${(indentation+2)*0.5}em; position: relative;">
31
+        <div id='pod-menu-item-${node.node_id}' class="pod-toolbar-parent ${'pod-status-active' if current_node!=None and node.node_id==current_node.node_id else ''}" style="padding-left: ${(indentation+2)*0.5}em; position: relative;">
26
           <a class="toggle-child-menu-items"><i class='${node.getIconClass()}'></i></a>
32
           <a class="toggle-child-menu-items"><i class='${node.getIconClass()}'></i></a>
27
           <a href="${tg.url('/document/%s'%(node.node_id))}" title="${node.data_label}">
33
           <a href="${tg.url('/document/%s'%(node.node_id))}" title="${node.data_label}">
28
             % if node.getStatus().status_family=='closed' or node.getStatus().status_family=='invisible':
34
             % if node.getStatus().status_family=='closed' or node.getStatus().status_family=='invisible':
36
           <div class="pod-toolbar">
42
           <div class="pod-toolbar">
37
             <a href="${tg.url('/api/move_node_upper?node_id=%i'%(node.node_id))}" title="${_('Move up')}"><i class="fa fa-arrow-up"></i></a>
43
             <a href="${tg.url('/api/move_node_upper?node_id=%i'%(node.node_id))}" title="${_('Move up')}"><i class="fa fa-arrow-up"></i></a>
38
             <a href="${tg.url('/api/move_node_lower?node_id=%i'%(node.node_id))}" title="${_('Move down')}"><i class="fa fa-arrow-down"></i></a>
44
             <a href="${tg.url('/api/move_node_lower?node_id=%i'%(node.node_id))}" title="${_('Move down')}"><i class="fa fa-arrow-down"></i></a>
39
-            <a href="${tg.url('/api/create_document?parent_id=%i'%(node.node_id))}" title="${_('Add child document')}"><i class="fa  fa-plus-circle"></i></a>
40
           </div>
45
           </div>
41
           <div class="pod-status ${node.getStatus().css}" title='${node.getStatus().label}'>
46
           <div class="pod-status ${node.getStatus().css}" title='${node.getStatus().label}'>
42
              <i class='${node.getStatus().icon}'></i>
47
              <i class='${node.getStatus().icon}'></i>
80
     </div>
85
     </div>
81
     <div id='application-main-panel' class="span9">
86
     <div id='application-main-panel' class="span9">
82
 
87
 
88
+      % if current_node==None:
89
+        <div class="row">
90
+          ${DOC.FirstTimeFakeDocument()}
91
+        </div>
92
+        
93
+      % else:
83
       <div class="row">
94
       <div class="row">
84
         <div id='application-document-panel' class="span5">
95
         <div id='application-document-panel' class="span5">
85
           <div id='current-document-content' class="">
96
           <div id='current-document-content' class="">
96
           ${DOC.EditForm(current_node)}
107
           ${DOC.EditForm(current_node)}
97
         </div>
108
         </div>
98
         <div id='application-metadata-panel' class="span4">
109
         <div id='application-metadata-panel' class="span4">
110
+          ######
111
+          ##
112
+          ## HERE WE INCLUDE ALL MODAL DIALOG WHICH WILL BE ACCESSIBLE THROUGH TABS OR MENU
113
+          ##
114
+          ${DOC.DocumentEditModalDialog(current_node.node_id, None, tg.url('/api/create_document'), h.ID.AddDocumentModalForm(current_node), _('New Sub-document'))}
115
+          ${DOC.EventEditModalDialog(current_node.node_id, None, tg.url('/api/create_event'), h.ID.AddEventModalForm(current_node), _('Add an event'))}
116
+          ${DOC.ContactEditModalDialog(current_node.node_id, None, tg.url('/api/create_contact'), h.ID.AddContactModalForm(current_node), _('Add a new contact'))}
117
+          ${DOC.FileEditModalDialog(current_node.node_id, None, tg.url('/api/create_file'), h.ID.AddFileModalForm(current_node), _('Add a new file'))}
118
+          
99
           <div class="tabbable">
119
           <div class="tabbable">
100
-            <ul class="nav nav-tabs" style="margin-bottom: 0.5em;">
120
+            <ul class="nav nav-tabs" style="margin-bottom: 0em;">
101
                 <li>${DOC.MetadataTab('#subdocuments', 'tab', _('Subdocuments'), 'fa-file-text-o', current_node.getChildren())}</li>
121
                 <li>${DOC.MetadataTab('#subdocuments', 'tab', _('Subdocuments'), 'fa-file-text-o', current_node.getChildren())}</li>
102
                 <li class="active">${DOC.MetadataTab('#events', 'tab', _('Calendar'), 'fa-calendar', current_node.getEvents())}</li>
122
                 <li class="active">${DOC.MetadataTab('#events', 'tab', _('Calendar'), 'fa-calendar', current_node.getEvents())}</li>
103
                 <li>${DOC.MetadataTab('#contacts', 'tab', _('Address book'), 'fa-user', current_node.getContacts())}</li>
123
                 <li>${DOC.MetadataTab('#contacts', 'tab', _('Address book'), 'fa-user', current_node.getContacts())}</li>
105
                 <li>${DOC.MetadataTab('#files', 'tab', _('Attachments'), 'fa-paperclip', current_node.getFiles())}</li>
125
                 <li>${DOC.MetadataTab('#files', 'tab', _('Attachments'), 'fa-paperclip', current_node.getFiles())}</li>
106
                 <li class="pull-right">${DOC.MetadataTab('#accessmanagement', 'tab', _('Access Management'), 'fa-key', [])}</li>
126
                 <li class="pull-right">${DOC.MetadataTab('#accessmanagement', 'tab', _('Access Management'), 'fa-key', [])}</li>
107
             </ul>
127
             </ul>
128
+            ################################
129
+            ##
130
+            ## PANEL SHOWING ASSOCIATED DATA AND METADATA
131
+            ##
132
+            ################################
108
             <div class="tab-content">
133
             <div class="tab-content">
109
-                ################################
110
-                ##
111
-                ## PANEL SHOWING LIST OF SUB DOCUMENTS
112
-                ##
113
-                ################################
114
-                <!-- DEBUG - D.A. - 2013-11-07 - Not using tags for th moment -->
115
-                <div class="tab-pane" id="subdocuments">
116
-                  <p><strong>Sub-documents</strong></p> 
117
-                % if len(current_node.getChildren())<=0:
118
-                  <p class="pod-grey">
119
-                    ${_("There is currently no child documents.")}<br/>
120
-                  </p>
121
-                  <p>
122
-                    
123
-                    <a class="btn btn-success btn-small" href="${tg.url('/api/create_document?parent_id=%i'%current_node.node_id)}">
124
-                      <i class="fa fa-plus"></i> ${_("Add one")}
125
-                    </a>
126
-                  </p>
127
-                % else:
128
-                  <p>
129
-                    <a class="btn btn-success btn-small" href="${tg.url('/api/create_document?parent_id=%i'%current_node.node_id)}">
130
-                      <i class="fa fa-plus"></i> ${_("Add one")}
131
-                    </a>
132
-                  </p>
133
-
134
-                  <div>
135
-                    % for subnode in current_node.getChildren():
136
-                      <p style="list-style-type:none;">
137
-                        <i class="fa-fw ${subnode.getIconClass()}"></i>
138
-                          <a href="${tg.url('/document/%i'%subnode.node_id)}">
139
-                            ${subnode.data_label}
140
-                          </a>
141
-                      </p>
142
-                    % endfor
143
-                  </div>
144
-                % endif
145
-                </div>
146
-                
147
-                ################################
148
-                ##
149
-                ## PANEL SHOWING LIST OF EVENTS
150
-                ##
151
-                ################################
152
-                <div class="tab-pane active" id="events">
153
-                  <p><strong>Calendar</strong></p> 
154
-                % if len(current_node.getEvents())<=0:
155
-                  <p class="pod-grey">${_("The calendar is empty.")}<br/></p>
156
-                  <p>${POD.AddButton('current-document-add-event-button', True, _(' Add first event'))}</p>
157
-                % else:
158
-                  <p>${POD.AddButton('current-document-add-event-button', True, _(' Add an event'))}</p>
159
-                % endif
160
-                
161
-                  <form style='display: none;' id='current-document-add-event-form' action='${tg.url('/api/create_event')}' method='post' class="well">
162
-                    <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
163
-                    <fieldset>
164
-                      <legend>Add an event</legend>
165
-                      <label>
166
-                        <input type="text" name='data_label' placeholder="Event"/>
167
-                      </label>
168
-                      <label>
169
-                        <div class="datetime-picker-input-div input-append date">
170
-                          <input name='data_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
171
-                          <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
172
-                        </div>
173
-                      </label>
174
-                      <label>
175
-                        <div>
176
-                          <input type="hidden" id="add_event_data_content_textarea" name='data_content' />
177
-                          ${POD.RichTextEditor('add_event_data_content_textarea_wysiwyg', '', 'boldanditalic|undoredo|fullscreen')}
178
-                        </div>
179
-                      </label>
180
-                      <label class="checkbox">
181
-                        <input disabled name='add_reminder' type="checkbox"> add a reminder
182
-                      </label>
183
-                      <label>
184
-                        <div class="datetime-picker-input-div input-append date">
185
-                          <input disabled name='data_reminder_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
186
-                          <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
187
-                        </div>
188
-                      </label>
189
-
190
-
191
-                      ${POD.CancelButton('current-document-add-event-cancel-button', True)}
192
-                      ${POD.SaveButton('current-document-add-event-save-button', True)}
193
-                    </fieldset>
194
-                  </form>
195
-
196
-                % if len(current_node.getEvents())>0:
197
-                  <table class="table table-striped table-hover table-condensed">
198
-                    <thead>
199
-                      <tr>
200
-                        <th>Date</th>
201
-                        <th>Time</th>
202
-                        <th>
203
-                          Event
204
-                        </th>
205
-                        <th>
206
-                          <a href="" title="Add an event"><i class="icon-g-plus"></i></a>
207
-                        </th>
208
-                      </tr>
209
-                    </thead>
210
-                    % for event in current_node.getEvents():
211
-                      <tr class="item-with-data-popoverable" data-content="${event.data_content}" rel="popover" data-placement="left" data-trigger="hover">
212
-                        <td>${event.getFormattedDate(event.data_datetime)}</td>
213
-                        <td>${event.getFormattedTime(event.data_datetime)}</td>
214
-                        <td>${event.data_label}</td>
215
-                      </tr>
216
-  ## FIXME                    <script>
217
-  ##                      $('.item-with-data-popoverable').popover({ html: true});
218
-  ##                    </script>
219
-
220
-                    % endfor
221
-                  </table>
222
-                % endif
223
-                </div>
224
-                ##############################
225
-                ## 
226
-                ## PANEL SHOWING LIST OF CONTACTS
227
-                ##
228
-                ##############################
229
-                <div class="tab-pane" id="contacts">
230
-                  <p><strong>${_('Address book')}</strong></p> 
231
-                % if len(current_node.getContacts())<=0:
232
-                  <p class="pod-grey">${_("The address book is empty.")}<br/></p>
233
-                  <p>${POD.AddButton('current-document-add-contact-button', True, _(' Add first contact'), True)}</p>
234
-                % else:
235
-                  <p>${POD.AddButton('current-document-add-contact-button', True, _(' Add a contact'))}</p>
236
-                % endif
237
-
238
-                  <!-- ADD CONTACT FORM -->
239
-                  <form style='display: none;' id='current-document-add-contact-form' action='${tg.url('/api/create_contact')}' method='post' class="well">
240
-                    <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
241
-                    <fieldset>
242
-                      <legend>${_('Add a contact')}</legend>
243
-                      <label>
244
-                        <input type="text" name='data_label' placeholder="Title"/>
245
-                      </label>
246
-                      <label>
247
-                        <div>
248
-                          <input type="hidden" id="add_contact_data_content_textarea" name='data_content' />
249
-                          ${POD.RichTextEditor('add_contact_data_content_textarea_wysiwyg', '', 'boldanditalic|undoredo|fullscreen')}
250
-                        </div>
251
-                      </label>
252
-                      ${POD.CancelButton('current-document-add-contact-cancel-button', True)}
253
-                      ${POD.SaveButton('current-document-add-contact-save-button', True)}
254
-                    </fieldset>
255
-                  </form>
256
-
257
-                  <!-- LIST OF CONTACT NODES -->
258
-                  % for contact in current_node.getContacts():
259
-                    <div class="well">
260
-                      <legend class="text-info">
261
-                        ${contact.data_label}
262
-                        ## TODO - 2013-11-20 - Use the right form in order to update meta-data
263
-                        <a class="pull-right" href="${tg.url('/document/%i'%contact.node_id)}"><i class="fa fa-edit"></i></a>
264
-                      </legend>
265
-                      
266
-                      <div>
267
-                        ## FIXME - D.A. - 2013-11-15 - Implement localisation stuff <a style='float: right;' href="" title='${_('Search on google maps')}'><i class='icon-g-google-maps'></i></a>
268
-                        ${contact.data_content|n}
269
-                      </div>
270
-                    </div>
271
-                  % endfor
272
-                </div>
273
-                ################################
274
-                ##
275
-                ## PANEL SHOWING LIST OF COMMENTS
276
-                ##
277
-                ################################
278
-                <div class="tab-pane" id="comments">
279
-                  <p><strong>${_('Comment thread')}</strong></p> 
280
-                % if len(current_node.getComments())<=0:
281
-                  <p class="pod-grey">${_("The comment thread is empty.")}<br/></p>
282
-                  <p>${POD.AddButton('current-document-add-comment-button', True, _('Add first comment'), True)}</p>
283
-                % else:
284
-                  <p>${POD.AddButton('current-document-add-comment-button', True, _('Add a comment'))}</p>
285
-                % endif
286
-
287
-                  <!-- ADD COMMENT FORM -->
288
-                  <form style='display: none;' id='current-document-add-comment-form' action='${tg.url('/api/create_comment')}' method='post' class="well">
289
-                    <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
290
-                    <fieldset>
291
-                      <legend>${_('Add a comment')}</legend>
292
-                      <label>
293
-                        <input type="text" name='data_label' placeholder="Title"/>
294
-                      </label>
295
-                      <label>
296
-                        <div>
297
-                          <input type="hidden" id="add_comment_data_content_textarea" name='data_content' />
298
-                          ${POD.RichTextEditor('add_comment_data_content_textarea_wysiwyg', '', 'boldanditalic|undoredo|fullscreen')}
299
-                        </div>
300
-                      </label>
301
-                      ${POD.CancelButton('current-document-add-comment-cancel-button', True)}
302
-                      ${POD.SaveButton('current-document-add-comment-save-button', True)}
303
-                    </fieldset>
304
-                  </form>
305
-
306
-                  <!-- LIST OF COMMENTS -->
307
-                % if len(current_node.getComments())>0:
308
-                  <table class="table table-striped table-hover table-condensed">
309
-                    % for comment in current_node.getComments():
310
-                      <tr title="Last updated: ${comment.updated_at}">
311
-                        <td>
312
-                          <i>The ${comment.getFormattedDate(comment.updated_at)} at ${comment.getFormattedTime(comment.updated_at)}: </i><br/>
313
-                          <b>${comment.data_label}</b>
314
-                          ## TODO - 2013-11-20 - Use the right form in order to update meta-data
315
-                          <a class="pull-right" href="${tg.url('/document/%i'%comment.node_id)}"><i class="fa fa-edit"></i></a>
316
-                          <br/>
317
-                          <p>
318
-                            ${comment.data_content|n}
319
-                          </p>
320
-                        </td>
321
-                      </tr>
322
-                    % endfor
323
-                  </table>
324
-                % endif
325
-                </div>
326
-                ################################
327
-                ##
328
-                ## PANEL SHOWING LIST OF FILES
329
-                ##
330
-                ################################
331
-                <div class="tab-pane" id="files">
332
-                  <p><strong>${_('Attachments')}</strong></p> 
333
-                % if len(current_node.getFiles())<=0:
334
-                  <p class="pod-grey">${_("There is currently no attachment.")}<br/></p>
335
-                  <p>${POD.AddButton('current-document-add-file-button', True, _(' Attach first file'))}</p>
336
-                % else:
337
-                  <p>${POD.AddButton('current-document-add-file-button', True, _(' Attach a file'))}</p>
338
-                % endif
339
-
340
-                  <!-- ADD FILE FORM -->
341
-                  <form style='display: none;' id='current-document-add-file-form' enctype="multipart/form-data" action='${tg.url('/api/create_file')}' method='post' class="well">
342
-                    <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
343
-                    <fieldset>
344
-                      <legend>${_('Add a file')}</legend>
345
-                      <label>
346
-                        <input type="text" name='data_label' placeholder="Title"/>
347
-                      </label>
348
-                      <label>
349
-                        <input type="file" name='data_file' placeholder="choose a file..."/>
350
-                      </label>
351
-                      <label>
352
-                        <div>
353
-                          <input type="hidden" id="add_file_data_content_textarea" name='data_content' />
354
-                          ${POD.RichTextEditor('add_file_data_content_textarea_wysiwyg', '', 'boldanditalic|undoredo|fullscreen')}
355
-                        </div>
356
-                      </label>
357
-                      ${POD.CancelButton('current-document-add-file-cancel-button', True)}
358
-                      ${POD.SaveButton('current-document-add-file-save-button', True)}
359
-                    </fieldset>
360
-                  </form>
361
-
362
-                  <!-- LIST OF FILES -->
363
-                  <div>
364
-                % if len(current_node.getFiles())>0:
365
-                    % for current_file in current_node.getFiles():
366
-                      <p style="list-style-type:none; margin-bottom: 0.5em;">
367
-                        <i class="fa fa-paperclip"></i>
368
-                        <a
369
-                          href="${tg.url('/document/%i'%current_file.node_id)}"
370
-                          title="${_('View the attachment')}: ${current_file.data_label}"
371
-                        >
372
-                          ${current_file.getTruncatedLabel(50)}
373
-                        </a>
374
-                        <a
375
-                          class="pull-right"
376
-                          href="${tg.url('/api/get_file_content/%s'%(current_file.node_id))}"
377
-                          title="${_('View the attachment')}"
378
-                        >
379
-                          <i class="fa fa-download"></i>
380
-                        </a>
381
-                      </p>
382
-                    % endfor
383
-                % endif
384
-                  </div>
385
-                </div>
386
-                
387
-                
388
-                ################################
389
-                ##
390
-                ## PANEL SHOWING ACCESS MANAGEMENT
391
-                ##
392
-                ################################
393
-                <div class="tab-pane" id="accessmanagement">
394
-                  blabla
395
-                </div>
396
-              </div>
134
+              <div class="tab-pane" id="subdocuments">${DOCTABS.SubdocumentContent(current_node)}</div>
135
+              <div class="tab-pane active" id="events">${DOCTABS.EventTabContent(current_node)}</div>
136
+              <div class="tab-pane" id="contacts">${DOCTABS.ContactTabContent(current_node)}</div>
137
+              <div class="tab-pane" id="comments">${DOCTABS.CommentTabContent(current_node)}</div>
138
+              <div class="tab-pane" id="files">${DOCTABS.FileTabContent(current_node)}</div>
139
+              <div class="tab-pane" id="accessmanagement">${DOCTABS.AccessManagementTab(current_node)}</div>
397
             </div>
140
             </div>
398
           </div>
141
           </div>
399
         </div>
142
         </div>
400
       </div>
143
       </div>
144
+      % endif
401
     </div>
145
     </div>
402
   </div>
146
   </div>
403
-
147
+</div>

+ 6 - 2
pboard/pboard/templates/master.mak Parādīt failu

55
 
55
 
56
 <%def name="footer()">
56
 <%def name="footer()">
57
   <div class="footer hidden-tablet hidden-phone text-center">
57
   <div class="footer hidden-tablet hidden-phone text-center">
58
-    <p class="pod-blue"><i>${_("Using pod, you can: search a job, manage projects, track and manage clients and prospects, document processes and knowledge, ...")}</i></p>
58
+    <p class="pod-blue">
59
+      <i>${_("collaborative work  ♦  improved efficiency  ♦  full traceability")}</i>
60
+      <br/>
61
+      this is pod
62
+    </p>
59
     <hr style="width: 50%; margin: 0.5em auto;"/>
63
     <hr style="width: 50%; margin: 0.5em auto;"/>
60
     <p>Copyright &copy; 2013 - ${h.current_year()} pod project.</p>
64
     <p>Copyright &copy; 2013 - ${h.current_year()} pod project.</p>
61
   </div>
65
   </div>
95
             </li>
99
             </li>
96
 
100
 
97
             <li title="Rebuild document index">
101
             <li title="Rebuild document index">
98
-            % if current_node is UNDEFINED:
102
+            % if current_node is UNDEFINED or current_node==None:
99
               <a href="${tg.url('/api/reindex_nodes?back_to_node_id=0')}"><i class="fa fa-refresh"></i></a>
103
               <a href="${tg.url('/api/reindex_nodes?back_to_node_id=0')}"><i class="fa fa-refresh"></i></a>
100
             % else:
104
             % else:
101
               <a href="${tg.url('/api/reindex_nodes?back_to_node_id=%i'%(current_node.node_id))}"><i class="fa fa-refresh"></i></a>
105
               <a href="${tg.url('/api/reindex_nodes?back_to_node_id=%i'%(current_node.node_id))}"><i class="fa fa-refresh"></i></a>

+ 19 - 3
pboard/pboard/templates/pod.mak Parādīt failu

35
   <button id='${piId}' type="button" class="${psButtonCssClass}" title="${psButtonTitle}"><i class="${psButtonIcon}"></i>${'' if (pbWithLabel==False) else ' %s'%(psButtonLabel)}</button>
35
   <button id='${piId}' type="button" class="${psButtonCssClass}" title="${psButtonTitle}"><i class="${psButtonIcon}"></i>${'' if (pbWithLabel==False) else ' %s'%(psButtonLabel)}</button>
36
 </%def>
36
 </%def>
37
 
37
 
38
-<%def name="SaveButton(piId, pbWithLabel=False)" >
39
-  ${Button(piId, pbWithLabel, 'btn btn-small btn-success', _('Save'), ' icon-g-ok-2 icon-g-white', _('Save'))}
38
+<%def name="SaveButton(piId, pbWithLabel=False, psLabel='Save')" >
39
+## FIXME - Make the default value use _() in order to be translated
40
+  ${Button(piId, pbWithLabel, 'btn btn-small btn-success', psLabel, ' icon-g-ok-2 icon-g-white', psLabel)}
40
 </%def>
41
 </%def>
41
 <%def name="EditButton(piId, pbWithLabel=False)" >
42
 <%def name="EditButton(piId, pbWithLabel=False)" >
42
   ${Button(piId, pbWithLabel, 'btn btn-small', _('Edit'), 'fa fa-edit', _('Edit'))}
43
   ${Button(piId, pbWithLabel, 'btn btn-small', _('Edit'), 'fa fa-edit', _('Edit'))}
51
   ${Button(piId, pbWithLabel, 'btn btn-small', psLabel or _('New'), 'fa fa-plus', psLabel or _('New'))}
52
   ${Button(piId, pbWithLabel, 'btn btn-small', psLabel or _('New'), 'fa fa-plus', psLabel or _('New'))}
52
 % endif
53
 % endif
53
 </%def>
54
 </%def>
55
+
56
+###
57
+##
58
+## GREEN CALL-TO-ACTION BUTTONS IN THE INTERFACE
59
+##
60
+##
61
+<%def name="OpenModalButton(psModalAnchor, psLabel)">
62
+  <a href="#${psModalAnchor}" role="button" class="btn btn-success btn-small" data-toggle="modal"><i class="fa fa-plus"></i> ${psLabel}</a>
63
+</%def>
64
+<%def name="OpenLinkButton(psModalAnchor, psLabel)">
65
+  <a href="#${psModalAnchor}" class="btn btn-success btn-small"><i class="fa fa-plus"></i> ${psLabel}</a>
66
+</%def>
67
+## END OF GREEN CALL-TO-ACTION BUTTONS
68
+
54
 <%def name='Badge(psLabel, psCssClass="")'>
69
 <%def name='Badge(psLabel, psCssClass="")'>
55
   <span class='badge ${psCssClass}'>${psLabel}</span>
70
   <span class='badge ${psCssClass}'>${psLabel}</span>
56
 </%def>
71
 </%def>
153
       % endif
168
       % endif
154
       % if psMenuOptions.find('fullscreen')>=0:
169
       % if psMenuOptions.find('fullscreen')>=0:
155
         <div class="btn-group">
170
         <div class="btn-group">
156
-          <a class="btn btn-primary pod-toggle-full-screen-button"
171
+          <a class="btn btn-success pod-toggle-full-screen-button"
157
              title="Toggle fullscreen"
172
              title="Toggle fullscreen"
158
              onclick="toggleFullScreen('#${psRichTextEditorNodeId}-widget', '#${psRichTextEditorNodeId}-widget-inner')"
173
              onclick="toggleFullScreen('#${psRichTextEditorNodeId}-widget', '#${psRichTextEditorNodeId}-widget-inner')"
159
             >
174
             >
207
 
222
 
208
 </%def>
223
 </%def>
209
 
224
 
225
+<%def name="AddDocumentModalFormId(poNode)">add-document-modal-form-${poNode.node_id if poNode!=None else ''}</%def>