Append content of field to title field in Wagtail-CMS-admin - wagtail

In order to differentiate pages in Wagtail CMS admin (in a page listing view, not in the edit page view), the title is - in my case - not enough. I have a long list of pages of one page-type (say class BlogPage), and some of these pages could have the same title. So I would like to add a second identifying field (here: date_from, a DateField) to this title.
I thought of
class BlogPage(Page):
...
def title(self):
if self.date_from:
return self.date_from + " - " + self.title
else:
return self.title
but this does not work, the page.title without my def is used for the corresponding Wagtail-admin-template.
Short version: How to pre/-append an existing field to the title in Wagtail-admin?

If you'd like to replace the title across all of the model admin (Yes, this includes the Edit page), Wagtail has a built in mechanism for that.
def get_admin_display_title(self):
return '{} - {}'.format(self.date_from, super().get_admin_display_title())

title is an actual Django model attribute, don't try to override it: https://docs.djangoproject.com/en/dev/topics/db/models/#field-name-hiding-is-not-permitted
The actual admin template from wagtailadmin/pages/list.html generates the list of pages in a loop using {% for page in pages %} and calls {{ page.title}} via an include (templates/wagtailadmin/pages/listing/_page_title_explore.html) extensively throughout. So based on inspecting the code, there is no support for this in Wagtail itself. Check the other includes templates/wagtailadmin/pages/listing/*.
However, Django supports overriding one app's templates with your own. You can copy this template into your project's template folder, keeping the same path (e.g., templates/wagtailadmin/pages/listing/_page_title_explore.html assuming your project is set up with a templates directory).
You'll have to replace the calls to page.title with your own version. A filter or tag might make this easier. Add the following filter to your templatetags (e.g. myapp/templatetetags/myapp_tags.py):
register = template.Library()
#register.filter
def uniquify_title(page):
specific_page = page.specific
try:
return specific_page.date_from.strftime("%Y-%m-%d") + " - " + specific_page.title
except AttributeError:
return specific_page.title
And then replace usages of {{ page.title}} in the template starting around line 7 and 9 with:
{{ page|uniquify_title }}
The downside of this is you have to update your own version of list.html every time Wagtail is updated. You could try submitting an issue on Github and proposing a fork which supplies an "admin_title" callable or something like that.

wagtailmodeladmin seems to do exactly what I've searched for: extending the wagtailadmin to display a defined set of fields - not only title - on a page model basis; like I would have it in the Django admin site.
This way I do not alter the default wagtailadmin page listing - like I've tried it in my question, but hook in an additional page listing as an extra wagtail-sidebar-navigation-entry.
Thanks to an other answer I stumbled upon this possiblity.

Related

In WagTail, how do you display a full document url for document links embedded in richtext?

I'm setting up a wagtail site which needs to display all links as full urls since the pages will also be used as email templates.
Wagtail version 2.5.1
My main issue is document links which are embedded in RichTextFields. The current work around is to have them inserted as external links after they upload the documents.
I've looked at features.register_link_handler but am unclear on how to deal with Document links. I'm assuming that it will need to be in wagtail_hooks.py register_rich_text_features somehow.
I ended up creating a register_rich_text_features link handler for this in wagtail_hooks.py
class DocumentFullLinkHandler(DocumentLinkHandler):
#classmethod
def expand_db_attributes(cls, attrs):
try:
document = cls.get_instance(attrs)
current_site = Site.objects.get_current()
document_url = (
document.url
if document.url.startswith(settings.PATH_PREFIX)
else f"{settings.PATH_PREFIX}{document.url}"
)
full_url = f"https://{current_site.domain}{document_url}"
return f'<a href="{escape(full_url)}">'
except (ObjectDoesNotExist, KeyError):
return "<a>"
The real answer is to Upgrade to Wagtail 2.7 and then set
WAGTAILDOCS_SERVE_METHOD = "direct" in the settings.
This will provide the direct document when using remote storage.
#sw12k I tried to apply your same solution and I couldn't make it work, but... I think I found a much simpler solution that actually made the trick.
In the html template, when you render your RichTextField I moved from:
{{ page.description|safe }}
to this:
{{ page.description|richtext }}
And now document links are well formatted and download links work fine.
Hope it helps.

Fragment id linking in wagtail's rich text content

I have a bunch of content in a Wagtail 2.0 rich text field that looks like
Page heading
(intro blurb)
heading 1
(heading-1-relevant text)
heading 2
(heading-2-relevant text)
...
and I would like to give each heading an id so that any text can be made a link to jump to the relevant content. I can't seem to find an option to give headings an explicit id, and the "link" button in the rich text editor does not seem to let me pick active fragment identifiers in the content.
Is there a way to add fragment identifier based navigation on the same page work with Wagtail's rich text editor?
Revisiting my own question a year later because this is still something we need, the solution we came up with is to simply wrap the RichText html serialization, and putting fragment id injection on top:
import re
from django import template
from django.utils.text import slugify
from wagtail.core.rich_text import RichText
# We'll be wrapping the original RichText.__html__(), so make
# sure we have a reference to it that we can call.
__original__html__ = RichText.__html__
# This matches an h1/.../h6, using a regexp that is only
# guaranteed to work because we know that the source of
# the HTML code we'll be working with generates nice
# and predictable HTML code (and note the non-greedy
# "one or more" for the heading content).
heading_re = r"<h([1-6])([^>]*)>(.+?)</h\1>"
def add_id_attribute(match):
"""
This is a regexp replacement function that takes
in the above regex match results, and then turns:
<h1>some text</h1>
Into:
<h1><a id="some-text"></a>some text</h1>
where the id attribute value is generated by running
the heading text through Django's slugify() function.
"""
n = match.group(1)
attributes= match.group(2)
text_content = match.group(3)
id = slugify(text_content)
return f'<h{n}{attributes}><a id="{id}"></a>{text_content}</h{n}>'
def with_heading_ids(self):
"""
We don't actually change how RichText.__html__ works, we just replace
it with a function that does "whatever it already did", plus a
substitution pass that adds fragment ids and their associated link
elements to any headings that might be in the rich text content.
"""
html = __original__html__(self)
return re.sub(heading_re, add_id_attribute, html)
# Rebind the RichText's html serialization function such that
# the output is still entirely functional as far as wagtail
# can tell, except with headings enriched with fragment ids.
RichText.__html__ = with_heading_ids
This works rather well, does not require any hacking in draftail or wagtail, and is very easy to enable/disable simply by loading this code as part of the server startup process (we have it living in our wagtailcustom_tags.py file, so when Django loads up all template tag sets, the RichText "enrichment" kicks in automatically).
We had initially tried to extend the ... | richtext template filter, but while that's entirely possible, that only works for custom blocks we ourselves wrote, with our own custom templates, and so turned out to not be a solution given the idea that it should "just work".
To have control over the structure of your page body, it's preferable to encourage users to use heading blocks, rather than headings within the rich text block. Then you can have a heading block type which has two fields, a 'text' and an 'id', and you can specify a template that outputs the h element with the id attribute.
class Heading2Block(blocks.StructBlock):
heading = blocks.CharBlock(classname='full title')
link_id = blocks.CharBlock(help_text='For making hyperlinks to this heading')
class Meta:
template = 'blocks/h2.html'
Put the following in blocks/h2.html:
<h1{% if value.link_id %} id="{{ value.link_id|slugify }}"{% endif %}>{{ value.heading }}</h1>
In earlier versions of Wagtail it was possible to remove the h widget from the Hallo.js rich text editor, and this was a good way of encouraging user adoption of the heading block. Similar restriction is not currently present in Draftail, but there is a pull request which reimplements it.

how to access Hugo's template variables in a javascript file?

I'm trying to use react.js in Hugo. I know Go template variables are accessible in HTML file.
My question is how to access them in javascript. or is there a workaround?
thanks in advance.
UPDATE:
currently my workaround is to use meta tags in HTML and load Go template variables like this:
<meta name="title" content={{.Title}} />
and then in javascript,
function getMetaTitle() {
var metas = document.getElementsByTagName('meta');
for (i=0; i<metas.length; i++) {
if (metas[i].getAttribute("name") == "title") {
return metas[i].getAttribute("content");
}
}
return "failed to access...";
}
var metaTitle = getMetaTitle();
but this way is inconvenient when the number of meta tags growing, is there a more concise way to do this?
I doubt Hugo and React is a good pair but that's off topic and I might be wrong about that. You are asking, how to get Hugo variables into website's JavaScript. My answer:
Hugo is static website engine, so it only converts templates and markup documents (with your content) into HTML files. Now, when you upload your files onto your server, your JS cannot see anything Hugo — only your files.
The question becomes, how to transfer Hugo variables into some files of your website.
As you suggested, it's best to write variables into your HTML (or JSON) using Hugo, then read them by JS. If it's small amount, use attributes or tags. If there's a lot and it doesn't differ per-page, use a separate JSON file.
For example, personally I have a multilingual site which a) requires different language titles to appear dynamically via JS; b) uses JS which queries different Lunr.js search indexes in JSON format.
For both I use data-<name> attributes:
<section class="section-search" data-index="{{ .Site.BaseURL }}searchIndex.json" id="section-search">
<input type="search" id="search-input" placeholder="{{ ( index $.Site.Data.translations $.Site.Params.locale ).dataloading }}" data-loaded="{{ ( index $.Site.Data.translations $.Site.Params.locale ).dataloaded }}">
<!-- search button goes here -->
</section>
For example, on English templates (rendered into /public/), data-loaded attribute would be in English, but for Lithuanian templates (rendered into /public/lt/), data-loaded attribute would be in Lithuanian.
I wouldn't worry about "growing meta tags", but you could maybe write variables into a JSON file and then read it in JS if you are concerned about HTML bloat?
I'm building custom JSON first as HTML, then minifying/renaming it into JSON when building indexes for Hugo Lunr search as per this recipe. Instead of "baking in" the content with range as in mentioned recipe, you could simply list all the variables.
By the way, I'm using npm scripts as a build runner (instead of Grunt/Gulp) so I use json-minify:
"index:prepare": "json-minify public/json/index.html > public/site-index.json",
You could "bake" JSON files with any content (including Hugo template variables) via Hugo this way. Hope it helps.
You can specify a custom output format for Javascript within your config.toml so that Hugo then treats those particular formats and file extensions like it's content files where it replaces the template variables with adequate values.
So, an entry such as below in your config.toml will treat javascript files as one of the media type it needs to consider for its custom output formats:
[mediaTypes]
[mediaTypes."application/javascript"]
suffix = "js"
You can read more about it here
You can, of course, inline your JS in your layout files, but that is probably not what you want.
There have been some discussions about improvements in this area on the Hugo discussion site, but nothing concrete yet.

Unable to add a new field to a content type, widget drop down not working

These are my first steps with drupal.
I have created a taxonomy hierarchy for my articles and now I am trying to add a new field to the content type "Article" and "Media" so the content admin can assign a "category" to his new content.
So I've been to Structure > Content Types > Article > Manage Fields
Then "Add new field" with :
1- Label = Category
2- Name = "field_category
3- Field = "Term reference"
**4- Automatically changes to "Select list" but I am unable to see the drop down list options. Clicking the list doesn't do anything, I couldn't select Autocomplete or any other value I've seen on forums & tutorials. Using firebug I could see the options are there, but the list doesn't show up.**
This is happening with all types of fields, even with text fields, the most basic one.
Any idea why is this happening ?
As glumbo mentioned, the problem here is caused by jQuery 1.7 update. As of jQuery 1.6 DOM properties should be accessed using .prop() function.
There is an open issue with some hotfix solution:
You need to replace .attr() jQuery function call with .prop() in /modules/field_ui/field_ui.js at following lines:
100: $(this).html(html).attr('disabled', disabled ? 'disabled' : '');
253: $(ajaxElements).attr('disabled', true);
Please note that this fix modifies a Drupal core module and you'll probably want to use a patch (Dries would kill the kitty anyway:).
The problem is with jquery update. If you are using jquery 1.7 you will get this problem

How To Display HTML Tags Saved In Database Using Symfony 2 and Twig

I've managed to build a page that will display data from the database dynamically. However, this code which is saved in the database:
<p>This is the content for the radio page.</p>
Displays like this:
<p>This is the content for the radio page.</p>
The HTML tags aren't rendered. I understand that Symfony (for security purposes) renders any HTML code like this for security reasons. I want Symfony (obviously) to render these HTML tags. How can I achieve this just for this purpose only, so that Symfony still sanitises any HTML tag that is saved to the database elsewhere on the site?
For your information as well, this is the code that I am using to pull the data from the database:
public function mainpageAction($slug)
{
$content = $this->getDoctrine()
->getRepository('SiteMainBundle:Content')
->find($slug);
if (!$content) {
throw $this->createNotFoundException('No product found for slug '.$slug);
}
return $this->render('SiteMainBundle:Default:page.html.twig', array('content' => $content, 'slug' => $slug));
}
Also, just so I can learn more about Symfony, is the rendering of the HTML tags just the sole job of PHP or could it be rendered properly using Twig?
Many thanks!
If you want to do that, you need to use the raw filter in the twig template. Like described in the twig documentation, the raw filter marks the value as safe which means that in an environment with automatic escaping enabled this variable will not be escaped if raw is the last filter applied to it.
In your case, it's : {{ content | raw }}

Resources