def test_unit__delete_revision__err__nominal_case(self, admin_user, session, content_type_list): # file creation workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) session.add(content) session.flush() with new_revision(session=session, tm=transaction.manager, content=content): content.description = "TEST_CONTENT_DESCRIPTION_1_UPDATED" session.add(content) session.flush() session.delete(content.revisions[0]) # Raise ContentRevisionDeleteError because revision can't be deleted with pytest.raises(ContentRevisionDeleteError): session.flush()
def test_unit__content_depot_file__ok__nominal_case( self, admin_user, session, content_type_list): """ Depot file access thought content property methods. """ workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) session.add(content) session.flush() # tests uninitialized depot file assert content.depot_file is None # initializes depot file # which is able to behave like a python file object content.depot_file = b"test" # tests initialized depot file assert content.depot_file # tests type of initialized depot file assert type(content.depot_file) == UploadedFile # tests content of initialized depot file # using depot_file.file of type StoredFile to fetch content back assert content.depot_file.file.read() == b"test"
def test_unit__delete_revision__ok__with_unsafe_context( self, admin_user, session, content_type_list): with unprotected_content_revision(session) as unsafe_session: workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) unsafe_session.add(workspace) unsafe_session.flush() content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) unsafe_session.add(content) unsafe_session.flush() with new_revision(session=unsafe_session, tm=transaction.manager, content=content): content.description = "TEST_CONTENT_DESCRIPTION_1_UPDATED" unsafe_session.add(content) unsafe_session.flush() unsafe_session.delete(content.revisions[0]) unsafe_session.flush()
def new_revision( session: Session, tm: TransactionManager, content: Content, force_create_new_revision: bool = False, ) -> Content: """ Prepare context to update a Content. It will add a new updatable revision to the content. :param session: Database _session :param tm: TransactionManager :param content: Content instance to update :param force_create_new_revision: Decide if new_rev should or should not be forced. :return: """ with session.no_autoflush: try: if force_create_new_revision \ or inspect(content.revision).has_identity: content.new_revision() RevisionsIntegrity.add_to_updatable(content.revision) yield content except Exception as e: # INFO - GM - 14-11-2018 - rollback session and renew # transaction when error happened # This avoid bad _session data like new "temporary" revision # to be add when problem happen. session.rollback() tm.abort() tm.begin() raise e finally: RevisionsIntegrity.remove_from_updatable(content.revision)
def test_children(self, admin_user, session, app_config, content_api_factory, content_type_list): workspace = WorkspaceApi(current_user=admin_user, session=session, config=app_config).create_workspace( "workspace_1", save_now=True) folder = Content(type=content_type_list.Folder.slug, owner=admin_user) folder.label = "folder_1" folder.workspace = workspace session.add(folder) session.flush() thread = Content(type=content_type_list.Thread.slug, owner=admin_user, parent=folder) thread.label = "thread_1" thread.workspace = workspace session.add(folder) session.flush() workspace = session.query(Workspace).filter( Workspace.label == "workspace_1").one() content_api = content_api_factory.get() folder = content_api.get_canonical_query().filter( Content.label == "folder_1").one() assert [folder] == list(workspace.get_valid_children())
def test_unit__create_content__ok__nominal_case(self, admin_user, session, content_type_list): assert session.query(Workspace).filter( Workspace.label == "TEST_WORKSPACE_1").count() == 0 workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() assert session.query(Workspace).filter( Workspace.label == "TEST_WORKSPACE_1").count() == 1 assert (session.query(ContentRevisionRO).filter( ContentRevisionRO.label == "TEST_CONTENT_1").count() == 0) content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) session.add(content) session.flush() assert (session.query(ContentRevisionRO).filter( ContentRevisionRO.label == "TEST_CONTENT_1").count() == 1) searched_content = (session.query(ContentRevisionRO).filter( ContentRevisionRO.label == "TEST_CONTENT_1").one()) assert searched_content.label == content.label
class FakeTracimContext(TracimContext): current_user = User(user_id=2, email="*****@*****.**") current_comment = Content( content_id=15, type=content_type_list.Comment.slug, owner=User(user_id=3, email="*****@*****.**"), )
def test_unit__crud_caller__ok__content(self, session): hook = ContentHookImpl() session.context.plugin_manager.register(hook) owner = User(email="john") session.add(owner) workspace = Workspace(label="Hello", owner=owner) session.add(workspace) session.flush() content = Content( label="Foo", owner=owner, workspace=workspace, revision_type=ActionDescription.CREATION, type="html-document", ) session.add(content) session.flush() hook.mock_hooks.assert_called_with("created", content=content, context=session.context) with new_revision( session=session, tm=transaction.manager, content=content, ): content.label = "Bar" session.add(content) session.flush() hook.mock_hooks.assert_called_with("modified", content=content, context=session.context) # TODO SGD 2020/05/06: add this test when deleting a Content is possible session.delete(content) session.flush() hook.mock_hooks.assert_called_with("deleted", content=content, context=session.context)
def _create_content_and_test(self, name, workspace, *args, **kwargs) -> Content: """ All extra parameters (*args, **kwargs) are for Content init :return: Created Content instance """ content = Content(*args, **kwargs) content.label = name content.workspace = workspace self.session.add(content) self.session.flush() content_api = ContentApi(current_user=None, session=self.session, config=self.app_config) eq_( 1, content_api.get_canonical_query().filter( Content.label == name).count()) return content_api.get_canonical_query().filter( Content.label == name).one()
def test_unit__get_allowed_content_type__ok(self, admin_user, session, content_type_list) -> None: workspace = Workspace(label="TEST_WORKSPACE", owner=admin_user) session.add(workspace) session.flush() content1 = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) content1.properties = {"allowed_content": {"unknown_type": True}} try: assert content1.get_allowed_content_types() == [] except ValueError: pytest.fail( "Unknown content type should not raise exception anymore " "when getting allowed content_type")
def test_unit__get_children__ok__nominal_case(sel, admin_user, session, content_type_list): workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() parent_folder = Content( owner=admin_user, workspace=workspace, type=content_type_list.Folder.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) session.add(parent_folder) session.flush() assert parent_folder.children.all() == [] children_folder = Content( owner=admin_user, workspace=workspace, type=content_type_list.Folder.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, parent=parent_folder, ) session.add(children_folder) session.flush() assert [type(child) == Content for child in parent_folder.children] assert [child.revision_id for child in parent_folder.children ] == [children_folder.cached_revision_id] with new_revision(session=session, tm=transaction.manager, content=children_folder): children_folder.parent = None session.flush() assert parent_folder.children.all() == []
def test_unit__update__err__without_prepare(self, admin_user, session, content_type_list): # file creation workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) session.add(content) session.flush() # file update with pytest.raises(ContentRevisionUpdateError): content.description = "FOO"
def delete_content(self, content: Content, recursively: bool = True) -> typing.List[str]: """ Delete content and associated stuff: - all content revisions - all children content if recursively is True - all content share :param content: content_id to delete :param recursively: should we delete children content too ? :return: list of content_id of content(s) deleted """ deleted_contents = [] # INFO - G.M - 2019-12-11 - delete content_share shares = self.session.query(ContentShare).filter( ContentShare.content_id == content.content_id) for share in shares: logger.info( self, "delete share {} from content {}".format( share.share_id, share.content_id)) self.safe_delete(share) # INFO - G.M - 2019-12-11 - delete children of content if recursively: for children in content.get_children(recursively=recursively): self.delete_content(children) content.cached_revision_id = None for revision in content.revisions: deleted_contents.append( self.delete_revision(revision, do_update_content_last_revision=False)) logger.info(self, "delete content {}".format(content.content_id)) deleted_contents.append(content.content_id) self.safe_delete(content) return deleted_contents
def test_unit__workspace_get_size(self, admin_user, session, content_type_list): """ Depot file access thought content property methods. """ workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) content.depot_file = b"test" session.add(content) assert workspace.get_size() == 4 assert workspace.get_size(include_deleted=True) == 4 assert workspace.get_size(include_archived=True) == 4 with new_revision(session=session, tm=transaction.manager, content=content): content.is_deleted = True content.is_archived = False session.flush() transaction.commit() assert workspace.get_size() == 0 assert workspace.get_size(include_deleted=True) == 8 assert workspace.get_size(include_archived=True) == 0 with new_revision(session=session, tm=transaction.manager, content=content): content.is_deleted = False content.is_archived = True session.flush() transaction.commit() assert workspace.get_size() == 0 assert workspace.get_size(include_deleted=True) == 0 assert workspace.get_size(include_archived=True) == 12
def designThread(content: data.Content, content_revision: data.ContentRevisionRO, comments) -> str: hist = content.get_history(drop_empty_revision=False) allT = [] allT += comments allT += hist allT.sort(key=lambda x: x.created, reverse=True) disc = '' participants = {} for t in allT: if t.type == content_type_list.Comment.slug: disc += ''' <div class="row comment comment-row"> <i class="fa fa-comment-o comment-icon"></i> <div class="comment-content"> <h5> <span class="comment-author"><b>%s</b> wrote :</span> <div class="pull-right text-right">%s</div> </h5> %s </div> </div> ''' % (t.owner.display_name, create_readable_date( t.created), t.description) if t.owner.display_name not in participants: participants[t.owner.display_name] = [1, t.created] else: participants[t.owner.display_name][0] += 1 else: if isinstance(t, VirtualEvent) and t.type.id != 'comment': label = _LABELS[t.type.id] disc += ''' <div class="%s row comment comment-row to-hide"> <i class="fa %s comment-icon"></i> <div class="comment-content"> <h5> <span class="comment-author"><b>%s</b></span> <div class="pull-right text-right">%s</div> </h5> %s %s </div> </div> ''' % ( 'warning' if t.id == content_revision.revision_id else '', t.type.fa_icon, t.owner.display_name, t.create_readable_date(), label, # NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0 '<i class="fa fa-caret-left"></i> shown' if t.id == content_revision.revision_id else '' # else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % ( # content.label, # t.id, # t.ref_object.label) if t.type.id in ['revision', 'creation', 'edition'] else '') ) thread = ''' <html> <head> <meta charset="utf-8" /> <title>%s</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css"> <style>%s</style> <script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script> </head> <body> <div id="left" class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="title thread"> <div class="title-text"> <i class="fa fa-comments-o title-icon thread"></i> <h1>%s</h1> <h6>thread created on <b>%s</b> by <b>%s</b></h6> </div> <div class="pull-right"> <div class="btn-group btn-group-vertical"> <!-- NOTE: Not omplemented yet, don't display not working link <a class="btn btn-default" onclick="hide_elements()"> <i id="hideshow" class="fa fa-eye-slash"></i> <span id="hideshowtxt" >Hide history</span></a> </a>--> <a class="btn btn-default"> <i class="fa fa-external-link"></i> View in tracim</a> </a> </div> </div> </div> <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12"> <div class="description"> <span class="description-text">%s</span> </div> %s </div> </div> <script type="text/javascript"> window.onload = function() { file_location = window.location.href file_location = file_location.replace(/\/[^/]*$/, '') file_location = file_location.replace(/\/.history\/[^/]*$/, '') // NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0 // $('.revision-link').each(function() { // $(this).attr('href', file_location + $(this).attr('href')) // }); } function hide_elements() { elems = document.getElementsByClassName('to-hide'); if (elems.length > 0) { for(var i = 0; i < elems.length; i++) { $(elems[i]).addClass('to-show') $(elems[i]).hide(); } while (elems.length>0) { $(elems[0]).removeClass('comment-row'); $(elems[0]).removeClass('to-hide'); } $('#hideshow').addClass('fa-eye').removeClass('fa-eye-slash'); $('#hideshowtxt').html('Show history'); } else { elems = document.getElementsByClassName('to-show'); for(var i = 0; i<elems.length; i++) { $(elems[0]).addClass('comment-row'); $(elems[i]).addClass('to-hide'); $(elems[i]).show(); } while (elems.length>0) { $(elems[0]).removeClass('to-show'); } $('#hideshow').removeClass('fa-eye').addClass('fa-eye-slash'); $('#hideshowtxt').html('Hide history'); } } </script> </body> </html> ''' % (content_revision.label, style, content_revision.label, content.created.strftime("%B %d, %Y at %H:%m"), content.owner.display_name, content_revision.description, disc) return thread
def test_dummy_notifier__notify_content_update(self): c = Content() notifier = DummyNotifier(self.app_config, self.session) notifier.notify_content_update(c)
def _create_content(self, *args, **kwargs): content = Content(*args, **kwargs) self.session.add(content) self.session.flush() return content
def notify_content_update(self, content: Content): if content.get_last_action().id not \ in self.config.EMAIL_NOTIFICATION_NOTIFIED_EVENTS: logger.info( self, 'Skip email notification for update of content {}' 'by user {} (the action is {})'.format( content.content_id, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id ) ) return logger.info(self, 'About to email-notify update' 'of content {} by user {}'.format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0 ) ) if content.type not \ in self.config.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS: logger.info( self, 'Skip email notification for update of content {}' 'by user {} (the content type is {})'.format( content.type, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id ) ) return logger.info(self, 'About to email-notify update' 'of content {} by user {}'.format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0 ) ) #### # # INFO - D.A. - 2014-11-05 - Emails are sent through asynchronous jobs. # For that reason, we do not give SQLAlchemy objects but ids only # (SQLA objects are related to a given thread/session) # try: if self.config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == self.config.CST.ASYNC.lower(): logger.info(self, 'Sending email in ASYNC mode') # TODO - D.A - 2014-11-06 # This feature must be implemented in order to be able to scale to large communities raise NotImplementedError('Sending emails through ASYNC mode is not working yet') else: logger.info(self, 'Sending email in SYNC mode') EmailManager( self._smtp_config, self.config, self.session, ).notify_content_update(self._user.user_id, content.content_id) except Exception as e: # TODO - G.M - 2018-08-27 - Do Better catching for exception here logger.error(self, 'Exception catched during email notification: {}'.format(e.__str__())) logger.exception(self, e)
def notify_content_update(self, content: Content): if content.get_last_action( ).id not in self.config.EMAIL__NOTIFICATION__NOTIFIED_EVENTS: logger.info( self, "Skip email notification for update of content {}" "by user {} (the action is {})".format( content.content_id, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id, ), ) return logger.info( self, "About to email-notify update" "of content {} by user {}".format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0, ), ) if content.type not in self.config.EMAIL__NOTIFICATION__NOTIFIED_CONTENTS: logger.info( self, "Skip email notification for update of content {}" "by user {} (the content type is {})".format( content.type, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id, ), ) return logger.info( self, "About to email-notify update" "of content {} by user {}".format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0, ), ) #### # # INFO - D.A. - 2014-11-05 - Emails are sent through asynchronous jobs. # For that reason, we do not give SQLAlchemy objects but ids only # (SQLA objects are related to a given thread/session) # try: if (self.config.EMAIL__NOTIFICATION__PROCESSING_MODE.lower() == self.config.CST.ASYNC.lower()): logger.info(self, "Sending email in ASYNC mode") # TODO - D.A - 2014-11-06 # This feature must be implemented in order to be able to scale to large communities raise NotImplementedError( "Sending emails through ASYNC mode is not working yet") else: logger.info(self, "Sending email in SYNC mode") EmailManager(self._smtp_config, self.config, self.session).notify_content_update( self._user.user_id, content.content_id) except Exception: logger.exception(self, "Exception catched during email notification")
def test_unit__update__ok__nominal_case(self, admin_user, session, content_type_list): workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() with freeze_time("1999-12-31 23:59:59"): content = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) # TODO - G.M - 2019-07-05 - for unknown reason freeze-time doesn't work implicitly at # content creation, we do override date here to have correct behaviour. content.updated = content.created = datetime.utcnow() session.add(content) session.flush() transaction.commit() searched_content = session.query(Content).filter( Content.id == content.id).one() assert (session.query(ContentRevisionRO).filter( ContentRevisionRO.label == "TEST_CONTENT_1").count() == 1) assert session.query(Content).filter( Content.id == searched_content.id).count() == 1 with freeze_time("2000-01-01 00:00:05"): with new_revision(session=session, tm=transaction.manager, content=content): content.description = "TEST_CONTENT_DESCRIPTION_1_UPDATED" session.flush() assert (session.query(ContentRevisionRO).filter( ContentRevisionRO.label == "TEST_CONTENT_1").count() == 2) assert session.query(Content).filter( Content.id == searched_content.id).count() == 1 with freeze_time("2003-12-31 23:59:59"): with new_revision(session=session, tm=transaction.manager, content=content): content.description = "TEST_CONTENT_DESCRIPTION_1_UPDATED_2" session.flush() assert (session.query(ContentRevisionRO).filter( ContentRevisionRO.label == "TEST_CONTENT_1").count() == 3) assert session.query(Content).filter( Content.id == searched_content.id).count() == 1 revision_1 = (session.query(ContentRevisionRO).filter( ContentRevisionRO.description == "TEST_CONTENT_DESCRIPTION_1").one()) revision_2 = (session.query(ContentRevisionRO).filter( ContentRevisionRO.description == "TEST_CONTENT_DESCRIPTION_1_UPDATED").one()) revision_3 = (session.query(ContentRevisionRO).filter( ContentRevisionRO.description == "TEST_CONTENT_DESCRIPTION_1_UPDATED_2").one()) # Updated dates must be different assert revision_1.updated < revision_2.updated < revision_3.updated # Created dates must be equal assert revision_1.created == revision_2.created == revision_3.created
def test_unit__query_content__ok__nominal_case(self, admin_user, session, content_type_list): workspace = Workspace(label="TEST_WORKSPACE_1", owner=admin_user) session.add(workspace) session.flush() workspace2 = Workspace(label="TEST_WORKSPACE_2", owner=admin_user) session.add(workspace2) session.flush() content1 = Content( owner=admin_user, workspace=workspace, type=content_type_list.Page.slug, label="TEST_CONTENT_1", description="TEST_CONTENT_DESCRIPTION_1", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) with new_revision(session=session, tm=transaction.manager, content=content1): content1.description = "TEST_CONTENT_DESCRIPTION_1_UPDATED" session.add(content1) session.flush() content2 = Content( owner=admin_user, workspace=workspace2, type=content_type_list.Page.slug, label="TEST_CONTENT_2", description="TEST_CONTENT_DESCRIPTION_2", revision_type=ActionDescription.CREATION, is_deleted=False, is_archived=False, ) with new_revision(session=session, tm=transaction.manager, content=content2): content2.description = "TEST_CONTENT_DESCRIPTION_2_UPDATED" session.add(content2) session.flush() workspace1 = session.query(Workspace).filter( Workspace.label == "TEST_WORKSPACE_1").one() workspace2 = session.query(Workspace).filter( Workspace.label == "TEST_WORKSPACE_2").one() # To get Content in database # we have to join Content and ContentRevisionRO # with particular condition: # Join have to be on most recent revision join_sub_query = (session.query(ContentRevisionRO.revision_id).filter( ContentRevisionRO.content_id == Content.id).order_by( ContentRevisionRO.revision_id.desc()).limit(1).correlate( Content)) base_query = session.query(Content).join( ContentRevisionRO, and_( Content.id == ContentRevisionRO.content_id, ContentRevisionRO.revision_id == join_sub_query, ), ) pattern = "TEST_CONTENT_DESCRIPTION_%_UPDATED" assert base_query.filter( Content.description.like(pattern)).count() == 2 assert base_query.filter(Content.workspace == workspace1).count() == 1 assert base_query.filter(Content.workspace == workspace2).count() == 1 content1_from_query = base_query.filter( Content.workspace == workspace1).one() assert content1.id == content1_from_query.id assert "TEST_CONTENT_DESCRIPTION_1_UPDATED" == content1_from_query.description
class FakeTracimContext(TracimContext): current_user = User(user_id=2, email='*****@*****.**') current_comment = Content(content_id=15, type=content_type_list.Comment.slug, owner=current_user)
class FakeTracimContext(TracimContext): current_content = Content( content_id=15, type='unexistent_type', )
class FakeTracimContext(TracimContext): current_content = Content(content_id=15, type=content_type_list.Thread.slug)
class FakeBaseFakeTracimContext(BaseFakeTracimContext): current_content = Content(content_id=15, type="unexistent_type")
def designPage(content: data.Content, content_revision: data.ContentRevisionRO) -> str: hist = content.get_history(drop_empty_revision=False) histHTML = '<table class="table table-striped table-hover">' for event in hist: if isinstance(event, VirtualEvent): date = event.create_readable_date() label = _LABELS[event.type.id] histHTML += ''' <tr class="%s"> <td class="my-align"><span class="label label-default"><i class="fa %s"></i> %s</span></td> <td>%s</td> <td>%s</td> <td>%s</td> </tr> ''' % ( 'warning' if event.id == content_revision.revision_id else '', event.type.fa_icon, label, date, event.owner.display_name, # NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0 '<i class="fa fa-caret-left"></i> shown' if event.id == content_revision.revision_id else '' # '''<span><a class="revision-link" href="/.history/%s/(%s - %s) %s.html">(View revision)</a></span>''' % ( # content.label, event.id, event.type.id, event.ref_object.label) if event.type.id in ['revision', 'creation', 'edition'] else '') ) histHTML += '</table>' page = ''' <html> <head> <meta charset="utf-8" /> <title>%s</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css"> <style>%s</style> <script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script> <script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s=" crossorigin="anonymous"></script> </head> <body> <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12"> <div class="title page"> <div class="title-text"> <i class="fa fa-file-text-o title-icon page"></i> <h1>%s</h1> <h6>page created on <b>%s</b> by <b>%s</b></h6> </div> <div class="pull-right"> <div class="btn-group btn-group-vertical"> <!-- NOTE: Not omplemented yet, don't display not working link <a class="btn btn-default"> <i class="fa fa-external-link"></i> View in tracim</a> </a>--> </div> </div> </div> <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12"> %s </div> </div> <div id="right" class="col-lg-4 col-md-12 col-sm-12 col-xs-12"> <h4>History</h4> %s </div> <script type="text/javascript"> window.onload = function() { file_location = window.location.href file_location = file_location.replace(/\/[^/]*$/, '') file_location = file_location.replace(/\/.history\/[^/]*$/, '') // NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0 // $('.revision-link').each(function() { // $(this).attr('href', file_location + $(this).attr('href')) // }); } </script> </body> </html> ''' % (content_revision.label, style, content_revision.label, content.created.strftime("%B %d, %Y at %H:%m"), content.owner.display_name, content_revision.description, histHTML) return page