Browse Source

massive refactoring for better understanding of the code and sql schema. minor bugfixes + css improvments

Damien ACCORSI 9 years ago
parent
commit
f17975039f
37 changed files with 935 additions and 1946 deletions
  1. 273 0
      doc/database/tracim-init-database.new.sql
  2. 1 1
      install/requirements.txt
  3. 19 3
      tracim/tracim/config/app_cfg.py
  4. 32 32
      tracim/tracim/controllers/__init__.py
  5. 4 4
      tracim/tracim/controllers/admin/user.py
  6. 9 9
      tracim/tracim/controllers/admin/workspace.py
  7. 46 46
      tracim/tracim/controllers/content.py
  8. 0 1
      tracim/tracim/controllers/help.py
  9. 5 5
      tracim/tracim/controllers/workspace.py
  10. BIN
      tracim/tracim/i18n/fr/LC_MESSAGES/tracim.mo
  11. 150 187
      tracim/tracim/i18n/fr/LC_MESSAGES/tracim.po
  12. 3 0
      tracim/tracim/lib/__init__.py
  13. 0 65
      tracim/tracim/lib/auth.py
  14. 0 4
      tracim/tracim/lib/base.py
  15. 63 68
      tracim/tracim/lib/content.py
  16. 0 432
      tracim/tracim/lib/dbapi.py
  17. 13 70
      tracim/tracim/lib/helpers.py
  18. 6 144
      tracim/tracim/lib/user.py
  19. 1 1
      tracim/tracim/lib/userworkspace.py
  20. 3 3
      tracim/tracim/lib/workspace.py
  21. 1 1
      tracim/tracim/model/__init__.py
  22. 22 62
      tracim/tracim/model/auth.py
  23. 102 539
      tracim/tracim/model/data.py
  24. 134 229
      tracim/tracim/model/serializers.py
  25. 14 9
      tracim/tracim/public/assets/css/dashboard.css
  26. 1 1
      tracim/tracim/templates/debug/iconset-tango.mak
  27. 3 2
      tracim/tracim/templates/master_anonymous.mak
  28. 6 5
      tracim/tracim/templates/master_authenticated.mak
  29. 1 0
      tracim/tracim/templates/pod.mak
  30. 1 1
      tracim/tracim/templates/user_get_me.mak
  31. 6 6
      tracim/tracim/templates/user_workspace_folder_file_get_one.mak
  32. 1 1
      tracim/tracim/templates/user_workspace_folder_get_one.mak
  33. 2 2
      tracim/tracim/templates/user_workspace_folder_page_get_one.mak
  34. 8 8
      tracim/tracim/templates/user_workspace_folder_thread_get_one.mak
  35. 1 1
      tracim/tracim/templates/user_workspace_get_one.mak
  36. 2 2
      tracim/tracim/tests/models/test_auth.py
  37. 2 2
      tracim/tracim/websetup/bootstrap.py

+ 273 - 0
doc/database/tracim-init-database.new.sql View File

@@ -0,0 +1,273 @@
1
+SET statement_timeout = 0;
2
+SET client_encoding = 'UTF8';
3
+SET standard_conforming_strings = on;
4
+SET check_function_bodies = false;
5
+SET client_min_messages = warning;
6
+
7
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
8
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
9
+SET search_path = public, pg_catalog;
10
+
11
+CREATE FUNCTION update_node() RETURNS trigger
12
+    LANGUAGE plpgsql
13
+    AS $$
14
+BEGIN
15
+INSERT INTO content_revisions (content_id, parent_id, type, created, updated, 
16
+       label, description, status, 
17
+       file_name, file_content, file_mimetype, parent_tree_path, 
18
+       node_depth, owner_id, revision_id, workspace_id, is_deleted, is_archived, properties, revision_type) VALUES (NEW.content_id, NEW.parent_id, NEW.type, NEW.created, NEW.updated, NEW.label, NEW.description, NEW.status, NEW.file_name, NEW.file_content, NEW.file_mimetype, NEW.parent_tree_path, NEW.node_depth, NEW.owner_id, nextval('seq__content_revisions__revision_id'), NEW.workspace_id, NEW.is_deleted, NEW.is_archived, NEW.properties, NEW.revision_type);
19
+return new;
20
+END;
21
+$$;
22
+
23
+CREATE FUNCTION set_created() RETURNS trigger
24
+    LANGUAGE plpgsql
25
+    AS $$
26
+BEGIN
27
+    NEW.created = CURRENT_TIMESTAMP;
28
+    NEW.updated = CURRENT_TIMESTAMP;
29
+    RETURN NEW;
30
+END;
31
+$$;
32
+
33
+CREATE FUNCTION set_updated() RETURNS trigger
34
+    LANGUAGE plpgsql
35
+    AS $$
36
+BEGIN
37
+    NEW.updated = CURRENT_TIMESTAMP;
38
+    RETURN NEW;
39
+END;
40
+$$;
41
+
42
+SET default_tablespace = '';
43
+SET default_with_oids = false;
44
+
45
+CREATE TABLE migrate_version (
46
+    version_num character varying(32) NOT NULL
47
+);
48
+
49
+CREATE TABLE groups (
50
+    group_id integer NOT NULL,
51
+    group_name character varying(16) NOT NULL,
52
+    display_name character varying(255),
53
+    created timestamp without time zone
54
+);
55
+
56
+CREATE SEQUENCE seq__groups__group_id
57
+    START WITH 1
58
+    INCREMENT BY 1
59
+    NO MINVALUE
60
+    NO MAXVALUE
61
+    CACHE 1;
62
+
63
+ALTER SEQUENCE seq__groups__group_id OWNED BY groups.group_id;
64
+
65
+CREATE TABLE group_permission (
66
+    group_id integer NOT NULL,
67
+    permission_id integer NOT NULL
68
+);
69
+
70
+CREATE SEQUENCE seq__content_revisions__revision_id
71
+    START WITH 1
72
+    INCREMENT BY 1
73
+    NO MINVALUE
74
+    NO MAXVALUE
75
+    CACHE 1;
76
+
77
+CREATE TABLE content_revisions (
78
+    content_id integer NOT NULL,
79
+    parent_id integer,
80
+    type character varying(16) DEFAULT 'data'::character varying NOT NULL,
81
+    created timestamp without time zone,
82
+    updated timestamp without time zone,
83
+    label character varying(1024),
84
+    description text DEFAULT ''::text NOT NULL,
85
+    status character varying(32) DEFAULT 'new'::character varying,
86
+    file_name character varying(255),
87
+    file_content bytea,
88
+    file_mimetype character varying(255),
89
+    parent_tree_path character varying(255),
90
+    node_depth integer DEFAULT 0 NOT NULL,
91
+    owner_id integer,
92
+    revision_id integer DEFAULT nextval('seq__content_revisions__revision_id'::regclass) NOT NULL,
93
+    workspace_id integer,
94
+    is_deleted boolean DEFAULT false NOT NULL,
95
+    is_archived boolean DEFAULT false NOT NULL,
96
+    properties text,
97
+    revision_type character varying(32)
98
+);
99
+
100
+COMMENT ON COLUMN content_revisions.properties IS 'This column contain properties specific to a given type. these properties are json encoded (so there is no structure "a priori")';
101
+
102
+CREATE VIEW contents AS
103
+    SELECT DISTINCT ON (content_revisions.content_id) content_revisions.content_id, content_revisions.parent_id, content_revisions.type, content_revisions.created, content_revisions.updated, content_revisions.label, content_revisions.description, content_revisions.status, content_revisions.file_name, content_revisions.file_content, content_revisions.file_mimetype, content_revisions.parent_tree_path, content_revisions.node_depth, content_revisions.owner_id, content_revisions.workspace_id, content_revisions.is_deleted, content_revisions.is_archived, content_revisions.properties, content_revisions.revision_type FROM content_revisions ORDER BY content_revisions.content_id, content_revisions.updated DESC, content_revisions.created DESC;
104
+
105
+CREATE SEQUENCE seq__contents__content_id
106
+    START WITH 1
107
+    INCREMENT BY 1
108
+    NO MINVALUE
109
+    NO MAXVALUE
110
+    CACHE 1;
111
+
112
+ALTER SEQUENCE seq__contents__content_id OWNED BY content_revisions.content_id;
113
+
114
+CREATE TABLE permissions (
115
+    permission_id integer NOT NULL,
116
+    permission_name character varying(63) NOT NULL,
117
+    description character varying(255)
118
+);
119
+
120
+CREATE SEQUENCE seq__permissions__permission_id
121
+    START WITH 1
122
+    INCREMENT BY 1
123
+    NO MINVALUE
124
+    NO MAXVALUE
125
+    CACHE 1;
126
+
127
+ALTER SEQUENCE seq__permissions__permission_id OWNED BY permissions.permission_id;
128
+
129
+CREATE TABLE users (
130
+    user_id integer NOT NULL,
131
+    email character varying(255) NOT NULL,
132
+    display_name character varying(255),
133
+    password character varying(128),
134
+    created timestamp without time zone,
135
+    is_active boolean DEFAULT true NOT NULL
136
+);
137
+
138
+CREATE TABLE user_group (
139
+    user_id integer NOT NULL,
140
+    group_id integer NOT NULL
141
+);
142
+
143
+CREATE SEQUENCE seq__users__user_id
144
+    START WITH 1
145
+    INCREMENT BY 1
146
+    NO MINVALUE
147
+    NO MAXVALUE
148
+    CACHE 1;
149
+
150
+ALTER SEQUENCE seq__users__user_id OWNED BY users.user_id;
151
+
152
+CREATE TABLE user_workspace (
153
+    user_id integer NOT NULL,
154
+    workspace_id integer NOT NULL,
155
+    role integer
156
+);
157
+
158
+CREATE TABLE workspaces (
159
+    workspace_id integer NOT NULL,
160
+    label character varying(1024),
161
+    description text,
162
+    created timestamp without time zone,
163
+    updated timestamp without time zone,
164
+    is_deleted boolean DEFAULT false NOT NULL
165
+);
166
+
167
+CREATE SEQUENCE seq__workspaces__workspace_id
168
+    START WITH 11
169
+    INCREMENT BY 1
170
+    NO MINVALUE
171
+    NO MAXVALUE
172
+    CACHE 1;
173
+
174
+ALTER TABLE ONLY groups ALTER COLUMN group_id SET DEFAULT nextval('seq__groups__group_id'::regclass);
175
+ALTER TABLE ONLY content_revisions ALTER COLUMN content_id SET DEFAULT nextval('seq__contents__content_id'::regclass);
176
+ALTER TABLE ONLY permissions ALTER COLUMN permission_id SET DEFAULT nextval('seq__permissions__permission_id'::regclass);
177
+ALTER TABLE ONLY users ALTER COLUMN user_id SET DEFAULT nextval('seq__users__user_id'::regclass);
178
+ALTER TABLE ONLY workspaces ALTER COLUMN workspace_id SET DEFAULT nextval('seq__workspaces__workspace_id'::regclass);
179
+
180
+-- COPY migrate_version (version_num) FROM stdin;
181
+
182
+INSERT INTO groups (group_id, group_name, display_name, created) VALUES
183
+(1, 'users',	'Users',	'2014-10-08 14:55:43.329136'),
184
+(2, 'managers',	'Global Managers',	'2014-10-08 14:55:43.329136'),
185
+(3, 'administrators',	'Administrators',	'2014-10-08 14:55:43.329136');
186
+
187
+SELECT pg_catalog.setval('seq__groups__group_id', 4, true);
188
+
189
+SELECT pg_catalog.setval('seq__contents__content_id', 1, true);
190
+
191
+SELECT pg_catalog.setval('seq__content_revisions__revision_id', 2568, true);
192
+
193
+SELECT pg_catalog.setval('seq__permissions__permission_id', 1, true);
194
+
195
+INSERT INTO users(user_id, email, display_name, password, created, is_active)
196
+VALUES(1, 'demo.michel@tracim.org', 'Michel', '1533a541f0f24746a21b622a88ee8a43ce0197fb73300f633f8860abdbd22e6b8ebb1542dc4bae072729f84b4f0020c37abc60dd769dec7951e4ab80d10ce39e', '2014-10-23 15:28:56.268502', 't');
197
+
198
+INSERT INTO user_group(user_id, group_id) VALUES (1,1), (1, 2), (1,3);
199
+
200
+SELECT pg_catalog.setval('seq__users__user_id', 2, true);
201
+
202
+SELECT pg_catalog.setval('seq__workspaces__workspace_id', 1, true);
203
+
204
+ALTER TABLE ONLY user_workspace
205
+    ADD CONSTRAINT pk__user_workspace__user_id__workspace_id PRIMARY KEY (user_id, workspace_id);
206
+
207
+ALTER TABLE ONLY workspaces
208
+    ADD CONSTRAINT pk__workspace__workspace_id PRIMARY KEY (workspace_id);
209
+
210
+ALTER TABLE ONLY groups
211
+    ADD CONSTRAINT uk__groups__group_name UNIQUE (group_name);
212
+
213
+ALTER TABLE ONLY group_permission
214
+    ADD CONSTRAINT pk__group_permission__group_id__permission_id PRIMARY KEY (group_id, permission_id);
215
+
216
+ALTER TABLE ONLY groups
217
+    ADD CONSTRAINT pk__groups__group_id PRIMARY KEY (group_id);
218
+
219
+ALTER TABLE ONLY content_revisions
220
+    ADD CONSTRAINT pk__content_revisions__revision_id PRIMARY KEY (revision_id);
221
+
222
+ALTER TABLE ONLY permissions
223
+    ADD CONSTRAINT uk__permissions__permission_name UNIQUE (permission_name);
224
+
225
+ALTER TABLE ONLY permissions
226
+    ADD CONSTRAINT pk__permissions__permission_id PRIMARY KEY (permission_id);
227
+
228
+ALTER TABLE ONLY users
229
+    ADD CONSTRAINT uk__users__email UNIQUE (email);
230
+
231
+ALTER TABLE ONLY user_group
232
+    ADD CONSTRAINT pk__user_group__user_id__group_id PRIMARY KEY (user_id, group_id);
233
+
234
+ALTER TABLE ONLY users
235
+    ADD CONSTRAINT pk__users__user_id PRIMARY KEY (user_id);
236
+
237
+CREATE INDEX idx__content_revisions__owner_id ON content_revisions USING btree (owner_id);
238
+
239
+CREATE INDEX idx__content_revisions__parent_id ON content_revisions USING btree (parent_id);
240
+
241
+CREATE INDEX idx__content_revisions__parent_tree_path ON content_revisions USING btree (parent_tree_path);
242
+
243
+CREATE RULE rul__insert__new_node AS ON INSERT TO contents DO INSTEAD INSERT INTO content_revisions (content_id, parent_id, type, created, updated, label, description, status, file_name, file_content, file_mimetype, parent_tree_path, node_depth, owner_id, revision_id, workspace_id, is_deleted, is_archived, properties, revision_type) VALUES (nextval('seq__contents__content_id'::regclass), new.parent_id, new.type, new.created, new.updated, new.label, new.description, new.status, new.file_name, new.file_content, new.file_mimetype, new.parent_tree_path, new.node_depth, new.owner_id, nextval('seq__content_revisions__revision_id'::regclass), new.workspace_id, new.is_deleted, new.is_archived, new.properties, new.revision_type) RETURNING content_revisions.content_id, content_revisions.parent_id, content_revisions.type, content_revisions.created, content_revisions.updated, content_revisions.label, content_revisions.description, content_revisions.status, content_revisions.file_name, content_revisions.file_content, content_revisions.file_mimetype, content_revisions.parent_tree_path, content_revisions.node_depth, content_revisions.owner_id, content_revisions.workspace_id, content_revisions.is_deleted, content_revisions.is_archived, content_revisions.properties, content_revisions.revision_type;
244
+
245
+CREATE TRIGGER trg__contents__on_insert__set_created BEFORE INSERT ON content_revisions FOR EACH ROW EXECUTE PROCEDURE set_created();
246
+CREATE TRIGGER trg__contents__on_update__set_updated BEFORE UPDATE ON content_revisions FOR EACH ROW EXECUTE PROCEDURE set_updated();
247
+
248
+CREATE TRIGGER trg__contents__on_update INSTEAD OF UPDATE ON contents FOR EACH ROW EXECUTE PROCEDURE update_node();
249
+CREATE TRIGGER trg__workspaces__on_insert__set_created BEFORE INSERT ON workspaces FOR EACH ROW EXECUTE PROCEDURE set_created();
250
+CREATE TRIGGER trg__workspaces__on_update__set_updated BEFORE UPDATE ON workspaces FOR EACH ROW EXECUTE PROCEDURE set_updated();
251
+
252
+ALTER TABLE ONLY user_workspace
253
+    ADD CONSTRAINT fk__user_workspace__user_id FOREIGN KEY (user_id) REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE CASCADE;
254
+
255
+ALTER TABLE ONLY user_workspace
256
+    ADD CONSTRAINT fk__user_workspace__workspace_id FOREIGN KEY (workspace_id) REFERENCES workspaces(workspace_id) ON UPDATE CASCADE ON DELETE CASCADE;
257
+
258
+ALTER TABLE ONLY group_permission
259
+    ADD CONSTRAINT fk__group_permission__group_id FOREIGN KEY (group_id) REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE CASCADE;
260
+
261
+ALTER TABLE ONLY group_permission
262
+    ADD CONSTRAINT fk__group_permission__permission_id FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON UPDATE CASCADE ON DELETE CASCADE;
263
+
264
+ALTER TABLE ONLY content_revisions
265
+    ADD CONSTRAINT fk__content_revisions__owner_id FOREIGN KEY (owner_id) REFERENCES users(user_id);
266
+
267
+ALTER TABLE ONLY user_group
268
+    ADD CONSTRAINT fk__user_group__group_id FOREIGN KEY (group_id) REFERENCES groups(group_id) ON UPDATE CASCADE ON DELETE CASCADE;
269
+
270
+ALTER TABLE ONLY user_group
271
+    ADD CONSTRAINT fk__user_group__user_id FOREIGN KEY (user_id) REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE CASCADE;
272
+
273
+

+ 1 - 1
install/requirements.txt View File

@@ -7,7 +7,7 @@ PasteDeploy==1.5.2
7 7
 Pillow==2.4.0
8 8
 SQLAlchemy==0.9.4
9 9
 Tempita==0.5.3dev
10
-TurboGears2==2.3.3
10
+TurboGears2==2.3.4
11 11
 WebOb==1.4
12 12
 WebTest==1.4.3
13 13
 alembic==0.6.5

+ 19 - 3
tracim/tracim/config/app_cfg.py View File

@@ -77,11 +77,11 @@ class ApplicationAuthMetadata(TGAuthMetadata):
77 77
     def __init__(self, sa_auth):
78 78
         self.sa_auth = sa_auth
79 79
     def authenticate(self, environ, identity):
80
-        user = self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(self.sa_auth.user_class.is_active==True, self.sa_auth.user_class.email_address==identity['login'])).first()
80
+        user = self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(self.sa_auth.user_class.is_active==True, self.sa_auth.user_class.email==identity['login'])).first()
81 81
         if user and user.validate_password(identity['password']):
82 82
             return identity['login']
83 83
     def get_user(self, identity, userid):
84
-        return self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(self.sa_auth.user_class.is_active==True, self.sa_auth.user_class.email_address==userid)).first()
84
+        return self.sa_auth.dbsession.query(self.sa_auth.user_class).filter(and_(self.sa_auth.user_class.is_active==True, self.sa_auth.user_class.email==userid)).first()
85 85
     def get_groups(self, identity, userid):
86 86
         return [g.group_name for g in identity['user'].groups]
87 87
     def get_permissions(self, identity, userid):
@@ -116,8 +116,24 @@ base_config.sa_auth.post_logout_url = '/post_logout'
116 116
 # INFO - This is the way to specialize the resetpassword email properties
117 117
 # plug(base_config, 'resetpassword', None, mail_subject=reset_password_email_subject)
118 118
 plug(base_config, 'resetpassword', 'reset_password')
119
+
120
+def fake_unicode(some_string_or_bytes):
121
+    return some_string_or_bytes
122
+    if isinstance(some_string_or_bytes, str):
123
+        print("IS STRING")
124
+        return some_string_or_bytes
125
+
126
+    print("IS ", some_string_or_bytes.__class__.__name__)
127
+    return str(some_string_or_bytes, 'utf-8')
128
+#     if isinstance(some_string_or_bytes, bytes):
129
+#         return some_string_or_bytes.encode('utf-8')
130
+#     return some_string_or_bytes
131
+#
132
+import resetpassword.lib as rplib
133
+# rplib.unicode = fake_unicode
134
+
119 135
 replace_template(base_config, 'resetpassword.templates.index', 'tracim.templates.reset_password_index')
120
-replace_template(base_config, 'resetpassword.templates.change_password', 'tracim.templates.reset_password_change_password')
136
+replace_template(base_config, 'resetpassword.templates.change_password', 'mako:tracim.templates.reset_password_change_password')
121 137
 
122 138
 # Note: here are fake translatable strings that allow to translate messages for reset password email content
123 139
 duplicated_email_subject = l_('Password reset request')

+ 32 - 32
tracim/tracim/controllers/__init__.py View File

@@ -17,8 +17,8 @@ from tracim.lib.predicates import current_user_is_content_manager
17 17
 from tracim.model.auth import User
18 18
 from tracim.model.data import ActionDescription
19 19
 from tracim.model.data import BreadcrumbItem
20
-from tracim.model.data import PBNode
21
-from tracim.model.data import PBNodeType
20
+from tracim.model.data import Content
21
+from tracim.model.data import ContentType
22 22
 from tracim.model.data import Workspace
23 23
 
24 24
 from tracim.lib.content import ContentApi
@@ -53,10 +53,10 @@ class TIMRestPathContextSetup(object):
53 53
 
54 54
 
55 55
     @classmethod
56
-    def current_folder(cls) -> PBNode:
56
+    def current_folder(cls) -> Content:
57 57
         content_api = ContentApi(tg.tmpl_context.current_user)
58 58
         folder_id = int(tg.request.controller_state.routing_args.get('folder_id'))
59
-        folder = content_api.get_one(folder_id, PBNodeType.Folder, tg.tmpl_context.workspace)
59
+        folder = content_api.get_one(folder_id, ContentType.Folder, tg.tmpl_context.workspace)
60 60
 
61 61
         tg.tmpl_context.folder_id = folder_id
62 62
         tg.tmpl_context.folder = folder
@@ -65,10 +65,10 @@ class TIMRestPathContextSetup(object):
65 65
 
66 66
 
67 67
     @classmethod
68
-    def current_folder(cls) -> PBNode:
68
+    def current_folder(cls) -> Content:
69 69
         content_api = ContentApi(tg.tmpl_context.current_user)
70 70
         folder_id = int(tg.request.controller_state.routing_args.get('folder_id'))
71
-        folder = content_api.get_one(folder_id, PBNodeType.Folder, tg.tmpl_context.workspace)
71
+        folder = content_api.get_one(folder_id, ContentType.Folder, tg.tmpl_context.workspace)
72 72
 
73 73
         tg.tmpl_context.folder_id = folder_id
74 74
         tg.tmpl_context.folder = folder
@@ -77,32 +77,32 @@ class TIMRestPathContextSetup(object):
77 77
 
78 78
 
79 79
     @classmethod
80
-    def _current_item_manually(cls, item_id: int, item_type: str) -> PBNode:
80
+    def _current_item_manually(cls, item_id: int, item_type: str) -> Content:
81 81
         # in case thread or page or other stuff is instanciated, then force
82 82
         # the associated item to be available through generic name tmpl_context.item to be available
83 83
         content_api = ContentApi(tg.tmpl_context.current_user)
84 84
         item = content_api.get_one(item_id, item_type, tg.tmpl_context.workspace)
85 85
 
86
-        tg.tmpl_context.item_id = item.node_id
86
+        tg.tmpl_context.item_id = item.content_id
87 87
         tg.tmpl_context.item = item
88 88
 
89 89
         return item
90 90
 
91 91
 
92 92
     @classmethod
93
-    def current_thread(cls) -> PBNode:
93
+    def current_thread(cls) -> Content:
94 94
         thread_id = int(tg.request.controller_state.routing_args.get('thread_id'))
95
-        thread = cls._current_item_manually(thread_id, PBNodeType.Thread)
96
-        tg.tmpl_context.thread_id = thread.node_id
95
+        thread = cls._current_item_manually(thread_id, ContentType.Thread)
96
+        tg.tmpl_context.thread_id = thread.content_id
97 97
         tg.tmpl_context.thread = thread
98 98
         return thread
99 99
 
100 100
 
101 101
     @classmethod
102
-    def current_page(cls) -> PBNode:
102
+    def current_page(cls) -> Content:
103 103
         page_id = int(tg.request.controller_state.routing_args.get('page_id'))
104
-        page = cls._current_item_manually(page_id, PBNodeType.Page)
105
-        tg.tmpl_context.page_id = page.node_id
104
+        page = cls._current_item_manually(page_id, ContentType.Page)
105
+        tg.tmpl_context.page_id = page.content_id
106 106
         tg.tmpl_context.page = page
107 107
         return page
108 108
 
@@ -136,19 +136,19 @@ class TIMRestControllerWithBreadcrumb(TIMRestController):
136 136
         workspace_id = tmpl_context.workspace_id
137 137
         breadcrumb = []
138 138
 
139
-        breadcrumb.append(BreadcrumbItem(PBNodeType.icon(PBNodeType.FAKE_Dashboard), _('Workspaces'), tg.url('/workspaces')))
140
-        breadcrumb.append(BreadcrumbItem(PBNodeType.icon(PBNodeType.FAKE_Workspace), workspace.data_label, tg.url('/workspaces/{}'.format(workspace.workspace_id))))
139
+        breadcrumb.append(BreadcrumbItem(ContentType.icon(ContentType.FAKE_Dashboard), _('Workspaces'), tg.url('/workspaces')))
140
+        breadcrumb.append(BreadcrumbItem(ContentType.icon(ContentType.FAKE_Workspace), workspace.label, tg.url('/workspaces/{}'.format(workspace.workspace_id))))
141 141
 
142 142
         content_api = ContentApi(tmpl_context.current_user)
143 143
         if folder_id:
144 144
             breadcrumb_folder_items = []
145
-            current_item = content_api.get_one(folder_id, PBNodeType.Any, workspace)
145
+            current_item = content_api.get_one(folder_id, ContentType.Any, workspace)
146 146
             is_active = True
147 147
 
148 148
             while current_item:
149
-                breadcrumb_item = BreadcrumbItem(PBNodeType.icon(current_item.node_type),
150
-                                                 current_item.data_label,
151
-                                                 tg.url('/workspaces/{}/folders/{}'.format(workspace_id, current_item.node_id)),
149
+                breadcrumb_item = BreadcrumbItem(ContentType.icon(current_item.type),
150
+                                                 current_item.label,
151
+                                                 tg.url('/workspaces/{}/folders/{}'.format(workspace_id, current_item.content_id)),
152 152
                                                  is_active)
153 153
                 is_active = False # the first item is True, then all other are False => in the breadcrumb, only the last item is "active"
154 154
                 breadcrumb_folder_items.append(breadcrumb_item)
@@ -273,7 +273,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
273 273
 
274 274
             msg = _('{} updated').format(self._item_type_label)
275 275
             tg.flash(msg, CST.STATUS_OK)
276
-            tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item.node_id))
276
+            tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item.content_id))
277 277
 
278 278
         except ValueError as e:
279 279
             msg = _('{} not updated - error: {}').format(self._item_type_label, str(e))
@@ -292,14 +292,14 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
292 292
             content_api.save(item, ActionDescription.STATUS_UPDATE)
293 293
             msg = _('{} status updated').format(self._item_type_label)
294 294
             tg.flash(msg, CST.STATUS_OK)
295
-            tg.redirect(self._std_url.format(item.workspace_id, item.parent_id, item.node_id))
295
+            tg.redirect(self._std_url.format(item.workspace_id, item.parent_id, item.content_id))
296 296
         except ValueError as e:
297 297
             msg = _('{} status not updated: {}').format(self._item_type_label, str(e))
298 298
             tg.flash(msg, CST.STATUS_ERROR)
299
-            tg.redirect(self._err_url.format(item.workspace_id, item.parent_id, item.node_id))
299
+            tg.redirect(self._err_url.format(item.workspace_id, item.parent_id, item.content_id))
300 300
 
301 301
 
302
-    def get_all_fake(self, context_workspace: Workspace, context_folder: PBNode) -> [PBNode]:
302
+    def get_all_fake(self, context_workspace: Workspace, context_folder: Content) -> [Content]:
303 303
         """
304 304
         fake methods are used in other controllers in order to simulate a client/server api.
305 305
         the "client" controller method will include the result into its own fake_api object
@@ -310,7 +310,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
310 310
         """
311 311
         workspace = context_workspace
312 312
         content_api = ContentApi(tmpl_context.current_user)
313
-        items = content_api.get_all(context_folder.node_id, self._item_type, workspace)
313
+        items = content_api.get_all(context_folder.content_id, self._item_type, workspace)
314 314
 
315 315
         dictified_items = Context(self._get_all_context).toDict(items)
316 316
         return DictLikeClass(result = dictified_items)
@@ -325,7 +325,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
325 325
         item = content_api.get_one(item_id, self._item_type, tmpl_context.workspace)
326 326
         try:
327 327
             next_url = self._parent_url.format(item.workspace_id, item.parent_id)
328
-            undo_url = self._std_url.format(item.workspace_id, item.parent_id, item.node_id)+'/put_archive_undo'
328
+            undo_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)+'/put_archive_undo'
329 329
             msg = _('{} archived. <a class="alert-link" href="{}">Cancel action</a>').format(self._item_type_label, undo_url)
330 330
 
331 331
             content_api.archive(item)
@@ -334,7 +334,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
334 334
             tg.flash(msg, CST.STATUS_OK, no_escape=True) # TODO allow to come back
335 335
             tg.redirect(next_url)
336 336
         except ValueError as e:
337
-            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.node_id)
337
+            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)
338 338
             msg = _('{} not archived: {}').format(self._item_type_label, str(e))
339 339
             tg.flash(msg, CST.STATUS_ERROR)
340 340
             tg.redirect(next_url)
@@ -348,7 +348,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
348 348
         content_api = ContentApi(tmpl_context.current_user, True) # Here we do not filter archived items
349 349
         item = content_api.get_one(item_id, self._item_type, tmpl_context.workspace)
350 350
         try:
351
-            next_url = self._parent_url.format(item.workspace_id, item.parent_id)
351
+            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)
352 352
             msg = _('{} unarchived.').format(self._item_type_label)
353 353
             content_api.unarchive(item)
354 354
             content_api.save(item, ActionDescription.UNARCHIVING)
@@ -358,7 +358,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
358 358
 
359 359
         except ValueError as e:
360 360
             msg = _('{} not un-archived: {}').format(self._item_type_label, str(e))
361
-            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.node_id)
361
+            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)
362 362
             # We still use std url because the item has not been archived
363 363
             tg.flash(msg, CST.STATUS_ERROR)
364 364
             tg.redirect(next_url)
@@ -374,7 +374,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
374 374
         try:
375 375
 
376 376
             next_url = self._parent_url.format(item.workspace_id, item.parent_id)
377
-            undo_url = self._std_url.format(item.workspace_id, item.parent_id, item.node_id)+'/put_delete_undo'
377
+            undo_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)+'/put_delete_undo'
378 378
             msg = _('{} deleted. <a class="alert-link" href="{}">Cancel action</a>').format(self._item_type_label, undo_url)
379 379
             content_api.delete(item)
380 380
             content_api.save(item, ActionDescription.DELETION)
@@ -383,7 +383,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
383 383
             tg.redirect(next_url)
384 384
 
385 385
         except ValueError as e:
386
-            back_url = self._std_url.format(item.workspace_id, item.parent_id, item.node_id)
386
+            back_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)
387 387
             msg = _('{} not deleted: {}').format(self._item_type_label, str(e))
388 388
             tg.flash(msg, CST.STATUS_ERROR)
389 389
             tg.redirect(back_url)
@@ -398,7 +398,7 @@ class TIMWorkspaceContentRestController(TIMRestControllerWithBreadcrumb):
398 398
         content_api = ContentApi(tmpl_context.current_user, True, True) # Here we do not filter deleted items
399 399
         item = content_api.get_one(item_id, self._item_type, tmpl_context.workspace)
400 400
         try:
401
-            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.node_id)
401
+            next_url = self._std_url.format(item.workspace_id, item.parent_id, item.content_id)
402 402
             msg = _('{} undeleted.').format(self._item_type_label)
403 403
             content_api.undelete(item)
404 404
             content_api.save(item, ActionDescription.UNDELETION)

+ 4 - 4
tracim/tracim/controllers/admin/user.py View File

@@ -246,11 +246,11 @@ class UserRestController(TIMRestController):
246 246
 
247 247
     @tg.require(predicates.in_group(Group.TIM_MANAGER_GROUPNAME))
248 248
     @tg.expose()
249
-    def post(self, name, email, password, is_tracim_manager='off', is_pod_admin='off'):
249
+    def post(self, name, email, password, is_tracim_manager='off', is_tracim_admin='off'):
250 250
         is_tracim_manager = h.on_off_to_boolean(is_tracim_manager)
251
-        is_tracim_admin = h.on_off_to_boolean(is_pod_admin)
251
+        is_tracim_admin = h.on_off_to_boolean(is_tracim_admin)
252 252
         current_user = tmpl_context.current_user
253
-        current_user = User()
253
+
254 254
         if current_user.profile.id < Group.TIM_ADMIN:
255 255
             # A manager can't give large rights
256 256
             is_tracim_manager = False
@@ -264,7 +264,7 @@ class UserRestController(TIMRestController):
264 264
             tg.redirect(self.url())
265 265
 
266 266
         user = api.create_user()
267
-        user.email_address = email
267
+        user.email = email
268 268
         user.display_name = name
269 269
         if password:
270 270
             user.password = password

+ 9 - 9
tracim/tracim/controllers/admin/workspace.py View File

@@ -17,8 +17,8 @@ from tracim.lib.workspace import WorkspaceApi
17 17
 
18 18
 from tracim.model.auth import Group
19 19
 from tracim.model.data import NodeTreeItem
20
-from tracim.model.data import PBNode
21
-from tracim.model.data import PBNodeType
20
+from tracim.model.data import Content
21
+from tracim.model.data import ContentType
22 22
 from tracim.model.data import Workspace
23 23
 from tracim.model.data import UserRoleInWorkspace
24 24
 
@@ -109,7 +109,7 @@ class RoleInWorkspaceRestController(TIMRestController, BaseController):
109 109
 
110 110
         tg.flash(flash_msg_template.format(
111 111
             role.user.get_display_name(),
112
-            tg.tmpl_context.workspace.data_label,
112
+            tg.tmpl_context.workspace.label,
113 113
             role.role_as_label()), CST.STATUS_OK)
114 114
 
115 115
         tg.redirect(self.parent_controller.url(tg.tmpl_context.workspace_id))
@@ -199,7 +199,7 @@ class WorkspaceRestController(TIMRestController, BaseController):
199 199
 
200 200
         workspace = workspace_api_controller.create_workspace(name, description)
201 201
 
202
-        tg.flash(_('{} workspace created.').format(workspace.data_label), CST.STATUS_OK)
202
+        tg.flash(_('{} workspace created.').format(workspace.label), CST.STATUS_OK)
203 203
         tg.redirect(self.url())
204 204
         return
205 205
 
@@ -219,11 +219,11 @@ class WorkspaceRestController(TIMRestController, BaseController):
219 219
         workspace_api_controller = WorkspaceApi(user)
220 220
 
221 221
         workspace = workspace_api_controller.get_one(id)
222
-        workspace.data_label = name
223
-        workspace.data_comment = description
222
+        workspace.label = name
223
+        workspace.description = description
224 224
         workspace_api_controller.save(workspace)
225 225
 
226
-        tg.flash(_('{} workspace updated.').format(workspace.data_label), CST.STATUS_OK)
226
+        tg.flash(_('{} workspace updated.').format(workspace.label), CST.STATUS_OK)
227 227
         tg.redirect(self.url(workspace.workspace_id))
228 228
         return
229 229
 
@@ -245,7 +245,7 @@ class WorkspaceRestController(TIMRestController, BaseController):
245 245
         workspace = api.get_one(workspace_id)
246 246
         api.delete_one(workspace_id)
247 247
 
248
-        workspace_label = workspace.data_label
248
+        workspace_label = workspace.label
249 249
         undo_url = self.url(workspace_id, self.restore.__name__)
250 250
 
251 251
         tg.flash(_('{} workspace deleted. In case of error, you can <a class="alert-link" href="{}">restore it</a>.').format(workspace_label, undo_url), CST.STATUS_OK, no_escape=True)
@@ -258,7 +258,7 @@ class WorkspaceRestController(TIMRestController, BaseController):
258 258
         api = WorkspaceApi(tg.tmpl_context.current_user)
259 259
         workspace = api.restore_one(workspace_id, True)
260 260
 
261
-        workspace_label = workspace.data_label
261
+        workspace_label = workspace.label
262 262
         undo_url = self.url(workspace_id, 'delete')
263 263
 
264 264
         tg.flash(_('{} workspace restored.').format(workspace_label), CST.STATUS_OK)

+ 46 - 46
tracim/tracim/controllers/content.py View File

@@ -24,8 +24,8 @@ from tracim.lib.predicates import current_user_is_content_manager
24 24
 
25 25
 from tracim.model.serializers import Context, CTX, DictLikeClass
26 26
 from tracim.model.data import ActionDescription
27
-from tracim.model.data import PBNode
28
-from tracim.model.data import PBNodeType
27
+from tracim.model.data import Content
28
+from tracim.model.data import ContentType
29 29
 from tracim.model.data import Workspace
30 30
 
31 31
 
@@ -71,7 +71,7 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
71 71
 
72 72
     @property
73 73
     def _item_type(self):
74
-        return PBNodeType.File
74
+        return ContentType.File
75 75
 
76 76
     @property
77 77
     def _item_type_label(self):
@@ -135,25 +135,25 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
135 135
                 if not revision_to_send:
136 136
                     revision_to_send = revision
137 137
 
138
-                if revision.version_id>revision_to_send.version_id:
138
+                if revision.revision_id>revision_to_send.revision_id:
139 139
                     revision_to_send = revision
140 140
         else:
141 141
             for revision in item.revisions:
142
-                if revision.version_id==item.revision_to_serialize:
142
+                if revision.revision_id==item.revision_to_serialize:
143 143
                     revision_to_send = revision
144 144
                     break
145 145
 
146 146
         content_type = 'application/x-download'
147
-        if revision_to_send.data_file_mime_type:
148
-            content_type = str(revision_to_send.data_file_mime_type)
149
-            tg.response.headers['Content-type'] = str(revision_to_send.data_file_mime_type)
147
+        if revision_to_send.file_mimetype:
148
+            content_type = str(revision_to_send.file_mimetype)
149
+            tg.response.headers['Content-type'] = str(revision_to_send.file_mimetype)
150 150
 
151 151
         tg.response.headers['Content-Type'] = content_type
152
-        tg.response.headers['Content-Disposition'] = str('attachment; filename="{}"'.format(revision_to_send.data_file_name))
153
-        return revision_to_send.data_file_content
152
+        tg.response.headers['Content-Disposition'] = str('attachment; filename="{}"'.format(revision_to_send.file_name))
153
+        return revision_to_send.file_content
154 154
 
155 155
 
156
-    def get_all_fake(self, context_workspace: Workspace, context_folder: PBNode):
156
+    def get_all_fake(self, context_workspace: Workspace, context_folder: Content):
157 157
         """
158 158
         fake methods are used in other controllers in order to simulate a client/server api.
159 159
         the "client" controller method will include the result into its own fake_api object
@@ -164,7 +164,7 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
164 164
         """
165 165
         workspace = context_workspace
166 166
         content_api = ContentApi(tmpl_context.current_user)
167
-        files = content_api.get_all(context_folder.node_id, PBNodeType.File, workspace)
167
+        files = content_api.get_all(context_folder.content_id, ContentType.File, workspace)
168 168
 
169 169
         dictified_files = Context(CTX.FILES).toDict(files)
170 170
         return DictLikeClass(result = dictified_files)
@@ -178,12 +178,12 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
178 178
 
179 179
         api = ContentApi(tmpl_context.current_user)
180 180
 
181
-        file = api.create(PBNodeType.File, workspace, tmpl_context.folder, label)
181
+        file = api.create(ContentType.File, workspace, tmpl_context.folder, label)
182 182
         api.update_file_data(file, file_data.filename, file_data.type, file_data.file.read())
183 183
         api.save(file, ActionDescription.CREATION)
184 184
 
185 185
         tg.flash(_('File created'), CST.STATUS_OK)
186
-        tg.redirect(tg.url('/workspaces/{}/folders/{}/files/{}').format(tmpl_context.workspace_id, tmpl_context.folder_id, file.node_id))
186
+        tg.redirect(tg.url('/workspaces/{}/folders/{}/files/{}').format(tmpl_context.workspace_id, tmpl_context.folder_id, file.content_id))
187 187
 
188 188
 
189 189
     @tg.require(current_user_is_contributor())
@@ -196,7 +196,7 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
196 196
             api = ContentApi(tmpl_context.current_user)
197 197
             item = api.get_one(int(item_id), self._item_type, workspace)
198 198
             if comment:
199
-                api.update_content(item, label if label else item.data_label, comment)
199
+                api.update_content(item, label if label else item.label, comment)
200 200
 
201 201
             if isinstance(file_data, FieldStorage):
202 202
                 api.update_file_data(item, file_data.filename, file_data.type, file_data.file.read())
@@ -205,7 +205,7 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
205 205
 
206 206
             msg = _('{} updated').format(self._item_type_label)
207 207
             tg.flash(msg, CST.STATUS_OK)
208
-            tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item.node_id))
208
+            tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, item.content_id))
209 209
 
210 210
         except ValueError as e:
211 211
             msg = _('{} not updated - error: {}').format(self._item_type_label, str(e))
@@ -229,7 +229,7 @@ class UserWorkspaceFolderPageRestController(TIMWorkspaceContentRestController):
229 229
 
230 230
     @property
231 231
     def _item_type(self):
232
-        return PBNodeType.Page
232
+        return ContentType.Page
233 233
 
234 234
     @property
235 235
     def _item_type_label(self):
@@ -261,9 +261,9 @@ class UserWorkspaceFolderPageRestController(TIMWorkspaceContentRestController):
261 261
 
262 262
         content_api = ContentApi(user)
263 263
         if revision_id:
264
-            page = content_api.get_one_from_revision(page_id, PBNodeType.Page, workspace, revision_id)
264
+            page = content_api.get_one_from_revision(page_id, ContentType.Page, workspace, revision_id)
265 265
         else:
266
-            page = content_api.get_one(page_id, PBNodeType.Page, workspace)
266
+            page = content_api.get_one(page_id, ContentType.Page, workspace)
267 267
 
268 268
         fake_api_breadcrumb = self.get_breadcrumb(page_id)
269 269
         fake_api_content = DictLikeClass(breadcrumb=fake_api_breadcrumb, current_user=current_user_content)
@@ -273,7 +273,7 @@ class UserWorkspaceFolderPageRestController(TIMWorkspaceContentRestController):
273 273
         return DictLikeClass(result = dictified_page, fake_api=fake_api)
274 274
 
275 275
 
276
-    def get_all_fake(self, context_workspace: Workspace, context_folder: PBNode):
276
+    def get_all_fake(self, context_workspace: Workspace, context_folder: Content):
277 277
         """
278 278
         fake methods are used in other controllers in order to simulate a client/server api.
279 279
         the "client" controller method will include the result into its own fake_api object
@@ -284,7 +284,7 @@ class UserWorkspaceFolderPageRestController(TIMWorkspaceContentRestController):
284 284
         """
285 285
         workspace = context_workspace
286 286
         content_api = ContentApi(tmpl_context.current_user)
287
-        pages = content_api.get_all(context_folder.node_id, PBNodeType.Page, workspace)
287
+        pages = content_api.get_all(context_folder.content_id, ContentType.Page, workspace)
288 288
 
289 289
         dictified_pages = Context(CTX.PAGES).toDict(pages)
290 290
         return DictLikeClass(result = dictified_pages)
@@ -298,12 +298,12 @@ class UserWorkspaceFolderPageRestController(TIMWorkspaceContentRestController):
298 298
 
299 299
         api = ContentApi(tmpl_context.current_user)
300 300
 
301
-        page = api.create(PBNodeType.Page, workspace, tmpl_context.folder, label)
302
-        page.data_content = content
301
+        page = api.create(ContentType.Page, workspace, tmpl_context.folder, label)
302
+        page.description = content
303 303
         api.save(page, ActionDescription.CREATION)
304 304
 
305 305
         tg.flash(_('Page created'), CST.STATUS_OK)
306
-        tg.redirect(tg.url('/workspaces/{}/folders/{}/pages/{}').format(tmpl_context.workspace_id, tmpl_context.folder_id, page.node_id))
306
+        tg.redirect(tg.url('/workspaces/{}/folders/{}/pages/{}').format(tmpl_context.workspace_id, tmpl_context.folder_id, page.content_id))
307 307
 
308 308
 
309 309
 
@@ -337,7 +337,7 @@ class UserWorkspaceFolderThreadRestController(TIMWorkspaceContentRestController)
337 337
 
338 338
     @property
339 339
     def _item_type(self):
340
-        return PBNodeType.Thread
340
+        return ContentType.Thread
341 341
 
342 342
 
343 343
     @property
@@ -374,17 +374,17 @@ class UserWorkspaceFolderThreadRestController(TIMWorkspaceContentRestController)
374 374
 
375 375
         api = ContentApi(tmpl_context.current_user)
376 376
 
377
-        thread = api.create(PBNodeType.Thread, workspace, tmpl_context.folder, label)
378
-        # FIXME - DO NOT DUPLCIATE FIRST MESSAGE thread.data_content = content
377
+        thread = api.create(ContentType.Thread, workspace, tmpl_context.folder, label)
378
+        # FIXME - DO NOT DUPLCIATE FIRST MESSAGE thread.description = content
379 379
         api.save(thread, ActionDescription.CREATION)
380 380
 
381
-        comment = api.create(PBNodeType.Comment, workspace, thread, label)
382
-        comment.data_label = ''
383
-        comment.data_content = content
381
+        comment = api.create(ContentType.Comment, workspace, thread, label)
382
+        comment.label = ''
383
+        comment.description = content
384 384
         api.save(comment, ActionDescription.COMMENT)
385 385
 
386 386
         tg.flash(_('Thread created'), CST.STATUS_OK)
387
-        tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, thread.node_id))
387
+        tg.redirect(self._std_url.format(tmpl_context.workspace_id, tmpl_context.folder_id, thread.content_id))
388 388
 
389 389
 
390 390
     @tg.require(current_user_is_reader())
@@ -398,7 +398,7 @@ class UserWorkspaceFolderThreadRestController(TIMWorkspaceContentRestController)
398 398
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
399 399
 
400 400
         content_api = ContentApi(user)
401
-        thread = content_api.get_one(thread_id, PBNodeType.Thread, workspace)
401
+        thread = content_api.get_one(thread_id, ContentType.Thread, workspace)
402 402
 
403 403
         fake_api_breadcrumb = self.get_breadcrumb(thread_id)
404 404
         fake_api_content = DictLikeClass(breadcrumb=fake_api_breadcrumb, current_user=current_user_content)
@@ -418,7 +418,7 @@ class ItemLocationController(TIMWorkspaceContentRestController, BaseController):
418 418
         user = tmpl_context.current_user
419 419
         workspace = tmpl_context.workspace
420 420
 
421
-        item = ContentApi(user).get_one(item_id, PBNodeType.Any, workspace)
421
+        item = ContentApi(user).get_one(item_id, ContentType.Any, workspace)
422 422
         raise NotImplementedError
423 423
         return item
424 424
 
@@ -438,7 +438,7 @@ class ItemLocationController(TIMWorkspaceContentRestController, BaseController):
438 438
         workspace = tmpl_context.workspace
439 439
 
440 440
         content_api = ContentApi(user)
441
-        item = content_api.get_one(item_id, PBNodeType.Any, workspace)
441
+        item = content_api.get_one(item_id, ContentType.Any, workspace)
442 442
 
443 443
         dictified_item = Context(CTX.DEFAULT).toDict(item, 'item')
444 444
         return DictLikeClass(result = dictified_item)
@@ -458,11 +458,11 @@ class ItemLocationController(TIMWorkspaceContentRestController, BaseController):
458 458
         new_workspace, new_parent = convert_id_into_instances(folder_id)
459 459
 
460 460
         api = ContentApi(tmpl_context.current_user)
461
-        item = api.get_one(item_id, PBNodeType.Any, workspace)
461
+        item = api.get_one(item_id, ContentType.Any, workspace)
462 462
         api.move(item, new_parent)
463 463
         next_url = self.parent_controller.url(item_id)
464 464
         if new_parent:
465
-            tg.flash(_('Item moved to {}').format(new_parent.data_label), CST.STATUS_OK)
465
+            tg.flash(_('Item moved to {}').format(new_parent.label), CST.STATUS_OK)
466 466
         else:
467 467
             tg.flash(_('Item moved to workspace root'))
468 468
 
@@ -498,7 +498,7 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
498 498
         workspace = tmpl_context.workspace
499 499
 
500 500
         content_api = ContentApi(user)
501
-        folder = content_api.get_one(folder_id, PBNodeType.Folder, workspace)
501
+        folder = content_api.get_one(folder_id, ContentType.Folder, workspace)
502 502
 
503 503
         dictified_folder = Context(CTX.FOLDER).toDict(folder, 'folder')
504 504
         return DictLikeClass(result = dictified_folder)
@@ -516,10 +516,10 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
516 516
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
517 517
 
518 518
         content_api = ContentApi(user)
519
-        folder = content_api.get_one(folder_id, PBNodeType.Folder, workspace)
519
+        folder = content_api.get_one(folder_id, ContentType.Folder, workspace)
520 520
 
521 521
         fake_api_breadcrumb = self.get_breadcrumb(folder_id)
522
-        fake_api_subfolders = self.get_all_fake(workspace, folder.node_id).result
522
+        fake_api_subfolders = self.get_all_fake(workspace, folder.content_id).result
523 523
         fake_api_pages = self.pages.get_all_fake(workspace, folder).result
524 524
         fake_api_files = self.files.get_all_fake(workspace, folder).result
525 525
         fake_api_threads = self.threads.get_all_fake(workspace, folder).result
@@ -550,7 +550,7 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
550 550
         """
551 551
         workspace = context_workspace
552 552
         content_api = ContentApi(tmpl_context.current_user)
553
-        parent_folder = content_api.get_one(parent_id, PBNodeType.Folder)
553
+        parent_folder = content_api.get_one(parent_id, ContentType.Folder)
554 554
         folders = content_api.get_child_folders(parent_folder, workspace)
555 555
 
556 556
         folders = Context(CTX.FOLDERS).toDict(folders)
@@ -572,8 +572,8 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
572 572
         try:
573 573
             parent = None
574 574
             if parent_id:
575
-                parent = api.get_one(int(parent_id), PBNodeType.Folder, workspace)
576
-            folder = api.create(PBNodeType.Folder, workspace, parent, label)
575
+                parent = api.get_one(int(parent_id), ContentType.Folder, workspace)
576
+            folder = api.create(ContentType.Folder, workspace, parent, label)
577 577
 
578 578
             subcontent = dict(
579 579
                 folder = True if can_contain_folders=='on' else False,
@@ -585,7 +585,7 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
585 585
             api.save(folder)
586 586
 
587 587
             tg.flash(_('Folder created'), CST.STATUS_OK)
588
-            redirect_url = redirect_url_tmpl.format(tmpl_context.workspace_id, folder.node_id)
588
+            redirect_url = redirect_url_tmpl.format(tmpl_context.workspace_id, folder.content_id)
589 589
         except Exception as e:
590 590
             logger.error(self, 'An unexpected exception has been catched. Look at the traceback below.')
591 591
             traceback.print_exc()
@@ -614,20 +614,20 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
614 614
         next_url = ''
615 615
 
616 616
         try:
617
-            folder = api.get_one(int(folder_id), PBNodeType.Folder, workspace)
617
+            folder = api.get_one(int(folder_id), ContentType.Folder, workspace)
618 618
             subcontent = dict(
619 619
                 folder = True if can_contain_folders=='on' else False,
620 620
                 thread = True if can_contain_threads=='on' else False,
621 621
                 file = True if can_contain_files=='on' else False,
622 622
                 page = True if can_contain_pages=='on' else False
623 623
             )
624
-            api.update_content(folder, label, folder.data_content)
624
+            api.update_content(folder, label, folder.description)
625 625
             api.set_allowed_content(folder, subcontent)
626 626
             api.save(folder)
627 627
 
628 628
             tg.flash(_('Folder updated'), CST.STATUS_OK)
629 629
 
630
-            next_url = self.url(folder.node_id)
630
+            next_url = self.url(folder.content_id)
631 631
 
632 632
         except Exception as e:
633 633
             tg.flash(_('Folder not updated: {}').format(str(e)), CST.STATUS_ERROR)

+ 0 - 1
tracim/tracim/controllers/help.py View File

@@ -25,7 +25,6 @@ class HelpController(BaseController):
25 25
         # FIXME - NOT REALLY SAFE BECAUSE SOME UNWANTED FILE MAY BE USED AS HELP PAGE
26 26
         if help_page:
27 27
             help_page_path = 'mako:tracim.templates.help.page-{}'.format(help_page)
28
-            print('TEMPLATE:', help_page_path)
29 28
             override_template(HelpController.page, help_page_path)
30 29
 
31 30
         return dict(mode=mode)

+ 5 - 5
tracim/tracim/controllers/workspace.py View File

@@ -17,8 +17,8 @@ from tracim.lib.content import ContentApi
17 17
 from tracim.lib.workspace import WorkspaceApi
18 18
 
19 19
 from tracim.model.data import NodeTreeItem
20
-from tracim.model.data import PBNode
21
-from tracim.model.data import PBNodeType
20
+from tracim.model.data import Content
21
+from tracim.model.data import ContentType
22 22
 from tracim.model.data import Workspace
23 23
 from tracim.model.data import UserRoleInWorkspace
24 24
 
@@ -85,7 +85,7 @@ class UserWorkspaceRestController(TIMRestController):
85 85
             return dictified_workspaces
86 86
 
87 87
 
88
-        allowed_content_types = PBNodeType.allowed_types_from_str(folder_allowed_content_types)
88
+        allowed_content_types = ContentType.allowed_types_from_str(folder_allowed_content_types)
89 89
         ignored_item_ids = [int(ignore_id)] if ignore_id else []
90 90
 
91 91
         # Now complex case: we must return a structured tree
@@ -147,11 +147,11 @@ class UserWorkspaceRestController(TIMRestController):
147 147
 
148 148
     def _build_sibling_list_of_tree_items(self,
149 149
                                           workspace: Workspace,
150
-                                          content: PBNode,
150
+                                          content: Content,
151 151
                                           children: [NodeTreeItem],
152 152
                                           select_active_node = False,
153 153
                                           allowed_content_types: list = [],
154
-                                          ignored_item_ids: list = []) -> (PBNode, [NodeTreeItem]):
154
+                                          ignored_item_ids: list = []) -> (Content, [NodeTreeItem]):
155 155
         api = ContentApi(tmpl_context.current_user)
156 156
         tree_items = []
157 157
 

BIN
tracim/tracim/i18n/fr/LC_MESSAGES/tracim.mo View File


+ 150 - 187
tracim/tracim/i18n/fr/LC_MESSAGES/tracim.po View File

@@ -7,8 +7,8 @@ msgid ""
7 7
 msgstr ""
8 8
 "Project-Id-Version: pod 0.1\n"
9 9
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
10
-"POT-Creation-Date: 2014-10-23 15:20+0200\n"
11
-"PO-Revision-Date: 2014-10-22 19:26+0100\n"
10
+"POT-Creation-Date: 2014-10-24 13:36+0200\n"
11
+"PO-Revision-Date: 2014-10-24 13:36+0100\n"
12 12
 "Last-Translator: Damien Accorsi <damien.accorsi@free.fr>\n"
13 13
 "Language-Team: fr_FR <LL@li.org>\n"
14 14
 "Plural-Forms: nplurals=2; plural=(n > 1)\n"
@@ -30,24 +30,21 @@ msgid ""
30 30
 "\n"
31 31
 "%(password_reset_link)s\n"
32 32
 "\n"
33
-"If you no longer wish to make the above change, or if you did not "
34
-"initiate this request, please disregard and/or delete this e-mail.\n"
33
+"If you no longer wish to make the above change, or if you did not initiate this request, please disregard and/or delete this e-mail.\n"
35 34
 msgstr ""
36 35
 "\n"
37
-"Nous avons reçu une requête de réinitialisation du mot de passe pour ce "
38
-"compte utilisateur.\n"
36
+"Nous avons reçu une requête de réinitialisation du mot de passe pour ce compte utilisateur.\n"
39 37
 "Cliquer sur le lien ci-dessous pour réinitialiser votre mot de passe :\n"
40 38
 "\n"
41 39
 "%(password_reset_link)s\n"
42 40
 "\n"
43
-"Si vous ne souhaitez plus procéder à ce changement, ou si vous n'êtes pas"
44
-" à l'originie de cette requête, merci d'ignorer et/ou supprimer cet "
45
-"e-mail.\n"
41
+"Si vous ne souhaitez plus procéder à ce changement, ou si vous n'êtes pas à l'originie de cette requête, merci d'ignorer et/ou supprimer cet e-mail.\n"
46 42
 
47 43
 #: tracim/controllers/__init__.py:139
48 44
 #: tracim/templates/master_authenticated.mak:87
49 45
 #: tracim/templates/master_no_toolbar_no_login.mak:112
50
-#: tracim/templates/user_get_one.mak:39 tracim/templates/user_profile.mak:39
46
+#: tracim/templates/user_get_one.mak:39
47
+#: tracim/templates/user_profile.mak:39
51 48
 #: tracim/templates/user_workspace_folder_file_get_one.mak:10
52 49
 #: tracim/templates/user_workspace_folder_get_one.mak:11
53 50
 #: tracim/templates/user_workspace_folder_page_get_one.mak:11
@@ -58,11 +55,13 @@ msgstr ""
58 55
 msgid "Workspaces"
59 56
 msgstr "Espaces de travail"
60 57
 
61
-#: tracim/controllers/__init__.py:274 tracim/controllers/content.py:206
58
+#: tracim/controllers/__init__.py:274
59
+#: tracim/controllers/content.py:206
62 60
 msgid "{} updated"
63 61
 msgstr "{} mis(e) à jour"
64 62
 
65
-#: tracim/controllers/__init__.py:279 tracim/controllers/content.py:211
63
+#: tracim/controllers/__init__.py:279
64
+#: tracim/controllers/content.py:211
66 65
 msgid "{} not updated - error: {}"
67 66
 msgstr "{} pas mis(e) à jour - erreur : {}"
68 67
 
@@ -92,9 +91,7 @@ msgstr "{} non désarchivé(e) : {}"
92 91
 
93 92
 #: tracim/controllers/__init__.py:378
94 93
 msgid "{} deleted. <a class=\"alert-link\" href=\"{}\">Cancel action</a>"
95
-msgstr ""
96
-"{} supprimé(e). <a class=\"alert-link\" href=\"{}\">Annuler "
97
-"l'opération</a>"
94
+msgstr "{} supprimé(e). <a class=\"alert-link\" href=\"{}\">Annuler l'opération</a>"
98 95
 
99 96
 #: tracim/controllers/__init__.py:387
100 97
 msgid "{} not deleted: {}"
@@ -173,19 +170,23 @@ msgstr "Bonjour %s. Ravis de vous revoir !"
173 170
 msgid "Successfully logged out. We hope to see you soon!"
174 171
 msgstr "Déconnexion réussie. Nous espérons vous revoir bientôt !"
175 172
 
176
-#: tracim/controllers/user.py:64 tracim/controllers/admin/user.py:186
173
+#: tracim/controllers/user.py:64
174
+#: tracim/controllers/admin/user.py:201
177 175
 msgid "Empty password is not allowed."
178 176
 msgstr "Le mot de passe ne doit pas être vide"
179 177
 
180
-#: tracim/controllers/user.py:68 tracim/controllers/admin/user.py:190
178
+#: tracim/controllers/user.py:68
179
+#: tracim/controllers/admin/user.py:205
181 180
 msgid "The current password you typed is wrong"
182 181
 msgstr "Le mot de passe que vous avez tapé est erroné"
183 182
 
184
-#: tracim/controllers/user.py:72 tracim/controllers/admin/user.py:194
183
+#: tracim/controllers/user.py:72
184
+#: tracim/controllers/admin/user.py:209
185 185
 msgid "New passwords do not match."
186 186
 msgstr "Les mots de passe ne concordent pas"
187 187
 
188
-#: tracim/controllers/user.py:78 tracim/controllers/admin/user.py:200
188
+#: tracim/controllers/user.py:78
189
+#: tracim/controllers/admin/user.py:215
189 190
 msgid "Your password has been changed"
190 191
 msgstr "Votre mot de passe a été changé"
191 192
 
@@ -193,51 +194,52 @@ msgstr "Votre mot de passe a été changé"
193 194
 msgid "profile updated."
194 195
 msgstr "Profil mis à jour"
195 196
 
196
-#: tracim/controllers/admin/user.py:73
197
+#: tracim/controllers/admin/user.py:84
197 198
 msgid "You can't change your own profile"
198 199
 msgstr "Vous ne pouvez pas changer votre propre profil"
199 200
 
200
-#: tracim/controllers/admin/user.py:80
201
+#: tracim/controllers/admin/user.py:91
202
+#: tracim/controllers/admin/user.py:142
201 203
 msgid "Unknown profile"
202 204
 msgstr "Profil inconnu"
203 205
 
204
-#: tracim/controllers/admin/user.py:87
206
+#: tracim/controllers/admin/user.py:98
205 207
 msgid "User updated."
206 208
 msgstr "Utilisateur mis à jour"
207 209
 
208
-#: tracim/controllers/admin/user.py:103
210
+#: tracim/controllers/admin/user.py:114
209 211
 msgid "User {} is now a basic user"
210 212
 msgstr "L'utilisateur {} est désormais un utilisateur standard"
211 213
 
212
-#: tracim/controllers/admin/user.py:116
214
+#: tracim/controllers/admin/user.py:127
213 215
 msgid "User {} can now workspaces"
214 216
 msgstr "L'utilisateur {} peut désormais créer des espaces de travail"
215 217
 
216
-#: tracim/controllers/admin/user.py:127
218
+#: tracim/controllers/admin/user.py:138
217 219
 msgid "User {} is now an administrator"
218 220
 msgstr "L'utilisateur {} est désormais administrateur"
219 221
 
220
-#: tracim/controllers/admin/user.py:248
222
+#: tracim/controllers/admin/user.py:263
221 223
 msgid "A user with email address \"{}\" already exists."
222 224
 msgstr "Un utilisateur avec l'adresse email \"{}\" existe déjà."
223 225
 
224
-#: tracim/controllers/admin/user.py:268
226
+#: tracim/controllers/admin/user.py:283
225 227
 msgid "User {} created."
226 228
 msgstr "Utilisateur {} créé."
227 229
 
228
-#: tracim/controllers/admin/user.py:307
230
+#: tracim/controllers/admin/user.py:322
229 231
 msgid "User {} updated."
230 232
 msgstr "Utilisateur {} mis à jour."
231 233
 
232
-#: tracim/controllers/admin/user.py:321
234
+#: tracim/controllers/admin/user.py:336
233 235
 msgid "User {} activated."
234 236
 msgstr "Utilisateur {} activé."
235 237
 
236
-#: tracim/controllers/admin/user.py:332
238
+#: tracim/controllers/admin/user.py:347
237 239
 msgid "You can't de-activate your own account"
238 240
 msgstr "Vous ne pouvez pas désactiver votre propre compte"
239 241
 
240
-#: tracim/controllers/admin/user.py:337
242
+#: tracim/controllers/admin/user.py:352
241 243
 msgid "User {} desactivated"
242 244
 msgstr "Utilisateur {} désactivé."
243 245
 
@@ -246,9 +248,7 @@ msgid "You can't remove yourself from this workgroup"
246 248
 msgstr "Vous ne pouvez pas vous retirer de cet espace de travail."
247 249
 
248 250
 #: tracim/controllers/admin/workspace.py:85
249
-msgid ""
250
-"User {} removed. You can <a class=\"alert-link\" href=\"{}\">restore "
251
-"it</a>"
251
+msgid "User {} removed. You can <a class=\"alert-link\" href=\"{}\">restore it</a>"
252 252
 msgstr "{} archivé(e). <a class=\"alert-link\" href=\"{}\">Annuler l'opération</a>"
253 253
 
254 254
 #: tracim/controllers/admin/workspace.py:93
@@ -280,12 +280,8 @@ msgid "{} workspace updated."
280 280
 msgstr "Espace de travail {} mis à jour."
281 281
 
282 282
 #: tracim/controllers/admin/workspace.py:251
283
-msgid ""
284
-"{} workspace deleted. In case of error, you can <a class=\"alert-link\" "
285
-"href=\"{}\">restore it</a>."
286
-msgstr ""
287
-"Espace de travail {} supprimé. Vous pouvez <a class=\"alert-link\" "
288
-"href=\"{}\">annuler l'opération</a>."
283
+msgid "{} workspace deleted. In case of error, you can <a class=\"alert-link\" href=\"{}\">restore it</a>."
284
+msgstr "Espace de travail {} supprimé. Vous pouvez <a class=\"alert-link\" href=\"{}\">annuler l'opération</a>."
289 285
 
290 286
 #: tracim/controllers/admin/workspace.py:264
291 287
 msgid "{} workspace restored."
@@ -296,164 +292,122 @@ msgstr "Espace de travail {} restauré."
296 292
 msgid "%B %d at %I:%M%p"
297 293
 msgstr "le %d %B à %H:%M"
298 294
 
299
-#: tracim/lib/helpers.py:71
300
-msgid "This is the current status."
301
-msgstr "C'est le statut actuel"
302
-
303
-#: tracim/lib/helpers.py:74
304
-msgid "The item is a normal document, like a howto or a text document."
305
-msgstr "L'élément est un document normal, comme un howto ou un document texte"
306
-
307
-#: tracim/lib/helpers.py:76
308
-msgid ""
309
-"The item will be automatically computed as \"in progress\" or \"done\" "
310
-"according to its children status."
311
-msgstr ""
312
-"L'élément sera automatiquement mis en \"En cours\" ou \"Fait\" suivant le"
313
-" statut de ses enfants."
314
-
315
-#: tracim/lib/helpers.py:78
316
-msgid "No action done on the item."
317
-msgstr "Aucun changement sur l'élément"
318
-
319
-#: tracim/lib/helpers.py:80
320
-msgid "The item is being worked on."
321
-msgstr "L'élément est en cours d'utilisation."
322
-
323
-#: tracim/lib/helpers.py:82
324
-msgid "Waiting for some external actions."
325
-msgstr "En attente d'actions externes."
326
-
327
-#: tracim/lib/helpers.py:84
328
-msgid "The work associated with the item is finished."
329
-msgstr "L'action associée à l'élément n'est pas terminée."
330
-
331
-#: tracim/lib/helpers.py:86
332
-msgid ""
333
-"Close the item if you want not to see it anymore. The data won't be "
334
-"deleted"
335
-msgstr ""
336
-"Fermez l'élément si vous ne voulez plus le voir. Les données ne seront "
337
-"pas supprimées"
338
-
339
-#: tracim/lib/helpers.py:88
340
-msgid "This status tells that the item has been deleted."
341
-msgstr "Ce status indique que l'élément a été supprimé."
342
-
343 295
 #: tracim/lib/predicates.py:12
344 296
 msgid "You are not authorized to access this resource"
345 297
 msgstr "Vous n'êtes pas autorisé à accéder à cette ressource"
346 298
 
347
-#: tracim/model/auth.py:131
299
+#: tracim/model/auth.py:85
348 300
 msgid "Nobody"
349 301
 msgstr "Personne"
350 302
 
351
-#: tracim/model/auth.py:132 tracim/templates/master_authenticated.mak:86
303
+#: tracim/model/auth.py:86
304
+#: tracim/templates/master_authenticated.mak:86
352 305
 #: tracim/templates/master_no_toolbar_no_login.mak:111
353 306
 #: tracim/templates/user_get_all.mak:13
354 307
 msgid "Users"
355 308
 msgstr "Utilisateurs"
356 309
 
357
-#: tracim/model/auth.py:133
310
+#: tracim/model/auth.py:87
358 311
 msgid "Global managers"
359 312
 msgstr "Managers globaux"
360 313
 
361
-#: tracim/model/auth.py:134
314
+#: tracim/model/auth.py:88
362 315
 msgid "Administrators"
363 316
 msgstr "Administrateurs"
364 317
 
365
-#: tracim/model/data.py:88
318
+#: tracim/model/data.py:79
366 319
 msgid "N/A"
367 320
 msgstr "N/A"
368 321
 
369
-#: tracim/model/data.py:89
322
+#: tracim/model/data.py:80
370 323
 #: tracim/templates/help/page-user-role-definition.mak:15
371 324
 msgid "Reader"
372 325
 msgstr "Lecteur"
373 326
 
374
-#: tracim/model/data.py:90
327
+#: tracim/model/data.py:81
375 328
 #: tracim/templates/help/page-user-role-definition.mak:19
376 329
 msgid "Contributor"
377 330
 msgstr "Contributeurs"
378 331
 
379
-#: tracim/model/data.py:91
332
+#: tracim/model/data.py:82
380 333
 #: tracim/templates/help/page-user-role-definition.mak:23
381 334
 msgid "Content Manager"
382 335
 msgstr "Gestionnaire de contenu"
383 336
 
384
-#: tracim/model/data.py:92
337
+#: tracim/model/data.py:83
385 338
 #: tracim/templates/help/page-user-role-definition.mak:27
386 339
 msgid "Workspace Manager"
387 340
 msgstr "Responsable"
388 341
 
389
-#: tracim/model/data.py:162
342
+#: tracim/model/data.py:153
390 343
 msgid "Item archived"
391 344
 msgstr "Element archivé"
392 345
 
393
-#: tracim/model/data.py:163
346
+#: tracim/model/data.py:154
394 347
 msgid "Item commented"
395 348
 msgstr "Element commenté"
396 349
 
397
-#: tracim/model/data.py:164
350
+#: tracim/model/data.py:155
398 351
 msgid "Item created"
399 352
 msgstr "Elément créé"
400 353
 
401
-#: tracim/model/data.py:165
354
+#: tracim/model/data.py:156
402 355
 msgid "Item deleted"
403 356
 msgstr "Element supprimé"
404 357
 
405
-#: tracim/model/data.py:166
358
+#: tracim/model/data.py:157
406 359
 msgid "Item modified"
407 360
 msgstr "Elément modifié"
408 361
 
409
-#: tracim/model/data.py:167
362
+#: tracim/model/data.py:158
410 363
 msgid "New revision"
411 364
 msgstr "Nouvelle version"
412 365
 
413
-#: tracim/model/data.py:168
366
+#: tracim/model/data.py:159
414 367
 msgid "Status modified"
415 368
 msgstr "Statut modifié"
416 369
 
417
-#: tracim/model/data.py:169
370
+#: tracim/model/data.py:160
418 371
 msgid "Item un-archived"
419 372
 msgstr "Elément désarchivé"
420 373
 
421
-#: tracim/model/data.py:170
374
+#: tracim/model/data.py:161
422 375
 msgid "Item undeleted"
423 376
 msgstr "Elément restauré"
424 377
 
425
-#: tracim/model/data.py:207 tracim/model/data.py:217
378
+#: tracim/model/data.py:198
379
+#: tracim/model/data.py:208
426 380
 msgid "work in progress"
427 381
 msgstr "travail en cours"
428 382
 
429
-#: tracim/model/data.py:208 tracim/model/data.py:218
383
+#: tracim/model/data.py:199
384
+#: tracim/model/data.py:209
430 385
 msgid "closed — validated"
431 386
 msgstr "clos(e) — validé(e)"
432 387
 
433
-#: tracim/model/data.py:209 tracim/model/data.py:219
388
+#: tracim/model/data.py:200
389
+#: tracim/model/data.py:210
434 390
 msgid "closed — cancelled"
435 391
 msgstr "clos(e) — annulé(e)"
436 392
 
437
-#: tracim/model/data.py:210 tracim/model/data.py:215 tracim/model/data.py:220
393
+#: tracim/model/data.py:201
394
+#: tracim/model/data.py:206
395
+#: tracim/model/data.py:211
438 396
 msgid "deprecated"
439 397
 msgstr "obsolète"
440 398
 
441
-#: tracim/model/data.py:212
399
+#: tracim/model/data.py:203
442 400
 msgid "subject in progress"
443 401
 msgstr "discussion en cours"
444 402
 
445
-#: tracim/model/data.py:213
403
+#: tracim/model/data.py:204
446 404
 msgid "subject closed — resolved"
447 405
 msgstr "discussion close — résolue"
448 406
 
449
-#: tracim/model/data.py:214
407
+#: tracim/model/data.py:205
450 408
 msgid "subject closed — cancelled"
451 409
 msgstr "discussion close — annulée"
452 410
 
453
-#: tracim/model/data.py:711
454
-msgid "Titleless Document"
455
-msgstr "Document sans titre"
456
-
457 411
 #: tracim/templates/create_account.mak:5
458 412
 msgid "Create account"
459 413
 msgstr "Créer un compte"
@@ -480,7 +434,8 @@ msgstr "Accéder à mes espaces de travail"
480 434
 msgid "Go to my profile"
481 435
 msgstr "Consulter mon profil"
482 436
 
483
-#: tracim/templates/error.mak:23 tracim/templates/error_authenticated.mak:20
437
+#: tracim/templates/error.mak:23
438
+#: tracim/templates/error_authenticated.mak:20
484 439
 msgid "Something went wrong!"
485 440
 msgstr "Quelque chose s'est mal passé"
486 441
 
@@ -496,7 +451,8 @@ msgstr "de retourner à la page d'accueil"
496 451
 msgid "to contact the support"
497 452
 msgstr "de contacter le support"
498 453
 
499
-#: tracim/templates/error.mak:44 tracim/templates/error_authenticated.mak:42
454
+#: tracim/templates/error.mak:44
455
+#: tracim/templates/error_authenticated.mak:42
500 456
 msgid "The error was: error {code}"
501 457
 msgstr "L'erreur était : erreur {code}"
502 458
 
@@ -540,7 +496,8 @@ msgstr "Modifier le dossier courant"
540 496
 msgid "Move current folder"
541 497
 msgstr "Déplacer le dossier courant"
542 498
 
543
-#: tracim/templates/index.mak:25 tracim/templates/index.mak:46
499
+#: tracim/templates/index.mak:25
500
+#: tracim/templates/index.mak:46
544 501
 #: tracim/templates/master_authenticated.mak:138
545 502
 msgid "Login"
546 503
 msgstr "Login"
@@ -568,9 +525,7 @@ msgstr "Déplacer le dossier"
568 525
 #: tracim/templates/master_anonymous.mak:67
569 526
 #: tracim/templates/master_authenticated.mak:48
570 527
 msgid "Create your own email-ready collaborative workspace on trac.im"
571
-msgstr ""
572
-"Créez votre propre espace de travail collaboratif compatible avec l'email"
573
-" sur trac.im"
528
+msgstr "Créez votre propre espace de travail collaboratif compatible avec l'email sur trac.im"
574 529
 
575 530
 #: tracim/templates/master_authenticated.mak:80
576 531
 #: tracim/templates/master_no_toolbar_no_login.mak:106
@@ -673,7 +628,8 @@ msgstr "Supprimer la discussion"
673 628
 msgid "Create a user account..."
674 629
 msgstr "Créer un compte utilisateur"
675 630
 
676
-#: tracim/templates/user_get_all.mak:29 tracim/templates/user_get_all.mak:30
631
+#: tracim/templates/user_get_all.mak:29
632
+#: tracim/templates/user_get_all.mak:30
677 633
 #: tracim/templates/user_workspace_folder_file_get_one.mak:70
678 634
 #: tracim/templates/user_workspace_forms.mak:43
679 635
 #: tracim/templates/user_workspace_forms.mak:44
@@ -687,7 +643,8 @@ msgstr "Créer un compte utilisateur"
687 643
 msgid "Name"
688 644
 msgstr "Nom"
689 645
 
690
-#: tracim/templates/user_get_all.mak:33 tracim/templates/user_get_all.mak:93
646
+#: tracim/templates/user_get_all.mak:33
647
+#: tracim/templates/user_get_all.mak:93
691 648
 #: tracim/templates/user_workspace_forms.mak:227
692 649
 #: tracim/templates/workspace_get_one.mak:87
693 650
 msgid "Email"
@@ -734,14 +691,8 @@ msgstr "Valider"
734 691
 
735 692
 #: tracim/templates/user_get_all.mak:86
736 693
 #: tracim/templates/workspace_get_all.mak:58
737
-msgid ""
738
-"There are no workspace yet. Start by <a class=\"alert-link\" data-"
739
-"toggle=\"collapse\" data-target=\"#create-workspace-form\">creating a "
740
-"workspace</a>."
741
-msgstr ""
742
-"Il n'y a pas encore d'espaces de travail. Commencez en <a class=\"alert-"
743
-"link\" data-toggle=\"collapse\" data-target=\"#create-workspace-"
744
-"form\">créant un espace de travail</a>."
694
+msgid "There are no workspace yet. Start by <a class=\"alert-link\" data-toggle=\"collapse\" data-target=\"#create-workspace-form\">creating a workspace</a>."
695
+msgstr "Il n'y a pas encore d'espaces de travail. Commencez en <a class=\"alert-link\" data-toggle=\"collapse\" data-target=\"#create-workspace-form\">créant un espace de travail</a>."
745 696
 
746 697
 #: tracim/templates/user_get_all.mak:92
747 698
 #: tracim/templates/workspace_get_one.mak:42
@@ -765,16 +716,19 @@ msgstr "Utilisateur activé. Cliquez pour le désactiver"
765 716
 msgid "User disabled. Click to enable this user"
766 717
 msgstr "Utilisateur désactivé. Cliquez pour l'activer"
767 718
 
768
-#: tracim/templates/user_get_me.mak:5 tracim/templates/user_get_one.mak:5
719
+#: tracim/templates/user_get_me.mak:5
720
+#: tracim/templates/user_get_one.mak:5
769 721
 #: tracim/templates/user_profile.mak:5
770 722
 msgid "My profile"
771 723
 msgstr "Mon profil"
772 724
 
773
-#: tracim/templates/user_get_me.mak:26 tracim/templates/user_get_one.mak:26
725
+#: tracim/templates/user_get_me.mak:26
726
+#: tracim/templates/user_get_one.mak:26
774 727
 msgid "This user can create workspaces."
775 728
 msgstr "Cet utilisateur peut créer des espaces de travail."
776 729
 
777
-#: tracim/templates/user_get_me.mak:29 tracim/templates/user_get_one.mak:29
730
+#: tracim/templates/user_get_me.mak:29
731
+#: tracim/templates/user_get_one.mak:29
778 732
 msgid "This user is an administrator."
779 733
 msgstr "Cet utilisateur est un administrateur"
780 734
 
@@ -784,7 +738,12 @@ msgstr "Cet utilisateur est un administrateur"
784 738
 msgid "My workspaces"
785 739
 msgstr "Mes espaces de travail"
786 740
 
787
-#: tracim/templates/user_get_me.mak:42 tracim/templates/user_get_one.mak:42
741
+#: tracim/templates/user_get_me.mak:42
742
+#| msgid "This user is not member of any workspace."
743
+msgid "You are not member of any workspace."
744
+msgstr "Vous n'êtes membre d'aucun espace de travail"
745
+
746
+#: tracim/templates/user_get_one.mak:42
788 747
 #: tracim/templates/user_profile.mak:42
789 748
 msgid "This user is not member of any workspace."
790 749
 msgstr "Cet utilisateur n'est membre d'aucun espace de travail"
@@ -848,12 +807,8 @@ msgid "Write here the file content"
848 807
 msgstr "Indiquez le contenu du fichier"
849 808
 
850 809
 #: tracim/templates/user_workspace_folder_file_get_one.mak:33
851
-msgid ""
852
-"You are reading <b>an old revision</b> of the current file. (the shown "
853
-"revision is r{})."
854
-msgstr ""
855
-"Vous consultez <b>une ancienne version</b> du fichier courant. (la "
856
-"version affichée est v{})."
810
+msgid "You are reading <b>an old revision</b> of the current file. (the shown revision is r{})."
811
+msgstr "Vous consultez <b>une ancienne version</b> du fichier courant. (la version affichée est v{})."
857 812
 
858 813
 #: tracim/templates/user_workspace_folder_file_get_one.mak:34
859 814
 #: tracim/templates/user_workspace_folder_page_get_one.mak:35
@@ -906,12 +861,8 @@ msgid "Description"
906 861
 msgstr "Description"
907 862
 
908 863
 #: tracim/templates/user_workspace_folder_file_get_one.mak:99
909
-msgid ""
910
-"<b>Note</b>: You need to change status in case you want to upload a new "
911
-"version"
912
-msgstr ""
913
-"<b>Note</b>: Vous devez changer le statut du fichier avant de pouvoir "
914
-"télécharger une nouvelle version"
864
+msgid "<b>Note</b>: You need to change status in case you want to upload a new version"
865
+msgstr "<b>Note</b>: Vous devez changer le statut du fichier avant de pouvoir télécharger une nouvelle version"
915 866
 
916 867
 #: tracim/templates/user_workspace_folder_file_get_one.mak:104
917 868
 #: tracim/templates/user_workspace_folder_file_get_one.mak:108
@@ -1011,12 +962,8 @@ msgid "Write here the page content"
1011 962
 msgstr "Rédigez le contenu de la page"
1012 963
 
1013 964
 #: tracim/templates/user_workspace_folder_page_get_one.mak:34
1014
-msgid ""
1015
-"You are reading <b>an old revision</b> of the current page. (the shown "
1016
-"revision is r{})."
1017
-msgstr ""
1018
-"Vous consultez <b>une ancienne version</b> de al page courante. (la "
1019
-"version affichée est la v{})."
965
+msgid "You are reading <b>an old revision</b> of the current page. (the shown revision is r{})."
966
+msgstr "Vous consultez <b>une ancienne version</b> de al page courante. (la version affichée est la v{})."
1020 967
 
1021 968
 #: tracim/templates/user_workspace_folder_page_get_one.mak:65
1022 969
 msgid "Links extracted from the page"
@@ -1045,12 +992,8 @@ msgid "Optionnaly, you can describe the subject"
1045 992
 msgstr "Vous pouvez décrire le sujet (facultatif)"
1046 993
 
1047 994
 #: tracim/templates/user_workspace_folder_thread_get_one.mak:64
1048
-msgid ""
1049
-"<b>Note</b>: In case you'd like to post a reply, you must first open "
1050
-"again the thread"
1051
-msgstr ""
1052
-"<b>Note</b> : si vous souhaitez commenter cette discussion, vous devez "
1053
-"tout d'abord en modifier le statut pour la ré-ouvrir"
995
+msgid "<b>Note</b>: In case you'd like to post a reply, you must first open again the thread"
996
+msgstr "<b>Note</b> : si vous souhaitez commenter cette discussion, vous devez tout d'abord en modifier le statut pour la ré-ouvrir"
1054 997
 
1055 998
 #: tracim/templates/user_workspace_folder_thread_get_one.mak:69
1056 999
 msgid "Post a reply..."
@@ -1262,14 +1205,8 @@ msgid "Role"
1262 1205
 msgstr "Rôle"
1263 1206
 
1264 1207
 #: tracim/templates/workspace_get_one.mak:80
1265
-msgid ""
1266
-"There are no user associated to the current workspace. Start by <a class"
1267
-"=\"alert-link\" data-toggle=\"collapse\" data-target=\"#add-role-from-"
1268
-"existing-user-form\">adding members</a>  to the workspace."
1269
-msgstr ""
1270
-"Il n'y a aucun membre dans cet espace de travail. Commencez par <a class"
1271
-"=\"alert-link\" data-toggle=\"collapse\" data-target=\"#add-role-from-"
1272
-"existing-user-form\">ajouter des membres</a> à cet espace."
1208
+msgid "There are no user associated to the current workspace. Start by <a class=\"alert-link\" data-toggle=\"collapse\" data-target=\"#add-role-from-existing-user-form\">adding members</a>  to the workspace."
1209
+msgstr "Il n'y a aucun membre dans cet espace de travail. Commencez par <a class=\"alert-link\" data-toggle=\"collapse\" data-target=\"#add-role-from-existing-user-form\">ajouter des membres</a> à cet espace."
1273 1210
 
1274 1211
 #: tracim/templates/workspace_get_one.mak:107
1275 1212
 msgid "Change role to..."
@@ -1325,38 +1262,23 @@ msgstr "Les utilisateurs ont un rôle spécifique à chaque espace de travail."
1325 1262
 
1326 1263
 #: tracim/templates/help/page-user-role-definition.mak:11
1327 1264
 msgid "Roles give progressive rights on what the user can or can't do."
1328
-msgstr ""
1329
-"Les rôles donnent des droits progressifs qui déterminent ce que "
1330
-"l'utilisateur peut ou ne peut pas faire."
1265
+msgstr "Les rôles donnent des droits progressifs qui déterminent ce que l'utilisateur peut ou ne peut pas faire."
1331 1266
 
1332 1267
 #: tracim/templates/help/page-user-role-definition.mak:16
1333 1268
 msgid "This role gives read-only access to workspace resources."
1334 1269
 msgstr "Ce rôle donne accès en lecteur aux informations de l'espace de travail."
1335 1270
 
1336 1271
 #: tracim/templates/help/page-user-role-definition.mak:20
1337
-msgid ""
1338
-"Same as <span style=\"color: #1fdb11;\">reader</span> + contribution "
1339
-"rights: edit, comments, status update."
1340
-msgstr ""
1341
-"Comme <span style=\"color: #1fdb11;\">lecteur</span> + contribution : "
1342
-"modifications, commentaires, mise à jour du statut."
1272
+msgid "Same as <span style=\"color: #1fdb11;\">reader</span> + contribution rights: edit, comments, status update."
1273
+msgstr "Comme <span style=\"color: #1fdb11;\">lecteur</span> + contribution : modifications, commentaires, mise à jour du statut."
1343 1274
 
1344 1275
 #: tracim/templates/help/page-user-role-definition.mak:24
1345
-msgid ""
1346
-"Same as <span style=\"color: #759ac5;\">contributor</span> + content "
1347
-"management rights: move content, folders management, delete archive "
1348
-"operations."
1349
-msgstr ""
1350
-"Comme <span style=\"color: #759ac5;\">contributeur</span> + gestion du "
1351
-"contenu : déplacer le contenu, les dossiers"
1276
+msgid "Same as <span style=\"color: #759ac5;\">contributor</span> + content management rights: move content, folders management, delete archive operations."
1277
+msgstr "Comme <span style=\"color: #759ac5;\">contributeur</span> + gestion du contenu : déplacer le contenu, les dossiers"
1352 1278
 
1353 1279
 #: tracim/templates/help/page-user-role-definition.mak:28
1354
-msgid ""
1355
-"Same as <span style=\"color: #ea983d;\">content manager</span> + "
1356
-"workspace management rights: edit workspace, invite users, revoke them."
1357
-msgstr ""
1358
-"Comme <span style=\"color: #ea983d;\">gestionnaire de contenu</span> + "
1359
-"modification de l'espace de travail."
1280
+msgid "Same as <span style=\"color: #ea983d;\">content manager</span> + workspace management rights: edit workspace, invite users, revoke them."
1281
+msgstr "Comme <span style=\"color: #ea983d;\">gestionnaire de contenu</span> + modification de l'espace de travail."
1360 1282
 
1361 1283
 #~ msgid "You have no document yet."
1362 1284
 #~ msgstr "Vous n'avez pas de document pour le moment."
@@ -1877,3 +1799,44 @@ msgstr ""
1877 1799
 #~ msgid "page"
1878 1800
 #~ msgstr "page"
1879 1801
 
1802
+#~ msgid "This is the current status."
1803
+#~ msgstr "C'est le statut actuel"
1804
+
1805
+#~ msgid "The item is a normal document, like a howto or a text document."
1806
+#~ msgstr "L'élément est un document normal, comme un howto ou un document texte"
1807
+
1808
+#~ msgid ""
1809
+#~ "The item will be automatically computed"
1810
+#~ " as \"in progress\" or \"done\" "
1811
+#~ "according to its children status."
1812
+#~ msgstr ""
1813
+#~ "L'élément sera automatiquement mis en "
1814
+#~ "\"En cours\" ou \"Fait\" suivant le "
1815
+#~ "statut de ses enfants."
1816
+
1817
+#~ msgid "No action done on the item."
1818
+#~ msgstr "Aucun changement sur l'élément"
1819
+
1820
+#~ msgid "The item is being worked on."
1821
+#~ msgstr "L'élément est en cours d'utilisation."
1822
+
1823
+#~ msgid "Waiting for some external actions."
1824
+#~ msgstr "En attente d'actions externes."
1825
+
1826
+#~ msgid "The work associated with the item is finished."
1827
+#~ msgstr "L'action associée à l'élément n'est pas terminée."
1828
+
1829
+#~ msgid ""
1830
+#~ "Close the item if you want not "
1831
+#~ "to see it anymore. The data won't"
1832
+#~ " be deleted"
1833
+#~ msgstr ""
1834
+#~ "Fermez l'élément si vous ne voulez "
1835
+#~ "plus le voir. Les données ne "
1836
+#~ "seront pas supprimées"
1837
+
1838
+#~ msgid "This status tells that the item has been deleted."
1839
+#~ msgstr "Ce status indique que l'élément a été supprimé."
1840
+
1841
+#~ msgid "Titleless Document"
1842
+#~ msgstr "Document sans titre"

+ 3 - 0
tracim/tracim/lib/__init__.py View File

@@ -1,6 +1,9 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 from tg.i18n import lazy_ugettext as l_
3 3
 
4
+class NotFoundError(Exception):
5
+    pass
6
+
4 7
 class CST(object):
5 8
     STATUS_ERROR = 'error'
6 9
     STATUS_OK = 'ok'

+ 0 - 65
tracim/tracim/lib/auth.py View File

@@ -1,65 +0,0 @@
1
-# -*- coding: utf-8 -*-
2
-"""Predicates for authorizations"""
3
-from tg.predicates import Predicate
4
-from tracim.model import DBSession as session
5
-from tracim.model.auth import Permission, User
6
-import logging as l
7
-
8
-DIRTY_canReadOrCanWriteSqlQuery = """
9
-SELECT
10
-    pgn.node_id
11
-FROM
12
-    pod_group_node AS pgn
13
-    JOIN pod_nodes AS pn ON pn.node_id = pgn.node_id AND pn.is_shared = 't'
14
-    JOIN pod_user_group AS pug ON pug.group_id = pgn.group_id
15
-    JOIN pod_user AS pu ON pug.user_id = pu.user_id
16
-WHERE
17
-    rights > :excluded_right_low_level
18
-    AND email_address = :email
19
-    AND pgn.node_id = :node_id
20
-UNION
21
-    SELECT
22
-        pnn.node_id
23
-    FROM
24
-        pod_nodes AS pnn,
25
-        pod_user AS puu
26
-    WHERE
27
-        pnn.node_id = :node_id
28
-        AND pnn.owner_id = puu.user_id
29
-        AND puu.email_address = :email
30
-"""
31
-
32
-class can_read(Predicate):
33
-    message = ""
34
-
35
-    def __init__(self, **kwargs):
36
-        pass
37
-
38
-    def evaluate(self, environ, credentials):
39
-        if 'node_id' in environ['webob.adhoc_attrs']['validation']['values']:
40
-            node_id = environ['webob.adhoc_attrs']['validation']['values']['node_id']
41
-            if node_id!=0:
42
-                has_right = session.execute(
43
-                    DIRTY_canReadOrCanWriteSqlQuery,
44
-                    {"email":credentials["repoze.who.userid"], "node_id":node_id, "excluded_right_low_level": 0}
45
-                )
46
-                if has_right.rowcount == 0 :
47
-                    l.info("User {} don't have read right on node {}".format(credentials["repoze.who.userid"], node_id))
48
-                    self.unmet()
49
-
50
-class can_write(Predicate):
51
-    message = ""
52
-
53
-    def __init__(self, **kwargs):
54
-        pass
55
-
56
-    def evaluate(self, environ, credentials):
57
-        node_id = environ['webob.adhoc_attrs']['validation']['values']['node_id']
58
-        if node_id!=0:
59
-            has_right = session.execute(
60
-                DIRTY_canReadOrCanWriteSqlQuery,
61
-                {"email":credentials["repoze.who.userid"], "node_id":node_id, "excluded_right_low_level": 1}
62
-            )
63
-            if has_right.rowcount == 0 :
64
-                self.unmet()
65
-

+ 0 - 4
tracim/tracim/lib/base.py View File

@@ -96,10 +96,6 @@ class BaseController(TGController):
96 96
         :param id:
97 97
         :return:
98 98
         """
99
-        print('***********************************')
100
-        print("searching for an id")
101
-        print(cls)
102
-        print(cls)
103 99
         return getattr(tmpl_context, cls.current_item_id_key_in_context(), '')
104 100
 
105 101
     def back_with_error(self, message):

+ 63 - 68
tracim/tracim/lib/content.py View File

@@ -8,8 +8,8 @@ from sqlalchemy.orm.attributes import get_history
8 8
 from tracim.model import DBSession
9 9
 from tracim.model.auth import User
10 10
 from tracim.model.data import ContentStatus, ContentRevisionRO, ActionDescription
11
-from tracim.model.data import PBNode
12
-from tracim.model.data import PBNodeType
11
+from tracim.model.data import Content
12
+from tracim.model.data import ContentType
13 13
 from tracim.model.data import Workspace
14 14
 
15 15
 class ContentApi(object):
@@ -20,27 +20,25 @@ class ContentApi(object):
20 20
         self._show_deleted = show_deleted
21 21
 
22 22
     def _base_query(self, workspace: Workspace=None):
23
-        result = DBSession.query(PBNode)
23
+        result = DBSession.query(Content)
24 24
         if workspace:
25
-            result = result.filter(PBNode.workspace_id==workspace.workspace_id)
25
+            result = result.filter(Content.workspace_id==workspace.workspace_id)
26 26
 
27 27
         if not self._show_deleted:
28
-            result = result.filter(PBNode.is_deleted==False)
28
+            result = result.filter(Content.is_deleted==False)
29 29
 
30 30
         if not self._show_archived:
31
-            result = result.filter(PBNode.is_archived==False)
31
+            result = result.filter(Content.is_archived==False)
32 32
 
33 33
         return result
34 34
 
35
-    def get_child_folders(self, parent: PBNode=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[]) -> [PBNode]:
36
-        # assert parent is None or isinstance(parent, PBNode) # DYN_REMOVE
37
-        # assert workspace is None or isinstance(workspace, Workspace) # DYN_REMOVE
35
+    def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[]) -> [Content]:
38 36
 
39
-        parent_id = parent.node_id if parent else None
37
+        parent_id = parent.content_id if parent else None
40 38
         folders = self._base_query(workspace).\
41
-            filter(PBNode.parent_id==parent_id).\
42
-            filter(PBNode.node_type==PBNodeType.Folder).\
43
-            filter(PBNode.node_id.notin_(removed_item_ids)).\
39
+            filter(Content.parent_id==parent_id).\
40
+            filter(Content.type==ContentType.Folder).\
41
+            filter(Content.content_id.notin_(removed_item_ids)).\
44 42
             all()
45 43
 
46 44
         if not filter_by_allowed_content_types or len(filter_by_allowed_content_types)<=0:
@@ -50,46 +48,43 @@ class ContentApi(object):
50 48
         result = []
51 49
         for folder in folders:
52 50
             for allowed_content_type in filter_by_allowed_content_types:
53
-                print('ALLOWED = ', filter_by_allowed_content_types)
54
-                print('CONTENT = ', folder.properties['allowed_content'])
55
-                # exit()
56 51
                 if folder.properties['allowed_content'][allowed_content_type]==True:
57 52
                     result.append(folder)
58 53
 
59 54
         return result
60 55
 
61
-    def create(self, content_type: str, workspace: Workspace=None, parent: PBNode=None, label:str ='', do_save=False) -> PBNode:
62
-        assert content_type in PBNodeType.allowed_types()
63
-        content = PBNode()
56
+    def create(self, content_type: str, workspace: Workspace=None, parent: Content=None, label:str ='', do_save=False) -> Content:
57
+        assert content_type in ContentType.allowed_types()
58
+        content = Content()
64 59
         content.owner = self._user
65 60
         content.parent = parent
66 61
         content.workspace = workspace
67
-        content.node_type = content_type
68
-        content.data_label = label
69
-        content.last_action = ActionDescription.CREATION
62
+        content.type = content_type
63
+        content.label = label
64
+        content.revision_type = ActionDescription.CREATION
70 65
 
71 66
         if do_save:
72 67
             self.save(content)
73 68
         return content
74 69
 
75 70
 
76
-    def create_comment(self, workspace: Workspace=None, parent: PBNode=None, content:str ='', do_save=False) -> PBNode:
77
-        assert parent  and parent.node_type!=PBNodeType.Folder
78
-        item = PBNode()
71
+    def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
72
+        assert parent  and parent.type!=ContentType.Folder
73
+        item = Content()
79 74
         item.owner = self._user
80 75
         item.parent = parent
81 76
         item.workspace = workspace
82
-        item.node_type = PBNodeType.Comment
83
-        item.data_content = content
84
-        item.data_label = ''
85
-        item.last_action = ActionDescription.COMMENT
77
+        item.type = ContentType.Comment
78
+        item.description = content
79
+        item.label = ''
80
+        item.revision_type = ActionDescription.COMMENT
86 81
 
87 82
         if do_save:
88 83
             self.save(item)
89 84
         return content
90 85
 
91 86
 
92
-    def get_one_from_revision(self, content_id: int, content_type: str, workspace: Workspace=None, revision_id=None) -> PBNode:
87
+    def get_one_from_revision(self, content_id: int, content_type: str, workspace: Workspace=None, revision_id=None) -> Content:
93 88
         """
94 89
         This method is a hack to convert a node revision item into a node
95 90
         :param content_id:
@@ -100,16 +95,16 @@ class ContentApi(object):
100 95
         """
101 96
 
102 97
         content = self.get_one(content_id, content_type, workspace)
103
-        revision = DBSession.query(ContentRevisionRO).filter(ContentRevisionRO.version_id==revision_id).one()
98
+        revision = DBSession.query(ContentRevisionRO).filter(ContentRevisionRO.revision_id==revision_id).one()
104 99
 
105
-        if revision.node_id==content.node_id:
106
-            content.revision_to_serialize = revision.version_id
100
+        if revision.content_id==content.content_id:
101
+            content.revision_to_serialize = revision.revision_id
107 102
         else:
108 103
             raise ValueError('Revision not found for given content')
109 104
 
110 105
         return content
111 106
 
112
-    def get_one(self, content_id: int, content_type: str, workspace: Workspace=None) -> PBNode:
107
+    def get_one(self, content_id: int, content_type: str, workspace: Workspace=None) -> Content:
113 108
         assert content_id is None or isinstance(content_id, int) # DYN_REMOVE
114 109
         assert content_type is not None # DYN_REMOVE
115 110
         assert isinstance(content_type, str) # DYN_REMOVE
@@ -117,17 +112,17 @@ class ContentApi(object):
117 112
         if not content_id:
118 113
             return
119 114
 
120
-        if content_type==PBNodeType.Any:
115
+        if content_type==ContentType.Any:
121 116
             return self._base_query(workspace).\
122
-                filter(PBNode.node_id==content_id).\
117
+                filter(Content.content_id==content_id).\
123 118
                 one()
124 119
 
125 120
         return self._base_query(workspace).\
126
-            filter(PBNode.node_id==content_id).\
127
-            filter(PBNode.node_type==content_type).\
121
+            filter(Content.content_id==content_id).\
122
+            filter(Content.type==content_type).\
128 123
             one()
129 124
 
130
-    def get_all(self, parent_id: int, content_type: str, workspace: Workspace=None) -> PBNode:
125
+    def get_all(self, parent_id: int, content_type: str, workspace: Workspace=None) -> Content:
131 126
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
132 127
         assert content_type is not None # DYN_REMOVE
133 128
         assert isinstance(content_type, str) # DYN_REMOVE
@@ -136,11 +131,11 @@ class ContentApi(object):
136 131
             return
137 132
 
138 133
         return self._base_query(workspace).\
139
-            filter(PBNode.parent_id==parent_id).\
140
-            filter(PBNode.node_type==content_type).\
134
+            filter(Content.parent_id==parent_id).\
135
+            filter(Content.type==content_type).\
141 136
             all()
142 137
 
143
-    def set_allowed_content(self, folder: PBNode, allowed_content_dict:dict):
138
+    def set_allowed_content(self, folder: Content, allowed_content_dict:dict):
144 139
         """
145 140
         :param folder: the given folder instance
146 141
         :param allowed_content_dict: must be something like this:
@@ -152,7 +147,7 @@ class ContentApi(object):
152 147
             )
153 148
         :return:
154 149
         """
155
-        assert folder.node_type==PBNodeType.Folder
150
+        assert folder.type==ContentType.Folder
156 151
         assert 'file' in allowed_content_dict.keys()
157 152
         assert 'folder' in allowed_content_dict.keys()
158 153
         assert 'page' in allowed_content_dict.keys()
@@ -162,55 +157,55 @@ class ContentApi(object):
162 157
         folder.properties = properties
163 158
 
164 159
 
165
-    def set_status(self, content: PBNode, new_status: str):
160
+    def set_status(self, content: Content, new_status: str):
166 161
         if new_status in ContentStatus.allowed_values():
167
-            content.node_status = new_status
168
-            content.last_action = ActionDescription.STATUS_UPDATE
162
+            content.status = new_status
163
+            content.revision_type = ActionDescription.STATUS_UPDATE
169 164
         else:
170 165
             raise ValueError('The given value {} is not allowed'.format(new_status))
171 166
 
172 167
 
173
-    def move(self, item: PBNode, new_parent: PBNode, must_stay_in_same_workspace:bool=True):
168
+    def move(self, item: Content, new_parent: Content, must_stay_in_same_workspace:bool=True):
174 169
         if must_stay_in_same_workspace:
175 170
             if new_parent and new_parent.workspace_id!=item.workspace_id:
176 171
                 raise ValueError('the item should stay in the same workspace')
177 172
 
178 173
         item.parent = new_parent
179
-        item.last_action = ActionDescription.EDITION
174
+        item.revision_type = ActionDescription.EDITION
180 175
 
181 176
 
182
-    def update_content(self, item: PBNode, new_label: str, new_content: str) -> PBNode:
183
-        item.data_label = new_label
184
-        item.data_content = new_content # TODO: convert urls into links
185
-        item.last_action = ActionDescription.EDITION
177
+    def update_content(self, item: Content, new_label: str, new_content: str) -> Content:
178
+        item.label = new_label
179
+        item.description = new_content # TODO: convert urls into links
180
+        item.revision_type = ActionDescription.EDITION
186 181
         return item
187 182
 
188
-    def update_file_data(self, item: PBNode, new_filename: str, new_mimetype: str, new_file_content) -> PBNode:
189
-        item.data_file_name = new_filename
190
-        item.data_file_mime_type = new_mimetype
191
-        item.data_file_content = new_file_content
183
+    def update_file_data(self, item: Content, new_filename: str, new_mimetype: str, new_file_content) -> Content:
184
+        item.file_name = new_filename
185
+        item.file_mimetype = new_mimetype
186
+        item.file_content = new_file_content
192 187
         return item
193 188
 
194
-    def archive(self, content: PBNode):
189
+    def archive(self, content: Content):
195 190
         content.is_archived = True
196
-        content.last_action = ActionDescription.ARCHIVING
191
+        content.revision_type = ActionDescription.ARCHIVING
197 192
 
198
-    def unarchive(self, content: PBNode):
193
+    def unarchive(self, content: Content):
199 194
         content.is_archived = False
200
-        content.last_action = ActionDescription.UNARCHIVING
195
+        content.revision_type = ActionDescription.UNARCHIVING
201 196
 
202 197
 
203
-    def delete(self, content: PBNode):
198
+    def delete(self, content: Content):
204 199
         content.is_deleted = True
205
-        content.last_action = ActionDescription.DELETION
200
+        content.revision_type = ActionDescription.DELETION
206 201
 
207
-    def undelete(self, content: PBNode):
202
+    def undelete(self, content: Content):
208 203
         content.is_deleted = False
209
-        content.last_action = ActionDescription.UNDELETION
204
+        content.revision_type = ActionDescription.UNDELETION
210 205
 
211
-    def save(self, content: PBNode, action_description: str=None, do_flush=True):
206
+    def save(self, content: Content, action_description: str=None, do_flush=True):
212 207
         """
213
-        Save an object, flush the session and set the last_action property
208
+        Save an object, flush the session and set the revision_type property
214 209
         :param content:
215 210
         :param action_description:
216 211
         :return:
@@ -219,11 +214,11 @@ class ContentApi(object):
219 214
 
220 215
         if not action_description:
221 216
             # See if the last action has been modified
222
-            if content.last_action==None or len(get_history(content, 'last_action'))<=0:
217
+            if content.revision_type==None or len(get_history(content, 'revision_type'))<=0:
223 218
                 # The action has not been modified, so we set it to default edition
224 219
                 action_description = ActionDescription.EDITION
225 220
 
226
-        content.last_action = action_description
221
+        content.revision_type = action_description
227 222
 
228 223
         if do_flush:
229 224
             DBSession.flush()

+ 0 - 432
tracim/tracim/lib/dbapi.py View File

@@ -1,432 +0,0 @@
1
-# -*- coding: utf-8 -*-
2
-"""
3
-"""
4
-import os
5
-from datetime import datetime
6
-from hashlib import sha256
7
-
8
-from sqlalchemy import Table, ForeignKey, Column
9
-from sqlalchemy.types import Unicode, Integer, DateTime, Text
10
-from sqlalchemy.orm import relation, synonym
11
-from sqlalchemy.orm import joinedload_all
12
-import sqlalchemy.orm as sqlao
13
-import sqlalchemy as sqla
14
-
15
-from tracim.model import data as pbmd
16
-from tracim.model import auth as pbma
17
-import tracim.model as pbm
18
-
19
-from tracim.model.auth import User, Group
20
-
21
-import tg
22
-
23
-FIXME_ERROR_CODE=-1
24
-
25
-class PODUserFilteredApiController(object):
26
-  
27
-  def __init__(self, piUserId, piExtraUserIdList=[]):
28
-    self._iCurrentUserId       = piUserId
29
-    self._iExtraUserIdList     = piExtraUserIdList
30
-    self._iUserIdFilteringList = None
31
-    self._cache_allowed_nodes = None
32
-
33
-  def _getUserIdListForFiltering(self):
34
-    if self._iUserIdFilteringList==None:
35
-      self._iUserIdFilteringList = list()
36
-      self._iUserIdFilteringList.append(self._iCurrentUserId)
37
-      for liUserId in self._iExtraUserIdList:
38
-        self._iUserIdFilteringList.append(liUserId)
39
-    return self._iUserIdFilteringList
40
-
41
-
42
-  def createNode(self, parent_id=0, inherit_rights=True):
43
-    loNode          = pbmd.PBNode()
44
-    loNode.owner_id = self._iCurrentUserId
45
-    if int(parent_id)!=0:
46
-        loNode.parent_id = parent_id
47
-
48
-    if inherit_rights:
49
-        parent_node = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.node_id==parent_id).one()
50
-        self.copy_rights(parent_node, loNode)
51
-
52
-    pbm.DBSession.add(loNode)
53
-
54
-    return loNode
55
-
56
-  def copy_rights(self, from_node: pbmd.PBNode, to_node: pbmd.PBNode, copy_also_is_shared=True):
57
-    """
58
-    copy rights from first node to second one
59
-    """
60
-    for parent_right in from_node._lRights:
61
-        new_right = self.createRight()
62
-        new_right.group_id = parent_right.group_id
63
-        new_right.rights = parent_right.rights
64
-        to_node._lRights.append(new_right)
65
-
66
-    if copy_also_is_shared:
67
-        to_node.is_shared = from_node.is_shared
68
-
69
-  def createDummyNode(self, parent_id, inherit_rights=True):
70
-    loNewNode = self.createNode(parent_id, inherit_rights)
71
-    loNewNode.data_label   = ''
72
-    loNewNode.data_content = ''
73
-    return loNewNode
74
-
75
-
76
-  def getNode(self, liNodeId: int) -> pbmd.PBNode:
77
-
78
-    lsSqlSelectQuery = """pod_nodes.node_id IN
79
-        (SELECT
80
-            pgn.node_id
81
-        FROM
82
-            pod_group_node AS pgn
83
-            join pod_user_group AS pug ON pug.group_id = pgn.group_id
84
-            join pod_user AS pu ON pug.user_id = pu.user_id
85
-        WHERE
86
-            rights > 0
87
-            AND pu.user_id = %s)
88
-    """
89
-    lsNodeIdFiltering = lsSqlSelectQuery % (str(self._iCurrentUserId))
90
-
91
-    if liNodeId!=None and liNodeId!=0:
92
-      return pbm.DBSession.query(pbmd.PBNode).options(sqlao.joinedload_all("_oParent"), sqlao.joinedload_all("_lAllChildren"))\
93
-        .filter(pbmd.PBNode.node_id==liNodeId)\
94
-        .filter(
95
-          sqla.or_(
96
-            pbmd.PBNode.owner_id==self._iCurrentUserId,
97
-            lsNodeIdFiltering
98
-          )
99
-        )\
100
-        .one()
101
-    return None
102
-
103
-  def getLastModifiedNodes(self, piMaxNodeNb: int):
104
-    """
105
-    Returns a list of nodes order by modification time and limited to piMaxNodeNb nodes
106
-    """
107
-    liOwnerIdList = self._getUserIdListForFiltering()
108
-    return pbm.DBSession.query(pbmd.PBNode)\
109
-        .outerjoin(pbma.Rights)\
110
-        .outerjoin(pbma.user_group_table, pbma.Rights.group_id==pbma.user_group_table.columns['group_id'])\
111
-        .options(joinedload_all("_lAllChildren"))\
112
-        .filter((pbmd.PBNode.owner_id.in_(liOwnerIdList)) | ((pbma.user_group_table.c.user_id.in_(liOwnerIdList)) & (pbmd.PBNode.is_shared == True)))\
113
-        .order_by(pbmd.PBNode.updated_at.desc())\
114
-        .limit(piMaxNodeNb).all()
115
-
116
-
117
-  def getListOfAllowedNodes(self, reset_cache=False) -> pbmd.PBNode:
118
-      if self._cache_allowed_nodes==None or reset_cache==True:
119
-        lsSqlQuery = """
120
-            SELECT
121
-                pn.node_id,
122
-                pn.parent_id,
123
-                pn.node_order,
124
-                pn.node_type,
125
-                pn.created_at,
126
-                pn.updated_at,
127
-                pn.data_label,
128
-                pn.data_content,
129
-                pn.data_datetime,
130
-                pn.node_status,
131
-                pn.data_reminder_datetime,
132
-                pn.parent_tree_path,
133
-                pn.node_depth,
134
-                pn.owner_id,
135
-                pn.is_shared,
136
-                pn.is_public,
137
-                pn.public_url_key
138
-            FROM
139
-                pod_group_node AS pgn
140
-                join pod_user_group AS pug ON pug.group_id = pgn.group_id
141
-                join pod_nodes AS pn ON pgn.node_id=pn.node_id AND pn.is_shared='t'
142
-            WHERE
143
-                pn.node_type='data'
144
-                AND pn.node_status NOT IN ('deleted', 'closed')
145
-                AND pn.node_id=pgn.node_id
146
-                AND pgn.rights > 0
147
-                AND pug.user_id = :owner_id
148
-
149
-            UNION
150
-                SELECT
151
-                    pn.node_id,
152
-                    pn.parent_id,
153
-                    pn.node_order,
154
-                    pn.node_type,
155
-                    pn.created_at,
156
-                    pn.updated_at,
157
-                    pn.data_label,
158
-                    pn.data_content,
159
-                    pn.data_datetime,
160
-                    pn.node_status,
161
-                    pn.data_reminder_datetime,
162
-                    pn.parent_tree_path,
163
-                    pn.node_depth,
164
-                    pn.owner_id,
165
-                    pn.is_shared,
166
-                    pn.is_public,
167
-                    pn.public_url_key
168
-                FROM
169
-                    pod_nodes AS pn
170
-                WHERE
171
-                    pn.node_type = 'data'
172
-                    AND pn.node_status NOT IN ('deleted', 'closed')
173
-                    AND pn.owner_id = :owner_id
174
-
175
-            ORDER BY node_id ASC
176
-        """
177
-
178
-        loNodeListResult = pbm.DBSession.query(pbmd.PBNode).\
179
-            from_statement(lsSqlQuery).\
180
-            params(owner_id=self._iCurrentUserId)
181
-        allowed_nodes = loNodeListResult.all()
182
-
183
-        self._cache_allowed_nodes = allowed_nodes
184
-
185
-      return self._cache_allowed_nodes
186
-
187
-
188
-  def searchNodesByText(self, plKeywordList: [str], piMaxNodeNb=100):
189
-    """
190
-    Returns a list of nodes order by type, nodes which contain at least one of the keywords
191
-    """
192
-    liOwnerIdList = self._getUserIdListForFiltering()
193
-    loKeywordFilteringClauses = []
194
-    for keyword in plKeywordList:
195
-        loKeywordFilteringClauses.append(pbmd.PBNode.data_label.ilike('%'+keyword+'%'))
196
-        loKeywordFilteringClauses.append(pbmd.PBNode.data_content.ilike('%'+keyword+'%'))
197
-        loKeywordFilteringClauses.append(pbmd.PBNode.data_file_name.ilike('%'+keyword+'%'))
198
-
199
-    loKeywordFilteringClausesAsOr = sqla.or_(*loKeywordFilteringClauses) # Combine them with or to a BooleanClauseList
200
-
201
-    loResultsForSomeKeywords = pbm.DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).outerjoin(pbma.Rights).outerjoin(pbma.user_group_table, pbma.Rights.group_id==pbma.user_group_table.columns['group_id'])\
202
-        .filter(loKeywordFilteringClausesAsOr)\
203
-        .filter((pbmd.PBNode.owner_id.in_(liOwnerIdList)) | (pbma.user_group_table.c.user_id.in_(liOwnerIdList) & pbmd.PBNode.is_shared))\
204
-        .order_by(sqla.desc(pbmd.PBNode.node_type))\
205
-        .limit(piMaxNodeNb)\
206
-        .all()
207
-
208
-    return loResultsForSomeKeywords
209
-
210
-  def getNodesByStatus(self, psNodeStatus, piMaxNodeNb=5):
211
-    liOwnerIdList = self._getUserIdListForFiltering()
212
-    return pbm.DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_status==psNodeStatus).order_by(pbmd.PBNode.updated_at).limit(piMaxNodeNb).all()
213
-
214
-
215
-  def getChildNodesForMenu(self, poParentNode: pbmd.PBNode, allowed_nodes: [pbmd.PBNode]) -> [pbmd.PBNode]:
216
-    visible_child_nodes = []
217
-
218
-    if poParentNode!=None:
219
-        # standard case
220
-        print(" -------------- BEFORE @@@@@@@@@@@@@@@@ ")
221
-        all_child_nodes = poParentNode.getChildren()
222
-        print("@@@@@@@@@@@@@@@@ AFTER @@@@@@@@@@@@@@@@ ")
223
-        for child_node in all_child_nodes:
224
-          if child_node in allowed_nodes:
225
-            visible_child_nodes.append(child_node)
226
-    else:
227
-        # Root case
228
-        parent_nodes = pbm.DBSession
229
-        for allowed_node in allowed_nodes:
230
-            print("     @@@@@@@@@@@@@@@@ BEFORE @@@@@@@@@@@@@@@@ ")
231
-            # loParent = allowed_node._oParent
232
-            # print("@@@@@@@@@@@@@@@@ AFTER @@@@@@@@@@@@@@@@ {0}".format(allowed_node._oParent.node_id if allowed_node._oParent!=None else '0'))
233
-            # print("==== EXTRA {0}".format(allowed_node._oParent.node_id if allowed_node._oParent!=None else '0'))
234
-            print("====> ")
235
-
236
-            if allowed_node._oParent is None or allowed_node._oParent==allowed_node:
237
-                # D.A. - HACK - 2014-05-27
238
-                # I don't know why, but as from now (with use of sqlao.contains_eager in getListOfAllowedNodes()
239
-                # the root items have at first iteration itself as parent
240
-                print("==== EXTRA END")
241
-                # this is a root item => add it
242
-                visible_child_nodes.append(allowed_node)
243
-            else:
244
-                if allowed_node._oParent not in allowed_nodes:
245
-                    # the node is visible but not the parent => put it at the root
246
-                    visible_child_nodes.append(allowed_node)
247
-                else:
248
-                    print("==== EXTRA END 2 {0}".format(allowed_node._oParent.node_id))
249
-
250
-    print(" @@@@@@@@@@@@@@@@ PRE FAILURE @@@@@@@@@@@@@@@@ ")
251
-    return visible_child_nodes
252
-
253
-
254
-
255
-
256
-  def buildTreeListForMenu(self, current_node: pbmd.PBNode, allowed_nodes: [pbmd.PBNode]) -> [pbmd.NodeTreeItem]:
257
-    # The algorithm is:
258
-    # 1. build an intermediate tree containing only current node and parent path
259
-    #    + complete it with sibling at each level (except root)
260
-    # 2. add sibling nodes at root level
261
-    # 3. complete it with shared documents (which are not at root but shared with current user)
262
-
263
-    node_tree = []
264
-
265
-    previous_tree_item = None
266
-    tmp_children_nodes = []
267
-
268
-    if current_node is not None:
269
-        breadcrumb_nodes = current_node.getBreadCrumbNodes()
270
-        breadcrumb_nodes.append(current_node) # by default the current node is not included
271
-
272
-        for breadcrumb_node in reversed(breadcrumb_nodes):
273
-            if previous_tree_item is None:
274
-                # First iteration. We add all current_node children
275
-                for child_node in breadcrumb_node.getChildren():
276
-                    if child_node in allowed_nodes:
277
-                        child_item = pbmd.NodeTreeItem(child_node, [])
278
-                        tmp_children_nodes.append(child_item)
279
-
280
-                previous_tree_item = pbmd.NodeTreeItem(breadcrumb_node, tmp_children_nodes)
281
-
282
-            else:
283
-                tmp_children_nodes = []
284
-                for child_node in breadcrumb_node.getChildren():
285
-                    if child_node in allowed_nodes:
286
-                        if child_node == previous_tree_item.node:
287
-                            tmp_children_nodes.append(previous_tree_item)
288
-                        else:
289
-                            sibling_node = pbmd.NodeTreeItem(child_node, [])
290
-                            tmp_children_nodes.append(sibling_node)
291
-
292
-                previous_tree_item = pbmd.NodeTreeItem(breadcrumb_node, tmp_children_nodes)
293
-
294
-    for node in allowed_nodes:
295
-        # This part of the algorithm insert root items
296
-        if node.parent_id==0 or node.parent_id is None:
297
-            if previous_tree_item is not None and node == previous_tree_item.node:
298
-                assert(isinstance(previous_tree_item, pbmd.NodeTreeItem))
299
-                node_tree.append(previous_tree_item)
300
-            else:
301
-                node_tree.append(pbmd.NodeTreeItem(node, []))
302
-        else:
303
-            # Try to add nodes shared with me but with a parent which is not shared
304
-            if node.owner_id!=self._iCurrentUserId:
305
-                # this is a node shared with me
306
-                if node._oParent!=None and node._oParent not in allowed_nodes:
307
-                    # the node is shared but not the parent => put it in the root
308
-                    node_tree.append(pbmd.NodeTreeItem(node, []))
309
-
310
-
311
-    return node_tree
312
-
313
-
314
-
315
-  def DIRTY_OLDbuildTreeListForMenu(self, plViewableStatusId: []) -> [pbmd.PBNode]:
316
-
317
-    liOwnerIdList = self._getUserIdListForFiltering()
318
-    
319
-    # loNodeList = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).filter(pbmd.PBNode.node_status.in_(plViewableStatusId)).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()
320
-    loNodeListNotFiltered = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).filter(pbmd.PBNode.node_status.in_(plViewableStatusId)).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()
321
-
322
-    loNodeList = []
323
-    for loNode in loNodeListNotFiltered:
324
-      if loNode.owner_id in self._getUserIdListForFiltering():
325
-        loNodeList.append(loNode)
326
-      else:
327
-        for loRight in loNode._lRights:
328
-          for loUser in loRight._oGroup.users:
329
-            if loUser.user_id in self._getUserIdListForFiltering():
330
-              loNodeList.append(loNode)
331
-
332
-    loTreeList = []
333
-    loTmpDict = {}
334
-    for loNode in loNodeList:
335
-      loTmpDict[loNode.node_id] = loNode
336
-  
337
-      if loNode.parent_id==None or loNode.parent_id==0:
338
-        loTreeList.append(loNode)
339
-      else:
340
-        # append the node to the parent list
341
-        # FIXME - D.A - 2013-10-08
342
-        # The following line may raise an exception
343
-        # We suppose that the parent node has already been added
344
-        # this *should* be the case, but the code does not check it
345
-        if loNode.parent_id not in loTmpDict.keys():
346
-          try:
347
-
348
-            loTmpDict[loNode.parent_id] = self.getNode(loNode.parent_id)
349
-          except Exception as e:
350
-            # loTreeList.append(
351
-            # FIXME - D.A. - 2014-05-22 This may be wrong code:
352
-            # we are in the case when the node parent is not shared with the current user
353
-            # So the node should be added at the root
354
-            pass
355
-        if loNode.parent_id in loTmpDict.keys():
356
-          # HACK- D.A. - 2014-05-22 - See FIXME upper
357
-          loTmpDict[loNode.parent_id].appendStaticChild(loNode)
358
-  
359
-    return loTreeList
360
-
361
-  def getParentNode(self, loNode):
362
-    liOwnerIdList = self._getUserIdListForFiltering()
363
-    return pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_id==loNode.parent_id).one()
364
-
365
-  def getSiblingNodes(self, poNode, pbReverseOrder=False):
366
-    liOwnerIdList = self._getUserIdListForFiltering()
367
-    
368
-    if pbReverseOrder:
369
-      return pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.parent_id==poNode.parent_id).order_by(pbmd.PBNode.node_order.desc()).all()
370
-    else:
371
-      return pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.parent_id==poNode.parent_id).order_by(pbmd.PBNode.node_order).all()
372
-
373
-  def resetNodeOrderOfSiblingNodes(self, loSiblingNodes):
374
-    liNewWeight = 0
375
-    for loNode in loSiblingNodes:
376
-      liNewWeight = liNewWeight + 1
377
-      loNode.node_order = liNewWeight
378
-    # DBSession.save()
379
-
380
-  def moveNodeUpper(self, loNode):
381
-    # FIXME - manage errors and logging
382
-    
383
-    loSiblingNodes = self.getSiblingNodes(loNode)
384
-    self.resetNodeOrderOfSiblingNodes(loSiblingNodes)
385
-  
386
-    loPreviousItem = None
387
-    for loItem in loSiblingNodes:
388
-      if loItem==loNode:
389
-        if loPreviousItem==None:
390
-          return FIXME_ERROR_CODE # FIXME - D.A. Do not use hard-coded error codes
391
-          print("No previous node")
392
-        else:
393
-          liPreviousItemOrder       = loPreviousItem.node_order
394
-          loPreviousItem.node_order = loNode.node_order
395
-          loNode.node_order         = liPreviousItemOrder
396
-          # DBSession.save()
397
-          break
398
-      loPreviousItem = loItem
399
-
400
-  def moveNodeLower(self, loNode):
401
-    # FIXME - manage errors and logging
402
-    
403
-    loSiblingNodes = self.getSiblingNodes(loNode)
404
-    self.resetNodeOrderOfSiblingNodes(loSiblingNodes)
405
-  
406
-    loPreviousItem = None
407
-    for loItem in reversed(loSiblingNodes):
408
-      if loItem==loNode:
409
-        if loPreviousItem==None:
410
-          return FIXME_ERROR_CODE # FIXME - D.A. Do not use hard-coded error codes
411
-          # FIXME
412
-          print("No previous node")
413
-        else:
414
-          liPreviousItemOrder       = loPreviousItem.node_order
415
-          loPreviousItem.node_order = loNode.node_order
416
-          loNode.node_order         = liPreviousItemOrder
417
-          # DBSession.save()
418
-          break
419
-      loPreviousItem = loItem
420
-
421
-  def getNodeFileContent(self, liNodeId):
422
-    liOwnerIdList = self._getUserIdListForFiltering()
423
-    return pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_id==liNodeId).one().data_file_content
424
-
425
-  def deleteNode(loNode):
426
-    # INFO - D.A. - 2013-11-07 - should be save as getNode should return only accessible nodes
427
-    pbm.DBSession.delete(loNode)
428
-    return
429
-
430
-  def createRight(self):
431
-    loRight = pbma.Rights()
432
-    return loRight

+ 13 - 70
tracim/tracim/lib/helpers.py View File

@@ -15,8 +15,8 @@ from tracim.lib.content import ContentApi
15 15
 from tracim.lib.workspace import WorkspaceApi
16 16
 
17 17
 from tracim.model.data import ContentStatus
18
-from tracim.model.data import PBNode
19
-from tracim.model.data import PBNodeType
18
+from tracim.model.data import Content
19
+from tracim.model.data import ContentType
20 20
 from tracim.model.data import UserRoleInWorkspace
21 21
 from tracim.model.data import Workspace
22 22
 
@@ -64,69 +64,6 @@ def icon(icon_name, white=False):
64 64
     else:
65 65
         return Markup('<i class="icon-%s"></i>' % icon_name)
66 66
 
67
-
68
-def getExplanationAboutStatus(psStatusId, psCurrentStatusId):
69
-  lsMsg = ""
70
-  if psStatusId==psCurrentStatusId:
71
-    return _("This is the current status.")
72
-  else:
73
-    if psStatusId=='information':
74
-      return _("The item is a normal document, like a howto or a text document.")
75
-    if psStatusId=='automatic':
76
-      return _("The item will be automatically computed as \"in progress\" or \"done\" according to its children status.")
77
-    if psStatusId=='new':
78
-      return _("No action done on the item.")
79
-    if psStatusId=='inprogress':
80
-      return _("The item is being worked on.")
81
-    if psStatusId=='standby':
82
-      return _("Waiting for some external actions.")
83
-    if psStatusId=='done':
84
-      return _("The work associated with the item is finished.")
85
-    if psStatusId=='closed':
86
-      return _("Close the item if you want not to see it anymore. The data won't be deleted")
87
-    if psStatusId=='deleted':
88
-      return _("This status tells that the item has been deleted.")
89
-
90
-class ID(object):
91
-  """ Helper class that will manage html items ids that need to be shared"""
92
-
93
-  @classmethod
94
-  def AddDocumentModalForm(cls, poNode=None):
95
-    if poNode:
96
-      return 'add-document-modal-form-%d'%poNode.node_id
97
-    else:
98
-      return 'add-document-modal-form'
99
-
100
-  @classmethod
101
-  def AddContactModalForm(cls, poNode=None):
102
-    if poNode:
103
-      return 'add-contact-modal-form-%d'%poNode.node_id
104
-    else:
105
-      return 'add-contact-modal-form'
106
-
107
-  @classmethod
108
-  def AddFileModalForm(cls, poNode=None):
109
-    if poNode:
110
-      return 'add-file-modal-form-%d'%poNode.node_id
111
-    else:
112
-      return 'add-file-modal-form'
113
-
114
-  @classmethod
115
-  def MoveDocumentModalForm(cls, poNode):
116
-      return 'move-document-modal-form-{0}'.format(poNode.node_id)
117
-
118
-  @classmethod
119
-  def AddEventModalForm(cls, poNode=None):
120
-    if poNode:
121
-      return 'add-event-modal-form-%d'%poNode.node_id
122
-    else:
123
-      return 'add-event-modal-form'
124
-    ## Original id is 'current-document-add-event-form'
125
-
126
-  @classmethod
127
-  def AddCommentInlineForm(cls):
128
-    return 'current-document-add-comment-form'
129
-
130 67
 class ICON(object):
131 68
   Shared = '<i class="fa fa-group"></i>'
132 69
   Private = '<i class="fa fa-key"></i>'
@@ -158,8 +95,8 @@ def RoleLevelAssociatedCss(role_level):
158 95
     elif role_level==UserRoleInWorkspace.WORKSPACE_MANAGER:
159 96
         return '#F00'
160 97
 
161
-def AllStatus(node_type=''):
162
-    return ContentStatus.all(node_type)
98
+def AllStatus(type=''):
99
+    return ContentStatus.all(type)
163 100
 
164 101
 
165 102
 def is_debug_mode():
@@ -169,7 +106,7 @@ def is_debug_mode():
169 106
 def on_off_to_boolean(on_or_off: str) -> bool:
170 107
     return True if on_or_off=='on' else False
171 108
 
172
-def convert_id_into_instances(id: str) -> (Workspace, PBNode):
109
+def convert_id_into_instances(id: str) -> (Workspace, Content):
173 110
     """
174 111
     TODO - D.A. - 2014-10-18 - Refactor and move this function where it must be placed
175 112
     convert an id like 'workspace_<workspace_id>|content_<content_id>'
@@ -179,7 +116,13 @@ def convert_id_into_instances(id: str) -> (Workspace, PBNode):
179 116
     if id=='#':
180 117
         return None, None
181 118
 
182
-    workspace_str, content_str = id.split(CST.TREEVIEW_MENU.ITEM_SEPARATOR)
119
+    workspace_str = ''
120
+    content_str = ''
121
+    try:
122
+        workspace_str, content_str = id.split(CST.TREEVIEW_MENU.ITEM_SEPARATOR)
123
+    except:
124
+        pass
125
+
183 126
     workspace = None
184 127
     content = None
185 128
 
@@ -193,7 +136,7 @@ def convert_id_into_instances(id: str) -> (Workspace, PBNode):
193 136
     try:
194 137
         content_data = content_str.split(CST.TREEVIEW_MENU.ID_SEPARATOR)
195 138
         content_id = int(content_data[1])
196
-        content = ContentApi(tg.tmpl_context.current_user).get_one(content_id, PBNodeType.Folder)
139
+        content = ContentApi(tg.tmpl_context.current_user).get_one(content_id, ContentType.Folder)
197 140
     except (IndexError, ValueError) as e:
198 141
         content = None
199 142
 

+ 6 - 144
tracim/tracim/lib/user.py View File

@@ -9,7 +9,6 @@ from tracim.model.auth import User
9 9
 from tracim.model import auth as pbma
10 10
 from tracim.model import DBSession
11 11
 import tracim.model.data as pmd
12
-# import DIRTY_GroupRightsOnNode, DIRTY_UserDedicatedGroupRightOnNodeSqlQuery, DIRTY_RealGroupRightOnNodeSqlQuery, PBNode
13 12
 
14 13
 class UserApi(object):
15 14
 
@@ -26,14 +25,18 @@ class UserApi(object):
26 25
         return self._base_query().filter(User.user_id==user_id).one()
27 26
 
28 27
     def get_one_by_email(self, email: str):
29
-        return self._base_query().filter(User.email_address==email).one()
28
+        return self._base_query().filter(User.email==email).one()
30 29
 
31 30
     def update(self, user: User, name: str, email: str, do_save):
32 31
         user.display_name = name
33
-        user.email_address = email
32
+        user.email = email
34 33
         if do_save:
35 34
             self.save(user)
36 35
 
36
+        if user.user_id==self._user.user_id:
37
+            # this is required for the session to keep on being up-to-date
38
+            tg.request.identity['repoze.who.userid'] = email
39
+
37 40
     def user_with_email_exists(self, email: str):
38 41
         try:
39 42
             self.get_one_by_email(email)
@@ -58,148 +61,7 @@ class UserStaticApi(object):
58 61
   @classmethod
59 62
   def get_current_user(cls) -> User:
60 63
     identity = tg.request.identity
61
-    print (tg.request.identity)
62 64
 
63 65
     if tg.request.identity:
64 66
         return pbma.User.by_email_address(tg.request.identity['repoze.who.userid'])
65 67
     return None
66
-
67
-  @classmethod
68
-  def getUserByEmailAddress(cls, psEmailAddress):
69
-    loUser = pbma.User.by_email_address(psEmailAddress)
70
-    return loUser
71
-
72
-  @classmethod
73
-  def createNewUser(cls, real_name, email_address, password, groups):
74
-    loUser = pbma.User()
75
-    new_user = pbma.User()
76
-    new_user.email_address = email_address
77
-    new_user.display_name  = real_name if real_name!='' else email_address
78
-    new_user.password      = password
79
-
80
-    public_group = cls.getGroupById(pbma.Group.TIM_USER)
81
-    public_group.users.append(new_user)
82
-
83
-    DBSession.add(new_user)
84
-    DBSession.flush()
85
-    DBSession.refresh(new_user)
86
-
87
-    user_dedicated_group = cls.createGroup()
88
-    user_dedicated_group.group_id = 0-new_user.user_id # group id of a given user is the opposite of the user id
89
-    user_dedicated_group.group_name = 'user_%d' % new_user.user_id
90
-    user_dedicated_group.personnal_group = True
91
-    user_dedicated_group.users.append(new_user)
92
-
93
-    for group_id in groups:
94
-        selected_group = cls.getGroupById(group_id)
95
-        selected_group.users.append(new_user)
96
-
97
-    DBSession.flush()
98
-
99
-    return new_user
100
-
101
-  @classmethod
102
-  def updateUser(cls, user_id, real_name, email, group_ids):
103
-
104
-      group_ids = list(map(int, group_ids))
105
-      group_ids.append(pbma.Group.TIM_USER)
106
-      print('new group ids:', group_ids)
107
-      user_to_update = DBSession.query(pbma.User).filter(pbma.User.user_id==user_id).one()
108
-      user_to_update.display_name = real_name
109
-      user_to_update.email_address = email
110
-
111
-      merged_new_groups = []
112
-
113
-      for group in user_to_update.groups:
114
-          if group.group_id==pbma.Group.TIM_ADMIN:
115
-              print('adding group (3)', group.group_id)
116
-              merged_new_groups.append(group)
117
-
118
-          elif group.group_id==pbma.Group.TIM_USER:
119
-              print('adding group (2)', group.group_id)
120
-              merged_new_groups.append(group)
121
-
122
-          elif group.group_id in group_ids:
123
-              print('adding group', group.group_id)
124
-              merged_new_groups.append(group)
125
-
126
-          else:
127
-              print('remove group', group.group_id)
128
-              user_to_update.groups.remove(group)
129
-
130
-      for group_id in group_ids:
131
-          group = cls.getGroupById(group_id)
132
-
133
-          if group not in merged_new_groups:
134
-              merged_new_groups.append(group)
135
-
136
-      user_to_update.groups = merged_new_groups
137
-
138
-      for group in merged_new_groups:
139
-          print("group => ", group.group_id)
140
-      DBSession.flush()
141
-
142
-  @classmethod
143
-  def deleteUser(cls, user_id):
144
-      user_to_delete = DBSession.query(pbma.User).filter(pbma.User.user_id==user_id).one()
145
-      user_dedicated_group = DBSession.query(pbma.Group).filter(pbma.Group.group_id==-user_id).one()
146
-      DBSession.delete(user_to_delete)
147
-      DBSession.delete(user_dedicated_group)
148
-      DBSession.flush()
149
-
150
-  @classmethod
151
-  def getGroup(cls, psGroupName):
152
-    loGroup = pbma.Group.by_group_name(psGroupName)
153
-    return loGroup
154
-
155
-  @classmethod
156
-  def getGroupById(cls, group_id):
157
-    return DBSession.query(pbma.Group).filter(pbma.Group.group_id==group_id).one()
158
-
159
-  @classmethod
160
-  def createGroup(cls):
161
-    loGroup = pbma.Group()
162
-    return loGroup
163
-
164
-  @classmethod
165
-  def getGroups(cls):
166
-    loGroups = pbma.Group.real_groups_first()
167
-    return loGroups
168
-
169
-  @classmethod
170
-  def getRealGroupRightsOnNode(cls, piNodeId: int) -> pmd.DIRTY_GroupRightsOnNode:
171
-
172
-    groupRightsOnNodeCustomSelect = DBSession\
173
-        .query(pmd.DIRTY_GroupRightsOnNode)\
174
-        .from_statement(pmd.DIRTY_RealGroupRightOnNodeSqlQuery)\
175
-        .params(node_id=piNodeId)\
176
-        .all()
177
-
178
-    return groupRightsOnNodeCustomSelect
179
-
180
-  @classmethod
181
-  def getUserDedicatedGroupRightsOnNode(cls, node: pmd.PBNode) -> pmd.DIRTY_GroupRightsOnNode:
182
-
183
-    group_rights_on_node = []
184
-    if node:
185
-        group_rights_on_node = DBSession\
186
-            .query(pmd.DIRTY_GroupRightsOnNode)\
187
-            .from_statement(pmd.DIRTY_UserDedicatedGroupRightOnNodeSqlQuery)\
188
-            .params(node_id=node.node_id)\
189
-            .all()
190
-
191
-    return group_rights_on_node
192
-
193
-  @classmethod
194
-  def DIRTY_get_rights_on_node(self, user_id, node_id):
195
-      rights = DBSession\
196
-              .execute("""select max(rights) as rights
197
-                      from pod_user_group
198
-                      natural join pod_group_node
199
-                      where node_id=:node_id
200
-                      and user_id=:user_id""", {"node_id":node_id, "user_id":user_id})\
201
-              .fetchone()
202
-      r = pmd.DIRTY_GroupRightsOnNode()
203
-      r.rights = rights[0]
204
-      return r
205
-

+ 1 - 1
tracim/tracim/lib/userworkspace.py View File

@@ -71,7 +71,7 @@ class RoleApi(object):
71 71
         return self._get_all_for_user(user_id).all()
72 72
 
73 73
     def get_all_for_user_order_by_workspace(self, user_id: int) -> UserRoleInWorkspace:
74
-        return self._get_all_for_user(user_id).join(UserRoleInWorkspace.workspace).order_by(Workspace.data_label).all()
74
+        return self._get_all_for_user(user_id).join(UserRoleInWorkspace.workspace).order_by(Workspace.label).all()
75 75
 
76 76
     def get_all_for_workspace(self, workspace_id):
77 77
         return DBSession.query(UserRoleInWorkspace).filter(UserRoleInWorkspace.workspace_id==workspace_id).all()

+ 3 - 3
tracim/tracim/lib/workspace.py View File

@@ -41,8 +41,8 @@ class WorkspaceApi(object):
41 41
 
42 42
     def create_workspace(self, label: str, description: str='', save_now:bool=False) -> Workspace:
43 43
         workspace = Workspace()
44
-        workspace.data_label = label
45
-        workspace.data_comment = description
44
+        workspace.label = label
45
+        workspace.description = description
46 46
 
47 47
         # By default, we force the current user to be the workspace manager
48 48
         role = RoleApi(self._user).create_one(self._user, workspace, UserRoleInWorkspace.WORKSPACE_MANAGER)
@@ -70,7 +70,7 @@ class WorkspaceApi(object):
70 70
 
71 71
     def get_all_for_user(self, user: User):
72 72
         workspaces = [role.workspace for role in user.roles]
73
-        workspaces.sort(key=lambda workspace: workspace.data_label.lower())
73
+        workspaces.sort(key=lambda workspace: workspace.label.lower())
74 74
         return workspaces
75 75
 
76 76
     def save(self, workspace: Workspace):

+ 1 - 1
tracim/tracim/model/__init__.py View File

@@ -60,4 +60,4 @@ def init_model(engine):
60 60
 
61 61
 # Import your model modules here.
62 62
 from tracim.model.auth import User, Group, Permission
63
-from tracim.model.data import PBNode
63
+from tracim.model.data import Content

+ 22 - 62
tracim/tracim/model/auth.py View File

@@ -11,6 +11,7 @@ though.
11 11
 import os
12 12
 from datetime import datetime
13 13
 from hashlib import sha256
14
+from sqlalchemy.ext.hybrid import hybrid_property
14 15
 from tg.i18n import lazy_ugettext as l_
15 16
 
16 17
 __all__ = ['User', 'Group', 'Permission']
@@ -23,43 +24,22 @@ from tracim.model import DeclarativeBase, metadata, DBSession
23 24
 
24 25
 # This is the association table for the many-to-many relationship between
25 26
 # groups and permissions.
26
-group_permission_table = Table('pod_group_permission', metadata,
27
-    Column('group_id', Integer, ForeignKey('pod_group.group_id',
27
+group_permission_table = Table('group_permission', metadata,
28
+    Column('group_id', Integer, ForeignKey('groups.group_id',
28 29
         onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
29
-    Column('permission_id', Integer, ForeignKey('pod_permission.permission_id',
30
+    Column('permission_id', Integer, ForeignKey('permissions.permission_id',
30 31
         onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)
31 32
 )
32 33
 
33 34
 # This is the association table for the many-to-many relationship between
34 35
 # groups and members - this is, the memberships.
35
-user_group_table = Table('pod_user_group', metadata,
36
-    Column('user_id', Integer, ForeignKey('pod_user.user_id',
36
+user_group_table = Table('user_group', metadata,
37
+    Column('user_id', Integer, ForeignKey('users.user_id',
37 38
         onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
38
-    Column('group_id', Integer, ForeignKey('pod_group.group_id',
39
+    Column('group_id', Integer, ForeignKey('groups.group_id',
39 40
         onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)
40 41
 )
41 42
 
42
-
43
-
44
-
45
-class Rights(DeclarativeBase):
46
-
47
-    READ_ACCESS = 1
48
-    WRITE_ACCESS = 2
49
-
50
-    __tablename__ = 'pod_group_node'
51
-
52
-    group_id = Column(Integer, ForeignKey('pod_group.group_id'), primary_key=True)
53
-    node_id = Column(Integer, ForeignKey('pod_nodes.node_id'), primary_key=True)
54
-    rights = Column(Integer)
55
-
56
-    def hasReadAccess(self):
57
-        return self.rights & Rights.READ_ACCESS
58
-
59
-    def hasWriteAccess(self):
60
-        return self.rights & Rights.WRITE_ACCESS
61
-
62
-
63 43
 class Group(DeclarativeBase):
64 44
 
65 45
     TIM_NOBODY = 0
@@ -72,7 +52,7 @@ class Group(DeclarativeBase):
72 52
     TIM_MANAGER_GROUPNAME = 'managers'
73 53
     TIM_ADMIN_GROUPNAME = 'administrators'
74 54
 
75
-    __tablename__ = 'pod_group'
55
+    __tablename__ = 'groups'
76 56
 
77 57
     group_id = Column(Integer, autoincrement=True, primary_key=True)
78 58
     group_name = Column(Unicode(16), unique=True, nullable=False)
@@ -81,10 +61,6 @@ class Group(DeclarativeBase):
81 61
 
82 62
     users = relationship('User', secondary=user_group_table, backref='groups')
83 63
 
84
-    _lRights = relationship('Rights', backref='_oGroup', cascade = "all, delete-orphan")
85
-
86
-
87
-
88 64
     def __repr__(self):
89 65
         return '<Group: name=%s>' % repr(self.group_name)
90 66
 
@@ -96,27 +72,6 @@ class Group(DeclarativeBase):
96 72
         """Return the user object whose email address is ``email``."""
97 73
         return DBSession.query(cls).filter_by(group_name=group_name).first()
98 74
 
99
-    def getDisplayName(self) -> str:
100
-        if self.group_id<0:
101
-            # FIXME - D.A. - 2014-05-19 - MAKE THIS CODE CLEANER,
102
-            try:
103
-                return self.users[0].get_display_name()
104
-            except:
105
-                print('ERROR GROUP =>', self.group_id)
106
-
107
-
108
-        return self.display_name
109
-
110
-    @property
111
-    def rights(self):
112
-        return self._lRights
113
-
114
-    def hasSomeAccess(self, poNode):
115
-        for loRight in self._lRights:
116
-            if loRight.node_id == poNode.node_id and loRight.rights>0:
117
-                return True
118
-        return False
119
-
120 75
 
121 76
 
122 77
 class Profile(object):
@@ -146,23 +101,28 @@ class User(DeclarativeBase):
146 101
     User definition.
147 102
 
148 103
     This is the user definition used by :mod:`repoze.who`, which requires at
149
-    least the ``email_address`` column.
104
+    least the ``email`` column.
150 105
 
151 106
     """
152
-    __tablename__ = 'pod_user'
107
+    __tablename__ = 'users'
153 108
 
154 109
     user_id = Column(Integer, autoincrement=True, primary_key=True)
155
-    email_address = Column(Unicode(255), unique=True, nullable=False)
110
+    email = Column(Unicode(255), unique=True, nullable=False)
156 111
     display_name = Column(Unicode(255))
157 112
     _password = Column('password', Unicode(128))
158 113
     created = Column(DateTime, default=datetime.now)
159 114
     is_active = Column(Boolean, default=True, nullable=False)
115
+
116
+    @hybrid_property
117
+    def email_address(self):
118
+        return self.email
119
+
160 120
     def __repr__(self):
161 121
         return '<User: email=%s, display=%s>' % (
162
-                repr(self.email_address), repr(self.display_name))
122
+                repr(self.email), repr(self.display_name))
163 123
 
164 124
     def __unicode__(self):
165
-        return self.display_name or self.email_address
125
+        return self.display_name or self.email
166 126
 
167 127
     @property
168 128
     def permissions(self):
@@ -182,12 +142,12 @@ class User(DeclarativeBase):
182 142
     @classmethod
183 143
     def by_email_address(cls, email):
184 144
         """Return the user object whose email address is ``email``."""
185
-        return DBSession.query(cls).filter_by(email_address=email).first()
145
+        return DBSession.query(cls).filter_by(email=email).first()
186 146
 
187 147
     @classmethod
188 148
     def by_user_name(cls, username):
189 149
         """Return the user object whose user name is ``username``."""
190
-        return DBSession.query(cls).filter_by(email_address=username).first()
150
+        return DBSession.query(cls).filter_by(email=username).first()
191 151
 
192 152
     @classmethod
193 153
     def _hash_password(cls, password):
@@ -240,7 +200,7 @@ class User(DeclarativeBase):
240 200
         if self.display_name!=None and self.display_name!='':
241 201
             return self.display_name
242 202
         else:
243
-            return self.email_address
203
+            return self.email
244 204
 
245 205
 
246 206
 class Permission(DeclarativeBase):
@@ -251,7 +211,7 @@ class Permission(DeclarativeBase):
251 211
 
252 212
     """
253 213
 
254
-    __tablename__ = 'pod_permission'
214
+    __tablename__ = 'permissions'
255 215
 
256 216
 
257 217
     permission_id = Column(Integer, autoincrement=True, primary_key=True)

+ 102 - 539
tracim/tracim/model/data.py View File

@@ -1,37 +1,28 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3 3
 from bs4 import BeautifulSoup
4
-
4
+import datetime as datetime_root
5 5
 import json
6
-import re
7
-import datetime as datetimeroot
8
-from datetime import datetime
9
-from hashlib import sha256
10
-
11
-from sqlalchemy import Table, ForeignKey, Column, Sequence
12
-import sqlalchemy as sqla
13
-from sqlalchemy.sql.sqltypes import Boolean
14
-from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
15
-import sqlalchemy.types as sqlat
16
-from sqlalchemy.ext.orderinglist import ordering_list
17
-from sqlalchemy.orm import relation, synonym, relationship
18
-from sqlalchemy.orm import backref
19
-import sqlalchemy.orm as sqlao
20
-import sqlalchemy.orm.query as sqlaoq
21
-from sqlalchemy import orm as sqlao
6
+
7
+from sqlalchemy import Column
8
+from sqlalchemy import ForeignKey
9
+
22 10
 from sqlalchemy.ext.hybrid import hybrid_property
23 11
 
24
-from tg.i18n import ugettext as _, lazy_ugettext as l_
12
+from sqlalchemy.orm import relationship
13
+from sqlalchemy.orm import deferred
25 14
 
26
-import tg
27
-from tracim.model import DeclarativeBase, metadata, DBSession
28
-# from tracim.model import auth as pma
15
+from sqlalchemy.types import Boolean
16
+from sqlalchemy.types import DateTime
17
+from sqlalchemy.types import Integer
18
+from sqlalchemy.types import LargeBinary
19
+from sqlalchemy.types import Text
20
+from sqlalchemy.types import Unicode
29 21
 
30
-from tracim.model.auth import User
31
-from tracim.model.auth import Rights
32
-from tracim.model.auth import user_group_table
22
+from tg.i18n import lazy_ugettext as l_
33 23
 
34
-from tracim.lib.base import current_user
24
+from tracim.model import DeclarativeBase
25
+from tracim.model.auth import User
35 26
 
36 27
 class BreadcrumbItem(object):
37 28
 
@@ -47,17 +38,17 @@ class BreadcrumbItem(object):
47 38
 
48 39
 class Workspace(DeclarativeBase):
49 40
 
50
-    __tablename__ = 'pod_workspaces'
41
+    __tablename__ = 'workspaces'
51 42
 
52
-    workspace_id = Column(Integer, Sequence('pod_workspaces__workspace_id__sequence'), primary_key=True)
43
+    workspace_id = Column(Integer, autoincrement=True, primary_key=True)
53 44
 
54
-    data_label   = Column(Unicode(1024), unique=False, nullable=False, default='')
55
-    data_comment = Column(Text(),        unique=False, nullable=False, default='')
45
+    label   = Column(Unicode(1024), unique=False, nullable=False, default='')
46
+    description = Column(Text(), unique=False, nullable=False, default='')
56 47
 
57
-    created_at = Column(DateTime, unique=False, nullable=False)
58
-    updated_at = Column(DateTime, unique=False, nullable=False)
48
+    created = Column(DateTime, unique=False, nullable=False)
49
+    updated = Column(DateTime, unique=False, nullable=False)
59 50
 
60
-    is_deleted = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
51
+    is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
61 52
 
62 53
     def get_user_role(self, user: User) -> int:
63 54
         for role in user.roles:
@@ -69,10 +60,10 @@ class Workspace(DeclarativeBase):
69 60
 
70 61
 class UserRoleInWorkspace(DeclarativeBase):
71 62
 
72
-    __tablename__ = 'pod_user_workspace'
63
+    __tablename__ = 'user_workspace'
73 64
 
74
-    user_id = Column(Integer, ForeignKey('pod_user.user_id'), nullable=False, default=None, primary_key=True)
75
-    workspace_id = Column(Integer, ForeignKey('pod_workspaces.workspace_id'), nullable=False, default=None, primary_key=True)
65
+    user_id = Column(Integer, ForeignKey('users.user_id'), nullable=False, default=None, primary_key=True)
66
+    workspace_id = Column(Integer, ForeignKey('workspaces.workspace_id'), nullable=False, default=None, primary_key=True)
76 67
     role = Column(Integer, nullable=False, default=0, primary_key=False)
77 68
 
78 69
     workspace = relationship('Workspace', remote_side=[Workspace.workspace_id], backref='roles', lazy='joined')
@@ -149,7 +140,7 @@ class ActionDescription(object):
149 140
     _ICONS = {
150 141
         'archiving': 'mimetypes/package-x-generic',
151 142
         'content-comment': 'apps/internet-group-chat',
152
-        'creation': 'apps/accessories-text-editor',
143
+        'creation': 'actions/document-new',
153 144
         'deletion': 'status/user-trash-full',
154 145
         'edition': 'apps/accessories-text-editor',
155 146
         'revision': 'apps/accessories-text-editor',
@@ -174,7 +165,7 @@ class ActionDescription(object):
174 165
     def __init__(self, id):
175 166
         assert id in ActionDescription.allowed_values()
176 167
         self.id = id
177
-        self.label = ''
168
+        self.label = ActionDescription._LABELS[id]
178 169
         self.icon = ActionDescription._ICONS[id]
179 170
 
180 171
     @classmethod
@@ -233,130 +224,33 @@ class ContentStatus(object):
233 224
         'closed-deprecated': 'tracim-status-closed-deprecated',
234 225
     }
235 226
 
236
-    def __init__(self, id, node_type=''):
227
+    def __init__(self, id, type=''):
237 228
         self.id = id
238
-        print('ID', id)
239 229
         self.icon = ContentStatus._ICONS[id]
240 230
         self.css = ContentStatus._CSS[id]
241 231
 
242
-        if node_type==PBNodeType.Thread:
232
+        if type==ContentType.Thread:
243 233
             self.label = ContentStatus._LABELS_THREAD[id]
244
-        elif node_type==PBNodeType.File:
234
+        elif type==ContentType.File:
245 235
             self.label = ContentStatus._LABELS_FILE[id]
246 236
         else:
247 237
             self.label = ContentStatus._LABELS[id]
248 238
 
249 239
 
250 240
     @classmethod
251
-    def all(cls, node_type='') -> ['ContentStatus']:
241
+    def all(cls, type='') -> ['ContentStatus']:
252 242
         all = []
253
-        all.append(ContentStatus('open', node_type))
254
-        all.append(ContentStatus('closed-validated', node_type))
255
-        all.append(ContentStatus('closed-unvalidated', node_type))
256
-        all.append(ContentStatus('closed-deprecated', node_type))
243
+        all.append(ContentStatus('open', type))
244
+        all.append(ContentStatus('closed-validated', type))
245
+        all.append(ContentStatus('closed-unvalidated', type))
246
+        all.append(ContentStatus('closed-deprecated', type))
257 247
         return all
258 248
 
259 249
     @classmethod
260 250
     def allowed_values(cls):
261 251
         return ContentStatus._LABELS.keys()
262 252
 
263
-class PBNodeStatusItem(object):
264
-  def __init__(self, psStatusId, psStatusLabel, psStatusFamily, psIconId, psCssClass): #, psBackgroundColor):
265
-    self._sStatusId     = psStatusId
266
-    self._sStatusLabel  = psStatusLabel
267
-    self._sStatusFamily = psStatusFamily
268
-    self._sIconId   = psIconId
269
-    self._sCssClass = psCssClass
270
-    # self._sBackgroundColor = psBackgroundColor
271
-  
272
-  def getLabel(self):
273
-    return self._sStatusLabel
274
-    
275
-  @property
276
-  def status_family(self):
277
-    return self._sStatusFamily
278
-    
279
-  @property
280
-  def icon(self):
281
-    return self._sIconId
282
-    
283
-  def getId(self):
284
-    return self._sStatusId
285
-
286
-  @property
287
-  def css(self):
288
-    return self._sCssClass
289
-
290
-  @property
291
-  def status_id(self):
292
-    return self._sStatusId
293
-    
294
-  @property
295
-  def icon_id(self):
296
-    return self._sIconId
297
-
298
-  @property
299
-  def label(self):
300
-    return self._sStatusLabel
301
-
302
-class PBNodeStatus(object):
303
-    
304
-  StatusList = dict()
305
-  StatusList['information'] = PBNodeStatusItem('information', 'Information',         'normal', 'fa fa-info-circle',            'tracim-status-grey-light')
306
-  StatusList['automatic']   = PBNodeStatusItem('automatic',   'Automatic',           'open',   'fa fa-flash',                  'tracim-status-grey-light')
307
-  StatusList['new']         = PBNodeStatusItem('new',         'New',                 'open',   'fa fa-lightbulb-o fa-inverse', 'btn-success')
308
-  StatusList['inprogress']  = PBNodeStatusItem('inprogress',  'In progress',         'open',   'fa fa-gears fa-inverse',       'btn-info')
309
-  StatusList['standby']     = PBNodeStatusItem('standby',     'In standby',          'open',   'fa fa-spinner fa-inverse',     'btn-warning')
310
-  StatusList['done']        = PBNodeStatusItem('done',        'Done',                'closed', 'fa fa-check-square-o',         'tracim-status-grey-light')
311
-  StatusList['closed']      = PBNodeStatusItem('closed',      'Closed',              'closed', 'fa fa-lightbulb-o',            'tracim-status-grey-middle')
312
-  StatusList['deleted']     = PBNodeStatusItem('deleted',     'Deleted',             'closed', 'fa fa-trash-o',                'tracim-status-grey-dark')
313
-
314
-  @classmethod
315
-  def getChoosableList(cls):
316
-    return [
317
-      PBNodeStatus.StatusList['information'],
318
-      PBNodeStatus.StatusList['automatic'],
319
-      PBNodeStatus.StatusList['new'],
320
-      PBNodeStatus.StatusList['inprogress'],
321
-      PBNodeStatus.StatusList['standby'],
322
-      PBNodeStatus.StatusList['done'],
323
-      PBNodeStatus.StatusList['closed'],
324
-    ]
325
-
326
-  @classmethod
327
-  def getVisibleIdsList(cls):
328
-    return ['information', 'automatic', 'new', 'inprogress', 'standby', 'done' ]
329
-
330
-  @classmethod
331
-  def getVisibleList(cls):
332
-    return [
333
-      PBNodeStatus.StatusList['information'],
334
-      PBNodeStatus.StatusList['automatic'],
335
-      PBNodeStatus.StatusList['new'],
336
-      PBNodeStatus.StatusList['inprogress'],
337
-      PBNodeStatus.StatusList['standby'],
338
-      PBNodeStatus.StatusList['done'],
339
-    ]
340
-
341
-  @classmethod
342
-  def getList(cls):
343
-    return [
344
-      PBNodeStatus.StatusList['information'],
345
-      PBNodeStatus.StatusList['automatic'],
346
-      PBNodeStatus.StatusList['new'],
347
-      PBNodeStatus.StatusList['inprogress'],
348
-      PBNodeStatus.StatusList['standby'],
349
-      PBNodeStatus.StatusList['done'],
350
-      PBNodeStatus.StatusList['closed'],
351
-      PBNodeStatus.StatusList['deleted']
352
-    ]
353
-
354
-    
355
-  @classmethod
356
-  def getStatusItem(cls, psStatusId):
357
-    return PBNodeStatus.StatusList[psStatusId]
358
-
359
-class PBNodeType(object):
253
+class ContentType(object):
360 254
     Any = 'any'
361 255
 
362 256
     Folder  = 'folder'
@@ -365,12 +259,6 @@ class PBNodeType(object):
365 259
     Thread = 'thread'
366 260
     Page = 'page'
367 261
 
368
-    # Obsolete ones - to be removed
369
-    Node    = 'node'
370
-    Data    = 'data'
371
-    Event   = 'event'
372
-    Contact = 'contact'
373
-
374 262
     # Fake types, used for breadcrumb only
375 263
     FAKE_Dashboard = 'dashboard'
376 264
     FAKE_Workspace = 'workspace'
@@ -389,8 +277,8 @@ class PBNodeType(object):
389 277
 
390 278
     @classmethod
391 279
     def icon(cls, type: str):
392
-        assert(type in PBNodeType._ICONS) # DYN_REMOVE
393
-        return PBNodeType._ICONS[type]
280
+        assert(type in ContentType._ICONS) # DYN_REMOVE
281
+        return ContentType._ICONS[type]
394 282
 
395 283
     @classmethod
396 284
     def allowed_types(cls):
@@ -400,93 +288,48 @@ class PBNodeType(object):
400 288
     def allowed_types_from_str(cls, allowed_types_as_string: str):
401 289
         allowed_types = []
402 290
         # HACK - THIS
403
-        for item in allowed_types_as_string.split(PBNodeType._STRING_LIST_SEPARATOR):
404
-            if item and item in PBNodeType.allowed_types():
291
+        for item in allowed_types_as_string.split(ContentType._STRING_LIST_SEPARATOR):
292
+            if item and item in ContentType.allowed_types():
405 293
                 allowed_types.append(item)
406 294
         return allowed_types
407 295
 
408
-MINIMUM_DATE = datetimeroot.date(datetimeroot.MINYEAR, 1, 1)
409
-
410
-class PBNode(DeclarativeBase):
411
-
412
-    #def __init__(self):
413
-    #  self._lStaticChildList = []
414
-
415
-    @sqlao.reconstructor
416
-    def init_on_load(self):
417
-      self._lStaticChildList = []
418
-
419
-    def appendStaticChild(self, loNode):
420
-        print("%s has child %s" % (self.node_id, loNode.node_id))
421
-        self._lStaticChildList.append(loNode)
422
-
423
-    def getStaticChildList(self):
424
-        return self._lStaticChildList
425
-
426
-    def getStaticChildNb(self):
427
-        return len(self._lStaticChildList)
428
-
429
-    __tablename__ = 'pod_nodes'
296
+class Content(DeclarativeBase):
297
+    __tablename__ = 'contents'
430 298
 
431 299
     revision_to_serialize = -0  # This flag allow to serialize a given revision if required by the user
432 300
 
433
-    node_id          = Column(Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True)
434
-    parent_id        = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None)
435
-    node_depth       = Column(Integer, unique=False, nullable=False, default=0)
301
+    content_id = Column(Integer, autoincrement=True, primary_key=True)
302
+    parent_id = Column(Integer, ForeignKey('contents.content_id'), nullable=True, default=None)
303
+    node_depth = Column(Integer, unique=False, nullable=False, default=0)
436 304
     parent_tree_path = Column(Unicode(255), unique=False, nullable=False, default='')
437
-    owner_id         = Column(Integer, ForeignKey('pod_user.user_id'), nullable=True, default=None)
305
+    owner_id = Column(Integer, ForeignKey('users.user_id'), nullable=True, default=None)
438 306
 
439
-    node_order   = Column(Integer, nullable=True, default=1)
440
-    node_type    = Column(Unicode(32), unique=False, nullable=False)
441
-    node_status = Column(Unicode(32), unique=False, nullable=False, default=ContentStatus.OPEN)
307
+    type = Column(Unicode(32), unique=False, nullable=False)
308
+    status = Column(Unicode(32), unique=False, nullable=False, default=ContentStatus.OPEN)
442 309
 
443
-    created_at = Column(DateTime, unique=False, nullable=False)
444
-    updated_at = Column(DateTime, unique=False, nullable=False)
310
+    created = Column(DateTime, unique=False, nullable=False)
311
+    updated = Column(DateTime, unique=False, nullable=False)
445 312
 
446
-    workspace_id = Column(Integer, ForeignKey('pod_workspaces.workspace_id'), unique=False, nullable=True)
313
+    workspace_id = Column(Integer, ForeignKey('workspaces.workspace_id'), unique=False, nullable=True)
447 314
 
448 315
     workspace = relationship('Workspace', remote_side=[Workspace.workspace_id], backref='contents')
449 316
 
450 317
 
451
-    is_deleted = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
452
-    is_archived = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
453
-
454
-    is_shared = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
455
-    is_public = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
456
-    public_url_key = Column(Unicode(1024), unique=False, nullable=False, default='')
318
+    is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
319
+    is_archived = Column(Boolean, unique=False, nullable=False, default=False)
457 320
 
458
-    data_label = Column(Unicode(1024), unique=False, nullable=False, default='')
459
-    data_content = Column(Text(), unique=False, nullable=False, default='')
321
+    label = Column(Unicode(1024), unique=False, nullable=False, default='')
322
+    description = Column(Text(), unique=False, nullable=False, default='')
460 323
     _properties = Column('properties', Text(), unique=False, nullable=False, default='')
461 324
 
462
-    data_datetime = Column(DateTime, unique=False, nullable=False)
463
-    data_reminder_datetime = Column(DateTime, unique=False, nullable=True)
325
+    file_name = Column(Unicode(255),  unique=False, nullable=False, default='')
326
+    file_mimetype = Column(Unicode(255),  unique=False, nullable=False, default='')
327
+    file_content = deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
464 328
 
465
-    data_file_name = Column(Unicode(255),  unique=False, nullable=False, default='')
466
-    data_file_mime_type = Column(Unicode(255),  unique=False, nullable=False, default='')
467
-    data_file_content = sqlao.deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
329
+    revision_type = Column(Unicode(32), unique=False, nullable=False, default='')
468 330
 
469
-    last_action = Column(Unicode(32), unique=False, nullable=False, default='')
470
-
471
-    _lRights = relationship('Rights', backref='_oNode', cascade = "all, delete-orphan")
472
-
473
-    parent = relationship('PBNode', remote_side=[node_id], backref='children')
474
-    owner = relationship('User', remote_side=[User.user_id], backref='_lAllNodes')
475
-
476
-    @hybrid_property
477
-    def _lAllChildren(self):
478
-        # for backward compatibility method
479
-        return self.children
480
-
481
-    @property
482
-    def _oOwner(self):
483
-        # for backward compatibility method
484
-        return self.owner
485
-
486
-    @property
487
-    def _oParent(self):
488
-        # for backward compatibility method
489
-        return self.parent
331
+    parent = relationship('Content', remote_side=[content_id], backref='children')
332
+    owner = relationship('User', remote_side=[User.user_id])
490 333
 
491 334
     @hybrid_property
492 335
     def properties(self):
@@ -503,15 +346,14 @@ class PBNode(DeclarativeBase):
503 346
 
504 347
     def extract_links_from_content(self, other_content: str=None) -> [LinkItem]:
505 348
         """
506
-        parse html content and extract links. By default, it works on the data_content property
507
-        :param other_content: if not empty, then parse the given html content instead of data_content
349
+        parse html content and extract links. By default, it works on the description property
350
+        :param other_content: if not empty, then parse the given html content instead of description
508 351
         :return: a list of LinkItem
509 352
         """
510 353
         links = []
511
-        soup = BeautifulSoup(self.data_content if not other_content else other_content)
354
+        soup = BeautifulSoup(self.description if not other_content else other_content)
512 355
         for link in soup.findAll('a'):
513 356
             href = link.get('href')
514
-            print(href)
515 357
             label = link.contents
516 358
             links.append(LinkItem(href, label))
517 359
         links.sort(key=lambda link: link.href if link.href else '')
@@ -521,240 +363,38 @@ class PBNode(DeclarativeBase):
521 363
         return sorted_links
522 364
 
523 365
 
524
-    def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
525
-        """return all children nodes of type 'data' or 'node' or 'folder'"""
526
-        llChildren = []
527
-        user_id = current_user().user_id
528
-        llChildren = DBSession.query(PBNode).outerjoin(Rights)\
529
-                .outerjoin(user_group_table, Rights.group_id==user_group_table.columns['group_id'])\
530
-                .filter(PBNode.parent_id==self.node_id)\
531
-                .filter((PBNode.owner_id==user_id) | ((user_group_table.c.user_id==user_id) & (PBNode.is_shared == True)))\
532
-                .filter(PBNode.node_type.in_(plNodeTypeList))\
533
-                .all()
534
-        if poKeySortingMethod!=None:
535
-          llChildren = sorted(llChildren, key=poKeySortingMethod, reverse=pbDoReverseSorting)
536
-        return llChildren
537
-
538
-    def get_child_nb(self, content_type: PBNodeType, content_status = ''):
539
-        # V2 method - to keep
366
+    def get_child_nb(self, content_type: ContentType, content_status = ''):
540 367
         child_nb = 0
541
-        for child in self._lAllChildren:
542
-            if child.node_type==content_type:
368
+        for child in self.children:
369
+            if child.type==content_type:
543 370
                 if not content_status:
544 371
                     child_nb = child_nb+1
545
-                elif content_status==child.node_status:
372
+                elif content_status==child.status:
546 373
                     child_nb = child_nb+1
547 374
         return child_nb
548 375
 
549 376
     def get_status(self) -> ContentStatus:
550
-        return ContentStatus(self.node_status, self.node_type.__str__())
377
+        return ContentStatus(self.status, self.type.__str__())
378
+
551 379
 
552 380
     def get_last_action(self) -> ActionDescription:
553
-        return ActionDescription(self.last_action)
381
+        return ActionDescription(self.revision_type)
382
+
554 383
 
555 384
     def get_comments(self):
556 385
         children = []
557 386
         for child in self.children:
558
-            if child.node_type==PBNodeType.Comment:
387
+            if child.type==ContentType.Comment:
559 388
                 children.append(child)
560 389
         return children
561 390
 
562 391
 
563 392
 
564
-
565
-
566
-    def getChildNb(self):
567
-        return self.getChildNbOfType([PBNodeType.Data])
568
-
569
-    def getGroupsWithSomeAccess(self):
570
-        llRights = []
571
-        for loRight in self._lRights:
572
-            if loRight.rights>0:
573
-                llRights.append(loRight)
574
-        return llRights
575
-
576
-    def getChildren(self, pbIncludeDeleted=False):
577
-        """return all children nodes of type 'data' or 'node' or 'folder'"""
578
-        # return self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
579
-        items = self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
580
-        items2 = list()
581
-        for item in items:
582
-            if pbIncludeDeleted==True or item.node_status!='deleted':
583
-                items2.append(item)
584
-        return items2
585
-
586
-    def getContacts(self):
587
-        """return all children nodes of type 'data' or 'node' or 'folder'"""
588
-        return self.getChildrenOfType([PBNodeType.Contact], PBNode.getSortingKeyForContact)
589
-
590
-    def getContactNb(self):
591
-        """return all children nodes of type 'data' or 'node' or 'folder'"""
592
-        return self.getChildNbOfType([PBNodeType.Contact])
593
-
594
-    @classmethod
595
-    def getSortingKeyBasedOnDataDatetime(cls, poDataNode):
596
-        return poDataNode.data_datetime or MINIMUM_DATE
597
-    
598
-    @classmethod
599
-    def getSortingKeyForContact(cls, poDataNode):
600
-        return poDataNode.data_label or ''
601
-
602
-    @classmethod
603
-    def getSortingKeyForComment(cls, poDataNode):
604
-        return poDataNode.data_datetime or ''
605
-
606
-    def getEvents(self):
607
-        return self.getChildrenOfType([PBNodeType.Event], PBNode.getSortingKeyBasedOnDataDatetime, True)
608
-    
609
-    def getFiles(self):
610
-        return self.getChildrenOfType([PBNodeType.File])
611
-
612
-    def getIconClass(self):
613
-        if self.node_type==PBNodeType.Data and self.getStaticChildNb()>0:
614
-            return PBNode.getIconClassForNodeType('folder')
615
-        else:
616
-            return PBNode.getIconClassForNodeType(self.node_type)
617
-
618
-    def getBreadCrumbNodes(self) -> list('PBNode'):
619
-        loNodes = []
620
-        if self._oParent!=None:
621
-            loNodes = self._oParent.getBreadCrumbNodes()
622
-            loNodes.append(self._oParent)
623
-        return loNodes
624
-
625
-    def getContentWithHighlightedKeywords(self, plKeywords, psPlainText):
626
-        if len(plKeywords)<=0:
627
-            return psPlainText
628
-
629
-        lsPlainText = psPlainText
630
-
631
-        for lsKeyword in plKeywords:
632
-            lsPlainText = re.sub('(?i)(%s)' % lsKeyword, '<strong>\\1</strong>', lsPlainText)
633
-
634
-        return lsPlainText
635
-
636
-
637
-    @classmethod
638
-    def getIconClassForNodeType(cls, psIconType):
639
-        laIconClass = dict()
640
-        laIconClass['node']   = 'fa fa-folder-open'
641
-        laIconClass['folder'] = 'fa fa-folder-open'
642
-        laIconClass['data']   = 'fa fa-file-text-o'
643
-
644
-        laIconClass['file']   = 'fa fa-paperclip'
645
-        laIconClass['event']  = 'fa fa-calendar'
646
-        laIconClass['contact'] = 'fa fa-user'
647
-        laIconClass['comment'] = 'fa fa-comments-o'
648
-        return laIconClass[psIconType]
649
-
650
-
651
-    def getUserFriendlyNodeType(self):
652
-        laNodeTypesLng = dict()
653
-        laNodeTypesLng['node']   = 'Document' # FIXME - D.A. - 2013-11-14 - Make text translatable
654
-        laNodeTypesLng['folder'] = 'Document'
655
-        laNodeTypesLng['data']   = 'Document'
656
-
657
-        laNodeTypesLng['file']   = 'File'
658
-        laNodeTypesLng['event']  = 'Event'
659
-        laNodeTypesLng['contact'] = 'Contact'
660
-        laNodeTypesLng['comment'] = 'Comment'
661
-
662
-        if self.node_type==PBNodeType.Data and self.getStaticChildNb()>0:
663
-            return laNodeTypesLng['folder']
664
-        else:
665
-            return laNodeTypesLng[self.node_type]
666
-
667
-    
668
-    def getFormattedDateTime(self, poDateTime, psDateTimeFormat = '%d/%m/%Y ~ %H:%M'):
669
-        return poDateTime.strftime(psDateTimeFormat)
670
-
671
-    def getFormattedDate(self, poDateTime, psDateTimeFormat = '%d/%m/%Y'):
672
-        return poDateTime.strftime(psDateTimeFormat)
673
-
674
-    def getFormattedTime(self, poDateTime, psDateTimeFormat = '%H:%M'):
675
-        return poDateTime.strftime(psDateTimeFormat)
676
-
677
-    def getStatus(self) -> PBNodeStatusItem:
678
-        loStatus = PBNodeStatus.getStatusItem(self.node_status)
679
-        if loStatus.status_id!='automatic':
680
-            return loStatus
681
-
682
-        # default case
683
-        # Compute the status:
684
-        # - if at least one child is 'new' or 'in progress' or 'in standby' => status is inprogress
685
-        # - else if all status are 'done', 'closed' or 'deleted' => 'done'
686
-        lsRealStatusId = 'done'
687
-        for loChild in self.getChildren():
688
-            if loChild.getStatus().status_id in ('new', 'inprogress', 'standby'):
689
-                lsRealStatusId = 'inprogress'
690
-                break
691
-        return PBNodeStatus.getStatusItem(lsRealStatusId)
692
-
693
-    def getTruncatedLabel(self, piCharNb: int):
694
-        """
695
-        return a truncated version of the data_label property.
696
-        if piCharNb is not > 0, then the full data_label is returned
697
-        note: if the node is a file and the data_label is empty, the file name is returned
698
-        """
699
-        lsTruncatedLabel = self.data_label
700
-
701
-        # 2014-05-06 - D.A. - HACK
702
-        # if the node is a file and label empty, then use the filename as data_label
703
-        if self.node_type==PBNodeType.File and lsTruncatedLabel=='':
704
-            lsTruncatedLabel = self.data_file_name
705
-
706
-        liMaxLength = int(piCharNb)
707
-        if liMaxLength>0 and len(lsTruncatedLabel)>liMaxLength:
708
-            lsTruncatedLabel = lsTruncatedLabel[0:liMaxLength-1]+'…'
709
-
710
-        if lsTruncatedLabel=='':
711
-            lsTruncatedLabel = _('Titleless Document')
712
-
713
-        return lsTruncatedLabel
714
-
715
-    def getTruncatedContentAsText(self, piCharNb):
716
-        lsPlainText = ''.join(BeautifulSoup(self.data_content).findAll(text=True))
717
-        lsTruncatedContent = ''
718
-
719
-        liMaxLength = int(piCharNb)
720
-        if len(lsPlainText)>liMaxLength:
721
-            lsTruncatedContent = lsPlainText[0:liMaxLength-1]+'…'
722
-        else:
723
-            lsTruncatedContent = lsPlainText
724
-        return lsTruncatedContent
725
-
726
-    def getTagList(self):
727
-        loPattern = re.compile('(^|\s|@)@(\w+)')
728
-        loResults = re.findall(loPattern, self.data_content)
729
-        lsResultList = []
730
-        for loResult in loResults:
731
-            lsResultList.append(loResult[1].replace('@', '').replace('_', ' '))
732
-        return lsResultList
733
-
734
-    @classmethod
735
-    def addTagReplacement(cls, matchobj):
736
-        return " <span class='badge'>%s</span> " %(matchobj.group(0).replace('@', '').replace('_', ' '))
737
-
738
-    @classmethod
739
-    def addDocLinkReplacement(cls, matchobj):
740
-        return " <a href='%s'>%s</a> " %(tg.url('/dashboard?node=%s')%(matchobj.group(1)), matchobj.group(0))
741
-
742
-    def getContentWithTags(self):
743
-        lsTemporaryResult = re.sub('(^|\s)@@(\w+)', '', self.data_content) # tags with @@ are explicitly removed from the body
744
-        lsTemporaryResult = re.sub('#([0-9]*)', PBNode.addDocLinkReplacement, lsTemporaryResult) # tags with @@ are explicitly removed from the body
745
-        return re.sub('(^|\s)@(\w+)', PBNode.addTagReplacement, lsTemporaryResult) # then, 'normal tags are transformed as labels'
746
-        # FIXME - D.A. - 2013-09-12
747
-        # Does not match @@ at end of content.
748
-  
749
-    def getHistory(self):
750
-        return DBSession.execute("select node_id, version_id, created_at from pod_nodes_history where node_id = :node_id order by created_at desc", {"node_id":self.node_id}).fetchall()
751
-
752
-
753 393
 class ContentChecker(object):
754 394
 
755 395
     @classmethod
756
-    def check_properties(cls, item: PBNode):
757
-        if item.node_type==PBNodeType.Folder:
396
+    def check_properties(cls, item: Content):
397
+        if item.type==ContentType.Folder:
758 398
             properties = item.properties
759 399
             if 'allowed_content' not in properties.keys():
760 400
                 return False
@@ -772,8 +412,8 @@ class ContentChecker(object):
772 412
         raise NotImplementedError
773 413
 
774 414
     @classmethod
775
-    def reset_properties(cls, item: PBNode):
776
-        if item.node_type==PBNodeType.Folder:
415
+    def reset_properties(cls, item: Content):
416
+        if item.type==ContentType.Folder:
777 417
             item.properties = dict(
778 418
                 allowed_content = dict (
779 419
                     folder = True,
@@ -784,42 +424,40 @@ class ContentChecker(object):
784 424
             )
785 425
             return
786 426
 
787
-        print('NODE TYPE', item.node_type)
788 427
         raise NotImplementedError
789 428
 
429
+
790 430
 class ContentRevisionRO(DeclarativeBase):
791 431
 
792
-    __tablename__ = 'pod_nodes_history'
793
-
794
-    version_id = Column(Integer, primary_key=True)
795
-    node_id = Column(Integer, ForeignKey('pod_nodes.node_id'))
796
-    # parent_id = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True)
797
-    owner_id = Column(Integer, ForeignKey('pod_user.user_id'), nullable=True)
798
-    data_label = Column(Unicode(1024), unique=False, nullable=False)
799
-    data_content = Column(Text(), unique=False, nullable=False, default='')
800
-    data_file_name = Column(Unicode(255),  unique=False, nullable=False, default='')
801
-    data_file_mime_type = Column(Unicode(255),  unique=False, nullable=False, default='')
802
-    data_file_content = sqlao.deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
803
-
804
-    node_status = Column(Unicode(32), unique=False, nullable=False)
805
-    created_at = Column(DateTime, unique=False, nullable=False)
806
-    updated_at = Column(DateTime, unique=False, nullable=False)
807
-    is_deleted = Column(sqlat.Boolean, unique=False, nullable=False)
808
-    is_archived = Column(sqlat.Boolean, unique=False, nullable=False)
809
-    last_action = Column(Unicode(32), unique=False, nullable=False, default='')
810
-
811
-    workspace_id = Column(Integer, ForeignKey('pod_workspaces.workspace_id'), unique=False, nullable=True)
432
+    __tablename__ = 'content_revisions'
433
+
434
+    revision_id = Column(Integer, primary_key=True)
435
+    content_id = Column(Integer, ForeignKey('contents.content_id'))
436
+    owner_id = Column(Integer, ForeignKey('users.user_id'), nullable=True)
437
+    label = Column(Unicode(1024), unique=False, nullable=False)
438
+    description = Column(Text(), unique=False, nullable=False, default='')
439
+    file_name = Column(Unicode(255),  unique=False, nullable=False, default='')
440
+    file_mimetype = Column(Unicode(255),  unique=False, nullable=False, default='')
441
+    file_content = deferred(Column(LargeBinary(), unique=False, nullable=False, default=None))
442
+
443
+    status = Column(Unicode(32), unique=False, nullable=False)
444
+    created = Column(DateTime, unique=False, nullable=False)
445
+    updated = Column(DateTime, unique=False, nullable=False)
446
+    is_deleted = Column(Boolean, unique=False, nullable=False)
447
+    is_archived = Column(Boolean, unique=False, nullable=False)
448
+    revision_type = Column(Unicode(32), unique=False, nullable=False, default='')
449
+
450
+    workspace_id = Column(Integer, ForeignKey('workspaces.workspace_id'), unique=False, nullable=True)
812 451
     workspace = relationship('Workspace', remote_side=[Workspace.workspace_id])
813 452
 
814
-    node = relationship('PBNode', remote_side=[PBNode.node_id], backref='revisions')
453
+    node = relationship('Content', remote_side=[Content.content_id], backref='revisions')
815 454
     owner = relationship('User', remote_side=[User.user_id])
816
-    # parent = relationship('PBNode', remote_side=[PBNode.node_id])
817 455
 
818 456
     def get_status(self):
819
-        return ContentStatus(self.node_status)
457
+        return ContentStatus(self.status)
820 458
 
821 459
     def get_last_action(self) -> ActionDescription:
822
-        return ActionDescription(self.last_action)
460
+        return ActionDescription(self.revision_type)
823 461
 
824 462
 
825 463
 class NodeTreeItem(object):
@@ -827,82 +465,7 @@ class NodeTreeItem(object):
827 465
         This class implements a model that allow to simply represents the left-panel menu items
828 466
          This model is used by dbapi but is not directly related to sqlalchemy and database
829 467
     """
830
-    def __init__(self, node: PBNode, children: list('NodeTreeItem'), is_selected = False):
468
+    def __init__(self, node: Content, children: list('NodeTreeItem'), is_selected = False):
831 469
         self.node = node
832 470
         self.children = children
833 471
         self.is_selected = is_selected
834
-
835
-
836
-
837
-
838
-#####
839
-#
840
-# HACK - 2014-05-21 - D.A
841
-#
842
-# The following hack is a horrible piece of code that allow to map a raw SQL select to a mapped class
843
-#
844
-class DIRTY_GroupRightsOnNode(object):
845
-    def hasSomeAccess(self):
846
-        return self.rights >= Rights.READ_ACCESS
847
-
848
-    def hasReadAccess(self):
849
-        return self.rights & Rights.READ_ACCESS
850
-
851
-    def hasWriteAccess(self):
852
-        return self.rights & Rights.WRITE_ACCESS
853
-
854
-DIRTY_group_rights_on_node_query = Table('fake_table', metadata,
855
-    Column('group_id', Integer, primary_key=True),
856
-    Column('node_id', Integer, primary_key=True),
857
-
858
-    Column('display_name', Unicode(255)),
859
-    Column('personnal_group', Boolean),
860
-    Column('rights', Integer, primary_key=True)
861
-)
862
-
863
-DIRTY_UserDedicatedGroupRightOnNodeSqlQuery = """
864
-SELECT
865
-    COALESCE(NULLIF(pg.display_name, ''), pu.display_name) AS display_name,
866
-    pg.personnal_group,
867
-    pg.group_id,
868
-    :node_id AS node_id,
869
-    COALESCE(pgn.rights, 0) AS rights
870
-FROM
871
-    pod_group AS pg
872
-    LEFT JOIN
873
-        pod_group_node AS pgn
874
-    ON
875
-        pg.group_id=pgn.group_id
876
-        AND pgn.node_id=:node_id
877
-    LEFT JOIN
878
-        pod_user AS pu
879
-    ON
880
-        pu.user_id=-pg.group_id
881
-WHERE
882
-    pg.personnal_group='t'
883
-ORDER BY
884
-    display_name
885
-;"""
886
-
887
-DIRTY_RealGroupRightOnNodeSqlQuery = """
888
-SELECT
889
-    pg.display_name AS display_name,
890
-    pg.personnal_group,
891
-    pg.group_id,
892
-    :node_id AS node_id,
893
-    COALESCE(pgn.rights, 0) AS rights
894
-FROM
895
-    pod_group AS pg
896
-    LEFT JOIN
897
-        pod_group_node AS pgn
898
-    ON
899
-        pg.group_id=pgn.group_id
900
-        AND pgn.node_id=:node_id
901
-WHERE
902
-    pg.personnal_group!='t'
903
-ORDER BY
904
-    display_name
905
-;"""
906
-
907
-sqlao.mapper(DIRTY_GroupRightsOnNode, DIRTY_group_rights_on_node_query)
908
-

+ 134 - 229
tracim/tracim/model/serializers.py View File

@@ -11,8 +11,8 @@ from tracim.model.data import ContentStatus
11 11
 from tracim.model.data import ContentRevisionRO
12 12
 from tracim.model.data import LinkItem
13 13
 from tracim.model.data import NodeTreeItem
14
-from tracim.model.data import PBNode
15
-from tracim.model.data import PBNodeType
14
+from tracim.model.data import Content
15
+from tracim.model.data import ContentType
16 16
 from tracim.model.data import RoleType
17 17
 from tracim.model.data import UserRoleInWorkspace
18 18
 from tracim.model.data import Workspace
@@ -20,94 +20,6 @@ from tracim.model.data import Workspace
20 20
 from tracim.model import data as pmd
21 21
 from tracim.lib import CST
22 22
 
23
-def node_to_dict(node: pmd.PBNode, children_content, new_item_state):
24
-    """
25
-    DEPRECATED - TODO - REMOVE
26
-        children_content may be boolean or a list containing json values
27
-    """
28
-    url = tg.url('/document/', dict(node_id=node.node_id)) ## FIXME - 2014-05-27 - Make this more flexible
29
-
30
-    return dict(
31
-        id = node.node_id,
32
-        children = children_content,
33
-        text = node.data_label,
34
-        a_attr = { "href" : url },
35
-        li_attr = { "title": node.data_label },
36
-        type = node.node_type, # this property is understandable by jstree (through "types" plugin)
37
-        state = new_item_state,
38
-        node_status = node.getStatus().getId() # this is not jstree understandable data. This requires a JS 'success' callback
39
-    )
40
-
41
-
42
-def PBNodeForMenu(func):
43
-
44
-    def process_item(item: pmd.PBNode):
45
-        """ convert given item into a dictionnary """
46
-        return node_to_dict(item, item.getChildNb()>0, None)
47
-
48
-    def pre_serialize(*args, **kws):
49
-        initial_result = func(*args, **kws)
50
-        real_result = None
51
-
52
-        if isinstance(initial_result, list):
53
-            real_result = list()
54
-            for value_item in initial_result:
55
-                real_result.append(process_item(value_item))
56
-        else:
57
-            # We suppose here that we have an object only
58
-            real_result = process_item(initial_result)
59
-
60
-        return dict(d = real_result)
61
-
62
-    return pre_serialize
63
-
64
-
65
-def NodeTreeItemForMenu(func):
66
-    """ works with structure NodeTreeItem """
67
-    def process_item(structure_item: pmd.NodeTreeItem, current_node_id=None):
68
-        """ convert given item into a dictionnary """
69
-
70
-        item = structure_item.node
71
-        children = []
72
-
73
-        for child_item in structure_item.children:
74
-            children.append(process_item(child_item, current_node_id))
75
-
76
-        children_field_value = None
77
-        if len(children)>0:
78
-            children_field_value = children
79
-        elif item.getChildNb()>0:
80
-            children_field_value = True
81
-        else:
82
-            children_field_value = False
83
-
84
-        new_item_state = dict(
85
-            opened = item.getChildNb()<=0 or len(children)>0,
86
-            selected = current_node_id!=None and item.node_id==current_node_id,
87
-        )
88
-
89
-        return node_to_dict(item, children_field_value, new_item_state)
90
-
91
-    def pre_serialize(*args, **kws):
92
-        initial_result = func(*args, **kws)
93
-        real_result = None
94
-
95
-        current_node_id = None
96
-        if "current_node_id" in kws:
97
-            current_node_id = int(kws['current_node_id'])
98
-
99
-        if isinstance(initial_result, list):
100
-            real_result = list()
101
-            for value_item in initial_result:
102
-                real_result.append(process_item(value_item, current_node_id))
103
-        else:
104
-            # We suppose here that we have an object only
105
-            real_result = process_item(initial_result, current_node_id)
106
-
107
-        return dict(d = real_result)
108
-
109
-    return pre_serialize
110
-
111 23
 #############################################################"
112 24
 ##
113 25
 ## HERE COMES THE SERIALIZATION CLASSES
@@ -324,66 +236,63 @@ def serialize_breadcrumb_item(item: BreadcrumbItem, context: Context):
324 236
 @pod_serializer(ContentRevisionRO, CTX.FILE)
325 237
 def serialize_version_for_page_or_file(version: ContentRevisionRO, context: Context):
326 238
     return DictLikeClass(
327
-        id = version.version_id,
328
-        node_id = version.node_id,
329
-        label = version.data_label if version.data_label else version.data_file_name,
239
+        id = version.revision_id,
240
+        label = version.label if version.label else version.file_name,
330 241
         owner = context.toDict(version.owner),
331
-        created = version.created_at,
242
+        created = version.created,
332 243
         action = context.toDict(version.get_last_action())
333 244
     )
334 245
 
335 246
 
336
-@pod_serializer(PBNode, CTX.DEFAULT)
337
-def serialize_breadcrumb_item(content: PBNode, context: Context):
247
+@pod_serializer(Content, CTX.DEFAULT)
248
+def serialize_breadcrumb_item(content: Content, context: Context):
338 249
     return DictLikeClass(
339
-        id = content.node_id,
340
-        label = content.data_label,
341
-        folder = context.toDict(DictLikeClass(id = content.parent.node_id if content.parent else None)),
342
-        # folder = None if not content.parent else context.toDict(DictLikeClass(id = content.parent.node_id)),
250
+        id = content.content_id,
251
+        label = content.label,
252
+        folder = context.toDict(DictLikeClass(id = content.parent.content_id if content.parent else None)),
343 253
         workspace = context.toDict(content.workspace)
344 254
     )
345 255
 
346 256
 
347
-@pod_serializer(PBNode, CTX.MENU_API)
348
-def serialize_content_for_menu_api(content: PBNode, context: Context):
349
-    content_id = content.node_id
257
+@pod_serializer(Content, CTX.MENU_API)
258
+def serialize_content_for_menu_api(content: Content, context: Context):
259
+    content_id = content.content_id
350 260
     workspace_id = content.workspace_id
351 261
 
352 262
     result = DictLikeClass(
353 263
         id = CST.TREEVIEW_MENU.ID_TEMPLATE__FULL.format(workspace_id, content_id),
354 264
         children = True, # TODO: make this dynamic
355
-        text = content.data_label,
265
+        text = content.label,
356 266
         a_attr = { 'href' : tg.url('/workspaces/{}/folders/{}'.format(workspace_id, content_id)) },
357
-        li_attr = { 'title': content.data_label, 'class': 'tracim-tree-item-is-a-folder' },
358
-        type = content.node_type,
267
+        li_attr = { 'title': content.label, 'class': 'tracim-tree-item-is-a-folder' },
268
+        type = content.type,
359 269
         state = { 'opened': False, 'selected': False }
360 270
     )
361 271
     return result
362 272
 
363 273
 
364
-@pod_serializer(PBNode, CTX.FILES)
365
-@pod_serializer(PBNode, CTX.PAGES)
366
-def serialize_node_for_page_list(content: PBNode, context: Context):
274
+@pod_serializer(Content, CTX.FILES)
275
+@pod_serializer(Content, CTX.PAGES)
276
+def serialize_node_for_page_list(content: Content, context: Context):
367 277
 
368
-    if content.node_type==PBNodeType.Page:
278
+    if content.type==ContentType.Page:
369 279
         if not content.parent:
370 280
             folder = None
371 281
         else:
372
-            print('FOLDER PARENT IS', content.parent)
373 282
             folder = Context(CTX.DEFAULT).toDict(content.parent)
374
-        print('FOLDER IS', folder)
283
+
375 284
         result = DictLikeClass(
376
-            id = content.node_id,
377
-            label = content.data_label,
285
+            id = content.content_id,
286
+            label = content.label,
378 287
             status = context.toDict(content.get_status()),
379 288
             folder = folder
380 289
         )
381 290
         return result
382 291
 
383
-    if content.node_type==PBNodeType.File:
292
+    if content.type==ContentType.File:
384 293
         result = DictLikeClass(
385
-            id = content.node_id,
386
-            label = content.data_label if content.data_label else content.data_file_name,
294
+            id = content.content_id,
295
+            label = content.label if content.label else content.file_name,
387 296
             status = context.toDict(content.get_status()),
388 297
             folder = Context(CTX.DEFAULT).toDict(content.parent)
389 298
         )
@@ -392,19 +301,19 @@ def serialize_node_for_page_list(content: PBNode, context: Context):
392 301
 
393 302
     # TODO - DA - 2014-10-16 - THE FOLLOWING CODE SHOULD BE REMOVED
394 303
     #
395
-    # if content.node_type==PBNodeType.Folder:
304
+    # if content.type==ContentType.Folder:
396 305
     #     return DictLikeClass(
397
-    #         id = content.node_id,
398
-    #         label = content.data_label,
306
+    #         id = content.content_id,
307
+    #         label = content.label,
399 308
     #     )
400 309
 
401
-    raise NotImplementedError('node type / context not implemented: {} {}'. format(content.node_type, context.context_string))
310
+    raise NotImplementedError('node type / context not implemented: {} {}'. format(content.type, context.context_string))
402 311
 
403 312
 
404
-@pod_serializer(PBNode, CTX.PAGE)
405
-@pod_serializer(PBNode, CTX.FILE)
406
-def serialize_node_for_page(content: PBNode, context: Context):
407
-    if content.node_type in (PBNodeType.Page, PBNodeType.File) :
313
+@pod_serializer(Content, CTX.PAGE)
314
+@pod_serializer(Content, CTX.FILE)
315
+def serialize_node_for_page(content: Content, context: Context):
316
+    if content.type in (ContentType.Page, ContentType.File) :
408 317
         data_container = content
409 318
 
410 319
 
@@ -412,121 +321,118 @@ def serialize_node_for_page(content: PBNode, context: Context):
412 321
         # The following properties are overriden by revision values
413 322
         if content.revision_to_serialize>0:
414 323
             for revision in content.revisions:
415
-                if revision.version_id==content.revision_to_serialize:
324
+                if revision.revision_id==content.revision_to_serialize:
416 325
                     data_container = revision
417 326
                     break
418 327
 
419 328
         result = DictLikeClass(
420
-            id = content.node_id,
329
+            id = content.content_id,
421 330
             parent = context.toDict(content.parent),
422 331
             workspace = context.toDict(content.workspace),
423
-            type = content.node_type,
332
+            type = content.type,
424 333
 
425
-            content = data_container.data_content,
426
-            created = data_container.created_at,
427
-            label = data_container.data_label,
428
-            icon = PBNodeType.icon(content.node_type),
334
+            content = data_container.description,
335
+            created = data_container.created,
336
+            label = data_container.label,
337
+            icon = ContentType.icon(content.type),
429 338
             owner = context.toDict(data_container.owner),
430 339
             status = context.toDict(data_container.get_status()),
431
-            links = context.toDict(content.extract_links_from_content(data_container.data_content)),
432
-            revisions = context.toDict(sorted(content.revisions, key=lambda v: v.created_at, reverse=True)),
340
+            links = context.toDict(content.extract_links_from_content(data_container.description)),
341
+            revisions = context.toDict(sorted(content.revisions, key=lambda v: v.created, reverse=True)),
433 342
             selected_revision = 'latest' if content.revision_to_serialize<=0 else content.revision_to_serialize
434 343
         )
435 344
 
436
-        if content.node_type==PBNodeType.File:
437
-            result.label = content.data_label if content.data_label else content.data_file_name
345
+        if content.type==ContentType.File:
346
+            result.label = content.label if content.label else content.file_name
438 347
             result['file'] = DictLikeClass(
439
-                name = data_container.data_file_name,
440
-                size = len(data_container.data_file_content),
441
-                mimetype = data_container.data_file_mime_type)
348
+                name = data_container.file_name,
349
+                size = len(data_container.file_content),
350
+                mimetype = data_container.file_mimetype)
442 351
         return result
443 352
 
444
-    if content.node_type==PBNodeType.Folder:
353
+    if content.type==ContentType.Folder:
445 354
         value = DictLikeClass(
446
-            id = content.node_id,
447
-            label = content.data_label,
355
+            id = content.content_id,
356
+            label = content.label,
448 357
         )
449 358
         return value
450 359
 
451 360
     raise NotImplementedError
452 361
 
453 362
 
454
-@pod_serializer(PBNode, CTX.THREAD)
455
-def serialize_node_for_page(item: PBNode, context: Context):
456
-    if item.node_type==PBNodeType.Thread:
363
+@pod_serializer(Content, CTX.THREAD)
364
+def serialize_node_for_page(item: Content, context: Context):
365
+    if item.type==ContentType.Thread:
457 366
         return DictLikeClass(
458
-            content = item.data_content,
459
-            created = item.created_at,
460
-            icon = PBNodeType.icon(item.node_type),
461
-            id = item.node_id,
462
-            label = item.data_label,
463
-            links = context.toDict(item.extract_links_from_content(item.data_content)),
367
+            content = item.description,
368
+            created = item.created,
369
+            icon = ContentType.icon(item.type),
370
+            id = item.content_id,
371
+            label = item.label,
372
+            links = context.toDict(item.extract_links_from_content(item.description)),
464 373
             owner = context.toDict(item.owner),
465 374
             parent = context.toDict(item.parent),
466 375
             selected_revision = 'latest',
467 376
             status = context.toDict(item.get_status()),
468
-            type = item.node_type,
377
+            type = item.type,
469 378
             workspace = context.toDict(item.workspace),
470 379
             comments = reversed(context.toDict(item.get_comments()))
471 380
         )
472 381
 
473
-    if item.node_type==PBNodeType.Comment:
382
+    if item.type==ContentType.Comment:
474 383
         return DictLikeClass(
475
-            content = item.data_content,
476
-            created = item.created_at,
477
-            icon = PBNodeType.icon(item.node_type),
478
-            id = item.node_id,
479
-            label = item.data_label,
384
+            content = item.description,
385
+            created = item.created,
386
+            icon = ContentType.icon(item.type),
387
+            id = item.content_id,
388
+            label = item.label,
480 389
             owner = context.toDict(item.owner),
481 390
             # REMOVE parent = context.toDict(item.parent),
482
-            type = item.node_type,
391
+            type = item.type,
483 392
         )
484 393
 
485
-    if item.node_type==PBNodeType.Folder:
394
+    if item.type==ContentType.Folder:
486 395
         return Context(CTX.DEFAULT).toDict(item)
487 396
     ### CODE BELOW IS REPLACED BY THE TWO LINES UP ^^
488 397
     # 2014-10-08 - IF YOU FIND THIS COMMENT, YOU CAn REMOVE THE CODE
489 398
     #
490
-    #if item.node_type==PBNodeType.Folder:
399
+    #if item.type==ContentType.Folder:
491 400
     #    value = DictLikeClass(
492
-    #        id = item.node_id,
493
-    #        label = item.data_label,
401
+    #        id = item.content_id,
402
+    #        label = item.label,
494 403
     #    )
495 404
     #    return value
496 405
 
497 406
     raise NotImplementedError
498 407
 
499 408
 
500
-@pod_serializer(PBNode, CTX.THREADS)
501
-def serialize_node_for_thread_list(content: PBNode, context: Context):
502
-    if content.node_type==PBNodeType.Thread:
409
+@pod_serializer(Content, CTX.THREADS)
410
+def serialize_node_for_thread_list(content: Content, context: Context):
411
+    if content.type==ContentType.Thread:
503 412
         return DictLikeClass(
504
-            id = content.node_id,
505
-            label = content.data_label,
413
+            id = content.content_id,
414
+            label = content.label,
506 415
             status = context.toDict(content.get_status()),
507 416
             folder = context.toDict(content.parent),
508 417
             comment_nb = len(content.get_comments())
509 418
         )
510 419
 
511
-    if content.node_type==PBNodeType.Folder:
420
+    if content.type==ContentType.Folder:
512 421
         return Context(CTX.DEFAULT).toDict(content)
513 422
 
514 423
     raise NotImplementedError
515 424
 
516
-@pod_serializer(PBNode, CTX.WORKSPACE)
517
-@pod_serializer(PBNode, CTX.FOLDERS)
518
-def serialize_content_for_workspace(content: PBNode, context: Context):
519
-    content_id = content.node_id
520
-    workspace_id = content.workspace_id
521
-
522
-    thread_nb_all  = content.get_child_nb(PBNodeType.Thread)
523
-    thread_nb_open = content.get_child_nb(PBNodeType.Thread)
524
-    file_nb_all  = content.get_child_nb(PBNodeType.File)
525
-    file_nb_open = content.get_child_nb(PBNodeType.File)
526
-    folder_nb_all  = content.get_child_nb(PBNodeType.Folder)
527
-    folder_nb_open = content.get_child_nb(PBNodeType.Folder)
528
-    page_nb_all  = content.get_child_nb(PBNodeType.Data)
529
-    page_nb_open = content.get_child_nb(PBNodeType.Data)
425
+@pod_serializer(Content, CTX.WORKSPACE)
426
+@pod_serializer(Content, CTX.FOLDERS)
427
+def serialize_content_for_workspace(content: Content, context: Context):
428
+    thread_nb_all  = content.get_child_nb(ContentType.Thread)
429
+    thread_nb_open = content.get_child_nb(ContentType.Thread)
430
+    file_nb_all  = content.get_child_nb(ContentType.File)
431
+    file_nb_open = content.get_child_nb(ContentType.File)
432
+    folder_nb_all  = content.get_child_nb(ContentType.Folder)
433
+    folder_nb_open = content.get_child_nb(ContentType.Folder)
434
+    page_nb_all  = content.get_child_nb(ContentType.Page)
435
+    page_nb_open = content.get_child_nb(ContentType.Page)
530 436
 
531 437
     content_nb_all = thread_nb_all +\
532 438
                      thread_nb_open +\
@@ -538,10 +444,11 @@ def serialize_content_for_workspace(content: PBNode, context: Context):
538 444
                      page_nb_open
539 445
 
540 446
 
541
-    if content.node_type==PBNodeType.Folder:
447
+    result = None
448
+    if content.type==ContentType.Folder:
542 449
         result = DictLikeClass(
543
-            id = content.node_id,
544
-            label = content.data_label,
450
+            id = content.content_id,
451
+            label = content.label,
545 452
             thread_nb = DictLikeClass(
546 453
                 all = thread_nb_all,
547 454
                 open = thread_nb_open,
@@ -563,19 +470,16 @@ def serialize_content_for_workspace(content: PBNode, context: Context):
563 470
 
564 471
     return result
565 472
 
566
-@pod_serializer(PBNode, CTX.FOLDER)
567
-def serialize_content_for_workspace_and_folder(content: PBNode, context: Context):
568
-    content_id = content.node_id
569
-    workspace_id = content.workspace_id
570
-
571
-    thread_nb_all  = content.get_child_nb(PBNodeType.Thread)
572
-    thread_nb_open = content.get_child_nb(PBNodeType.Thread)
573
-    file_nb_all  = content.get_child_nb(PBNodeType.File)
574
-    file_nb_open = content.get_child_nb(PBNodeType.File)
575
-    folder_nb_all  = content.get_child_nb(PBNodeType.Folder)
576
-    folder_nb_open = content.get_child_nb(PBNodeType.Folder)
577
-    page_nb_all  = content.get_child_nb(PBNodeType.Data)
578
-    page_nb_open = content.get_child_nb(PBNodeType.Data)
473
+@pod_serializer(Content, CTX.FOLDER)
474
+def serialize_content_for_workspace_and_folder(content: Content, context: Context):
475
+    thread_nb_all  = content.get_child_nb(ContentType.Thread)
476
+    thread_nb_open = content.get_child_nb(ContentType.Thread)
477
+    file_nb_all  = content.get_child_nb(ContentType.File)
478
+    file_nb_open = content.get_child_nb(ContentType.File)
479
+    folder_nb_all  = content.get_child_nb(ContentType.Folder)
480
+    folder_nb_open = content.get_child_nb(ContentType.Folder)
481
+    page_nb_all  = content.get_child_nb(ContentType.Page)
482
+    page_nb_open = content.get_child_nb(ContentType.Page)
579 483
 
580 484
     content_nb_all = thread_nb_all +\
581 485
                      thread_nb_open +\
@@ -587,11 +491,12 @@ def serialize_content_for_workspace_and_folder(content: PBNode, context: Context
587 491
                      page_nb_open
588 492
 
589 493
 
590
-    if content.node_type==PBNodeType.Folder:
494
+    result = None
495
+    if content.type==ContentType.Folder:
591 496
         result = DictLikeClass(
592
-            id = content.node_id,
593
-            label = content.data_label,
594
-            created = content.created_at,
497
+            id = content.content_id,
498
+            label = content.label,
499
+            created = content.created,
595 500
             workspace = context.toDict(content.workspace),
596 501
             allowed_content = DictLikeClass(content.properties['allowed_content']),
597 502
             selected_revision = 'latest',
@@ -616,15 +521,15 @@ def serialize_content_for_workspace_and_folder(content: PBNode, context: Context
616 521
             content_nb = DictLikeClass(all = content_nb_all)
617 522
         )
618 523
 
619
-    elif content.node_type==PBNodeType.Page:
524
+    elif content.type==ContentType.Page:
620 525
         result = DictLikeClass(
621
-            id = content.node_id,
622
-            label = content.data_label,
623
-            created = content.created_at,
526
+            id = content.content_id,
527
+            label = content.label,
528
+            created = content.created,
624 529
             workspace = context.toDict(content.workspace),
625 530
             owner = DictLikeClass(
626
-                id = content._oOwner.user_id,
627
-                name = content._oOwner.display_name
531
+                id = content.owner.user_id,
532
+                name = content.owner.get_display_name()
628 533
             ),
629 534
             status = DictLikeClass(id='', label=''), #FIXME - EXPORT DATA
630 535
         )
@@ -712,7 +617,7 @@ def serialize_user_list_default(user: User, context: Context):
712 617
     result = DictLikeClass()
713 618
     result['id'] = user.user_id
714 619
     result['name'] = user.get_display_name()
715
-    result['email'] = user.email_address
620
+    result['email'] = user.email
716 621
     result['enabled'] = user.is_active
717 622
     result['profile'] = user.profile
718 623
     return result
@@ -731,7 +636,7 @@ def serialize_user_for_user(user: User, context: Context):
731 636
     result = DictLikeClass()
732 637
     result['id'] = user.user_id
733 638
     result['name'] = user.get_display_name()
734
-    result['email'] = user.email_address
639
+    result['email'] = user.email
735 640
     result['roles'] = context.toDict(user.roles)
736 641
     result['enabled'] = user.is_active
737 642
     result['profile'] = user.profile
@@ -756,7 +661,7 @@ def serialize_role_in_workspace(role: UserRoleInWorkspace, context: Context):
756 661
     result['role'] = role.role
757 662
     result['style'] = role.style
758 663
     result['role_description'] = role.role_as_label()
759
-    result['email'] = role.user.email_address
664
+    result['email'] = role.user.email
760 665
     return result
761 666
 
762 667
 
@@ -774,7 +679,7 @@ def serialize_role_in_list_for_user(role: UserRoleInWorkspace, context: Context)
774 679
     result['label'] = role.role_as_label()
775 680
     result['style'] = RoleType(role.role).css_style
776 681
     result['workspace'] =  context.toDict(role.workspace)
777
-    # result['workspace_name'] = role.workspace.data_label
682
+    # result['workspace_name'] = role.workspace.label
778 683
 
779 684
     return result
780 685
 
@@ -785,7 +690,7 @@ def serialize_role_in_list_for_user(role: UserRoleInWorkspace, context: Context)
785 690
 def serialize_workspace_default(workspace: Workspace, context: Context):
786 691
     result = DictLikeClass(
787 692
         id = workspace.workspace_id,
788
-        label = workspace.data_label
693
+        label = workspace.label
789 694
     )
790 695
     return result
791 696
 
@@ -800,7 +705,7 @@ def serialize_workspace_in_list_for_one_user(workspace: Workspace, context: Cont
800 705
     """
801 706
     result = DictLikeClass()
802 707
     result['id'] = workspace.workspace_id
803
-    result['name'] = workspace.data_label
708
+    result['name'] = workspace.label
804 709
 
805 710
     return result
806 711
 
@@ -808,8 +713,8 @@ def serialize_workspace_in_list_for_one_user(workspace: Workspace, context: Cont
808 713
 def serialize_workspace_in_list(workspace: pmd.Workspace, context: Context):
809 714
     result = DictLikeClass()
810 715
     result['id'] = workspace.workspace_id
811
-    result['label'] = workspace.data_label
812
-    result['description'] = workspace.data_comment
716
+    result['label'] = workspace.label
717
+    result['description'] = workspace.description
813 718
     result['member_nb'] = len(workspace.roles)
814 719
 
815 720
     #    roles = serializableObject.roles
@@ -822,9 +727,9 @@ def serialize_workspace_in_list(workspace: pmd.Workspace, context: Context):
822 727
 def serialize_workspace_complete(workspace: pmd.Workspace, context: Context):
823 728
     result = DictLikeClass()
824 729
     result['id'] = workspace.workspace_id
825
-    result['label'] = workspace.data_label
826
-    result['description'] = workspace.data_comment
827
-    result['created'] = workspace.created_at
730
+    result['label'] = workspace.label
731
+    result['description'] = workspace.description
732
+    result['created'] = workspace.created
828 733
     result['members'] = context.toDict(workspace.roles)
829 734
     result['member_nb'] = len(workspace.roles)
830 735
 
@@ -835,9 +740,9 @@ def serialize_workspace_for_menu_api(workspace: Workspace, context: Context):
835 740
     result = DictLikeClass(
836 741
         id = CST.TREEVIEW_MENU.ID_TEMPLATE__WORKSPACE_ONLY.format(workspace.workspace_id),
837 742
         children = True, # TODO: make this dynamic
838
-        text = workspace.data_label,
743
+        text = workspace.label,
839 744
         a_attr = { 'href' : tg.url('/workspaces/{}'.format(workspace.workspace_id)) },
840
-        li_attr = { 'title': workspace.data_label, 'class': 'tracim-tree-item-is-a-workspace' },
745
+        li_attr = { 'title': workspace.label, 'class': 'tracim-tree-item-is-a-workspace' },
841 746
         type = 'workspace',
842 747
         state = { 'opened': False, 'selected': False }
843 748
     )
@@ -845,13 +750,13 @@ def serialize_workspace_for_menu_api(workspace: Workspace, context: Context):
845 750
 
846 751
 @pod_serializer(NodeTreeItem, CTX.MENU_API_BUILD_FROM_TREE_ITEM)
847 752
 def serialize_node_tree_item_for_menu_api_tree(item: NodeTreeItem, context: Context):
848
-    if isinstance(item.node, PBNode):
753
+    if isinstance(item.node, Content):
849 754
         return DictLikeClass(
850
-            id=CST.TREEVIEW_MENU.ID_TEMPLATE__FULL.format(item.node.workspace_id, item.node.node_id),
755
+            id=CST.TREEVIEW_MENU.ID_TEMPLATE__FULL.format(item.node.workspace_id, item.node.content_id),
851 756
             children=True if len(item.children)<=0 else context.toDict(item.children),
852
-            text=item.node.data_label,
853
-            a_attr={'href': tg.url('/workspaces/{}/folders/{}'.format(item.node.workspace_id, item.node.node_id)) },
854
-            li_attr={'title': item.node.data_label, 'class': 'tracim-tree-item-is-a-folder'},
757
+            text=item.node.label,
758
+            a_attr={'href': tg.url('/workspaces/{}/folders/{}'.format(item.node.workspace_id, item.node.content_id)) },
759
+            li_attr={'title': item.node.label, 'class': 'tracim-tree-item-is-a-folder'},
855 760
             type='folder',
856 761
             state={'opened': True if len(item.children)>0 else False, 'selected': item.is_selected}
857 762
         )
@@ -859,9 +764,9 @@ def serialize_node_tree_item_for_menu_api_tree(item: NodeTreeItem, context: Cont
859 764
         return DictLikeClass(
860 765
             id=CST.TREEVIEW_MENU.ID_TEMPLATE__WORKSPACE_ONLY.format(item.node.workspace_id),
861 766
             children=True if len(item.children)<=0 else context.toDict(item.children),
862
-            text=item.node.data_label,
767
+            text=item.node.label,
863 768
             a_attr={'href': tg.url('/workspaces/{}'.format(item.node.workspace_id))},
864
-            li_attr={'title': item.node.data_label, 'class': 'tracim-tree-item-is-a-workspace'},
769
+            li_attr={'title': item.node.label, 'class': 'tracim-tree-item-is-a-workspace'},
865 770
             type='workspace',
866 771
             state={'opened': True if len(item.children)>0 else False, 'selected': item.is_selected}
867 772
         )

+ 14 - 9
tracim/tracim/public/assets/css/dashboard.css View File

@@ -146,22 +146,27 @@ iframe { border: 5px solid #b3e7ff; }
146 146
 .pod-tree-item-is-a-folder > a { }
147 147
 
148 148
 
149
-.pod-less-visible { color: #999; }
149
+.tracim-less-visible { color: #999; }
150 150
 
151
-.pod-status-open { font-weight: bold; color: #759ac5; }
152
-.pod-status-closed-validated { font-weight: bold; color: #1fdb11;}
153
-.pod-status-closed-unvalidated { font-weight: bold; color: #F00;}
154
-.pod-status-closed-deprecated { font-weight: bold; color: #ea983d;}
155
-.pod-status-selected { background-color: #EEE; }
156
-.pod-panel-separator { border-width: 12px 0 0 0; }
151
+.tracim-status-open { font-weight: bold; color: #759ac5; }
152
+.tracim-status-closed-validated { font-weight: bold; color: #1fdb11;}
153
+.tracim-status-closed-unvalidated { font-weight: bold; color: #F00;}
154
+.tracim-status-closed-deprecated { font-weight: bold; color: #ea983d;}
155
+.tracim-status-selected { background-color: #EEE; }
156
+.tracim-panel-separator { border-width: 12px 0 0 0; }
157 157
 
158
-.pod-thread-item {}
159
-.pod-thread-item-content { margin-left: 12px; border-left: 8px solid #EEE; padding: 0 0.5em; }
158
+.tracim-thread-item {}
159
+.tracim-thread-item-content { margin-left: 12px; border-left: 8px solid #EEE; padding: 0 0.5em; }
160 160
 
161
+#tracim-footer-separator { margin-bottom: 30px; }
161 162
 .pod-footer {
162 163
     position: fixed;
163 164
     bottom: 0;
164 165
     width: 100%;
166
+    background-color: #F5F5F5;
167
+    border-top: 1px solid #CCC;
168
+    z-index: 1001;
165 169
 }
170
+.pod-footer p { margin: auto; }
166 171
 
167 172
 

+ 1 - 1
tracim/tracim/templates/debug/iconset-tango.mak View File

@@ -23,7 +23,7 @@
23 23
     import sys
24 24
 
25 25
     icon_files = dict()
26
-    rootdir = './pod/public/assets/icons/'
26
+    rootdir = './tracim/public/assets/icons/'
27 27
     
28 28
     for root, subFolders, files in os.walk(rootdir):
29 29
         for foundFile in files:

+ 3 - 2
tracim/tracim/templates/master_anonymous.mak View File

@@ -28,8 +28,9 @@
28 28
         <div class="container-fluid">
29 29
             ${self.main_menu()}
30 30
             ${self.content_wrapper()}
31
-            ${self.footer()}
31
+            <div id="tracim-footer-separator"></div>
32 32
         </div>
33
+        ${self.footer()}
33 34
 
34 35
         <script src="${tg.url('/assets/js/bootstrap.min.js')}"></script>
35 36
         ## HACK - D.A. - 2014-10-21
@@ -64,7 +65,7 @@
64 65
 <%def name="footer()">
65 66
     <div class="pod-footer footer hidden-tablet hidden-phone text-center">
66 67
         <p>
67
-            <a href="http://trac.im">${_('Create your own email-ready collaborative workspace on trac.im')}</a> &mdash;
68
+            <a href="http://trac.im">${_('Create your own collaborative workspace on trac.im')}</a> &mdash;
68 69
             copyright &copy; 2013 - ${h.current_year()} tracim project.
69 70
         </p>
70 71
     </div>

+ 6 - 5
tracim/tracim/templates/master_authenticated.mak View File

@@ -21,9 +21,10 @@
21 21
         <div class="container-fluid">
22 22
             ${self.main_menu()}
23 23
             ${self.content_wrapper()}
24
-            ${self.footer()}
25
-        </div>
26
-
24
+            <div id="tracim-footer-separator"></div>
25
+        </div>
26
+        ${self.footer()}
27
+
27 28
         <script src="${tg.url('/assets/js/bootstrap.min.js')}"></script>
28 29
         ${h.tracker_js()|n}
29 30
     </body>
@@ -45,7 +46,7 @@
45 46
 <%def name="footer()">
46 47
     <div class="pod-footer footer hidden-tablet hidden-phone text-center">
47 48
         <p>
48
-            <a href="http://trac.im">${_('Create your own email-ready collaborative workspace on trac.im')}</a> &mdash;
49
+            <a href="http://trac.im">${_('Create your own collaborative workspace on trac.im')}</a> &mdash;
49 50
             copyright &copy; 2013 - ${h.current_year()} tracim project.
50 51
         </p>
51 52
     </div>
@@ -121,7 +122,7 @@
121 122
 ##                        </li>
122 123
                         <li class="dropdown">
123 124
                             <a href="#" class="dropdown-toggle" data-toggle="dropdown">
124
-                              ${request.identity['user'].display_name}
125
+                              ${TIM.ICO(16, 'categories/applications-system')} ${request.identity['user'].display_name}
125 126
                             </a>
126 127
                             <ul class="dropdown-menu pull-right">
127 128
                                 <li>

+ 1 - 0
tracim/tracim/templates/pod.mak View File

@@ -1,5 +1,6 @@
1 1
 <%def name="ICO_URL(icon_size, icon_path)">${h.IconPath(icon_size, icon_path)|n}</%def>
2 2
 <%def name="ICO(icon_size, icon_path, title='')"><img src="${h.IconPath(icon_size, icon_path)|n}" alt="" title="${title}"/></%def>
3
+<%def name="ICO_TOOLTIP(icon_size, icon_path, title='')"><span rel="tooltip" data-toggle="tooltip" data-placement="bottom" title="${title}">${ICO(icon_size, icon_path, title)}</span></%def>
3 4
 <%def name="ICO_BADGED(icon_size, icon_path, title='', css_class='badge')"><span class="${css_class}" rel="tooltip" data-toggle="tooltip" data-placement="bottom" title="${title}">${ICO(icon_size, icon_path, title)}</span></%def>
4 5
 <%def name="ICO_FA_BADGED(fa_class='fa fa-flag', title='', css_style='')"><i style="${css_style}" class="${fa_class}" rel="tooltip" data-toggle="tooltip" data-placement="bottom" title="${title}"></i></%def>
5 6
 

+ 1 - 1
tracim/tracim/templates/user_get_me.mak View File

@@ -39,7 +39,7 @@
39 39
                             ${_('My workspaces')}
40 40
                         </h3>
41 41
                         % if len(result.user.roles)<=0:
42
-                            ${WIDGETS.EMPTY_CONTENT(_('This user is not member of any workspace.'))}
42
+                            ${WIDGETS.EMPTY_CONTENT(_('You are not member of any workspace.'))}
43 43
                         % else:
44 44
                             <table class="table">
45 45
                                 % for role in result.user.roles:

+ 6 - 6
tracim/tracim/templates/user_workspace_folder_file_get_one.mak View File

@@ -96,15 +96,15 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
96 96
 </div>
97 97
 
98 98
 % if result.file.status.id!='open':
99
-    <p class="pod-less-visible">${_('<b>Note</b>: You need to change status in case you want to upload a new version')|n}</p>
100
-    <hr class="pod-panel-separator"/>
99
+    <p class="tracim-less-visible">${_('<b>Note</b>: You need to change status in case you want to upload a new version')|n}</p>
100
+    <hr class="tracim-panel-separator"/>
101 101
 % else:
102 102
     % if h.user_role(fake_api.current_user, result.file.workspace)<=1: # User must be a contributor to be allowed to upload files
103
-        <hr class="pod-panel-separator"/>
103
+        <hr class="tracim-panel-separator"/>
104 104
         ${WIDGETS.SECURED_SECTION_TITLE(fake_api.current_user, result.file.workspace, 'file-revisions', _('File revisions'))}
105 105
         <p>${_('This file contains {} revision(s)').format(sum(1 for revision in result.file.revisions if revision.action.id=='revision'))}</p>
106 106
     % else:
107
-        <hr class="pod-panel-separator"/>
107
+        <hr class="tracim-panel-separator"/>
108 108
         ${WIDGETS.SECURED_SECTION_TITLE(fake_api.current_user, result.file.workspace, 'file-revisions', _('File revisions'), 'new-file-revision', _('upload a new revision and/or comment...'))}
109 109
         <p>${_('This file contains {} revision(s)').format(sum(1 for revision in result.file.revisions if revision.action.id=='revision'))}</p>
110 110
         ${FORMS.NEW_FILE_REVISION_WITH_COMMENT_FORM('new-file-revision', result.file.workspace.id, result.file.parent.id, result.file.id)}
@@ -114,7 +114,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
114 114
 <div>
115 115
     <table class="table table-striped table-hover">
116 116
         % for revid, revision in reversed(list(enumerate(reversed(result.file.revisions)))):
117
-            % if revision.action.id=='revision':
117
+            % if revision.action.id in ('creation', 'revision'):
118 118
                 ## INFO - D.A. - 2014-10-22
119 119
                 ## We do not show status update and other editions that are not revisions
120 120
                 ## (at least in this revision list table)
@@ -126,7 +126,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
126 126
                     <% rev_download_url = tg.url('/workspaces/{}/folders/{}/files/{}/download?revision_id={}'.format(result.file.workspace.id, result.file.parent.id, result.file.id, revision.id)) %>
127 127
                     <td><a href="${rev_download_url}">${TIM.ICO(16, 'actions/go-bottom', _('Download this particular revision'))}</a></td>
128 128
                     <td>${h.date_time_in_long_format(revision.created, _('%Y-%m-%d at %H:%M'))}</td>
129
-                    <td>${TIM.ICO(16, revision.action.icon, revision.action.label)}</td>
129
+                    <td>${TIM.ICO_TOOLTIP(16, revision.action.icon, revision.action.label)}</td>
130 130
                     <td>
131 131
                         % if warning_or_not:
132 132
                             ${TIM.ICO(16, 'actions/go-previous')} <strong>${_('Revision r{}').format(result.file.selected_revision)}</strong>

+ 1 - 1
tracim/tracim/templates/user_workspace_folder_get_one.mak View File

@@ -54,7 +54,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
54 54
         ${TIM.ICO_BADGED(16, 'mimetypes/text-html', _('pages'))}
55 55
     % endif
56 56
 </p>
57
-<hr class="pod-panel-separator"/>
57
+<hr class="tracim-panel-separator"/>
58 58
 
59 59
 % if result.folder.allowed_content.folder:
60 60
     % if h.user_role(fake_api.current_user, result.folder.workspace)<=2: # User must be a content manager to be allowed to create folders

+ 2 - 2
tracim/tracim/templates/user_workspace_folder_page_get_one.mak View File

@@ -60,7 +60,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
60 60
     ${result.page.content|n}
61 61
 </div>
62 62
 
63
-<hr class="pod-panel-separator"/>
63
+<hr class="tracim-panel-separator"/>
64 64
 <div>
65 65
     <h4 id="associated-links" class="anchored-title" >${_('Links extracted from the page')}</h4>
66 66
     <div>
@@ -74,7 +74,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
74 74
             </ul>
75 75
         % endif
76 76
     </div>
77
-    <hr/>
77
+    <hr class="tracim-panel-separator"/>
78 78
 
79 79
 
80 80
     <h4 id="associated-links" class="anchored-title" >${_('Page revisions')}</h4>

+ 8 - 8
tracim/tracim/templates/user_workspace_folder_thread_get_one.mak View File

@@ -58,13 +58,13 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
58 58
 
59 59
 % if h.user_role(fake_api.current_user, result.thread.workspace)<=1:
60 60
     ## READONLY USER
61
-    <hr class="pod-panel-separator"/>
61
+    <hr class="tracim-panel-separator"/>
62 62
 % else:
63 63
     % if result.thread.status.id!='open':
64
-        <p class="pod-less-visible">${_('<b>Note</b>: In case you\'d like to post a reply, you must first open again the thread')|n}</p>
65
-        <hr class="pod-panel-separator"/>
64
+        <p class="tracim-less-visible">${_('<b>Note</b>: In case you\'d like to post a reply, you must first open again the thread')|n}</p>
65
+        <hr class="tracim-panel-separator"/>
66 66
     % else:
67
-        <hr class="pod-panel-separator"/>
67
+        <hr class="tracim-panel-separator"/>
68 68
         <p>    
69 69
             ${WIDGETS.DATA_TARGET_BUTTON('new-comment', _('Post a reply...'))}
70 70
             ${FORMS.NEW_COMMENT_FORM_IN_THREAD('new-comment', result.thread.workspace.id, result.thread.parent.id, result.thread.id)}
@@ -73,7 +73,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
73 73
 % endif 
74 74
 
75 75
 % for comment in result.thread.comments:
76
-    <div class="pod-thread-item">
76
+    <div class="tracim-thread-item">
77 77
         <h5 style="margin: 0;">
78 78
             <div class="pull-right text-right">
79 79
                 <div class="label" style="font-size: 10px; border: 1px solid #CCC; color: #777; ">
@@ -85,9 +85,9 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
85 85
             </div>
86 86
    
87 87
             ${TIM.ICO(32, comment.icon)}
88
-            <span class="pod-less-visible">${_('<strong>{}</strong> wrote:').format(comment.owner.name)|n}</span>
88
+            <span class="tracim-less-visible">${_('<strong>{}</strong> wrote:').format(comment.owner.name)|n}</span>
89 89
         </h5>
90
-        <div class="pod-thread-item-content">
90
+        <div class="tracim-thread-item-content">
91 91
             <div>${comment.content|n}</div>
92 92
             <br/>
93 93
         </div>
@@ -95,7 +95,7 @@ ${WIDGETS.BREADCRUMB('current-page-breadcrumb', fake_api.breadcrumb)}
95 95
 
96 96
 % endfor
97 97
 
98
-## <hr class="pod-panel-separator"/>
98
+## <hr class="tracim-panel-separator"/>
99 99
 ## <div>
100 100
 ##     <h4 id="associated-links" class="anchored-title" >${_('Links extracted from the thread')}</h4>
101 101
 ##     <div>

+ 1 - 1
tracim/tracim/templates/user_workspace_get_one.mak View File

@@ -46,7 +46,7 @@
46 46
         % endfor
47 47
     % endif
48 48
 </p>
49
-<hr class="pod-panel-separator"/>
49
+<hr class="tracim-panel-separator"/>
50 50
 
51 51
 
52 52
 % if h.user_role(fake_api.current_user, result.workspace)<=2: # User must be a content manager to be allowed to create folders

+ 2 - 2
tracim/tracim/tests/models/test_auth.py View File

@@ -21,7 +21,7 @@ class TestUser(ModelTest):
21 21
     klass = model.User
22 22
     attrs = dict(
23 23
         user_name = "ignucius",
24
-        email_address = "ignucius@example.org"
24
+        email = "ignucius@example.org"
25 25
         )
26 26
 
27 27
     def test_obj_creation_username(self):
@@ -30,7 +30,7 @@ class TestUser(ModelTest):
30 30
 
31 31
     def test_obj_creation_email(self):
32 32
         """The obj constructor must set the email right"""
33
-        eq_(self.obj.email_address, "ignucius@example.org")
33
+        eq_(self.obj.email, "ignucius@example.org")
34 34
 
35 35
     def test_no_permissions_by_default(self):
36 36
         """User objects should have no permission by default."""

+ 2 - 2
tracim/tracim/websetup/bootstrap.py View File

@@ -16,7 +16,7 @@ def bootstrap(command, conf, vars):
16 16
         u = model.User()
17 17
         u.user_name = 'manager'
18 18
         u.display_name = 'Example manager'
19
-        u.email_address = 'manager@somedomain.com'
19
+        u.email = 'manager@somedomain.com'
20 20
         u.password = 'managepass'
21 21
     
22 22
         model.DBSession.add(u)
@@ -39,7 +39,7 @@ def bootstrap(command, conf, vars):
39 39
         u1 = model.User()
40 40
         u1.user_name = 'editor'
41 41
         u1.display_name = 'Example editor'
42
-        u1.email_address = 'editor@somedomain.com'
42
+        u1.email = 'editor@somedomain.com'
43 43
         u1.password = 'editpass'
44 44
     
45 45
         model.DBSession.add(u1)