{"id":839,"date":"2017-05-03T00:00:00","date_gmt":"2017-05-03T00:00:00","guid":{"rendered":"https:\/\/structr.com\/blog-app-tutorial\/"},"modified":"2025-01-31T21:01:46","modified_gmt":"2025-01-31T21:01:46","slug":"blog-app-tutorial","status":"publish","type":"post","link":"https:\/\/structr.com\/en\/blog\/blog-app-tutorial\/","title":{"rendered":"Blog App Tutorial"},"content":{"rendered":"<div class=\"wpb-content-wrapper\"><p>[vc_row][vc_column][vc_column_text]<\/p>\n<h3><a id=\"index\" href=\"#index\">Index<\/a><\/h3>\n<ul>\n<li><a href=\"#overview\">Overview<\/a>\n<ul>\n<li><a href=\"#what-you-will-learn\">What you will learn<\/a><\/li>\n<li><a href=\"#you-will-need\">You will need<\/a><\/li>\n<li><a href=\"#the-features\">The Features<\/a><\/li>\n<li><a href=\"#format\">Format<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#create-a-new-user\">Create a New User<\/a>\n<ul>\n<li><a href=\"#check-the-schema\">Check the Schema<\/a><\/li>\n<li><a href=\"#create-the-signup-form\">Create the Signup Form<\/a><\/li>\n<li><a href=\"#check-the-data\">Check the Data<\/a><\/li>\n<li><a href=\"#resource-access-grants\">Resource Access Grants<\/a><\/li>\n<li><a href=\"#add-bootstrap\">Add Bootstrap<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#login\">Login<\/a>\n<ul>\n<li><a href=\"#create-the-ui-and-functionality\">Create the UI and Functionality<\/a><\/li>\n<li><a href=\"#overview\">Security<\/a><\/li>\n<\/ul>\n<\/li>\n<li>Signup and Login: Loose Ends\n<ul>\n<li><a href=\"##redirect-users-on-login-and-signup\">Redirect users on login and signup<\/a><\/li>\n<li><a href=\"#add-basic-navigation\">Add basic navigation<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#public-feed\">Public Feed<\/a>\n<ul>\n<li><a href=\"#schema\">Schema<\/a><\/li>\n<li><a href=\"#data\">Data<\/a><\/li>\n<li><a href=\"#pages\">Pages<\/a>\n<ul>\n<li><a href=\"#create-the-feed-page\">Create the feed page<\/a><\/li>\n<li>Sidebar: Preventing future loose ends<\/li>\n<li><a href=\"#display-post-author\">Display post author<\/a><\/li>\n<li><a href=\"#test-in-incognito\">Test in Incognito<\/a><\/li>\n<li><a href=\"#redirect-users-to-feed-on-login\">Redirect users to Feed on Login<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#create-new-blog-posts\">Create new blog posts<\/a>\n<ul>\n<li><a href=\"#pages\">Pages<\/a><\/li>\n<li>Make the WRITER_OF Relationship<\/li>\n<li><a href=\"#security\">Security<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<hr \/>\n<h3><a id=\"overview\" href=\"#overview\">Overview<\/a><\/h3>\n<h4><a id=\"what-you-will-learn\" href=\"#what-you-will-learn\">What you will learn<\/a><\/h4>\n<p>This introduction to Structr will guide you through the creation of a simple Structr blog application. We will start from an empty Structr instance and build the blog application feature-by-feature, walking you through how to deal with access control, authentication, and applying basic bootstrap.<\/p>\n<h4><a id=\"you-will-need\" href=\"#you-will-need\">You will need<\/a><\/h4>\n<ul>\n<li>A fresh Structr instance running in your browser<\/li>\n<li>Access to the command line running Structr<\/li>\n<li>An incognito browser window open<\/li>\n<li>Access to the <a href=\"https:\/\/neo4j-users.slack.com\/#using-structr\" target=\"_blank\" rel=\"noopener\">neo4j-users Slack<\/a> so you can ask for support in #using-structr<\/li>\n<\/ul>\n<h4><a id=\"the-features\" href=\"#the-features\">The Features<\/a><\/h4>\n<p>Before we begin, we should define the features the blogging application will have. The tutorial application has the following features, presented as user stories below:<\/p>\n<ul>\n<li>As a public user, I can create an account<\/li>\n<li>As a public user, I can log in with an existing account<\/li>\n<li>As a public or authenticated user, I can see a feed of all the posts currently in the application, sorted earliest-first<\/li>\n<li>As an authenticated user, I can create new blog posts that include a title and body text<\/li>\n<\/ul>\n<p>The outline of the data model emerges from the following features:<\/p>\n<ul>\n<li><strong>User<\/strong> nodes will exist with some sort of <strong>name<\/strong> and <strong>password<\/strong> properties.<\/li>\n<li><strong>Post<\/strong> (as in blog post) nodes will exist with properties describing <strong>title<\/strong>, <strong>body<\/strong> text, and created <strong>date<\/strong><\/li>\n<li><strong>WRITER_OF<\/strong> relationships link User and Post nodes<\/li>\n<\/ul>\n<h4><a id=\"format\" href=\"#format\">Format<\/a><\/h4>\n<p>While developing each feature, the tutorial will cover the following points, named after the Structr menu item in which each appears:<\/p>\n<ul>\n<li>Pages<\/li>\n<li>Security<\/li>\n<li>Schema<\/li>\n<li>Data<\/li>\n<\/ul>\n<hr \/>\n<h3><a id=\"create-a-new-user\" href=\"#create-a-new-user\">Create a New User<\/a><\/h3>\n<ul>\n<li><strong>Pages<\/strong>: Public users will need to use the UI to create an account. Since \u201cSignup\u201d doesn\u2019t exist yet, we have to create a page<\/li>\n<li><strong>Security<\/strong>: Only public users should be able to see the signup UI to create new users<\/li>\n<li><strong>Schema<\/strong>: Since Structr comes with User nodes built in, we will not alter the schema to implement account creation<\/li>\n<li><strong>Data<\/strong>: Account creation creating a new <strong>User<\/strong> with each sign-up<\/li>\n<\/ul>\n<h4><a id=\"check-the-schema\" href=\"#check-the-schema\">Check the Schema<\/a><\/h4>\n<p>New Users will have <strong>name<\/strong> and <strong>password<\/strong> properties.<br \/>\nNavigate to the <strong>Schema<\/strong> tab and find User nodes in the main view. Select the pencil icon to inspect what properties users have, and verify both <strong>name<\/strong> and <strong>password<\/strong> already exist:[\/vc_column_text][vc_single_image image=&#8221;1076&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1068&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<h4><a id=\"create-the-signup-form\" href=\"#create-the-signup-form\">Create the Signup Form<\/a><\/h4>\n<p>Navigate to the <strong>Pages<\/strong> view and press the green + button to create a new page.<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1015&#8243; img_size=&#8221;large&#8221;][vc_column_text]Click on the newly created tab to rename the page to <strong>signup<\/strong>.<\/p>\n<p>The new page should look like this in the <strong>Pages Tree View<\/strong> (the top tab on the left):<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1021&#8243; img_size=&#8221;large&#8221;][vc_column_text]An eyeball icon should appear upon hovering over the signup tab in the main <strong>Pages<\/strong> view:[\/vc_column_text][vc_single_image image=&#8221;1047&#8243; img_size=&#8221;large&#8221;][vc_column_text]Click on the eyeball icon to see a preview of the page in your browser. <strong>Note<\/strong>: The app is only viewable from whichever user you logged in with admin permissions. To test public visibility, it\u2019s best to use an incognito browser session to simulate a visitor.<\/p>\n<p>Open the <strong>Widgets<\/strong> menu (located in a tab on the right side of the screen) and find the <strong>Panel With Create Form<\/strong> widget, drag and drop it to your page. When prompted, configure the widget to create new User nodes, then tap <strong>Append Widget<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1017&#8243; img_size=&#8221;large&#8221;][vc_column_text]This is what the <strong>signup<\/strong> page should looks like so far:[\/vc_column_text][vc_single_image image=&#8221;1073&#8243; img_size=&#8221;large&#8221;][vc_column_text]Users will need passwords as well. Later, we\u2019ll add a field for that in the <strong>Pages Tree View<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1040&#8243; img_size=&#8221;large&#8221;][vc_column_text]Select the <strong>div<\/strong> containing the <strong>name<\/strong> label and input. Press the clone-element icon to make a copy of the div. Move the copy underneath the original and delete the default \u201cInitial body text\u201d so the <strong>signup<\/strong> page looks like this:[\/vc_column_text][vc_single_image image=&#8221;1055&#8243; img_size=&#8221;large&#8221;][vc_column_text]In the copied <strong>div<\/strong>, edit the label to refer to the <strong>User<\/strong>\u2019s <strong>password<\/strong> by tapping \u201cName\u201d and changing the text to \u201cPassword\u201d.[\/vc_column_text][vc_single_image image=&#8221;1048&#8243; img_size=&#8221;large&#8221;][vc_column_text]In the copied <strong>div<\/strong>, edit the input.form-control to refer to the <strong>User<\/strong>\u2019s <strong>password<\/strong> by clicking the <strong>Edit Properties<\/strong> icon (third from the right) and changing the <strong>Field name<\/strong> to <strong>password<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1061&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1071&#8243; img_size=&#8221;large&#8221;][vc_column_text]In order to save the new users with a <strong>name<\/strong> and <strong>password<\/strong>, update the <strong>Attributes<\/strong> of the <strong>Create<\/strong> button to include <strong>password<\/strong>:[\/vc_column_text][vc_single_image image=&#8221;1057&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1018&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<h4><a id=\"check-the-data\" href=\"#check-the-data\">Check the Data<\/a><\/h4>\n<p>Click the preview eye icon and try to create a new user from the <strong>signup<\/strong> page.<br \/>\nAfter you enter a <strong>name<\/strong> and <strong>password<\/strong>, you should see your new user in the <strong>Data<\/strong> page of Structr.[\/vc_column_text][vc_single_image image=&#8221;1046&#8243; img_size=&#8221;large&#8221;][vc_column_text]Now there are two users: admin and a new user created using the signup form.<\/p>\n<h4><a id=\"resource-access-grants\" href=\"#resource-access-grants\">Resource Access Grants<\/a><\/h4>\n<p>We want more than just authenticated admin users to be able to create new users. In fact, only unauthenticated users should be able to create <strong>User<\/strong>s. Open the signup page in an incognito window to test the create user functionality as a public user. The page should display a 404 error.<br \/>\nIn the <strong>Pages Tree View<\/strong>, click the key icon to edit the <strong>Access Control and Visibility<\/strong> of the <strong>signup<\/strong> page<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1039&#8243; img_size=&#8221;large&#8221;][vc_column_text]Check the <strong>Apply visibility switches recursively?<\/strong> checkbox then select <strong>Visible to public Users<\/strong> and try to load the page again.<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1037&#8243; img_size=&#8221;large&#8221;][vc_column_text]You will run into a 401 error if you try to create a new <strong>User<\/strong> node:[\/vc_column_text][vc_single_image image=&#8221;1067&#8243; img_size=&#8221;large&#8221;][vc_column_text]In the <strong>Security<\/strong> view, give unauthenticated users the ability create new User nodes using a POST request:[\/vc_column_text][vc_single_image image=&#8221;1027&#8243; img_size=&#8221;large&#8221;][vc_column_text]Try again, and verify a new user was created.<\/p>\n<h4><a id=\"add-bootstrap\" href=\"#add-bootstrap\">Add Bootstrap<\/a><\/h4>\n<p>Add some style by finding the Bootstrap widget and drag it into your page\u2019s head:[\/vc_column_text][vc_single_image image=&#8221;1033&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1069&#8243; img_size=&#8221;large&#8221;][vc_column_text]Repeat the <a href=\"#resource-access-grants\">Resource Access Grants<\/a> step described above (open the menu by tapping the key on <strong>signup<\/strong> page row) to grant unauthenticated users the right to view the styling and enjoy your new signup page:[\/vc_column_text][vc_single_image image=&#8221;1035&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<hr \/>\n<h3><a id=\"login\" href=\"#login\">Login<\/a><\/h3>\n<p>Now that we can create users, we would like to log in using <strong>name<\/strong> and <strong>password<\/strong>.<\/p>\n<p>Back to the checklist:<\/p>\n<ul>\n<li><strong>Pages<\/strong>: Users will have to use a login page to login<\/li>\n<li><strong>Security<\/strong>: Only public users should be able to see the UI or login<\/li>\n<li><strong>Schema<\/strong>: We know Users already exist because we created some in the last step<\/li>\n<li><strong>Data<\/strong>: Logging in will not significantly alter any data<\/li>\n<\/ul>\n<h4><a id=\"create-the-ui-and-functionality\" href=\"#create-the-ui-and-functionality\">Create the UI and Functionality<\/a><\/h4>\n<p>Create a new page called <strong>login<\/strong>. You can create it from scratch or <strong>clone<\/strong> the already-existing <strong>Signup<\/strong> page and delete the create user form to save time setting up Bootstrap and access controls. To clone the <strong>signup<\/strong> page, tap the clone icon, which is the icon immediately to the right of the page name in the <strong>Pages Tree View<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1064&#8243; img_size=&#8221;large&#8221;][vc_column_text]From the Widget library, drag a <strong>Login Form<\/strong> widget (under <strong>Bootstrap &gt; Generic<\/strong>) into the page, and edit it to use <strong>name<\/strong> instead of <strong>email<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1014&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<h4><a id=\"security\" href=\"#security\">Security<\/a><\/h4>\n<p>Preview the page in an incognito browser and test using both valid and invalid credentials.<\/p>\n<p>Recursively re-toggle the access control access control settings for the login page so your non-admin user can view the new widget: tap the pages\u2019 key icon, select <strong>Apply visibility switches recursively?<\/strong> and deselect then reselect <strong>Visible to public users<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1025&#8243; img_size=&#8221;large&#8221;][vc_column_text]Open the <strong>login<\/strong> page in an incognito browser and try to log in. You\u2019ll get a \u201cWrong username or password.\u201d message even though your credentials are correct. If you check the console or terminal you\u2019re using to run Structr, you\u2019ll find this message:<\/p>\n<pre><code>INFO  org.structr.web.auth.UiAuthenticator - No resource access grant found for signature _login. (URI: \/structr\/rest\/login)\r\n<\/code><\/pre>\n<p>To fix this, add a signature called <strong>_login<\/strong> on the <strong>Security<\/strong> page.[\/vc_column_text][vc_single_image image=&#8221;1060&#8243; img_size=&#8221;large&#8221;][vc_column_text]Filter for this new signature and give public users the rights to <strong>POST<\/strong> to it.<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1052&#8243; img_size=&#8221;large&#8221;][vc_column_text]Try to log in again and keep an eye on the console.<br \/>\nWhen you log in successfully, you\u2019ll see<\/p>\n<pre><code>INFO  o.structr.web.resource.LoginResource - Login successful: bc6811eef0dc43408e3b30fb3a3e4931\r\n<\/code><\/pre>\n<p>Try to log in twice in a row and observe you\u2019ll see<\/p>\n<pre><code>org.structr.web.auth.UiAuthenticator - Resource access grant found for signature _login, but method POST not allowed for authenticated users.\r\n<\/code><\/pre>\n<hr \/>\n<h3><a id=\"signup-and-login-loose-ends\" href=\"#signup-and-login-loose-ends\">Signup and Login: Loose Ends<\/a><\/h3>\n<p>Before continuing, let\u2019s address some loose ends that have built up over the first half of this tutorial:<\/p>\n<ul>\n<li>On signup, users should be redirected to the <strong>login<\/strong> page<\/li>\n<li>On login, users should be redirected to the <strong>feed<\/strong> page that will be created soon<\/li>\n<li>Public users should be able to navigate to the login page from the signup page and vice versa<\/li>\n<\/ul>\n<h4><a id=\"redirect-users-on-login-and-signup\" href=\"#redirect-users-on-login-and-signup\">Redirect users on login and signup<\/a><\/h4>\n<p>On the login and signup buttons, go to the <strong>Edit Properties &gt; Edit Mode Binding<\/strong> menu, and enter the path to the target page ( <strong>\/feed<\/strong> for <strong>login<\/strong> and <strong>\/login<\/strong> for <strong>signup<\/strong>) in the Return URI after successful action field.<\/p>\n<h4><a id=\"add-basic-navigation\" href=\"#add-basic-navigation\">Add basic navigation<\/a><\/h4>\n<p>For the sake of improving the testing experience, open the <strong>HTML Palette<\/strong> and drag some <strong>a<\/strong> elements to the <strong>signup<\/strong> and <strong>login<\/strong> pages to be able to easily navigate from one page to the other without having to type in the URL.<\/p>\n<p>For example, in the <strong>signup<\/strong> page, you may want a link that says \u201cLogin instead!\u201d[\/vc_column_text][vc_single_image image=&#8221;1030&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1042&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<hr \/>\n<h3><a id=\"public-feed\" href=\"#public-feed\">Public Feed<\/a><\/h3>\n<p><em>As a public or authenticated user, I can see a feed of all the posts currently in the application, sorted earliest-first.<\/em><\/p>\n<p>Back to the checklist<\/p>\n<ul>\n<li><strong>Schema<\/strong>: Posts should exist to populate the feed, and have a <strong>WRITER_OF<\/strong> relationship with their author<\/li>\n<li><strong>Data<\/strong>: Admin will have to be able to create test Posts to populate the feed<\/li>\n<li><strong>Pages<\/strong>: UI for the feed should exist on its own page, displaying Post title, author, created date, and body<\/li>\n<li><strong>Security<\/strong>: Both public and authenticated users can should be able to see the feed and posts in the feed<\/li>\n<\/ul>\n<h4><a id=\"schema\" href=\"#schema\">Schema<\/a><\/h4>\n<p>In the <strong>Schema<\/strong> tab, add a <strong>Post<\/strong> node and a <strong>WRITER_OF<\/strong> relationship, as described in the image below. To create a relationship, tap on the anchor (circle on the edge of the the start node rectangle) and drag the green arrow to a circle on the edge of the end node rectangle.[\/vc_column_text][vc_single_image image=&#8221;1044&#8243; img_size=&#8221;large&#8221;][vc_column_text]Add <strong>body<\/strong> and <strong>title<\/strong> properties to the <strong>Post<\/strong> nodes. Save by adding the green checkmark under \u201cAction\u201d<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1059&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1074&#8243; img_size=&#8221;large&#8221;][vc_column_text]Make <strong>Post<\/strong> nodes visible to public and authenticated users using some <strong>onCreate<\/strong> functions. Tap the \u201cAdd onCreate\u201d button to add two onCreate-type functions as below:[\/vc_column_text][vc_single_image image=&#8221;1043&#8243; img_size=&#8221;large&#8221;][vc_column_text]Check that the data is appearing as expected by creating new <strong>Post<\/strong> nodes on the <strong>Data<\/strong> tab:[\/vc_column_text][vc_single_image image=&#8221;1022&#8243; img_size=&#8221;large&#8221;][vc_column_text]<em>Hint: Don\u2019t forget to save the local attributes using the little green checkmark.<\/em><\/p>\n<h4><a id=\"data\" href=\"#data\">Data<\/a><\/h4>\n<p>Since there is no way to create new <strong>Post<\/strong> nodes via the UI, use the Structr admin to create some additional test <strong>Post<\/strong>s. Add relationships between <strong>User<\/strong>s and <strong>Post<\/strong>s by creating new <strong>UserWRITER_OFPost<\/strong> instances.<\/p>\n<p><em>Hint: Don\u2019t forget to fill out the title and body fields.<\/em>[\/vc_column_text][vc_single_image image=&#8221;1049&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<h4><a id=\"pages\" href=\"#pages\">Pages<\/a><\/h4>\n<h5><a id=\"create-the-feed-page\" href=\"#create-the-feed-page\">Create the feed page<\/a><\/h5>\n<p>Copy the <strong>signup<\/strong> page, delete the signup portion, and label the new page <strong>feed<\/strong>. This is what the page should look like before you add any feed-specific elements:[\/vc_column_text][vc_single_image image=&#8221;1023&#8243; img_size=&#8221;large&#8221;][vc_column_text]Open the <strong>HTML Palette<\/strong> on the right hand side to drag in grab HTML elements as needed.<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1024&#8243; img_size=&#8221;large&#8221;][vc_column_text]The intermediate <strong>Post<\/strong> element might look something like this:<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1050&#8243; img_size=&#8221;large&#8221;][vc_column_text]Tap on the <strong>Edit Properties<\/strong> icon of the <strong>Post<\/strong>\u2019s parent <strong>div<\/strong> and add a Cypher query to populate the feed.<\/p>\n<p>Cypher Query:<\/p>\n<pre><code>MATCH (post:Post)\r\nRETURN post\r\n<\/code><\/pre>\n<p>Data Key:<\/p>\n<pre><code>post<\/code><\/pre>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1063&#8243; img_size=&#8221;large&#8221;][vc_column_text]To use the data, replace placeholders as following:[\/vc_column_text][vc_single_image image=&#8221;1053&#8243; img_size=&#8221;large&#8221;][vc_column_text]Notice that the <strong>Post<\/strong>\u2019s author is not filled in, since author isn\u2019t a property of <strong>Post<\/strong>.<\/p>\n<h4><a id=\"sidebar-preventing-future-loose-end\" href=\"#sidebar-preventing-future-loose-end\">Sidebar: Preventing future loose end<\/a><\/h4>\n<p>Before continuing, you might consider adding a logout button and a way know what user you\u2019re logged in as. This will be helpful when you are creating blog posts as a non-admin user a bit further down in the tutorial.<\/p>\n<p>Grab a <strong>Logout button (configurable)<\/strong> from the Widget menu and drag it to an easy-to-manage place on your feed view. Make the logout button visible only to authenticated users, and add a signature called <strong>_logout<\/strong> on the <strong>Security<\/strong> page.<\/p>\n<p>*Hint: Don\u2019t forget to give authenticated users the permission to <strong>POST<\/strong> on <strong>_logout<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1062&#8243; img_size=&#8221;large&#8221;][vc_column_text]Display your current user\u2019s name by changing the text of the button to:<\/p>\n<p><strong>Logged in as ${me.name}. Sign out.<\/strong><\/p>\n<p>For example, if you\u2019ve logged in as user bob:[\/vc_column_text][vc_single_image image=&#8221;1075&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<h4><a id=\"display-post-author\" href=\"#display-post-author\">Display post author<\/a><\/h4>\n<p>Feed items should each display:<\/p>\n<ul>\n<li>Post title<\/li>\n<li>Post body<\/li>\n<li>Post author<\/li>\n<li>Post creation date<\/li>\n<\/ul>\n<p>The current Posts have most of these elements, but are missing the authors of the Posts. Prepare the query the feed will use to populate Post items with author bylines, and test it using the built-in Cypher console, which opens with <strong>CTRL+ALT+C &gt; SHIFT + TAB<\/strong>.<\/p>\n<pre><code>MATCH (post:Post)&lt;-[]-(user:User) RETURN {title:post.title, body:post.body, createdDate:post.createdDate, author:user.name} AS post ORDER BY post.createdDate DESC LIMIT 10; \/\/limit for the sake of the Cypher console, remove in the app itself\r\n<\/code><\/pre>\n<p><strong>CTRL+ALT+C (or CTRL+SHIFT+C)<\/strong>[\/vc_column_text][vc_single_image image=&#8221;1012&#8243; img_size=&#8221;large&#8221;][vc_column_text]<strong>SHIFT + TAB<\/strong> to reach the Cypher console<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1034&#8243; img_size=&#8221;large&#8221;][vc_column_text]Open the Post <strong>div<\/strong> and paste the updated query, removing the <strong>LIMIT 10<\/strong>[\/vc_column_text][vc_single_image image=&#8221;1070&#8243; img_size=&#8221;large&#8221;][vc_column_text]Update the author placeholder to <strong>by ${post.author}<\/strong> and take a look at your posts:<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1054&#8243; img_size=&#8221;large&#8221;][vc_single_image image=&#8221;1077&#8243; img_size=&#8221;large&#8221;][vc_column_text]Fix the date formatting using the <strong>date_format<\/strong> function:<\/p>\n<p><strong>Posted on ${date_format(post.createdDate,\u2018yyyy-MM-dd\u2019)}<\/strong><\/p>\n<h4><a id=\"test-in-incognito\" href=\"#test-in-incognito\">Test in Incognito<\/a><\/h4>\n<p>Preview the feed in a new incognito browser and verify you can see the feed of posts as both a public and authenticated user, <strong>recursively toggling the security of the page<\/strong> until it performs as expected.<\/p>\n<h4><a id=\"redirect-users-to-feed-on-login\" href=\"#redirect-users-to-feed-on-login\">Redirect users to Feed on Login<\/a><\/h4>\n<p>Go back to the Login tree, and select the signin button.[\/vc_column_text][vc_single_image image=&#8221;1020&#8243; img_size=&#8221;large&#8221;][vc_column_text]Under <strong>Edit Mode Binding<\/strong>, fill in the <strong>Return URI<\/strong> with the page you\u2019d like the users see upon login, in this case, <strong>feed<\/strong>. Make sure the page is visible for authenticated users.<\/p>\n<p>You might want to do the same with <strong>signup<\/strong>.[\/vc_column_text][vc_single_image image=&#8221;1026&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<hr \/>\n<h3><a id=\"create-new-blog-posts\" href=\"#create-new-blog-posts\">Create new blog posts<\/a><\/h3>\n<p><em>As an authenticated user, I can create new blog posts that include a title and body text.<\/em><\/p>\n<ul>\n<li><strong>Schema<\/strong>: The schema is already prepared for the creation of blog posts<\/li>\n<li><strong>Data<\/strong>: Authenticated users will be able to create Posts, which will create more data!<\/li>\n<li><strong>Pages<\/strong>: The feed page will be edited to allow users to create Posts<\/li>\n<li><strong>Security<\/strong>: Authenticated users will be able to create Posts<\/li>\n<\/ul>\n<h4><a id=\"pages\" href=\"#pages\">Pages<\/a><\/h4>\n<p>Open the Widgets menu and search for the <strong>Panel with Create Form (configurable)<\/strong>. Then drag an instance of it into the body of the feed page, underneath the h1.[\/vc_column_text][vc_single_image image=&#8221;1041&#8243; img_size=&#8221;large&#8221;][vc_column_text]Select <strong>Post<\/strong> as the Type of the widget.[\/vc_column_text][vc_single_image image=&#8221;1032&#8243; img_size=&#8221;large&#8221;][vc_column_text]The widget creates Posts with name properties by default. It will be edited to create Posts with <strong>title<\/strong> and <strong>body<\/strong> properties.<\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1013&#8243; img_size=&#8221;large&#8221;][vc_column_text]Make a copy of the <strong>div<\/strong> wrapping the name <strong>input<\/strong> and drag it under the name parts.[\/vc_column_text][vc_single_image image=&#8221;1016&#8243; img_size=&#8221;large&#8221;][vc_column_text]Change the top field labelled Name to <strong>title<\/strong> and change the bottom field also labelled Name to create the <strong>body<\/strong> post. You must also edit their names by going to <strong>Edit Mode Binding &gt; Field<\/strong> to <strong>title<\/strong> and <strong>body<\/strong>, respectively[\/vc_column_text][vc_single_image image=&#8221;1058&#8243; img_size=&#8221;large&#8221;][vc_column_text]Edit the <strong>Create<\/strong> button accordingly:[\/vc_column_text][vc_single_image image=&#8221;1051&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<h4><a id=\"make-the-writer-of-relationship\" href=\"#make-the-writer-of-relationship\">Make the WRITER_OF Relationship<\/a><\/h4>\n<p>Add a<select>and<option>element to your<\/option><\/select><strong>Create Post<\/strong><select><option>form.<\/option><\/select><\/p>\n<p>[\/vc_column_text][vc_single_image image=&#8221;1028&#8243; img_size=&#8221;large&#8221;][vc_column_text]Configure the <strong>select<\/strong> element to reflect the name <strong>WRITER_OF<\/strong> relationship.[\/vc_column_text][vc_single_image image=&#8221;1072&#8243; img_size=&#8221;large&#8221;][vc_column_text]In order for the select element to be hidden, add the html class \u201chidden\u201d to under <strong>HTML attributes<\/strong>:[\/vc_column_text][vc_single_image image=&#8221;1056&#8243; img_size=&#8221;large&#8221;][vc_column_text]Configure the option to have the ID of the current user as its value and be auto-selected.[\/vc_column_text][vc_single_image image=&#8221;1036&#8243; img_size=&#8221;large&#8221;][vc_column_text]Add the attribute to the <strong>Create<\/strong> button.[\/vc_column_text][vc_single_image image=&#8221;1065&#8243; img_size=&#8221;large&#8221;][vc_column_text]Change the cardinality of the relationship between User and Post so that a Post can have only one author.[\/vc_column_text][vc_single_image image=&#8221;1038&#8243; img_size=&#8221;large&#8221;][vc_column_text]The currently logged in user will be set as the only writer of the current post.<br \/>\nThe final result should look something like so:[\/vc_column_text][vc_single_image image=&#8221;1031&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<hr \/>\n<h3><a id=\"security\" href=\"#security\">Security<\/a><\/h3>\n<p>Although authenticated and public users should be able to see the <strong>feed<\/strong> page, only authenticated users can write posts. Set the <strong>Visibility<\/strong> of the form to authenticated users.[\/vc_column_text][vc_single_image image=&#8221;1045&#8243; img_size=&#8221;large&#8221;][vc_column_text]In the <strong>Security &gt; Resource Access Grants<\/strong> tab, ensure that authenticated users can create Posts.[\/vc_column_text][vc_single_image image=&#8221;1066&#8243; img_size=&#8221;large&#8221;][vc_column_text]Test the app in an incognito window and ensure everything works as expected:<\/p>\n<ul>\n<li>Public users should be able to see the feed<\/li>\n<li>Authenticated users should be able to see the feed<\/li>\n<li>Authenticated users should be able to see and use the Create Post panel<\/li>\n<li>Public users should not be able to see or use the Create Post panel<\/li>\n<\/ul>\n<p>If any of the requirements are not being met, toggle the visibility options for the elements in question until they are met.[\/vc_column_text][vc_single_image image=&#8221;1029&#8243; img_size=&#8221;large&#8221;][vc_column_text]<\/p>\n<hr \/>\n<h3><a id=\"conclusion\" href=\"#conclusion\">Conclusion<\/a><\/h3>\n<p>You\u2019re done! Follow the steps here <a href=\"https:\/\/support.structr.com\/article\/225\">https:\/\/support.structr.com\/article\/225<\/a> to export your work, and create the app you\u2019ve always wanted using Structr. :)[\/vc_column_text][\/vc_column][\/vc_row]<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>[vc_row][vc_column][vc_column_text] Index Overview What you will learn You will need The Features Format Create a New User Check the Schema \u2026<\/p>\n","protected":false},"author":2,"featured_media":1038,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[24,41],"tags":[515,514,420,449,513,284,424,517,502,463,460,331,451,516,472,405,281,512,456,435],"class_list":["post-839","post","type-post","status-publish","format-standard","hentry","category-structr","category-tutorial","tag-accesscontrol","tag-authentication","tag-automation","tag-backend","tag-blogapplication","tag-bootstrap","tag-coding","tag-cypherquery","tag-datamodel","tag-frontend","tag-graphdatabase","tag-neo4j","tag-rest","tag-schema","tag-security","tag-softwaredevelopment","tag-structr","tag-tutorial","tag-ui","tag-webdevelopment"],"acf":[],"_links":{"self":[{"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/posts\/839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/comments?post=839"}],"version-history":[{"count":5,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/posts\/839\/revisions"}],"predecessor-version":[{"id":1558,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/posts\/839\/revisions\/1558"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/media\/1038"}],"wp:attachment":[{"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/media?parent=839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/categories?post=839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/structr.com\/en\/wp-json\/wp\/v2\/tags?post=839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}