Programatically add a page to a known parent - wagtail

I would like to create programatically a sub page for a known parent. How can I do that? The page creation will takes place in a signal receiver: the page is created on publication of another page.

Add a revision as well.
from wagtail.wagtailcore.models import Page
from models import MyPage
home = Page.objects.get(id=3) # or better Page query
my_page = MyPage(title="test", body="<h1>the body</h1>")
home.add_child(instance=my_page)
# later when a cms user updates the page manually
# there will be no first revision to compare against unless
# you add a page revision also programmatically.
my_page.save_revision().publish()
You can see how wagtail does this in the wagtailadmin pages create view (line 156).
https://github.com/wagtail/wagtail/blob/stable/1.13.x/wagtail/wagtailadmin/views/pages.py
Update 2018-09-18:
I built a 700 page site including 200 generated pages. I never added an initial Revision anywhere and no editors complained. After the first manual edit there will be a Revision. Go ahead and add an initial Revision if you think it is needed for traceability.

To create a page programmatically:
page = SomePageType(title="My new page", body="<p>Hello world</p>") # adjust fields to match your page type
parent_page.add_child(instance=page)

Below is my complete code to create a multi language page structure programatically. It will replace the "Wagtail Welcome Page" with a LanguageRedirectionPage instance.
More information about multi language pages:
Wagtail Docs - Internationalization
The page structure is as follows:
Page
LanguageRedirectionPage (will redirect to /en)
Page (en)
Page (de)
Page (fr)
where the created Site instance at the end of the code points to the LanguageRedirectionPage instance. This is the entry point of our application.
# Deletes existing pages and sites
Site.objects.all().delete()
Page.objects.filter(pk=2).delete() # Deletes Wagtail welcome page
root_page = Page.objects.filter(pk=1).get()
# Adds a LanguageRedirectionPage as a child of the Root Page
app_name = '[Your Project Name]'
page_slug = app_name.lower().replace(" ", "")
sub_root_page = LanguageRedirectionPage(
title=app_name,
draft_title=app_name,
slug=page_slug,
live=True,
owner=account,
)
root_page.add_child(instance=sub_root_page)
sub_root_page.save_revision().publish()
# Adds some language pages
for code,caption in dict(settings.LANGUAGES).items():
print(code, caption)
sub_root_page.add_child(instance=Page(
title=caption,
slug=code,
live=True,
owner=account,
))
# Adds a new Site instance (See Settings -> Sites in your Wagtail admin panel)
Site.objects.create(
hostname='localhost',
port='80',
site_name=app_name,
root_page=sub_root_page,
is_default_site=True,
)

Related

Programmatically creating permissions for pages and collections in Wagtail

I'm wondering if anyone has examples of how to create permissions in Wagtail for collections and pages and then give specific users access to that role. I see how to create the Collections and AuthGroups easily, but I don't see how to specify the AuthGroup permissions in detail.
Edit:
In my case, the answer below in addition, to this bit of code for module permissions allowed for everything to be automatically added for a group:
try:
perm = course_group.permissions.get(codename=x.codename, content_type=x.content_type)
except Permission.DoesNotExist:
perm_created = course_group.permissions.add(x)
I have some code like this in a population script. In this example, I want a group named Team to be able to add, change and choose wagtail images and documents in any collection. I also want them to be able to add, edit and publish any pages on the site. This example can hopefully be modified to suit another setup.
from wagtail.models import Page
from wagtail.core.models import Collection, GroupCollectionPermission, GroupPagePermission
from django.contrib.auth.models import Group, Permission
team_group, created = Group.objects.get_or_create(name='Team')
if created:
#This is only done once, when the group didn't already exist
root_collection = Collection.get_first_root_node()
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtailimages', codename='add_image'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtailimages', codename='change_image'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtailimages', codename='choose_image'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtaildocs', codename='add_document'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtaildocs', codename='change_document'))
GroupCollectionPermission.objects.create(group=team_group, collection=root_collection, permission=Permission.objects.get(content_type__app_label='wagtaildocs', codename='choose_document'))
root_page = Page.objects.get(id=1)
GroupPagePermission.objects.create(group=team_group, page=root_page, permission_type='add')
GroupPagePermission.objects.create(group=team_group, page=root_page, permission_type='edit')
GroupPagePermission.objects.create(group=team_group, page=root_page, permission_type='publish')
To find the available wagtail content types and list their identifiers, I ran
for x in Permission.objects.order_by().values('content_type__app_label').distinct():
print(x['content_type__app_label'])
To find the permission codenames of a given content type, I ran
for x in Permission.objects.filter(content_type__app_label='wagtailimages'):
print(x.codename)
For the GroupPagePermission parameter permission_type, I found the options in the wagtail source. It lists these options:
PAGE_PERMISSION_TYPES = [
('add', _("Add"), _("Add/edit pages you own")),
('edit', _("Edit"), _("Edit any page")),
('publish', _("Publish"), _("Publish any page")),
('bulk_delete', _("Bulk delete"), _("Delete pages with children")),
('lock', _("Lock"), _("Lock/unlock pages you've locked")),
('unlock', _("Unlock"), _("Unlock any page")),
]
In my project I'm not adding users to groups programmatically, but hopefully this answer helps

How do I embed code through my text editor?

I'm running Hugo and editing my pages using Notepad++. I'd like to embed some code similar to the page here.
My Hugo version is
Hugo Static Site Generator v0.55.6-A5D4C82D windows/amd64 BuildDate: 2019-05-18T07:57:00Z
My config.toml file is below. As you can see, I've added the pygments options to the top of the page:
pygmentsCodefences = true
pygmentsStyle = "autumn"
baseurl = "https://blakeshurtz.netlify.com/"
title = "Blake Shurtz"
theme = "hugo-creative-portfolio-theme"
languageCode = "en-us"
# Enable comments by entering your Disqus shortname
disqusShortname = ""
# Enable Google Analytics by entering your tracking code
googleAnalytics = ""
[params]
# Style options: default (pink), blue, green, pink, red, sea, violet
# Use custom.css for your custom styling
style = "default"
description = "Describe your website"
copyright = "©2019 Blake Shurtz"
sidebarAbout = [
"I am a research statistician who enjoys building models and apps.",
"Originally from the Bay Area, currently based in central CA."
]
# Contact page
# Since this template is static, the contact form uses www.formspree.io as a
# proxy. The form makes a POST request to their servers to send the actual
# email. Visitors can send up to a 1000 emails each month for free.
#
# What you need to do for the setup?
#
# - set your email address under 'email' below
# - upload the generated site to your server
# - send a dummy email yourself to confirm your account
# - click the confirm link in the email from www.formspree.io
# - you're done. Happy mailing!
email = "you#yoursite.com"
# Optional Matomo analytics (formerly piwik)
# [params.analytics.matomo]
# URL = "https://stats.example.com"
# ID = "42"
# # Track all subdomains with "*.example.com" (Optional)
# domain = "www.example.com"
# # Optional integrity check hash
# hash = ""
# Nav links in the side bar
[[params.navlinks]]
name = "Home"
url = "portfolio/"
home = true
[[params.navlinks]]
name = "About"
url = "about/"
[[params.navlinks]]
name = "Get in touch"
url = "contact/"
[params.social]
stackoverflow = "https://stats.stackexchange.com/users/206673/blake-shurtz"
twitter = "https://twitter.com/blakeobeans"
email = "blakeobeans#gmail.com"
linkedin = "https://www.linkedin.com/in/blakeshurtz/"
github = "https://github.com/blakeobeans"
Can someone give me an example of what I need to write in my text editor in order to include the code?
Thanks!
I'm assuming you mean using markdown syntax to format text as code.
Surround your code with three backticks at the beginning and at the end.
```python (or whatever language)
code here
```
As Ambrose Leung's answer mentions, you can include code blocks in markdown by wrapping them in 3 backticks:
```language
some code here
```
To get syntax highlighting, you can use Chroma, which is built into Hugo. Just add these lines to the top of your config.toml file (don't let the names confuse you, they say pygments but are for chroma):
pygmentsCodefences = true
pygmentsStyle = "pygments"
You can set the pygmentsStyle value to any of the styles from the style gallery.

How do you check in code if a request matches an EPiServer Visitor Group

We've set up a new "visitor group" in EPiServer 6r2, and we want to add a css class to the <body> tag of the site if the user is in that group, so different groups get different site designs. I'm trying to find out if the current visitor is in a matching group in the code-behind of a masterpage file in order to add this extra class and can't get the below code to return anything but false.
I'm not sure if the role name mentioned is the name you enter in the CMS UI when adding a visitor group.
Paul Smith blogged a proposed solution to this but I haven't been able to get it to return anything but false yet, and judging by the only comment on the blog article I'm not alone. Code sample #1 from this link (which is the one I'm using):
using EPiServer.Personalization.VisitorGroups;
...
bool match = EPiServer.Security.PrincipalInfo.CurrentPrincipal
.IsInRole("My Visitor Group", SecurityEntityType.VisitorGroup);
I found the developer guide to membership and role providers which states that replacePrincipal must be set to true for the correct principal to be in place. I checked and this is already the case for my config.
Documentation
EPiServer 7 doc
IPrincipal.IsInRole() extension
SecurityEntityType enum
Oddly I searched the 6r2 documentation from http://sdk.episerver.com/ and can't find the documentation for IPrincipalExtensions at all, even though I see the class in object browser in 6.2. in my sln. Details: Assembly EPiServer.ApplicationModules - C:\Windows\assembly\GAC_MSIL\EPiServer.ApplicationModules\6.2.267.1__8fe83dea738b45b7\EPiServer.ApplicationModules.dll - public static bool IsInRole(this System.Security.Principal.IPrincipal principal, string role, EPiServer.Security.SecurityEntityType type)
Member of EPiServer.Personalization.VisitorGroups.IPrinicipalExtensions
Please comment if you spot errors or I've missed anything as coding for EPiServer is a bit of a fog-of-war affair and I'm a little battle-weary.
Found it by browsing the object model and guessing. So much for documentation.
using EPiServer.Personalization.VisitorGroups;
using EPiServer.Security;
const string visitorGroupName = "Some users";
var groupHelper = new VisitorGroupHelper();
bool isPrincipalInGroup = groupHelper.IsPrincipalInGroup(
PrincipalInfo.CurrentPrincipal, visitorGroupName);
Tested and working in EPiServer 6r2 (aka 6.1).
String visitorGroupName must match the string entered into the "Name" box on the EPiServer admin interface when creating / editing the visitor group. See screenshot below:

Google App Engine, How to automatically load model class for ReferenceProperty

I have modular project structure, like this:
./main.py
./app.yaml
../articles
.../__init__.py
.../models.py
../blog
.../__init__.py
.../models.py
../comments
.../__init__.py
.../models.py
I have defined models in file models.py for each package (this is application). I have defined next models for "comments" application:
class Comment(db.Model):
author = db.UserProperty(auto_current_user_add=True)
title = db.StringProperty(default="Title")
text = db.TextProperty("Message", default="Your message")
# references to any model
object = db.ReferenceProperty()
and in "articles" application I have defined next models:
class Article(db.Model):
author = db.UserProperty(auto_current_user_add=True)
title = db.StringProperty(default="Title")
text = db.TextProperty("Message", default="Your message")
1) On first loading of page - I create new article:
from articles.models import Article
article = Article(title="First article", text="This is first article")
article.put()
2) On second loading of page I create new comment:
from articles.models import Article
from comments.models import Comment
article = Article.get_by_id(1)
comment = Comment(title="First comment", text="This is first comment")
comment.put()
3) On thind loading of page, I want to see all comments for all articles, blogs, and other objects in whole datastore:
from comments.models import Comment
comments = Commen.all()
for comment in comments:
# print comment and article title
print "%s: %s" % (comment.title, comment.object.title)
Actual result: "KindError: No implementation for kind 'Article'"
Expected result: automatically detect object type for reference and load this class
See more on: http://code.google.com/p/appengine-framework/issues/detail?id=17
Project need your help!
In order to be able to return entities of a given kind, App Engine has to be able to find the Model class for it. There's no mechanism built in for doing so, because all it has to look it up with is the entity kind, which can be any arbitrary string.
Instead, import the modules containing the models you may reference from the module containing the Comment model. That way, any time you can perform a query on Comment, all the relevant models are already loaded.
In my project GAE framework I have solve this issue. On the first page loading I have load all models in memory.
What if we have models with the same name, for example Comment model in "blog" and "board" applications? In this case we have automatically add prefix for models for the King of this model. In result we have the different names for models in different applications: BlogComment and BoardComment.
You can learn more in source code to understand how we do this implementation.

Dynamically add User Controls to a Silverlight 4 page

I am building an iGoogle like "dashboard" for our application with silverlight 4.
Each users dashboard (which snapins and their positions) will be stored in the database. Each snap in is a user control, for example... AwesomeSnapin.xaml.
On the dashboard page in silverlight I am retrieving the users dashboard which is a collection of snapin objects which include the information on the snapin. I can store the name of the page or the class or whatever is needed.
I have the following code which loops through the collection of snapins to add them to the dashboard page. In testing I have just hard coded a single snapin item. Here is the prototype code:
foreach (var UserSnapin in op.Entities)
{
UserControl uc = new AmsiSL.eFinancials.BudgetCheck();
Canvas.SetLeft(uc, UserSnapin.PositionLeft);
Canvas.SetTop(uc, UserSnapin.PositionTop);
Layout.Children.Add(uc);
MessageBox.Show(String.Format("Added {0}",UserSnapin.Snapin.Name));
}
The above works fine... but of course adds the BudgetCheck snapin for every item that is defined for the users dashboard. Of course the messagebox is for debugging purposes only.
How would I change line 3 of that to load the user control class (using classname or xaml path whichever is better) based on the data in the collection.
I'll assume for the moment that UserSnapin.Snapin.Name in this case is "BudgetCheck" and that all Snapins are found in the AmsiSL.eFinancials namespace. I'll also assume that your Usercontrols are compilied in to the same application assembly. You can use this code:-
foreach (var UserSnapin in op.Entities)
{
Type snapInType = Assembly.GetExecutingAssembly()
.GetType("AmsiSL.eFinancials." + UserSnapin.Snapin.Name);
UIElement snapIn = (UIElement)Activator.CreateInstance(snapInType );
Canvas.SetLeft(snapIn , UserSnapin.PositionLeft);
Canvas.SetTop(snapIn , UserSnapin.PositionTop);
Layout.Children.Add(snapIn);
}
IF the snapins are an a library instead you can probably use the Type.GetType(string) static method to resolve the value for snapInType but you would need to know the assembly name as well.

Resources