def __init__(self, parent, node=None, attach=False, enlarge=True, reserved=(), skip_slug=False, *args, **kwargs): """ Django will put the extra slug field at the bottom, below all model fields. I want it just after the title field """ super(BaseForm, self).__init__(*args, **kwargs) slug = self.fields.pop("slug") titlepos = self.fields.keyOrder.index("title") self.fields.insert(titlepos + 1, "slug", slug) self.node = node self.parent = parent self.attach = attach self.reserved = reserved self.advanced_fields = self.initial_advanced_fields if attach: self.fields.pop("slug") templates = template_registry.get(self._meta.model, []) if templates: self.fields["template"] = forms.ChoiceField(choices=templates, required=False) else: self.fields.pop("template") ## will default to content_view self.fields["state"] = forms.ChoiceField( choices=self.workflow_choices(), initial=self.workflow_default(), required=False ) if skip_slug: self.fields.pop("slug") if enlarge: for enlargable_field in self.fields.values(): self.enlarge_field(enlargable_field) ## make the description textarea a bit smaller if "description" in self.fields: self.fields["description"].widget.attrs["rows"] = 4 if "tags" in self.fields: self.fields["tags"].widget.attrs["class"] = "tagManager" self.fields["tags"].required = False ## lightforms don't have a language field if "language" in self.fields: if self.node: ## construct allowed languages. Exclude any language for which ## the node already has content current = self.instance.language if self.instance else None c = [] for lpair in translate.languages(): if current == lpair[0] or not self.node.content(language=lpair[0], fallback=False): c.append(lpair) self.fields["language"].choices = c else: self.fields["language"].choices = translate.languages() for e in type_registry.extenders(self.Meta.model): e.extend_form(self, *args, **kwargs)
def rename(self, slug, language=None): """ change the slug """ if self.isroot(): raise CantRenameRoot() ## if no language was specified, rename all languages = [language] if language else [l[0] for l in translate.languages()] for testmode in (True, False): ## first loop checks if all relevant languages can be renamed, second loop renames for language in languages: try: localized_path = Paths.objects.get(node=self, language=language) except Paths.DoesNotExist: continue newpath = localized_path.path.rsplit("/", 1)[0] + "/" + slug if testmode: if Paths.objects.filter(path=newpath, language=language).exists(): raise DuplicatePathException(newpath, language) else: for p in Paths.objects.filter(Q(path=localized_path.path) | Q(path__startswith=localized_path.path + '/'), language=language): remainder = p.path[len(localized_path.path):] p.path = newpath + remainder p.save()
def save(self, *args, **kw): ## If the object has not yet been saved (ever), create the node's paths saved = True if self.pk is None: saved = False ## first save the object so we can create references super(NodeBase, self).save(*args, **kw) ## create paths based on self._slug / self._parent which were passed to __init__ if not saved: for language, langname in translate.languages(): try: langpath = Paths.objects.get(node=self, language=language) except Paths.DoesNotExist: langpath = Paths(node=self, language=language) langslug = translate.language_slug(self._langslugs, self._slug, language) if not self._parent: path = '' # '/' + str(self.id) -- be consistent with 'old' behavior, for now langpath.path = langslug else: path = self._parent.tree_path + '/' + str(self.id) langpath.path = self._parent.get_path(language) + '/' + langslug langpath.save() self.tree_path = path super(NodeBase, self).save()
def get_active_language(): """ The active language is either forced in settings, set in the session (for admin), a GET argument or the translation default """ lang = getattr(settings, 'FORCE_LANGUAGE', None) if not lang: lang = locale.get_content_language() langids = (l[0] for l in translate.languages()) if lang not in langids and getattr(settings, 'FALLBACK', None): lang = settings.FALLBACK return lang
def translations(self): """ Return data for the translations/languages menu. This means "switch to" options for translated languages and "translate to" for untranslated languages. If there's no second language (ignoring 'Any'), don't return anything; this will hide the translation menu entirely. """ if not self.instance or self.status == Toolbar.SPECIAL: return None if len(settings.CONTENT_LANGUAGES) == 1: return None active = None translated = [] untranslated = [] active_language = get_active_language() for (lang, langtitle) in translate.languages(): option = dict(id=lang, language=langtitle) content = self.instance.content(language=lang) ## In view mode toch edit tonen om vertaling te maken! base_url = "switch_admin_language?path=" + self.instance.tree_path + "&switchto=" + lang if lang == active_language: active = option else: if self.status == Toolbar.UPDATE: option["action_url"] = base_url + "&rest=edit" elif self.status == Toolbar.VIEW: option["action_url"] = base_url + "" elif self.status == Toolbar.LIST: option["action_url"] = base_url + "&rest=list" elif self.status == Toolbar.CREATE: option["action_url"] = ( base_url + "&rest=" + urllib2.quote("create?type=" + self.request.GET.get("type")) ) if content and self.status != Toolbar.CREATE: translated.append(option) else: untranslated.append(option) return dict(active=active, translated=translated, untranslated=untranslated)
def handle_list(self): spoke = self.spoke() if spoke: perm = spoke.permissions.get('list') else: perm = Spoke.permissions.get('list') if not auth.has_access(self.request, spoke, spoke, perm): return self.forbidden() if self.toolbar: self.toolbar.status = Toolbar.LIST self.context['breadcrumb'] = self.breadcrumb(operation="Contents") self.context['can_paste'] = \ len(self.request.session.get('clipboard_copy', [])) + \ len(self.request.session.get('clipboard_cut', [])) active = self.active_language() children = [] for child in self.instance.children(): c = dict(node=child, active=None, translations=[], ipath=child.tree_path) for lang, langtitle in translate.languages(): langcontent = child.content(language=lang) c["translations"].append((lang, langcontent, "switch_admin_language?path=" + child.tree_path + "&switchto=" + lang + "&rest=edit")) if lang == active: c["active"] = langcontent if not c['active']: c['active'] = child.primary_content() children.append(c) self.context['children'] = children if spoke: return self.template(spoke.list_template()) return self.template("wheelcms_axle/contents.html")
def get_active_language(request=None): """ The active language is either forced in settings, set in the session (for admin), a GET argument or the translation default """ lang = getattr(settings, 'FORCE_LANGUAGE', None) if not lang and request: admin_language = request.session.get('admin_language') lang = admin_language or request.GET.get('language') if not lang: lang = translation.get_language() langids = (l[0] for l in translate.languages()) if lang not in langids and getattr(settings, 'FALLBACK', None): lang = settings.FALLBACK return lang
def translations(self): if not self.instance or self.status == "special": return None active = None translated = [] untranslated = [] active_language = get_active_language(self.request) for (lang, langtitle) in translate.languages(): option = dict(id=lang, language=langtitle) content = self.instance.content(language=lang) ## In view mode toch edit tonen om vertaling te maken! base_url = "switch_admin_language?path=" + self.instance.tree_path + "&language=" + lang if lang == active_language: active = option else: if self.status == "update": option['action_url'] = base_url + '&rest=edit' elif self.status == "view": option['action_url'] = base_url + '' elif self.status == "list": option['action_url'] = base_url + '&rest=list' elif self.status == "create": option['action_url'] = base_url + '&rest=' + urllib2.quote('create?type=' + self.request.GET.get('type')) if content and self.status != 'create': translated.append(option) else: untranslated.append(option) return dict(active=active, translated=translated, untranslated=untranslated)
def paste(self, node, copy=False): """ Move a node elsewhere in the tree, optionally copying the node (copy-paste) or deleting the original (cut-paste) """ ## a move is just rewriting/renaming the child and its offspring, ## a copy is recreating the node ## ancestor nodes cannot be moved into offspring nodes, they can be ## copied, but avoid recursion. ## how to deal with the position? Insert at the bottom? from .content import ContentCopyException failed = [] success = [] if copy: slug_per_lang = {} for p in node.paths.all(): lang, path = p.language, p.path if path == "": slug = "root" else: base_slug = slug = path.rsplit('/', 1)[1] count = 0 while self.child(slug, lang): slug = "copy_%s_of_%s" % (count, base_slug) slug_per_lang[lang] = slug base = self.add(langslugs=slug_per_lang) if node.content(): try: node.content().copy(node=base) success.append(node.tree_path) except ContentCopyException: failed.append((node.tree_path, "Content cannot be copied")) base.delete() ## no need to continue return base, success, failed for o in Node.objects.offspring(node).order_by("tree_path"): ## skip all offspring of a failed node for f, reason in failed: if o.tree_path.startswith(f + '/'): break else: langslugs = dict((p.language, p.path.rsplit("/", 1)[1]) for p in o.paths.all()) n = Node(langslugs=langslugs, parent=base) n.save() if o.content(): try: o.content().copy(node=n) success.append(o.tree_path) except ContentCopyException: n.delete() failed.append((o.tree_path, "Content cannot be copied")) return base, success, failed else: if node == self or node.is_ancestor(self): raise CantMoveToOffspring() oldpath = node.tree_path oldbase, slug = oldpath.rsplit("/", 1) if oldbase == self.tree_path: ## pasting into its own parent, nothing to do return node, success, failed ## XXX somehow batch/transaction this for o in node_proxy_factory(Node, self.preferred_language).objects.offspring(node): o.tree_path = self.tree_path + o.tree_path[len(oldbase):] o.save() success.append(o.tree_path) node.tree_path = self.tree_path + '/' + str(node.id) ## move to end node.position = self.find_position(position=-1) node.save() success.append(node.tree_path) ## the great renaming # import pytest; pytest.set_trace() for language, langname in translate.languages(): try: localized_path = Paths.objects.get(node=node, language=language) except Paths.DoesNotExist: continue slug = localized_path.path.rsplit('/', 1)[1] mypath = self.get_path(language) newpath = mypath + '/' + slug count = 0 while Paths.objects.filter(path=newpath, language=language).exists(): newpath = mypath + slug + "_" + str(count) count += 1 #if testmode: #if Paths.objects.filter(path=newpath, language=language).exists(): # raise DuplicatePathException(newpath, language) #else: for p in Paths.objects.filter(Q(path=localized_path.path) | Q(path__startswith=localized_path.path + '/'), language=language): remainder = p.path[len(localized_path.path):] p.path = newpath + remainder p.save() return node, success, failed
def edit(self): language = self.active_language() instance = self.instance supported_languages = (l[0] for l in translate.languages()) if language not in supported_languages: return self.redirect(instance.get_absolute_url(), error="Unsupported Language") if self.spoke(): ## update the context with addtional data from the spoke self.context.update(self.spoke().context(self, self.request, self.instance)) content = instance.content(language=language) create_translation = False if content is None: pcontent = instance.primary_content() typename = pcontent.get_name() typeinfo = type_registry.get(typename) create_translation = True spoke = pcontent.spoke() else: typename = content.get_name() typeinfo = type_registry.get(typename) spoke = content.spoke() perm = typeinfo.permissions.get('edit') if not auth.has_access(self.request, typeinfo, spoke, perm): return self.forbidden() # reset tabs with current language tabs self.context['tabs'] = self.tabs(spoke) parent = instance.parent() self.context['redirect_cancel'] = self.instance.get_absolute_url() + \ "?info=Update+cancelled" if self.toolbar: self.toolbar.status = Toolbar.UPDATE formclass = typeinfo.form slug = instance.slug(language=language) if self.is_post: args = dict(parent=parent, data=self.request.POST, reserved=self.reserved(), skip_slug=self.instance.isroot(), node=self.instance, files=self.request.FILES) if content: args['instance'] = content self.context['form'] = form = formclass(**args) if form.is_valid(): try: if create_translation: content = form.save(commit=False) else: # ordinary update content = form.save() except OSError, e: messages.error("An error occured while saving: %s" % str(e)) if create_translation: if self.user().is_authenticated(): content.owner = self.user() content.node = self.instance content.save() form.save_m2m() ## handle changed slug slug = form.cleaned_data.get('slug', None) content_language = form.cleaned_data.get('language', settings.FALLBACK) if slug and slug != self.instance.slug(language=content_language): self.instance.rename(slug, language=content_language) return self.redirect(instance.get_absolute_url(), success="Updated")
def __init__(self, parent, node=None, attach=False, enlarge=True, reserved=(), skip_slug=False, *args, **kwargs): """ Django will put the extra slug field at the bottom, below all model fields. I want it just after the title field """ super(BaseForm, self).__init__(*args, **kwargs) slug = self.fields.pop('slug') titlepos = self.fields.keyOrder.index('title') self.fields.insert(titlepos+1, 'slug', slug) self.node = node self.parent = parent self.attach = attach self.reserved = reserved self.advanced_fields = self.initial_advanced_fields ## Pass parent to ParentFields for field in self.fields.values(): if isinstance(field, ParentField): field.parent = parent if attach: self.fields.pop('slug') templates = template_registry.get(self._meta.model, []) if templates: self.fields['template'] = forms.ChoiceField(choices=templates, required=False) else: self.fields.pop('template') ## will default to content_view self.fields['state'] = forms.ChoiceField(choices=self.workflow_choices(), initial=self.workflow_default(), required=False) if skip_slug: self.fields.pop("slug") if enlarge: for enlargable_field in self.fields.values(): self.enlarge_field(enlargable_field) ## make the description textarea a bit smaller if 'description' in self.fields: self.fields['description'].widget.attrs['rows'] = 4 ## workaround for https://code.djangoproject.com/ticket/21173 for patch_dt in ("publication", "expire", "created"): if patch_dt in self.fields: f = self.fields[patch_dt] f.widget = DateTimeInput() ## lightforms don't have a language field if 'language' in self.fields: if self.node: ## construct allowed languages. Exclude any language for which ## the node already has content current = self.instance.language if self.instance else None c = [] for lpair in translate.languages(): if current == lpair[0] or \ not self.node.content(language=lpair[0], fallback=False): c.append(lpair) self.fields['language'].choices = c else: self.fields['language'].choices = translate.languages() for e in type_registry.extenders(self.Meta.model): e.extend_form(self, *args, **kwargs) if "allowed" in self.fields: self.fields["allowed"].choices = ((t.name(), t.title) for t in sorted(type_registry.values(), key=operator.attrgetter("title"))) if self.instance: allowed = self.instance.allowed if allowed == "" and "no_subcontent" in self.fields: self.fields["no_subcontent"].initial = True