My team and I are building a "page builder" using the StreamField capabilities in Wagtail 2.5.1. We've built a number of modules which will be used by our marketing team. One of the module requests is a "divider bar".
I've created it with some very simple code:
class DividerBlock(blocks.StructBlock):
include_divider = blocks.BooleanBlock(
required=False,
help_text='Display a divider bar',
default=True,
)
class Meta:
label = 'Divider Module'
icon = 'horizontalrule'
I don't actually need the checkbox because I'm simply looking for the existence of this module on the page, and we'll output the HR. But if I don't include at least one control, the streamfield shows an empty space, so a checkbox makes sense. However I want to force the checkbox to default to true (already done) AND be disabled or readonly so that the user cannot change it.
I've tried both disabled and readonly as properties in the BooleanBlock init but neither are working. Is there a way to accomplish this?
For blocks that just need to occupy a place in the stream but not have any data of their own, Wagtail provides the block type StaticBlock.
Related
I have reviewed the question on Is there any way to show a field on a listing page in Wagtail admin? but my situation seems to similar but also different enough that that particular solution won't work for me. Instead of on the Page listing I wish to achieve a similar thing on the Model Admin listing and I would think this should be such a common requirement that I am picking that someone must have done this before I have attempted it.
I haven't really figured out how to even try anything to get started but what I have looked at is the modeladmin template tags under wagtail.contrib.modeladmin on GitHub but I am completely guessing.
Can anyone point me to which templates I need to modify and whether I need to modify any of the template tags and how to override anything I need to override?
There's no need to override templates for this - this is standard functionality in ModelAdmin. Adding extra fields to the listing is done by setting list_display on the ModelAdmin class:
class BookAdmin(ModelAdmin):
model = Book
list_display = ('title', 'author')
For displaying images, ModelAdmin provides ThumbnailMixin:
from wagtail.contrib.modeladmin.mixins import ThumbnailMixin
from wagtail.contrib.modeladmin.options import ModelAdmin
class BookAdmin(ThumbnailMixin, ModelAdmin):
model = Book
thumb_image_field_name = 'cover_image'
list_display = ('title', 'author', 'admin_thumb')
('admin_thumb' is a special-purpose field name provided by ThumbnailMixin, and should be used rather than the actual image field on your model - cover_image in this example.)
I have a single page application with AngularJs and Django. On my main page, I get all the forms needed when loading the page. BUT, some fields are dynamically updated.
Let's say I have
class Model1(models.Model):
pass
class Model2(models.Model):
model_1 = models.ForeignKey(Model1)
forms:
class Model2Form(forms.ModelForm):
class Meta:
model = Model2
fields = ('model_1', )
My SPA allows me to create instances of Model1 (without reloading the page). I know how to filter the options shown and dynamically add the new instances in the select field BUT, doing so, when the html is first rendered, before angular magic takes place and filter the available options, I get the queryset made by django which is by default model.objects.all(). All right, I'd like to display none of that. I tried to add in the init of my function:
self.fields['model_1'].queryset = Model1.objects.none()
and indeed no option is displayed in the select field when the form is first rendered but then, I can't validate my form, I get the error: Select a valid choice. That choice is not one of the available choices. (obviously, it had no option available due to the queryset.none() )
I'd really like not to load forms when called but doing it when my page first load. Is there any option to help me do so?
Cheers guyz,
Keep rocking
You need to specify that the model_1 field of Model2 can be null, as specified here:
Allow null in foreign key to user. Django
model_1 = models.ForeignKey(Model1, null=True, blank=True, default = None)
I find out how to handle that problem. It is quite stupid, I did not give you all the parameters of the problem.
The forms are rendered on load but when I validate it, it goes through a CRUD operation and an OTHER form is initialized at this point which will handle the data I'm sending. So I can override the queryset in the init of that (second) form based on some extra kwargs to differentiate between the form I'm using for the first rendering and the form to handle my data.
No need to make any field nullable or add extra validation.
Hope I'm clear enough. Cheers
Preface
Our site uses C1 as a CMS for public area (info, help, faq, etc.).
Our private area is implemented as a standalone ASP.NET app, so there almost no connection between the CMS and the app.
In the app there are ~50 email templates (HTML+razor), and almost all of them are localized (8 languages).
At the moment those templates are stored as files, and they are under the source control. Consequently, any editing is done by developers.
The goal
We'd like to put those template files under the control of Composite C1 and thereby make them editable by people who manage the content.
The key requirements:
The template files must not be accessible by the internet user; the CMS must only show them in the administration console and allow to edit them. It's easy to achieve this by putting the files in e.g. App_Data, so they will be observable in the System perspective, however it's not the best way when considering security, and it doesn't help with the following requirements.
It would be great to have the published/unpublished feature applied to these files, just like the pages in the Content perspective.
It would be great to use the built-in C1 localization feature, so that at a time only those files are visible, which are in the currently selected language.
The questions
Is there anybody who had experience with putting such private content under the C1 control?
In order to meet the requirements, would it make us to store our email templates in the CMS database instead of storing them as files?
UPDATE
In the Data perspective it's possible to create a custom global datatype suitable for storing email templates: it's just necessary to have at least one field "Body" of type String with the xhtml editor assigned to it. This way it's possible to add all the templates into the database, and there are abilities to make them published/unpublished and localized. The application would have to access the templates via the database, what is OK.
The only actual problem is that the xhtml editor should be reconfigured to allow non-strict html with razor. Any advice on that?
So, here is the solution we've ended with.
Create custom global datatype EmailTemplate in the Data perspective.
Tick the "Has publishing" checkbox in the Settings tab.
The most important fields are:
TemplateID - the string identifier of the email template
Body - the template text
The Body field should be thoroughly configured:
Field type: String
String maximum length: Unlimited length
Advanced config: Widget type: VisualXhtmlEditor
Enable Localization for the newly created datatype (see its context menu).
Edit Form Markup for the newly created datatype (see its context menu).
Replace the InlineXhtmlEditor element with the following code:
<TextEditor Label="Template" Help="" MimeType="application/x-cshtml">
<cms:bind source="Body" />
</TextEditor>
This way the editor is configured to support the html+razor syntax.
Show the datatype in Content perspective (see its context menu).
This way the email templates appear in the Website Items of the Content tree.
How the application can access the published email templates
In the CMS database the following tables will be created:
dbo.<datatype_name_with_namespace>_<culture_code>
dbo.<datatype_name_with_namespace>_Unpublished_<culture_code>
For example
dbo.Composite_EmailTemplate_en_GB
dbo.Composite_EmailTemplate_Unpublished_en_GB
dbo.Composite_EmailTemplate_de_DE
dbo.Composite_EmailTemplate_Unpublished_de_DE
...
Each table has columns that correspond the fields configured for the datatype, e.g. TemplateID and Body.
I believe now it's clear how to find certain template in certain culture.
What i want: Active terms of vocabulary in main menu.
What i have done so far:
I have used taxonomy_menu module.
Created taxonomy(Category).
Added "Status" field having boolean values (0 = Inactive, 1 = Active).
Added two terms in Category(Cat1 and Cat2).
Cat1 = Active, Cat2 = Inactive.
Configured menu from "Edit vocabulary" section.
Problem: All terms of vocabulary are included in main menu. I want that only those terms whose Status = Active should be displayed in main menu.
The process to make taxonomy_menu module aware of your status field seems hard to accomplish, particularly if you need - as you probably would - the taxonomy menu rebuilt when a status value changes.
A possibly easier solution would be to act at the theme level, adding a .inactive class to inactive items and make it hidden in css. Since Taxonomy Menu module does nothing about themeing, this should be dealt with with Menu module theme functions. So you'll end up writing a (e.g.) CUSTOM_menu_link() function in which you'll need to test if the link is coming from a taxonomy generated menu (not sure how you can do that, anyway) for every menu item of your site and then, if this is the case, load the term, test the status field and act accordingly. Not exactly an elegant solution, and quite an overhead.
If possible, I'd rather suggest you use the built-in feature to enable or disable menu items from the admin interface at admin/structure/menu/manage/main-menu, after configuring your taxonomy-menu.
Try this module:
https://www.drupal.org/project/termstatus
This module adds a status-flag to taxonomy terms. Using this flag it is possible to specify whether terms should be published or not. Users with the appropriate permission may access unpublished terms.
In Drupal 7, I have added a block to the Content region of the User page. I did this by specifying
user/*
in the Visibility Settings -> Pages -> Show block on specific pages -> Only the listed pages setting.
However, because the wildcard accepts anything, now the block appears on the User's edit page.
Is there a single number wildcard, like
user/%integer
that I can use here so that the block only appears on the User view page?
How can I hide this block on the User edit page in Drupal 7?
You can try enable PHP filter module, so that you can enter php on Block visibility settings page.
There you can check for argument like
<?php
if(arg(0) == 'user' && arg(2) != 'edit' )
return true;
else
return false
?>
These days there is another alternative available ... which doesn't require the PHP filter to be enabled (which you should try to avoid whenever possible) ... Just use the Rules block visibility module. Here is a quote from its project page:
The Rules block visibility module allows Rules components to be used to control block visibility. This provides Drupal administrators and developers extreme flexibility in controlling when blocks should be displayed on their websites, in addition to the default visibility options provided by Drupal.
The general idea is that if you can do it with Rules, you can use it to control block visibility, so the possibilities are limitless.
Need to show a block only for users registered more than a month ago?
Perhaps you have a block that must be shown only between 8am-5pm on weekdays?
What about displaying or hiding a block based on current weather conditions?
All of this can be done by using Rules block visibility.
With that, and as per the "if you can do it with Rules, you can use it to control block visibility" above, you've reduced your question to making Rules intercept the situation where someone uses an URL like /user/*/edit (replace * here with any allowed value for uid). If you're not familiar with how to do that with Rules, then have a look at my answer to the question "How to make the permissions of a module more granular?" (and pay attention to the regex-expression included in the sample rule I included there).