def test_uuids_differ_between_forks(self): url = self.project.web_url_for('project_wiki_view', wname=self.wname) project_res = self.app.get(url, auth=self.user.auth) assert_equal(project_res.status_code, 200) self.project.reload() fork = self.project.fork_node(Auth(self.user)) assert_true(fork.is_fork_of(self.project)) fork_url = fork.web_url_for('project_wiki_view', wname=self.wname) fork_res = self.app.get(fork_url, auth=self.user.auth) assert_equal(fork_res.status_code, 200) fork.reload() # uuids are stored the same internally assert_equal( self.project.wiki_private_uuids.get(self.wkey), fork.wiki_private_uuids.get(self.wkey) ) project_uuid = get_sharejs_uuid(self.project, self.wname) fork_uuid = get_sharejs_uuid(fork, self.wname) assert_not_equal(project_uuid, fork_uuid) assert_in(project_uuid, project_res) assert_in(fork_uuid, fork_res) assert_not_in(project_uuid, fork_res) assert_not_in(fork_uuid, project_res)
def test_uuid_persists_after_delete(self, mock_sharejs): assert_is_none(self.project.wiki_private_uuids.get(self.wkey)) # Create wiki page self.project.update_node_wiki(self.wname, 'Hello world', Auth(self.user)) # Visit wiki edit page edit_url = self.project.web_url_for('project_wiki_view', wname=self.wname) res = self.app.get(edit_url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() original_private_uuid = self.project.wiki_private_uuids.get(self.wkey) original_sharejs_uuid = get_sharejs_uuid(self.project, self.wname) # Delete wiki delete_url = self.project.api_url_for('project_wiki_delete', wname=self.wname) res = self.app.delete(delete_url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() assert_equal(original_private_uuid, self.project.wiki_private_uuids.get(self.wkey)) # Revisit wiki edit page res = self.app.get(edit_url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() assert_equal(original_private_uuid, self.project.wiki_private_uuids.get(self.wkey)) assert_in(original_sharejs_uuid, res.body)
def project_wiki_rename(auth, wname, **kwargs): """View that handles user the X-editable input for wiki page renaming. :param wname: The target wiki page name. :param-json value: The new wiki page name. """ node = kwargs['node'] or kwargs['project'] wiki_name = wname.strip() new_wiki_name = request.get_json().get('value', None) try: node.rename_node_wiki(wiki_name, new_wiki_name, auth) except NameEmptyError: raise WIKI_NAME_EMPTY_ERROR except NameInvalidError as error: raise HTTPError(http.BAD_REQUEST, data=dict( message_short='Invalid name', message_long=error.args[0] )) except NameMaximumLengthError: raise WIKI_NAME_MAXIMUM_LENGTH_ERROR except PageCannotRenameError: raise WIKI_PAGE_CANNOT_RENAME_ERROR except PageConflictError: raise WIKI_PAGE_CONFLICT_ERROR except PageNotFoundError: raise WIKI_PAGE_NOT_FOUND_ERROR else: sharejs_uuid = wiki_utils.get_sharejs_uuid(node, new_wiki_name) wiki_utils.broadcast_to_sharejs('redirect', sharejs_uuid, node, new_wiki_name)
def project_wiki_rename(auth, wname, **kwargs): """View that handles user the X-editable input for wiki page renaming. :param wname: The target wiki page name. :param-json value: The new wiki page name. """ node = kwargs['node'] or kwargs['project'] wiki_name = wname.strip() new_wiki_name = request.get_json().get('value', None) try: node.rename_node_wiki(wiki_name, new_wiki_name, auth) except NameEmptyError: raise WIKI_NAME_EMPTY_ERROR except NameInvalidError as error: raise HTTPError(http.BAD_REQUEST, data=dict( message_short='Invalid name', message_long=error.args[0] )) except NameMaximumLengthError: raise WIKI_NAME_MAXIMUM_LENGTH_ERROR except PageCannotRenameError: raise WIKI_PAGE_CANNOT_RENAME_ERROR except PageConflictError: raise WIKI_PAGE_CONFLICT_ERROR except PageNotFoundError: raise WIKI_PAGE_NOT_FOUND_ERROR except ValidationError as err: raise HTTPError(http.BAD_REQUEST, data=dict( message_short='Invalid request', message_long=err.messages[0] )) else: sharejs_uuid = wiki_utils.get_sharejs_uuid(node, new_wiki_name) wiki_utils.broadcast_to_sharejs('redirect', sharejs_uuid, node, new_wiki_name)
def test_uuid_not_visible_without_write_permission(self): self.project.update_node_wiki(self.wname, 'some content', Auth(self.user)) self.project.save() assert_is_none(self.project.wiki_private_uuids.get(self.wkey)) url = self.project.web_url_for('project_wiki_view', wname=self.wname) res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() private_uuid = self.project.wiki_private_uuids.get(self.wkey) assert_true(private_uuid) assert_not_in(private_uuid, res.body) assert_in(get_sharejs_uuid(self.project, self.wname), res.body) # Users without write permission should not be able to access res = self.app.get(url) assert_equal(res.status_code, 200) assert_not_in(get_sharejs_uuid(self.project, self.wname), res.body)
def test_uuids_differ_between_pages(self): wname1 = 'foo.bar' url1 = self.project.web_url_for('project_wiki_view', wname=wname1) res1 = self.app.get(url1, auth=self.user.auth) assert_equal(res1.status_code, 200) wname2 = 'bar.baz' url2 = self.project.web_url_for('project_wiki_view', wname=wname2) res2 = self.app.get(url2, auth=self.user.auth) assert_equal(res2.status_code, 200) self.project.reload() uuid1 = get_sharejs_uuid(self.project, wname1) uuid2 = get_sharejs_uuid(self.project, wname2) assert_not_equal(uuid1, uuid2) assert_in(uuid1, res1) assert_in(uuid2, res2) assert_not_in(uuid1, res2) assert_not_in(uuid2, res1)
def project_wiki_delete(auth, wname, **kwargs): node = kwargs['node'] or kwargs['project'] wiki_name = wname.strip() wiki_page = node.get_wiki_page(wiki_name) sharejs_uuid = wiki_utils.get_sharejs_uuid(node, wiki_name) if not wiki_page: raise HTTPError(http.NOT_FOUND) node.delete_node_wiki(wiki_name, auth) wiki_utils.broadcast_to_sharejs('delete', sharejs_uuid, node) return {}
def test_get_sharejs_uuid(self): wname = 'foo.bar' wname2 = 'bar.baz' private_uuid = generate_private_uuid(self.project, wname) sharejs_uuid = get_sharejs_uuid(self.project, wname) # Provides consistent results assert_equal(sharejs_uuid, get_sharejs_uuid(self.project, wname)) # Provides obfuscation assert_not_in(wname, sharejs_uuid) assert_not_in(sharejs_uuid, wname) assert_not_in(private_uuid, sharejs_uuid) assert_not_in(sharejs_uuid, private_uuid) # Differs based on share uuid provided assert_not_equal(sharejs_uuid, get_sharejs_uuid(self.project, wname2)) # Differs across projects and forks project = ProjectFactory() assert_not_equal(sharejs_uuid, get_sharejs_uuid(project, wname)) fork = self.project.fork_node(Auth(self.project.creator)) assert_not_equal(sharejs_uuid, get_sharejs_uuid(fork, wname))
def test_migrate_uuid_no_mongo(self, mock_sharejs): # Case where no edits have been made to the wiki wname = 'bar.baz' wkey = to_mongo_key(wname) share_uuid = generate_private_uuid(self.project, wname) sharejs_uuid = get_sharejs_uuid(self.project, wname) self.project.update_node_wiki(wname, 'Hello world', Auth(self.user)) wiki_page = self.project.get_wiki_page(wname) migrate_uuid(self.project, wname) assert_not_equal(share_uuid, self.project.wiki_private_uuids.get(wkey)) assert_is_none(self.db.docs.find_one({'_id': sharejs_uuid})) assert_is_none(self.db.docs_ops.find_one({'name': sharejs_uuid}))
def test_migrate_uuid(self, mock_sharejs): migrate_uuid(self.project, self.wname) assert_is_none(self.db.docs.find_one({'_id': self.sharejs_uuid})) assert_is_none(self.db.docs_ops.find_one({'name': self.sharejs_uuid})) new_sharejs_uuid = get_sharejs_uuid(self.project, self.wname) assert_equal( EXAMPLE_DOCS[0]['_data'], self.db.docs.find_one({'_id': new_sharejs_uuid})['_data'] ) assert_equal( len([item for item in self.example_ops if item['name'] == self.sharejs_uuid]), len([item for item in self.db.docs_ops.find({'name': new_sharejs_uuid})]) )
def update_active_sharejs(self, node): """ Update all active sharejs sessions with latest wiki content. """ """ TODO: This def is meant to be used after updating wiki content via the v2 API, once updating is has been implemented. It should be removed if not used for that purpose. """ sharejs_uuid = wiki_utils.get_sharejs_uuid(node, self.page_name) contributors = [user._id for user in node.contributors] wiki_utils.broadcast_to_sharejs('reload', sharejs_uuid, data=contributors)
def test_uuid_generated_once(self): assert_is_none(self.project.wiki_private_uuids.get(self.wkey)) url = self.project.web_url_for('project_wiki_view', wname=self.wname) res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() private_uuid = self.project.wiki_private_uuids.get(self.wkey) assert_true(private_uuid) assert_not_in(private_uuid, res.body) assert_in(get_sharejs_uuid(self.project, self.wname), res.body) # Revisit page; uuid has not changed res = self.app.get(url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() assert_equal(private_uuid, self.project.wiki_private_uuids.get(self.wkey))
def get_draft(self, node): """ Return most recently edited version of wiki, whether that is the last saved version or the most recent sharejs draft. """ db = wiki_utils.share_db() sharejs_uuid = wiki_utils.get_sharejs_uuid(node, self.page_name) doc_item = db['docs'].find_one({'_id': sharejs_uuid}) if doc_item: sharejs_version = doc_item['_v'] sharejs_timestamp = doc_item['_m']['mtime'] sharejs_timestamp /= 1000 # Convert to appropriate units sharejs_date = datetime.datetime.utcfromtimestamp(sharejs_timestamp) if sharejs_version > 1 and sharejs_date > self.date: return doc_item['_data'] return self.content
def setUp(self): super(TestWikiShareJSMongo, self).setUp() self.user = AuthUserFactory() self.project = ProjectFactory(is_public=True, creator=self.user) self.wname = 'foo.bar' self.wkey = to_mongo_key(self.wname) self.private_uuid = generate_private_uuid(self.project, self.wname) self.sharejs_uuid = get_sharejs_uuid(self.project, self.wname) # Create wiki page self.project.update_node_wiki(self.wname, 'Hello world', Auth(self.user)) self.wiki_page = self.project.get_wiki_page(self.wname) # Insert mongo data for current project/wiki self.db = share_db() example_uuid = EXAMPLE_DOCS[0]['_id'] self.example_docs = deepcopy(EXAMPLE_DOCS) self.example_docs[0]['_id'] = self.sharejs_uuid self.db.docs.insert(self.example_docs) self.example_ops = deepcopy(EXAMPLE_OPS) for item in self.example_ops: item['_id'] = item['_id'].replace(example_uuid, self.sharejs_uuid) item['name'] = item['name'].replace(example_uuid, self.sharejs_uuid) self.db.docs_ops.insert(self.example_ops)
def test_uuid_persists_after_rename(self, mock_sharejs): new_wname = 'bar.baz' new_wkey = to_mongo_key(new_wname) assert_is_none(self.project.wiki_private_uuids.get(self.wkey)) assert_is_none(self.project.wiki_private_uuids.get(new_wkey)) # Create wiki page self.project.update_node_wiki(self.wname, 'Hello world', Auth(self.user)) wiki_page = self.project.get_wiki_page(self.wname) # Visit wiki edit page original_edit_url = self.project.web_url_for('project_wiki_view', wname=self.wname) res = self.app.get(original_edit_url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() original_private_uuid = self.project.wiki_private_uuids.get(self.wkey) original_sharejs_uuid = get_sharejs_uuid(self.project, self.wname) # Rename wiki rename_url = self.project.api_url_for('project_wiki_rename', wname=self.wname) res = self.app.put_json( rename_url, {'value': new_wname, 'pk': wiki_page._id}, auth=self.user.auth, ) assert_equal(res.status_code, 200) self.project.reload() assert_is_none(self.project.wiki_private_uuids.get(self.wkey)) assert_equal(original_private_uuid, self.project.wiki_private_uuids.get(new_wkey)) # Revisit original wiki edit page res = self.app.get(original_edit_url, auth=self.user.auth) assert_equal(res.status_code, 200) self.project.reload() assert_not_equal(original_private_uuid, self.project.wiki_private_uuids.get(self.wkey)) assert_not_in(original_sharejs_uuid, res.body)
def project_wiki_view(auth, wname, path=None, **kwargs): node = kwargs['node'] or kwargs['project'] anonymous = has_anonymous_link(node, auth) wiki_name = (wname or '').strip() wiki_key = to_mongo_key(wiki_name) wiki_page = node.get_wiki_page(wiki_name) wiki_settings = node.get_addon('wiki') can_edit = ( auth.logged_in and not node.is_registration and ( node.has_permission(auth.user, 'write') or wiki_settings.is_publicly_editable ) ) versions = _get_wiki_versions(node, wiki_name, anonymous=anonymous) # Determine panels used in view panels = {'view', 'edit', 'compare', 'menu'} if request.args and set(request.args).intersection(panels): panels_used = [panel for panel in request.args if panel in panels] num_columns = len(set(panels_used).intersection({'view', 'edit', 'compare'})) if num_columns == 0: panels_used.append('view') num_columns = 1 else: panels_used = ['view', 'menu'] num_columns = 1 try: view = wiki_utils.format_wiki_version( version=request.args.get('view'), num_versions=len(versions), allow_preview=True, ) compare = wiki_utils.format_wiki_version( version=request.args.get('compare'), num_versions=len(versions), allow_preview=False, ) except InvalidVersionError: raise WIKI_INVALID_VERSION_ERROR # Default versions for view and compare version_settings = { 'view': view or ('preview' if 'edit' in panels_used else 'current'), 'compare': compare or 'previous', } # ensure home is always lower case since it cannot be renamed if wiki_name.lower() == 'home': wiki_name = 'home' if wiki_page: version = wiki_page.version is_current = wiki_page.is_current content = wiki_page.html(node) use_python_render = wiki_page.rendered_before_update else: version = 'NA' is_current = False content = '' use_python_render = False if can_edit: if wiki_key not in node.wiki_private_uuids: wiki_utils.generate_private_uuid(node, wiki_name) sharejs_uuid = wiki_utils.get_sharejs_uuid(node, wiki_name) else: if wiki_key not in node.wiki_pages_current and wiki_key != 'home': raise WIKI_PAGE_NOT_FOUND_ERROR if 'edit' in request.args: if wiki_settings.is_publicly_editable: raise HTTPError(http.UNAUTHORIZED) raise HTTPError(http.FORBIDDEN) sharejs_uuid = None # Opens 'edit' panel when home wiki is empty if not content and can_edit and wiki_name == 'home': panels_used.append('edit') ret = { 'wiki_id': wiki_page._primary_key if wiki_page else None, 'wiki_name': wiki_page.page_name if wiki_page else wiki_name, 'wiki_content': content, 'use_python_render': use_python_render, 'page': wiki_page, 'version': version, 'versions': versions, 'sharejs_uuid': sharejs_uuid or '', 'sharejs_url': settings.SHAREJS_URL, 'is_current': is_current, 'version_settings': version_settings, 'pages_current': _get_wiki_pages_current(node), 'category': node.category, 'panels_used': panels_used, 'num_columns': num_columns, 'urls': { 'api': _get_wiki_api_urls(node, wiki_name, { 'content': node.api_url_for('wiki_page_content', wname=wiki_name), 'draft': node.api_url_for('wiki_page_draft', wname=wiki_name), }), 'web': _get_wiki_web_urls(node, wiki_name), 'gravatar': get_gravatar(auth.user, 25), }, } ret.update(_view_project(node, auth, primary=True)) ret['user']['can_edit_wiki_body'] = can_edit return ret
def project_wiki_view(auth, wname, path=None, **kwargs): node = kwargs['node'] or kwargs['project'] anonymous = has_anonymous_link(node, auth) wiki_name = (wname or '').strip() wiki_key = to_mongo_key(wiki_name) wiki_page = node.get_wiki_page(wiki_name) wiki_settings = node.get_addon('wiki') can_edit = ( auth.logged_in and not node.is_registration and ( node.has_permission(auth.user, 'write') or wiki_settings.is_publicly_editable ) ) versions = _get_wiki_versions(node, wiki_name, anonymous=anonymous) # Determine panels used in view panels = {'view', 'edit', 'compare', 'menu'} if request.args and set(request.args).intersection(panels): panels_used = [panel for panel in request.args if panel in panels] num_columns = len(set(panels_used).intersection({'view', 'edit', 'compare'})) if num_columns == 0: panels_used.append('view') num_columns = 1 else: panels_used = ['view', 'menu'] num_columns = 1 try: view = wiki_utils.format_wiki_version( version=request.args.get('view'), num_versions=len(versions), allow_preview=True, ) compare = wiki_utils.format_wiki_version( version=request.args.get('compare'), num_versions=len(versions), allow_preview=False, ) except InvalidVersionError: raise WIKI_INVALID_VERSION_ERROR # ensure home is always lower case since it cannot be renamed if wiki_name.lower() == 'home': wiki_name = 'home' if wiki_page: version = wiki_page.version is_current = wiki_page.is_current content = wiki_page.html(node) rendered_before_update = wiki_page.rendered_before_update else: version = 'NA' is_current = False content = '' rendered_before_update = False if can_edit: if wiki_key not in node.wiki_private_uuids: wiki_utils.generate_private_uuid(node, wiki_name) sharejs_uuid = wiki_utils.get_sharejs_uuid(node, wiki_name) else: if wiki_key not in node.wiki_pages_current and wiki_key != 'home': raise WIKI_PAGE_NOT_FOUND_ERROR if 'edit' in request.args: if wiki_settings.is_publicly_editable: raise HTTPError(http.UNAUTHORIZED) raise HTTPError(http.FORBIDDEN) sharejs_uuid = None # Opens 'edit' panel when home wiki is empty if not content and can_edit and wiki_name == 'home': panels_used.append('edit') # Default versions for view and compare version_settings = { 'view': view or ('preview' if 'edit' in panels_used else 'current'), 'compare': compare or 'previous', } ret = { 'wiki_id': wiki_page._primary_key if wiki_page else None, 'wiki_name': wiki_page.page_name if wiki_page else wiki_name, 'wiki_content': content, 'rendered_before_update': rendered_before_update, 'page': wiki_page, 'version': version, 'versions': versions, 'sharejs_uuid': sharejs_uuid or '', 'sharejs_url': settings.SHAREJS_URL, 'is_current': is_current, 'version_settings': version_settings, 'pages_current': _get_wiki_pages_current(node), 'category': node.category, 'panels_used': panels_used, 'num_columns': num_columns, 'urls': { 'api': _get_wiki_api_urls(node, wiki_name, { 'content': node.api_url_for('wiki_page_content', wname=wiki_name), 'draft': node.api_url_for('wiki_page_draft', wname=wiki_name), }), 'web': _get_wiki_web_urls(node, wiki_name), 'gravatar': get_gravatar(auth.user, 25), }, } ret.update(_view_project(node, auth, primary=True)) ret['user']['can_edit_wiki_body'] = can_edit return ret
def project_wiki_view(auth, wname, path=None, **kwargs): node = kwargs["node"] or kwargs["project"] anonymous = has_anonymous_link(node, auth) wiki_name = (wname or "").strip() wiki_key = to_mongo_key(wiki_name) wiki_page = node.get_wiki_page(wiki_name) wiki_settings = node.get_addon("wiki") can_edit = ( auth.logged_in and not node.is_registration and (node.has_permission(auth.user, "write") or wiki_settings.is_publicly_editable) ) versions = _get_wiki_versions(node, wiki_name, anonymous=anonymous) # Determine panels used in view panels = {"view", "edit", "compare", "menu"} if request.args and set(request.args).intersection(panels): panels_used = [panel for panel in request.args if panel in panels] num_columns = len(set(panels_used).intersection({"view", "edit", "compare"})) if num_columns == 0: panels_used.append("view") num_columns = 1 else: panels_used = ["view", "menu"] num_columns = 1 try: view = wiki_utils.format_wiki_version( version=request.args.get("view"), num_versions=len(versions), allow_preview=True ) compare = wiki_utils.format_wiki_version( version=request.args.get("compare"), num_versions=len(versions), allow_preview=False ) except InvalidVersionError: raise WIKI_INVALID_VERSION_ERROR # Default versions for view and compare version_settings = { "view": view or ("preview" if "edit" in panels_used else "current"), "compare": compare or "previous", } # ensure home is always lower case since it cannot be renamed if wiki_name.lower() == "home": wiki_name = "home" if wiki_page: version = wiki_page.version is_current = wiki_page.is_current content = wiki_page.html(node) use_python_render = wiki_page.rendered_before_update else: version = "NA" is_current = False content = "" use_python_render = False if can_edit: if wiki_key not in node.wiki_private_uuids: wiki_utils.generate_private_uuid(node, wiki_name) sharejs_uuid = wiki_utils.get_sharejs_uuid(node, wiki_name) else: if wiki_key not in node.wiki_pages_current and wiki_key != "home": raise WIKI_PAGE_NOT_FOUND_ERROR if "edit" in request.args: if wiki_settings.is_publicly_editable: raise HTTPError(http.UNAUTHORIZED) raise HTTPError(http.FORBIDDEN) sharejs_uuid = None ret = { "wiki_id": wiki_page._primary_key if wiki_page else None, "wiki_name": wiki_page.page_name if wiki_page else wiki_name, "wiki_content": content, "use_python_render": use_python_render, "page": wiki_page, "version": version, "versions": versions, "sharejs_uuid": sharejs_uuid or "", "sharejs_url": settings.SHAREJS_URL, "is_current": is_current, "version_settings": version_settings, "pages_current": _get_wiki_pages_current(node), "category": node.category, "panels_used": panels_used, "num_columns": num_columns, "urls": { "api": _get_wiki_api_urls( node, wiki_name, { "content": node.api_url_for("wiki_page_content", wname=wiki_name), "draft": node.api_url_for("wiki_page_draft", wname=wiki_name), }, ), "web": _get_wiki_web_urls(node, wiki_name), "gravatar": get_gravatar(auth.user, 25), }, } ret.update(_view_project(node, auth, primary=True)) ret["user"]["can_edit_wiki_body"] = can_edit return ret