def test_create_map(self, client): root = Node.root() r = root.add(langslugs=dict(fr="fr", en="en", nl="nl")) assert Node.get("/fr", language="fr") == \ Node.get("/en", language="en") == \ Node.get("/nl", language="nl") == r
def test_remove_single_root(self, client, root): """ single, non-recursive removal """ root.add("aaa") assert Node.get("/aaa") root.remove("aaa") assert not Node.get("/aaa")
def test_remove_single_child(self, client, root): """ single, non-recursive removal """ child = root.add("aaa") child.add("bbb") assert Node.get("/aaa/bbb") child.remove("bbb") assert not Node.get("/aaa/bbb")
def test_remove_recursive(self, client, root): """ recursive removal """ child = root.add("aaa") child.add("b1") child.add("b2") assert Node.get("/aaa/b1") root.remove("aaa") assert not Node.get("/aaa/b1") assert not Node.get("/aaa/b2")
def test_add_different_slugs(self, client): translation.activate('en') root = Node.root() child = root.add("child") child.rename("kind", language="nl") grandchild = child.add("grandchild") assert grandchild.get_path(language="nl") == "/kind/grandchild" assert grandchild.get_path(language="en") == "/child/grandchild" assert Node.get("/child/grandchild", language="nl") is None assert Node.get("/kind/grandchild", language="en") is None
def test_remove_recursive_child(self, client, root): """ recursive removal """ c1 = root.add("aaa") c2 = c1.add("bbb") c2.add("b1") c2.add("b2") assert Node.get("/aaa/bbb/b1") c1.remove("bbb") assert Node.get("/aaa") assert not Node.get("/aaa/bbb/b1") assert not Node.get("/aaa/bbb/b2")
def test_move_node(self, client, root): """ move a node and its descendants elsewhere """ src = root.add("src") src_c = src.add("child") target = root.add("target") res, success, failed = target.paste(src) assert Node.get('/target/src') == src assert Node.get('/target/src/child') == src_c assert Node.get('/src') is None assert res.path == "/target/src"
def test_remove_ignore_similar(self, client, root): """ removing /aaa shouldn't affect /aaaa """ root.add("aaa") root.add("aaaa") root.remove("aaa") assert Node.get("/aaaa")
def test_move_node_inuse(self, client, root): """ pasting a node to a node containing a child with the same name, e.g. pasting /foo to /target when there's already a /target/foo """ src = root.add("src") src_c = src.add("child") target = root.add("target") target_src = target.add("src") # import pytest; pytest.set_trace() res, success, failed = target.paste(src) assert Node.get('/target/src') == target_src assert src.path != "/src" assert src.path != "/target/src" assert Node.get(src.path + "/child")
def test_rename_complex(self, client): """ a rather confusing case: try to swap /r1 and /r2 for a specific language """ translation.activate('en') root = Node.root() r1 = root.add("r1") r11 = r1.add("r11") r2 = root.add("r2") r22 = r2.add("r22") r1.rename("rX", language="nl") r2.rename("r1", language="nl") r1.rename("r2", language="nl") assert Node.get("/r1", language="nl") == r2 assert Node.get("/r2", language="nl") == r1
def test_copy(self, client): root = Node.root() src = root.add(langslugs=dict(fr="source", nl="bron", en="src")) src2 = src.add(langslugs=dict(fr="fr", nl="nl", en="en")) target = root.add(langslugs=dict(fr="destination", nl="doel", en="target")) target.paste(src, copy=True) assert Node.get("/source", language="fr") == src assert Node.get("/destination/source", language="fr") is not None assert Node.get("/destination/source/fr", language="fr") is not None assert Node.get("/destination/source", language="fr") != src assert Node.get("/doel/bron", language="nl") is not None assert Node.get("/doel/bron/nl", language="nl") is not None assert Node.get("/doel/bron", language="nl") != src assert Node.get("/target/src", language="en") is not None assert Node.get("/target/src/en", language="en") is not None assert Node.get("/target/src", language="en") != src
def test_copy_node_position(self, client, root): """ a node loses its original position when copied, it should always be moved to the bottom """ src = root.add("src", position=0) target = root.add("target") target_child = target.add("child", position=10) res, success, failed = target.paste(src, copy=True) assert Node.get("/target/src").position > target_child.position
def clean_slug(self): if self.attach: return slug = self.data.get("slug", "").strip().lower() language = self.data.get("language", settings.FALLBACK) ## XXX move the whole slug generation / stopwords stuff to separate method title = self.cleaned_data.get("title", "").lower() title_no_sw = " ".join(x for x in title.split() if x not in set(stopwords.get(language, []))) parent_path = self.parent.get_path(language=language) if not slug: slug = re.sub("[^%s]+" % Node.ALLOWED_CHARS, "-", title_no_sw)[: Node.MAX_PATHLEN].strip("-") slug = re.sub("-+", "-", slug) ## slug may be empty now by all space/stopwords/dash removal if not slug: slug = "node" existing = Node.get(path=parent_path + "/" + slug, language=language) base_slug = slug[: Node.MAX_PATHLEN - 6] ## some space for counter count = 1 while (existing and existing != self.node) or (slug in self.reserved): slug = base_slug + str(count) existing = Node.get(path=self.parent.path + "/" + slug, language=language) count += 1 if slug in self.reserved: raise forms.ValidationError("This is a reserved name") if not Node.validpathre.match(slug): raise forms.ValidationError("Only numbers, letters, _-") existing = Node.get(path=parent_path + "/" + slug, language=language) if existing and existing != self.node: raise forms.ValidationError("Name in use") return slug
def test_move_node_duplicate_name(self, client, root): """ Move a node somewhere where there's already a similar slug """ # issue #789 src = root.add("src") src_c = src.add("child") root_child = root.add("child") res, success, failed = root.paste(src_c) assert Node.get('/child') == root_child assert src_c.parent() == root assert src_c.path.startswith('/')
def clean_slug(self): if self.attach: return slug = self.data.get('slug', '').strip().lower() language = self.data.get('language', settings.FALLBACK) parent_path = self.parent.get_path(language=language) if not slug: title = self.cleaned_data.get('title', '') slug = generate_slug(title, language=language, max_length=Node.MAX_PATHLEN, allowed=Node.ALLOWED_CHARS, default="node") existing = Node.get(path=parent_path + "/" + slug, language=language) base_slug = slug[:Node.MAX_PATHLEN-6] ## some space for counter count = 1 while (existing and existing != self.node) or \ (slug in self.reserved): slug = base_slug + str(count) existing = Node.get(path=self.parent.path + '/' + slug, language=language) count += 1 if slug in self.reserved: raise forms.ValidationError("This is a reserved name") if not Node.validpathre.match(slug): raise forms.ValidationError("Only numbers, letters, _-") existing = Node.get(path=parent_path + "/" + slug, language=language) if existing and existing != self.node: raise forms.ValidationError("Name in use") return slug
def test_copy_node_inuse(self, client, root): """ pasting a node to a node containing a child with the same name, e.g. pasting /foo to /target when there's already a /target/foo """ src = root.add("src") src_c = src.add("child") target = root.add("target") target_src = target.add("src") res, success, failed = target.paste(src) assert res.path != "/target/src" assert Node.get(res.path + "/child")
def test_node_slug_offspring_language(self, client): """ A node with different slugs for different languages, with children""" translation.activate('en') root = Node.root() child = root.add("child") child1 = child.add("grandchild1") child2 = child.add("grandchild2") # import pytest; pytest.set_trace() child.rename("kind", language="nl") child2.rename("kleinkind2", language="nl") translation.activate('nl') nl_child2 = Node.get("/kind/kleinkind2") assert nl_child2 == child2 assert nl_child2.path == "/kind/kleinkind2" nl_child1 = Node.get("/kind/grandchild1") assert nl_child1 == child1 assert nl_child1.path == "/kind/grandchild1"
def breadcrumb(self, operation="", details=""): """ generate breadcrumb path. """ language = self.active_language() base = self.instance or self.parent if not base: ## parent return [] parts = base.get_path(language=language).split("/") res = [] for i in range(len(parts)): subpath = "/".join(parts[:i+1]) node = Node.get(subpath, language=language) content = node.content(language=language) primary_content = node.primary_content() ## If we're in "contents" mode, link to the node's ## contents view. if operation == "Contents": subpath += '/contents' path = node.get_absolute_url() ## last entry should not get path if not operation: if i == len(parts) - 1: path = "" if node.isroot(): if primary_content and not content: title = "Home (untranslated)" elif content: title = "Home" else: title = "Unattached rootnode" else: if primary_content and not content: title = 'Untranslated content "%s"' % primary_content.title elif not content: title = "Unattached node %s" % (subpath or '/') else: title = content.title res.append((title, path)) if operation: res.append((operation + details, "")) return res
def test_node(self, client): translation.activate('en') root = Node.root() child = root.add("child") en_child = Node.get("/child") assert en_child == child assert en_child.path == "/child" assert en_child.slug() == "child" assert en_child.get_path("en") == "/child" assert en_child.slug("en") == "child" assert en_child.get_path("nl") == "/child" assert en_child.slug("nl") == "child" assert en_child.get_path("fr") == "/child" assert en_child.slug("fr") == "child"
def test_copy_content_node_unique_sub(self, client): root = Node.root() sub = root.add("sub") Type1(title="content on sub", node=sub).save() subc1 = sub.add("c1") TypeUnique(uniek="unique content on sub/c1", node=subc1).save() subc2 = sub.add("c2") Type1(title="content on sub/c2", node=subc2).save() subunique = subc1.add("subunique") sub2, success, failed = root.paste(sub, copy=True) assert len(sub2.children()) == 1 assert Node.get(sub2.path + "/c1/subunique") is None assert len(success) == 2 assert len(failed) == 1
def resolve(cls, nodepath): """ resolve a node path to an actual node in correct language context. """ ## Do a bit of path normalization: Except for root, start with /, ## remove trailing / ## Old 'coerce' used to # locale.activate_content_language(None) ## Is that necessary for anything? if nodepath in ("/", ""): nodepath = "" else: nodepath = "/{0}".format(nodepath.strip('/')) language = get_active_language() return Node.get(nodepath, language=language)
def test_copy_node(self, client, root): """ copy a node and its descendants elsewhere """ src = root.add("src") src_c = src.add("child") target = root.add("target") target.paste(src, copy=True) ## it has been copied and is not the original assert Node.get('/target/src') is not None assert Node.get('/target/src') != src assert Node.get('/target/src/child') is not None assert Node.get('/target/src/child') != src_c ## the original is still there assert Node.get('/src') is not None assert Node.get('/src') == src assert Node.get('/src/child') is not None assert Node.get('/src/child') == src_c
def test_node_slug_language(self, client): """ A node with different slugs for different languages """ translation.activate('en') root = Node.root() child = root.add("child") child.rename("kind", language="nl") child.rename("enfant", language="fr") translation.activate('nl') nl_child = Node.get("/kind") assert nl_child == child assert nl_child.path == "/kind" assert nl_child.slug() == "kind" assert nl_child.get_path("nl") == "/kind" assert nl_child.get_path("en") == "/child" assert nl_child.slug("en") == "child" assert nl_child.get_path("fr") == "/enfant" assert nl_child.slug("fr") == "enfant"
def test_move(self, client): ## meteen recursief root = Node.root() src = root.add(langslugs=dict(fr="source", nl="bron", en="src")) src2 = src.add(langslugs=dict(fr="fr", nl="nl", en="en")) # import pytest;pytest.set_trace() target = root.add(langslugs=dict(fr="destination", nl="doel", en="target")) target.paste(src, copy=False) assert Node.get("/source", language="fr") is None assert Node.get("/destination/source", language="fr") == src assert Node.get("/destination/source/fr", language="fr") == src2 assert Node.get("/doel/bron", language="nl") == src assert Node.get("/doel/bron/nl", language="nl") == src2 assert Node.get("/target/src", language="en") == src assert Node.get("/target/src/en", language="en") == src2
def test_direct_path(self, client): """ retrieve a node directly through its path """ n = Node.root().add("a").add("b").add("c") assert Node.get("/a/b/c") == n
def test_direct_root(self, client): """ retrieve the root node directly through its path """ n = Node.root() assert Node.get("") == n
def handle_panel_selection_details(self, path, type, klass="", title="", target="", download=False, newselection=False): """ Setup the panel to configure a link/image insert """ ## resolve relative / prefixed url to absolute node path path = resolve_path(path) node = Node.get(path) instance = None spoke = None default_title = title if node: instance = node.content() spoke = instance.spoke() if newselection: default_title = instance.title SIZE_CHOICES = ( ("img_content_original", "Original"), ("img_content_thumb", "Thumb"), ("img_content_small", "Small"), ("img_content_medium", "Medium"), ("img_content_large", "Large"), ) FLOAT_CHOICES = ( ("img_align_left", "Left"), ("img_align_center", "Center"), ("img_align_right", "Right") ) ALIGN_CHOICES = ( ("img_align_top", "Top"), ("img_align_middle", "Middle"), ("img_align_bottom", "Bottom") ) TARGET_CHOICES = ( ("_self", "Same window"), ("_blank", "New window"), ) ## _parent and _top are not sensible options, nor is an explicit ## framename ## translate klass back to size/float/align forminitial = dict(title=default_title, target=target, download=download, size=SIZE_CHOICES[-1][0], float=FLOAT_CHOICES[1][0], align=ALIGN_CHOICES[0][0]) ## if not target, determine local/new based on locality of url if not forminitial['target']: if path: forminitial['target'] = "_self" else: forminitial['target'] = "_blank" klass_parts = klass.split() for part in klass_parts: if part in [s[0] for s in SIZE_CHOICES]: forminitial['size'] = part if part in [f[0] for f in FLOAT_CHOICES]: forminitial['float'] = part if part in [a[0] for a in ALIGN_CHOICES]: forminitial['align'] = part class PropForm(AngularForm): ng_ns = "propsform" title = forms.CharField() if type == "link": target = forms.ChoiceField(choices=TARGET_CHOICES, initial="_self", help_text="Where should the link open in when clicked?") if spoke and isinstance(spoke, FileSpoke): download = forms.BooleanField( help_text="If checked, link will point to " "download immediately in stead of File content") if type == "image": size = forms.ChoiceField(choices=SIZE_CHOICES) float = forms.ChoiceField(choices=FLOAT_CHOICES) align = forms.ChoiceField(choices=ALIGN_CHOICES) propform = PropForm(initial=forminitial) return dict(initialdata=forminitial, template=self.render_template("wheelcms_axle/popup_properties.html", spoke=spoke, instance=instance, mode=type, form=propform))
def test_direct_path_notfound(self, client): """ retrieve a node directly through its path """ Node.root().add("a").add("b").add("c") assert Node.get("/d/e/f") is None
def test_root_lookup(self, client): """ get("") should implicitly create root node """ assert Node.get("").isroot()
def panels(self, path, original, mode): """ Generate panels for the file selection popup mode can be either "link" (any content) or "image" (only image based content) """ language = self.active_language() if not self.hasaccess(): return self.forbidden() ## ## No path means a new item is to be selected. Use the current ## item as a starting point if not path: path = self.instance.path else: path = resolve_path(path) if path is None: return self.notfound() original = resolve_path(original) or "" ## remove optional 'action', marked by a + ## not sure if this is the right place to do this, or if the browser ## modal should have been invoked without the action in the first place path = strip_action(path) original = strip_action(original) node = start = Node.get(path)# , language=language) ## ## Selectable means the node is a valid selection. Unattached ## nodes are never selectable and in image mode only image based ## content is a valid selection def is_selectable(node): content = node.content() if content: if mode == "link": return True elif isinstance(content, ImageContent): return True return False ## Is the starting point a valid selection? start_selectable = is_selectable(node) panels = [] ## first panel: bookmarks/shortcuts bookmarks_paths = [''] # root if self.instance.path not in bookmarks_paths: bookmarks_paths.append(self.instance.path) #if path not in bookmarks_paths: # bookmarks_paths.append(path) ## original can also be an external url, starting with http if original not in bookmarks_paths and not original.startswith("http"): bookmarks_paths.append(original) bookmarks = [] for p in bookmarks_paths: ## handle non-existing nodes and unattached nodes n = Node.get(p) if not n: continue content = n.content() if not content: ## unattached continue spoke = content.spoke() selectable = is_selectable(n) bookmarks.append(dict(children=[], path=n.get_absolute_url(), title=content.title, meta_type=content.meta_type, content=content, selectable=selectable, icon=spoke.icon_base() + '/' + spoke.icon, spoke=spoke)) panels.append(self.render_template("wheelcms_axle/popup_links.html", instance=self.instance, bookmarks=bookmarks )) upload = False for i in range(2): content = node.content() if content: ## FileSpoke also includes ImageSpoke spoke = content.spoke() addables = [x for x in spoke.addable_children() if issubclass(x, FileSpoke)] instance = dict(children=[], path=node.get_absolute_url(), title=content.title, meta_type=content.meta_type, content=content, spoke=spoke, addables=addables) else: addables = [x for x in type_registry.values() if issubclass(x, FileSpoke)] instance = dict(children=[], path=node.get_absolute_url(), title="Unattached node", meta_type="none", content=None, spoke=None, addables=addables) if i == 0: ## first iteration means current context. Check if uploading ## is possible. upload = bool(addables) for child in node.children(): content = child.content() if not content: continue ## ignore unattached nodes spoke = content.spoke() selectable = is_selectable(child) selected = path == child.path or \ path.startswith(child.path + '/') instance['children'].append( dict(title=content.title, path=child.get_absolute_url(), icon=spoke.icon_base() + '/' + spoke.icon, selectable=selectable, meta_type=content.meta_type, selected=selected)) panels.insert(1, self.render_template("wheelcms_axle/popup_list.html", instance=instance, path=node.get_absolute_url(), mode=mode, selectable=(i==0))) if node.isroot(): break node = node.parent() ## generate the crumbs crumbs = [] node = start while True: content = node.content() selectable = is_selectable(node) if node.isroot(): crumbs.insert(0, dict(path=node.get_absolute_url(), selectable=selectable, title="Home")) break crumbs.insert(0, dict(path=node.get_absolute_url(), selectable=selectable, title=node.content().title)) node = node.parent() crumbtpl = self.render_template("wheelcms_axle/popup_crumbs.html", crumbs=crumbs) return dict(panels=panels, path=start.get_absolute_url(), crumbs=crumbtpl, upload=upload, selectable=start_selectable)